]>
Commit | Line | Data |
---|---|---|
dcb445bc MS |
1 | /* |
2 | * "$Id$" | |
3 | * | |
7e86f2f6 | 4 | * Destination localization support for CUPS. |
dcb445bc | 5 | * |
7e86f2f6 | 6 | * Copyright 2012-2014 by Apple Inc. |
dcb445bc | 7 | * |
7e86f2f6 MS |
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/". | |
dcb445bc | 13 | * |
7e86f2f6 | 14 | * This file is subject to the Apple OS-Developed Software exception. |
dcb445bc MS |
15 | */ |
16 | ||
17 | /* | |
18 | * Include necessary headers... | |
19 | */ | |
20 | ||
21 | #include "cups-private.h" | |
22 | ||
23 | ||
a29fd7dd MS |
24 | /* |
25 | * Local functions... | |
26 | */ | |
27 | ||
28 | static void cups_create_localizations(http_t *http, cups_dinfo_t *dinfo); | |
29 | static int cups_read_strings(cups_file_t *fp, char *buffer, size_t bufsize, | |
30 | char **id, char **str); | |
31 | static char *cups_scan_strings(char *buffer); | |
32 | ||
33 | ||
104fd4ae MS |
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 | * | |
e1f19878 | 41 | * @since CUPS 2.0/OS X 10.10@ |
104fd4ae MS |
42 | */ |
43 | ||
44 | const char * /* O - Localized string */ | |
45 | cupsLocalizeDestMedia( | |
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; | |
85cbdbc7 MS |
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 | } | |
104fd4ae MS |
142 | } |
143 | ||
144 | if (mdb) | |
145 | { | |
85cbdbc7 MS |
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 | ||
104fd4ae MS |
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 | ||
dcb445bc MS |
198 | /* |
199 | * 'cupsLocalizeDestOption()' - Get the localized string for a destination | |
200 | * option. | |
201 | * | |
a29fd7dd MS |
202 | * The returned string is stored in the destination information and will become |
203 | * invalid if the destination information is deleted. | |
dcb445bc | 204 | * |
f3c17241 | 205 | * @since CUPS 1.6/OS X 10.8@ |
dcb445bc MS |
206 | */ |
207 | ||
208 | const char * /* O - Localized string */ | |
209 | cupsLocalizeDestOption( | |
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 | { | |
a29fd7dd MS |
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); | |
dcb445bc MS |
234 | } |
235 | ||
236 | ||
237 | /* | |
238 | * 'cupsLocalizeDestValue()' - Get the localized string for a destination | |
239 | * option+value pair. | |
240 | * | |
a29fd7dd MS |
241 | * The returned string is stored in the destination information and will become |
242 | * invalid if the destination information is deleted. | |
dcb445bc | 243 | * |
f3c17241 | 244 | * @since CUPS 1.6/OS X 10.8@ |
dcb445bc MS |
245 | */ |
246 | ||
247 | const char * /* O - Localized string */ | |
248 | cupsLocalizeDestValue( | |
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 | { | |
a29fd7dd MS |
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 | ||
284 | static void | |
285 | cups_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, | |
cb7f98ee | 334 | sizeof(resource)) < HTTP_URI_STATUS_OK) |
a29fd7dd MS |
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) && | |
a469f8a5 | 344 | port == httpAddrPort(http->hostaddr)) |
a29fd7dd MS |
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")) | |
cb7f98ee | 359 | encryption = HTTP_ENCRYPTION_ALWAYS; |
a29fd7dd | 360 | else |
cb7f98ee | 361 | encryption = HTTP_ENCRYPTION_IF_REQUESTED; |
a29fd7dd | 362 | |
cb7f98ee MS |
363 | if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, |
364 | 30000, NULL)) == NULL) | |
a29fd7dd MS |
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 | ||
cb7f98ee | 390 | if (status == HTTP_STATUS_OK) |
a29fd7dd MS |
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 | ||
446 | static int /* O - 1 on success, 0 on failure */ | |
447 | cups_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 | ||
494 | static char * /* O - End of string */ | |
495 | cups_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 | ||
7e86f2f6 | 512 | *bufptr = (char)(((((bufptr[1] - '0') << 3) | (bufptr[2] - '0')) << 3) | (bufptr[3] - '0')); |
a29fd7dd MS |
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); | |
dcb445bc MS |
533 | } |
534 | ||
535 | ||
a29fd7dd | 536 | |
dcb445bc MS |
537 | /* |
538 | * End of "$Id$". | |
539 | */ |