1 /*
2  * Destination localization support for CUPS.
3  *
4  * Copyright 2012-2017 by Apple Inc.
5  *
6  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
7  */
8 
9 /*
10  * Include necessary headers...
11  */
12 
13 #include "cups-private.h"
14 #include "debug-internal.h"
15 
16 
17 /*
18  * Local functions...
19  */
20 
21 static void	cups_create_localizations(http_t *http, cups_dinfo_t *dinfo);
22 
23 
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  *
31  * @since CUPS 2.0/macOS 10.10@
32  */
33 
34 const char *				/* O - Localized string */
cupsLocalizeDestMedia(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,unsigned flags,cups_size_t * size)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 */
48   char			lstr[1024],	/* Localized size name */
49 			temp[256];	/* Temporary string */
50   const char		*lsize,		/* Localized media size */
51 			*lsource,	/* Localized media source */
52 			*ltype;		/* Localized media type */
53 
54 
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 
57  /*
58   * Range check input...
59   */
60 
61   if (!http || !dest || !dinfo || !size)
62   {
63     DEBUG_puts("1cupsLocalizeDestMedia: Returning NULL.");
64     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
65     return (NULL);
66   }
67 
68  /*
69   * Find the matching media database entry...
70   */
71 
72   if (flags & CUPS_MEDIA_FLAGS_READY)
73     db = dinfo->ready_db;
74   else
75     db = dinfo->media_db;
76 
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)
88   {
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     }
94   }
95 
96  /*
97   * See if the localization is cached...
98   */
99 
100   lang = cupsLangDefault();
101 
102   if (!dinfo->localizations)
103     cups_create_localizations(http, dinfo);
104 
105   snprintf(temp, sizeof(temp), "media.%s", size->media);
106   key.msg = temp;
107 
108   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL)
109   {
110     lsize = match->str;
111   }
112   else
113   {
114    /*
115     * Not a media name, try a media-key name...
116     */
117 
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   }
124 
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   }
135 
136   if (!lsize)
137   {
138    /*
139     * Make a dimensional localization...
140     */
141 
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 
148       snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g \"")), size->width / 2540.0, size->length / 2540.0);
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 
162   if (mdb)
163   {
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));
165 
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"));
170   }
171   else
172   {
173     lsource = NULL;
174     ltype   = NULL;
175   }
176 
177   if (!lsource && !ltype)
178   {
179     if (!size->bottom && !size->left && !size->right && !size->top)
180       snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (Borderless)")), lsize);
181     else
182       strlcpy(lstr, lsize, sizeof(lstr));
183   }
184   else if (!lsource)
185   {
186     if (!size->bottom && !size->left && !size->right && !size->top)
187       snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, ltype);
188     else
189       snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (%s)")), lsize, ltype);
190   }
191   else if (!ltype)
192   {
193     if (!size->bottom && !size->left && !size->right && !size->top)
194       snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, lsource);
195     else
196       snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (%s)")), lsize, lsource);
197   }
198   else
199   {
200     if (!size->bottom && !size->left && !size->right && !size->top)
201       snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (Borderless, %s, %s)")), lsize, ltype, lsource);
202     else
203       snprintf(lstr, sizeof(lstr), _cupsLangString(lang, _("%s (%s, %s)")), lsize, ltype, lsource);
204   }
205 
206   if ((match = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
207     return (NULL);
208 
209   match->msg = strdup(size->media);
210   match->str = strdup(lstr);
211 
212   cupsArrayAdd(dinfo->localizations, match);
213 
214   DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
215 
216   return (match->str);
217 }
218 
219 
220 /*
221  * 'cupsLocalizeDestOption()' - Get the localized string for a destination
222  *                              option.
223  *
224  * The returned string is stored in the destination information and will become
225  * invalid if the destination information is deleted.
226  *
227  * @since CUPS 1.6/macOS 10.8@
228  */
229 
230 const char *				/* O - Localized string */
cupsLocalizeDestOption(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option)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 {
237   _cups_message_t	key,		/* Search key */
238 			*match;		/* Matching entry */
239   const char            *localized;     /* Localized string */
240 
241 
242   DEBUG_printf(("cupsLocalizeDestOption(http=%p, dest=%p, dinfo=%p, option=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option));
243 
244   if (!http || !dest || !dinfo)
245     return (option);
246 
247   if (!dinfo->localizations)
248     cups_create_localizations(http, dinfo);
249 
250   key.msg = (char *)option;
251   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL)
252     return (match->str);
253   else if ((localized = _cupsLangString(cupsLangDefault(), option)) != NULL)
254     return (localized);
255   else
256     return (option);
257 }
258 
259 
260 /*
261  * 'cupsLocalizeDestValue()' - Get the localized string for a destination
262  *                             option+value pair.
263  *
264  * The returned string is stored in the destination information and will become
265  * invalid if the destination information is deleted.
266  *
267  * @since CUPS 1.6/macOS 10.8@
268  */
269 
270 const char *				/* O - Localized string */
cupsLocalizeDestValue(http_t * http,cups_dest_t * dest,cups_dinfo_t * dinfo,const char * option,const char * value)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 {
278   _cups_message_t	key,		/* Search key */
279 			*match;		/* Matching entry */
280   char			pair[256];	/* option.value pair */
281   const char            *localized;     /* Localized string */
282 
283 
284   DEBUG_printf(("cupsLocalizeDestValue(http=%p, dest=%p, dinfo=%p, option=\"%s\", value=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option, value));
285 
286   if (!http || !dest || !dinfo)
287     return (value);
288 
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 
305   if (!dinfo->localizations)
306     cups_create_localizations(http, dinfo);
307 
308   snprintf(pair, sizeof(pair), "%s.%s", option, value);
309   key.msg = pair;
310   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL)
311     return (match->str);
312   else if ((localized = _cupsLangString(cupsLangDefault(), pair)) != NULL && strcmp(localized, pair))
313     return (localized);
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
cups_create_localizations(http_t * http,cups_dinfo_t * dinfo)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 
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    /*
352     * Nope, create an empty message catalog...
353     */
354 
355     dinfo->localizations = _cupsMessageNew(NULL);
356     DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) value.");
357     return;
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,
368                       sizeof(resource)) < HTTP_URI_STATUS_OK)
369   {
370     dinfo->localizations = _cupsMessageNew(NULL);
371     DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value \"%s\".", attr->values[0].string.text));
372     return;
373   }
374 
375   httpGetHostname(http, http_hostname, sizeof(http_hostname));
376 
377   if (!_cups_strcasecmp(http_hostname, hostname) &&
378       port == httpAddrPort(http->hostaddr))
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"))
393       encryption = HTTP_ENCRYPTION_ALWAYS;
394     else
395       encryption = HTTP_ENCRYPTION_IF_REQUESTED;
396 
397     if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1,
398                               30000, NULL)) == NULL)
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));
420   cupsFileClose(temp);
421 
422   DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource, httpStatus(status)));
423 
424   if (status == HTTP_STATUS_OK)
425   {
426    /*
427     * Got the file, read it...
428     */
429 
430     dinfo->localizations = _cupsMessageLoad(tempfile, _CUPS_MESSAGE_STRINGS);
431   }
432 
433   DEBUG_printf(("4cups_create_localizations: %d messages loaded.",
434                 cupsArrayCount(dinfo->localizations)));
435 
436  /*
437   * Cleanup...
438   */
439 
440   unlink(tempfile);
441 
442   if (http2 != http)
443     httpClose(http2);
444 }
445 
446