1/* 2 * "$Id: dest-localization.c 12104 2014-08-20 15:23:40Z msweet $" 3 * 4 * Destination localization support for CUPS. 5 * 6 * Copyright 2012-2014 by Apple Inc. 7 * 8 * These coded instructions, statements, and computer programs are the 9 * property of Apple Inc. and are protected by Federal copyright 10 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 11 * which should have been included with this file. If this file is 12 * file is missing or damaged, see the license at "http://www.cups.org/". 13 * 14 * This file is subject to the Apple OS-Developed Software exception. 15 */ 16 17/* 18 * Include necessary headers... 19 */ 20 21#include "cups-private.h" 22 23 24/* 25 * Local functions... 26 */ 27 28static void cups_create_localizations(http_t *http, cups_dinfo_t *dinfo); 29static int cups_read_strings(cups_file_t *fp, char *buffer, size_t bufsize, 30 char **id, char **str); 31static char *cups_scan_strings(char *buffer); 32 33 34/* 35 * 'cupsLocalizeDestMedia()' - Get the localized string for a destination media 36 * size. 37 * 38 * The returned string is stored in the destination information and will become 39 * invalid if the destination information is deleted. 40 * 41 * @since CUPS 2.0/OS X 10.10@ 42 */ 43 44const char * /* O - Localized string */ 45cupsLocalizeDestMedia( 46 http_t *http, /* I - Connection to destination */ 47 cups_dest_t *dest, /* I - Destination */ 48 cups_dinfo_t *dinfo, /* I - Destination information */ 49 unsigned flags, /* I - Media flags */ 50 cups_size_t *size) /* I - Media size */ 51{ 52 cups_lang_t *lang; /* Standard localizations */ 53 _cups_message_t key, /* Search key */ 54 *match; /* Matching entry */ 55 pwg_media_t *pwg; /* PWG media information */ 56 cups_array_t *db; /* Media database */ 57 _cups_media_db_t *mdb; /* Media database entry */ 58 char name[1024], /* Size name */ 59 temp[256]; /* Temporary string */ 60 const char *lsize, /* Localized media size */ 61 *lsource, /* Localized media source */ 62 *ltype; /* Localized media type */ 63 64 65 /* 66 * Range check input... 67 */ 68 69 if (!http || !dest || !dinfo || !size) 70 { 71 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 72 return (NULL); 73 } 74 75 /* 76 * See if the localization is cached... 77 */ 78 79 if (!dinfo->localizations) 80 cups_create_localizations(http, dinfo); 81 82 key.id = size->media; 83 if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL) 84 return (match->str); 85 86 /* 87 * If not, get the localized size, source, and type strings... 88 */ 89 90 lang = cupsLangDefault(); 91 pwg = pwgMediaForSize(size->width, size->length); 92 93 if (pwg->ppd) 94 lsize = _cupsLangString(lang, pwg->ppd); 95 else 96 lsize = NULL; 97 98 if (!lsize) 99 { 100 if ((size->width % 635) == 0 && (size->length % 635) == 0) 101 { 102 /* 103 * Use inches since the size is a multiple of 1/4 inch. 104 */ 105 106 snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g")), size->width / 2540.0, size->length / 2540.0); 107 } 108 else 109 { 110 /* 111 * Use millimeters since the size is not a multiple of 1/4 inch. 112 */ 113 114 snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%d x %d mm")), (size->width + 50) / 100, (size->length + 50) / 100); 115 } 116 117 lsize = temp; 118 } 119 120 if (flags & CUPS_MEDIA_FLAGS_READY) 121 db = dinfo->ready_db; 122 else 123 db = dinfo->media_db; 124 125 DEBUG_printf(("1cupsLocalizeDestMedia: size->media=\"%s\"", size->media)); 126 127 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db)) 128 { 129 if (mdb->key && !strcmp(mdb->key, size->media)) 130 break; 131 else if (mdb->size_name && !strcmp(mdb->size_name, size->media)) 132 break; 133 } 134 135 if (!mdb) 136 { 137 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db)) 138 { 139 if (mdb->width == size->width && mdb->length == size->length && mdb->bottom == size->bottom && mdb->left == size->left && mdb->right == size->right && mdb->top == size->top) 140 break; 141 } 142 } 143 144 if (mdb) 145 { 146 DEBUG_printf(("1cupsLocalizeDestMedia: MATCH mdb%p [key=\"%s\" size_name=\"%s\" source=\"%s\" type=\"%s\" width=%d length=%d B%d L%d R%d T%d]", mdb, mdb->key, mdb->size_name, mdb->source, mdb->type, mdb->width, mdb->length, mdb->bottom, mdb->left, mdb->right, mdb->top)); 147 148 lsource = cupsLocalizeDestValue(http, dest, dinfo, "media-source", mdb->source); 149 ltype = cupsLocalizeDestValue(http, dest, dinfo, "media-type", mdb->type); 150 } 151 else 152 { 153 lsource = NULL; 154 ltype = NULL; 155 } 156 157 if (!lsource && !ltype) 158 { 159 if (size->bottom || size->left || size->right || size->top) 160 snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless)")), lsize); 161 else 162 strlcpy(name, lsize, sizeof(name)); 163 } 164 else if (!lsource) 165 { 166 if (size->bottom || size->left || size->right || size->top) 167 snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, ltype); 168 else 169 snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s)")), lsize, ltype); 170 } 171 else if (!ltype) 172 { 173 if (size->bottom || size->left || size->right || size->top) 174 snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, lsource); 175 else 176 snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s)")), lsize, lsource); 177 } 178 else 179 { 180 if (size->bottom || size->left || size->right || size->top) 181 snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s, %s)")), lsize, ltype, lsource); 182 else 183 snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s, %s)")), lsize, ltype, lsource); 184 } 185 186 if ((match = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL) 187 return (NULL); 188 189 match->id = strdup(size->media); 190 match->str = strdup(name); 191 192 cupsArrayAdd(dinfo->localizations, match); 193 194 return (match->str); 195} 196 197 198/* 199 * 'cupsLocalizeDestOption()' - Get the localized string for a destination 200 * option. 201 * 202 * The returned string is stored in the destination information and will become 203 * invalid if the destination information is deleted. 204 * 205 * @since CUPS 1.6/OS X 10.8@ 206 */ 207 208const char * /* O - Localized string */ 209cupsLocalizeDestOption( 210 http_t *http, /* I - Connection to destination */ 211 cups_dest_t *dest, /* I - Destination */ 212 cups_dinfo_t *dinfo, /* I - Destination information */ 213 const char *option) /* I - Option to localize */ 214{ 215 _cups_message_t key, /* Search key */ 216 *match; /* Matching entry */ 217 218 219 if (!http || !dest || !dinfo) 220 return (option); 221 222 if (!dinfo->localizations) 223 cups_create_localizations(http, dinfo); 224 225 if (cupsArrayCount(dinfo->localizations) == 0) 226 return (option); 227 228 key.id = (char *)option; 229 if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, 230 &key)) != NULL) 231 return (match->str); 232 else 233 return (option); 234} 235 236 237/* 238 * 'cupsLocalizeDestValue()' - Get the localized string for a destination 239 * option+value pair. 240 * 241 * The returned string is stored in the destination information and will become 242 * invalid if the destination information is deleted. 243 * 244 * @since CUPS 1.6/OS X 10.8@ 245 */ 246 247const char * /* O - Localized string */ 248cupsLocalizeDestValue( 249 http_t *http, /* I - Connection to destination */ 250 cups_dest_t *dest, /* I - Destination */ 251 cups_dinfo_t *dinfo, /* I - Destination information */ 252 const char *option, /* I - Option to localize */ 253 const char *value) /* I - Value to localize */ 254{ 255 _cups_message_t key, /* Search key */ 256 *match; /* Matching entry */ 257 char pair[256]; /* option.value pair */ 258 259 260 if (!http || !dest || !dinfo) 261 return (value); 262 263 if (!dinfo->localizations) 264 cups_create_localizations(http, dinfo); 265 266 if (cupsArrayCount(dinfo->localizations) == 0) 267 return (value); 268 269 snprintf(pair, sizeof(pair), "%s.%s", option, value); 270 key.id = pair; 271 if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, 272 &key)) != NULL) 273 return (match->str); 274 else 275 return (value); 276} 277 278 279/* 280 * 'cups_create_localizations()' - Create the localizations array for a 281 * destination. 282 */ 283 284static void 285cups_create_localizations( 286 http_t *http, /* I - Connection to destination */ 287 cups_dinfo_t *dinfo) /* I - Destination informations */ 288{ 289 http_t *http2; /* Connection for strings file */ 290 http_status_t status; /* Request status */ 291 ipp_attribute_t *attr; /* "printer-strings-uri" attribute */ 292 char scheme[32], /* URI scheme */ 293 userpass[256], /* Username/password info */ 294 hostname[256], /* Hostname */ 295 resource[1024], /* Resource */ 296 http_hostname[256], 297 /* Hostname of connection */ 298 tempfile[1024]; /* Temporary filename */ 299 int port; /* Port number */ 300 http_encryption_t encryption; /* Encryption to use */ 301 cups_file_t *temp; /* Temporary file */ 302 303 304 /* 305 * Create an empty message catalog... 306 */ 307 308 dinfo->localizations = _cupsMessageNew(NULL); 309 310 /* 311 * See if there are any localizations... 312 */ 313 314 if ((attr = ippFindAttribute(dinfo->attrs, "printer-strings-uri", 315 IPP_TAG_URI)) == NULL) 316 { 317 /* 318 * Nope... 319 */ 320 321 DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) " 322 "value."); 323 return; /* Nope */ 324 } 325 326 /* 327 * Pull apart the URI and determine whether we need to try a different 328 * server... 329 */ 330 331 if (httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, 332 scheme, sizeof(scheme), userpass, sizeof(userpass), 333 hostname, sizeof(hostname), &port, resource, 334 sizeof(resource)) < HTTP_URI_STATUS_OK) 335 { 336 DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value " 337 "\"%s\".", attr->values[0].string.text)); 338 return; 339 } 340 341 httpGetHostname(http, http_hostname, sizeof(http_hostname)); 342 343 if (!_cups_strcasecmp(http_hostname, hostname) && 344 port == httpAddrPort(http->hostaddr)) 345 { 346 /* 347 * Use the same connection... 348 */ 349 350 http2 = http; 351 } 352 else 353 { 354 /* 355 * Connect to the alternate host... 356 */ 357 358 if (!strcmp(scheme, "https")) 359 encryption = HTTP_ENCRYPTION_ALWAYS; 360 else 361 encryption = HTTP_ENCRYPTION_IF_REQUESTED; 362 363 if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 364 30000, NULL)) == NULL) 365 { 366 DEBUG_printf(("4cups_create_localizations: Unable to connect to " 367 "%s:%d: %s", hostname, port, cupsLastErrorString())); 368 return; 369 } 370 } 371 372 /* 373 * Get a temporary file... 374 */ 375 376 if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL) 377 { 378 DEBUG_printf(("4cups_create_localizations: Unable to create temporary " 379 "file: %s", cupsLastErrorString())); 380 if (http2 != http) 381 httpClose(http2); 382 return; 383 } 384 385 status = cupsGetFd(http2, resource, cupsFileNumber(temp)); 386 387 DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource, 388 httpStatus(status))); 389 390 if (status == HTTP_STATUS_OK) 391 { 392 /* 393 * Got the file, read it... 394 */ 395 396 char buffer[8192], /* Message buffer */ 397 *id, /* ID string */ 398 *str; /* Translated message */ 399 _cups_message_t *m; /* Current message */ 400 401 lseek(cupsFileNumber(temp), 0, SEEK_SET); 402 403 while (cups_read_strings(temp, buffer, sizeof(buffer), &id, &str)) 404 { 405 if ((m = malloc(sizeof(_cups_message_t))) == NULL) 406 break; 407 408 m->id = strdup(id); 409 m->str = strdup(str); 410 411 if (m->id && m->str) 412 cupsArrayAdd(dinfo->localizations, m); 413 else 414 { 415 if (m->id) 416 free(m->id); 417 418 if (m->str) 419 free(m->str); 420 421 free(m); 422 break; 423 } 424 } 425 } 426 427 DEBUG_printf(("4cups_create_localizations: %d messages loaded.", 428 cupsArrayCount(dinfo->localizations))); 429 430 /* 431 * Cleanup... 432 */ 433 434 unlink(tempfile); 435 cupsFileClose(temp); 436 437 if (http2 != http) 438 httpClose(http2); 439} 440 441 442/* 443 * 'cups_read_strings()' - Read a pair of strings from a .strings file. 444 */ 445 446static int /* O - 1 on success, 0 on failure */ 447cups_read_strings(cups_file_t *strings, /* I - .strings file */ 448 char *buffer, /* I - Line buffer */ 449 size_t bufsize, /* I - Size of line buffer */ 450 char **id, /* O - Pointer to ID string */ 451 char **str) /* O - Pointer to translation string */ 452{ 453 char *bufptr; /* Pointer into buffer */ 454 455 456 while (cupsFileGets(strings, buffer, bufsize)) 457 { 458 if (buffer[0] != '\"') 459 continue; 460 461 *id = buffer + 1; 462 bufptr = cups_scan_strings(buffer); 463 464 if (*bufptr != '\"') 465 continue; 466 467 *bufptr++ = '\0'; 468 469 while (*bufptr && *bufptr != '\"') 470 bufptr ++; 471 472 if (!*bufptr) 473 continue; 474 475 *str = bufptr + 1; 476 bufptr = cups_scan_strings(bufptr); 477 478 if (*bufptr != '\"') 479 continue; 480 481 *bufptr = '\0'; 482 483 return (1); 484 } 485 486 return (0); 487} 488 489 490/* 491 * 'cups_scan_strings()' - Scan a quoted string. 492 */ 493 494static char * /* O - End of string */ 495cups_scan_strings(char *buffer) /* I - Start of string */ 496{ 497 char *bufptr; /* Pointer into string */ 498 499 500 for (bufptr = buffer + 1; *bufptr && *bufptr != '\"'; bufptr ++) 501 { 502 if (*bufptr == '\\') 503 { 504 if (bufptr[1] >= '0' && bufptr[1] <= '3' && 505 bufptr[2] >= '0' && bufptr[2] <= '7' && 506 bufptr[3] >= '0' && bufptr[3] <= '7') 507 { 508 /* 509 * Decode \nnn octal escape... 510 */ 511 512 *bufptr = (char)(((((bufptr[1] - '0') << 3) | (bufptr[2] - '0')) << 3) | (bufptr[3] - '0')); 513 _cups_strcpy(bufptr + 1, bufptr + 4); 514 } 515 else 516 { 517 /* 518 * Decode \C escape... 519 */ 520 521 _cups_strcpy(bufptr, bufptr + 1); 522 if (*bufptr == 'n') 523 *bufptr = '\n'; 524 else if (*bufptr == 'r') 525 *bufptr = '\r'; 526 else if (*bufptr == 't') 527 *bufptr = '\t'; 528 } 529 } 530 } 531 532 return (bufptr); 533} 534 535 536 537/* 538 * End of "$Id: dest-localization.c 12104 2014-08-20 15:23:40Z msweet $". 539 */ 540