]>
Commit | Line | Data |
---|---|---|
dcb445bc | 1 | /* |
7e86f2f6 | 2 | * Destination localization support for CUPS. |
dcb445bc | 3 | * |
89550f3f | 4 | * Copyright 2012-2017 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" | |
fb863569 | 14 | #include "debug-internal.h" |
dcb445bc MS |
15 | |
16 | ||
a29fd7dd MS |
17 | /* |
18 | * Local functions... | |
19 | */ | |
20 | ||
21 | static void cups_create_localizations(http_t *http, cups_dinfo_t *dinfo); | |
a29fd7dd MS |
22 | |
23 | ||
104fd4ae MS |
24 | /* |
25 | * 'cupsLocalizeDestMedia()' - Get the localized string for a destination media | |
26 | * size. | |
27 | * | |
28 | * The returned string is stored in the destination information and will become | |
29 | * invalid if the destination information is deleted. | |
30 | * | |
8072030b | 31 | * @since CUPS 2.0/macOS 10.10@ |
104fd4ae MS |
32 | */ |
33 | ||
34 | const char * /* O - Localized string */ | |
35 | cupsLocalizeDestMedia( | |
36 | http_t *http, /* I - Connection to destination */ | |
37 | cups_dest_t *dest, /* I - Destination */ | |
38 | cups_dinfo_t *dinfo, /* I - Destination information */ | |
39 | unsigned flags, /* I - Media flags */ | |
40 | cups_size_t *size) /* I - Media size */ | |
41 | { | |
42 | cups_lang_t *lang; /* Standard localizations */ | |
43 | _cups_message_t key, /* Search key */ | |
44 | *match; /* Matching entry */ | |
45 | pwg_media_t *pwg; /* PWG media information */ | |
46 | cups_array_t *db; /* Media database */ | |
47 | _cups_media_db_t *mdb; /* Media database entry */ | |
71dc43af | 48 | char lstr[1024], /* Localized size name */ |
104fd4ae MS |
49 | temp[256]; /* Temporary string */ |
50 | const char *lsize, /* Localized media size */ | |
51 | *lsource, /* Localized media source */ | |
52 | *ltype; /* Localized media type */ | |
53 | ||
54 | ||
a9357c9d MS |
55 | 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)")); |
56 | ||
104fd4ae MS |
57 | /* |
58 | * Range check input... | |
59 | */ | |
60 | ||
61 | if (!http || !dest || !dinfo || !size) | |
62 | { | |
a9357c9d | 63 | DEBUG_puts("1cupsLocalizeDestMedia: Returning NULL."); |
104fd4ae MS |
64 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
65 | return (NULL); | |
66 | } | |
67 | ||
68 | /* | |
71dc43af | 69 | * Find the matching media database entry... |
104fd4ae MS |
70 | */ |
71 | ||
71dc43af MS |
72 | if (flags & CUPS_MEDIA_FLAGS_READY) |
73 | db = dinfo->ready_db; | |
74 | else | |
75 | db = dinfo->media_db; | |
104fd4ae | 76 | |
71dc43af MS |
77 | DEBUG_printf(("1cupsLocalizeDestMedia: size->media=\"%s\"", size->media)); |
78 | ||
79 | for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db)) | |
80 | { | |
81 | if (mdb->key && !strcmp(mdb->key, size->media)) | |
82 | break; | |
83 | else if (mdb->size_name && !strcmp(mdb->size_name, size->media)) | |
84 | break; | |
85 | } | |
86 | ||
87 | if (!mdb) | |
a9357c9d | 88 | { |
71dc43af MS |
89 | for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db)) |
90 | { | |
91 | 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) | |
92 | break; | |
93 | } | |
a9357c9d | 94 | } |
104fd4ae MS |
95 | |
96 | /* | |
71dc43af | 97 | * See if the localization is cached... |
104fd4ae MS |
98 | */ |
99 | ||
100 | lang = cupsLangDefault(); | |
a9357c9d | 101 | |
71dc43af MS |
102 | if (!dinfo->localizations) |
103 | cups_create_localizations(http, dinfo); | |
104 | ||
a9357c9d | 105 | snprintf(temp, sizeof(temp), "media.%s", size->media); |
71dc43af MS |
106 | key.msg = temp; |
107 | ||
108 | if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL) | |
a9357c9d | 109 | { |
71dc43af | 110 | lsize = match->str; |
a9357c9d | 111 | } |
71dc43af MS |
112 | else |
113 | { | |
114 | /* | |
115 | * Not a media name, try a media-key name... | |
116 | */ | |
a9357c9d | 117 | |
71dc43af MS |
118 | snprintf(temp, sizeof(temp), "media-key.%s", size->media); |
119 | if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL) | |
120 | lsize = match->str; | |
121 | else | |
122 | lsize = NULL; | |
123 | } | |
104fd4ae | 124 | |
71dc43af MS |
125 | if (!lsize && (pwg = pwgMediaForSize(size->width, size->length)) != NULL && pwg->ppd) |
126 | { | |
127 | /* | |
128 | * Get a standard localization... | |
129 | */ | |
130 | ||
131 | snprintf(temp, sizeof(temp), "media.%s", pwg->pwg); | |
132 | if ((lsize = _cupsLangString(lang, temp)) == temp) | |
133 | lsize = NULL; | |
134 | } | |
104fd4ae MS |
135 | |
136 | if (!lsize) | |
137 | { | |
71dc43af MS |
138 | /* |
139 | * Make a dimensional localization... | |
140 | */ | |
141 | ||
104fd4ae MS |
142 | if ((size->width % 635) == 0 && (size->length % 635) == 0) |
143 | { | |
144 | /* | |
145 | * Use inches since the size is a multiple of 1/4 inch. | |
146 | */ | |
147 | ||
a9357c9d | 148 | snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g \"")), size->width / 2540.0, size->length / 2540.0); |
104fd4ae MS |
149 | } |
150 | else | |
151 | { | |
152 | /* | |
153 | * Use millimeters since the size is not a multiple of 1/4 inch. | |
154 | */ | |
155 | ||
156 | snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%d x %d mm")), (size->width + 50) / 100, (size->length + 50) / 100); | |
157 | } | |
158 | ||
159 | lsize = temp; | |
160 | } | |
161 | ||
104fd4ae MS |
162 | if (mdb) |
163 | { | |
807315e6 | 164 | 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 | 165 | |
84de5e92 MS |
166 | if ((lsource = cupsLocalizeDestValue(http, dest, dinfo, "media-source", mdb->source)) == mdb->source && mdb->source) |
167 | lsource = _cupsLangString(lang, _("Other Tray")); | |
168 | if ((ltype = cupsLocalizeDestValue(http, dest, dinfo, "media-type", mdb->type)) == mdb->type && mdb->type) | |
169 | ltype = _cupsLangString(lang, _("Other Media")); | |
104fd4ae MS |
170 | } |
171 | else | |
172 | { | |
173 | lsource = NULL; | |
174 | ltype = NULL; | |
175 | } | |
176 | ||
177 | if (!lsource && !ltype) | |
178 | { | |
982be458 | 179 | if (!size->bottom && !size->left && !size->right && !size->top) |
71dc43af | 180 | snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (Borderless)")), lsize); |
104fd4ae | 181 | else |
71dc43af | 182 | strlcpy(lstr, lsize, sizeof(lstr)); |
104fd4ae MS |
183 | } |
184 | else if (!lsource) | |
185 | { | |
982be458 | 186 | if (!size->bottom && !size->left && !size->right && !size->top) |
71dc43af | 187 | snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, ltype); |
104fd4ae | 188 | else |
71dc43af | 189 | snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (%s)")), lsize, ltype); |
104fd4ae MS |
190 | } |
191 | else if (!ltype) | |
192 | { | |
982be458 | 193 | if (!size->bottom && !size->left && !size->right && !size->top) |
71dc43af | 194 | snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, lsource); |
104fd4ae | 195 | else |
71dc43af | 196 | snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (%s)")), lsize, lsource); |
104fd4ae MS |
197 | } |
198 | else | |
199 | { | |
982be458 | 200 | if (!size->bottom && !size->left && !size->right && !size->top) |
71dc43af | 201 | snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (Borderless, %s, %s)")), lsize, ltype, lsource); |
104fd4ae | 202 | else |
71dc43af | 203 | snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (%s, %s)")), lsize, ltype, lsource); |
104fd4ae MS |
204 | } |
205 | ||
206 | if ((match = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL) | |
207 | return (NULL); | |
208 | ||
89550f3f | 209 | match->msg = strdup(size->media); |
71dc43af | 210 | match->str = strdup(lstr); |
104fd4ae MS |
211 | |
212 | cupsArrayAdd(dinfo->localizations, match); | |
213 | ||
a9357c9d MS |
214 | DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str)); |
215 | ||
104fd4ae MS |
216 | return (match->str); |
217 | } | |
218 | ||
219 | ||
dcb445bc MS |
220 | /* |
221 | * 'cupsLocalizeDestOption()' - Get the localized string for a destination | |
222 | * option. | |
223 | * | |
a29fd7dd MS |
224 | * The returned string is stored in the destination information and will become |
225 | * invalid if the destination information is deleted. | |
dcb445bc | 226 | * |
8072030b | 227 | * @since CUPS 1.6/macOS 10.8@ |
dcb445bc MS |
228 | */ |
229 | ||
230 | const char * /* O - Localized string */ | |
231 | cupsLocalizeDestOption( | |
232 | http_t *http, /* I - Connection to destination */ | |
233 | cups_dest_t *dest, /* I - Destination */ | |
234 | cups_dinfo_t *dinfo, /* I - Destination information */ | |
235 | const char *option) /* I - Option to localize */ | |
236 | { | |
a29fd7dd MS |
237 | _cups_message_t key, /* Search key */ |
238 | *match; /* Matching entry */ | |
a9357c9d MS |
239 | const char *localized; /* Localized string */ |
240 | ||
a29fd7dd | 241 | |
a9357c9d | 242 | DEBUG_printf(("cupsLocalizeDestOption(http=%p, dest=%p, dinfo=%p, option=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option)); |
a29fd7dd MS |
243 | |
244 | if (!http || !dest || !dinfo) | |
245 | return (option); | |
246 | ||
247 | if (!dinfo->localizations) | |
248 | cups_create_localizations(http, dinfo); | |
249 | ||
89550f3f MS |
250 | key.msg = (char *)option; |
251 | if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL) | |
a29fd7dd | 252 | return (match->str); |
a9357c9d MS |
253 | else if ((localized = _cupsLangString(cupsLangDefault(), option)) != NULL) |
254 | return (localized); | |
a29fd7dd MS |
255 | else |
256 | return (option); | |
dcb445bc MS |
257 | } |
258 | ||
259 | ||
260 | /* | |
261 | * 'cupsLocalizeDestValue()' - Get the localized string for a destination | |
262 | * option+value pair. | |
263 | * | |
a29fd7dd MS |
264 | * The returned string is stored in the destination information and will become |
265 | * invalid if the destination information is deleted. | |
dcb445bc | 266 | * |
8072030b | 267 | * @since CUPS 1.6/macOS 10.8@ |
dcb445bc MS |
268 | */ |
269 | ||
270 | const char * /* O - Localized string */ | |
271 | cupsLocalizeDestValue( | |
272 | http_t *http, /* I - Connection to destination */ | |
273 | cups_dest_t *dest, /* I - Destination */ | |
274 | cups_dinfo_t *dinfo, /* I - Destination information */ | |
275 | const char *option, /* I - Option to localize */ | |
276 | const char *value) /* I - Value to localize */ | |
277 | { | |
a29fd7dd MS |
278 | _cups_message_t key, /* Search key */ |
279 | *match; /* Matching entry */ | |
280 | char pair[256]; /* option.value pair */ | |
a9357c9d | 281 | const char *localized; /* Localized string */ |
a29fd7dd MS |
282 | |
283 | ||
a9357c9d MS |
284 | DEBUG_printf(("cupsLocalizeDestValue(http=%p, dest=%p, dinfo=%p, option=\"%s\", value=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option, value)); |
285 | ||
a29fd7dd MS |
286 | if (!http || !dest || !dinfo) |
287 | return (value); | |
288 | ||
a9357c9d MS |
289 | if (!strcmp(option, "media")) |
290 | { | |
291 | pwg_media_t *media = pwgMediaForPWG(value); | |
292 | cups_size_t size; | |
293 | ||
294 | strlcpy(size.media, value, sizeof(size.media)); | |
295 | size.width = media ? media->width : 0; | |
296 | size.length = media ? media->length : 0; | |
297 | size.left = 0; | |
298 | size.right = 0; | |
299 | size.bottom = 0; | |
300 | size.top = 0; | |
301 | ||
302 | return (cupsLocalizeDestMedia(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size)); | |
303 | } | |
304 | ||
a29fd7dd MS |
305 | if (!dinfo->localizations) |
306 | cups_create_localizations(http, dinfo); | |
307 | ||
a29fd7dd | 308 | snprintf(pair, sizeof(pair), "%s.%s", option, value); |
89550f3f MS |
309 | key.msg = pair; |
310 | if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL) | |
a29fd7dd | 311 | return (match->str); |
a9357c9d MS |
312 | else if ((localized = _cupsLangString(cupsLangDefault(), pair)) != NULL && strcmp(localized, pair)) |
313 | return (localized); | |
a29fd7dd MS |
314 | else |
315 | return (value); | |
316 | } | |
317 | ||
318 | ||
319 | /* | |
320 | * 'cups_create_localizations()' - Create the localizations array for a | |
321 | * destination. | |
322 | */ | |
323 | ||
324 | static void | |
325 | cups_create_localizations( | |
326 | http_t *http, /* I - Connection to destination */ | |
327 | cups_dinfo_t *dinfo) /* I - Destination informations */ | |
328 | { | |
329 | http_t *http2; /* Connection for strings file */ | |
330 | http_status_t status; /* Request status */ | |
331 | ipp_attribute_t *attr; /* "printer-strings-uri" attribute */ | |
332 | char scheme[32], /* URI scheme */ | |
333 | userpass[256], /* Username/password info */ | |
334 | hostname[256], /* Hostname */ | |
335 | resource[1024], /* Resource */ | |
336 | http_hostname[256], | |
337 | /* Hostname of connection */ | |
338 | tempfile[1024]; /* Temporary filename */ | |
339 | int port; /* Port number */ | |
340 | http_encryption_t encryption; /* Encryption to use */ | |
341 | cups_file_t *temp; /* Temporary file */ | |
342 | ||
343 | ||
a29fd7dd MS |
344 | /* |
345 | * See if there are any localizations... | |
346 | */ | |
347 | ||
348 | if ((attr = ippFindAttribute(dinfo->attrs, "printer-strings-uri", | |
349 | IPP_TAG_URI)) == NULL) | |
350 | { | |
351 | /* | |
89550f3f | 352 | * Nope, create an empty message catalog... |
a29fd7dd MS |
353 | */ |
354 | ||
89550f3f MS |
355 | dinfo->localizations = _cupsMessageNew(NULL); |
356 | DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) value."); | |
357 | return; | |
a29fd7dd MS |
358 | } |
359 | ||
360 | /* | |
361 | * Pull apart the URI and determine whether we need to try a different | |
362 | * server... | |
363 | */ | |
364 | ||
365 | if (httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, | |
366 | scheme, sizeof(scheme), userpass, sizeof(userpass), | |
367 | hostname, sizeof(hostname), &port, resource, | |
cb7f98ee | 368 | sizeof(resource)) < HTTP_URI_STATUS_OK) |
a29fd7dd | 369 | { |
89550f3f MS |
370 | dinfo->localizations = _cupsMessageNew(NULL); |
371 | DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value \"%s\".", attr->values[0].string.text)); | |
a29fd7dd MS |
372 | return; |
373 | } | |
374 | ||
375 | httpGetHostname(http, http_hostname, sizeof(http_hostname)); | |
376 | ||
377 | if (!_cups_strcasecmp(http_hostname, hostname) && | |
a469f8a5 | 378 | port == httpAddrPort(http->hostaddr)) |
a29fd7dd MS |
379 | { |
380 | /* | |
381 | * Use the same connection... | |
382 | */ | |
383 | ||
384 | http2 = http; | |
385 | } | |
386 | else | |
387 | { | |
388 | /* | |
389 | * Connect to the alternate host... | |
390 | */ | |
391 | ||
392 | if (!strcmp(scheme, "https")) | |
cb7f98ee | 393 | encryption = HTTP_ENCRYPTION_ALWAYS; |
a29fd7dd | 394 | else |
cb7f98ee | 395 | encryption = HTTP_ENCRYPTION_IF_REQUESTED; |
a29fd7dd | 396 | |
cb7f98ee MS |
397 | if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, |
398 | 30000, NULL)) == NULL) | |
a29fd7dd MS |
399 | { |
400 | DEBUG_printf(("4cups_create_localizations: Unable to connect to " | |
401 | "%s:%d: %s", hostname, port, cupsLastErrorString())); | |
402 | return; | |
403 | } | |
404 | } | |
405 | ||
406 | /* | |
407 | * Get a temporary file... | |
408 | */ | |
409 | ||
410 | if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL) | |
411 | { | |
412 | DEBUG_printf(("4cups_create_localizations: Unable to create temporary " | |
413 | "file: %s", cupsLastErrorString())); | |
414 | if (http2 != http) | |
415 | httpClose(http2); | |
416 | return; | |
417 | } | |
418 | ||
419 | status = cupsGetFd(http2, resource, cupsFileNumber(temp)); | |
89550f3f | 420 | cupsFileClose(temp); |
a29fd7dd | 421 | |
89550f3f | 422 | DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource, httpStatus(status))); |
a29fd7dd | 423 | |
cb7f98ee | 424 | if (status == HTTP_STATUS_OK) |
a29fd7dd MS |
425 | { |
426 | /* | |
427 | * Got the file, read it... | |
428 | */ | |
429 | ||
89550f3f | 430 | dinfo->localizations = _cupsMessageLoad(tempfile, _CUPS_MESSAGE_STRINGS); |
a29fd7dd MS |
431 | } |
432 | ||
433 | DEBUG_printf(("4cups_create_localizations: %d messages loaded.", | |
434 | cupsArrayCount(dinfo->localizations))); | |
435 | ||
436 | /* | |
437 | * Cleanup... | |
438 | */ | |
439 | ||
440 | unlink(tempfile); | |
a29fd7dd MS |
441 | |
442 | if (http2 != http) | |
443 | httpClose(http2); | |
444 | } | |
445 |