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