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