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