]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest-localization.c
4ed2675b41b47c9a35122c7d5b36b55e0e33c58c
[thirdparty/cups.git] / cups / dest-localization.c
1 /*
2 * "$Id$"
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
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
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@
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;
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
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 {
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
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 {
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,
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
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
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$".
539 */