Home | History | Annotate | Download | only in cups
      1 /*
      2  * Destination localization support for CUPS.
      3  *
      4  * Copyright 2012-2014 by Apple Inc.
      5  *
      6  * These coded instructions, statements, and computer programs are the
      7  * property of Apple Inc. and are protected by Federal copyright
      8  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
      9  * which should have been included with this file.  If this file is
     10  * missing or damaged, see the license at "http://www.cups.org/".
     11  *
     12  * This file is subject to the Apple OS-Developed Software exception.
     13  */
     14 
     15 /*
     16  * Include necessary headers...
     17  */
     18 
     19 #include "cups-private.h"
     20 
     21 
     22 /*
     23  * Local functions...
     24  */
     25 
     26 static void	cups_create_localizations(http_t *http, cups_dinfo_t *dinfo);
     27 static int	cups_read_strings(cups_file_t *fp, char *buffer, size_t bufsize,
     28 		                  char **id, char **str);
     29 static char	*cups_scan_strings(char *buffer);
     30 
     31 
     32 /*
     33  * 'cupsLocalizeDestMedia()' - Get the localized string for a destination media
     34  *                             size.
     35  *
     36  * The returned string is stored in the destination information and will become
     37  * invalid if the destination information is deleted.
     38  *
     39  * @since CUPS 2.0/macOS 10.10@
     40  */
     41 
     42 const char *				/* O - Localized string */
     43 cupsLocalizeDestMedia(
     44     http_t       *http,			/* I - Connection to destination */
     45     cups_dest_t  *dest,			/* I - Destination */
     46     cups_dinfo_t *dinfo,		/* I - Destination information */
     47     unsigned     flags,			/* I - Media flags */
     48     cups_size_t  *size)			/* I - Media size */
     49 {
     50   cups_lang_t		*lang;		/* Standard localizations */
     51   _cups_message_t	key,		/* Search key */
     52 			*match;		/* Matching entry */
     53   pwg_media_t		*pwg;		/* PWG media information */
     54   cups_array_t		*db;		/* Media database */
     55   _cups_media_db_t	*mdb;		/* Media database entry */
     56   char			name[1024],	/* Size name */
     57 			temp[256];	/* Temporary string */
     58   const char		*lsize,		/* Localized media size */
     59 			*lsource,	/* Localized media source */
     60 			*ltype;		/* Localized media type */
     61 
     62 
     63   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)"));
     64 
     65  /*
     66   * Range check input...
     67   */
     68 
     69   if (!http || !dest || !dinfo || !size)
     70   {
     71     DEBUG_puts("1cupsLocalizeDestMedia: Returning NULL.");
     72     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
     73     return (NULL);
     74   }
     75 
     76  /*
     77   * See if the localization is cached...
     78   */
     79 
     80   if (!dinfo->localizations)
     81     cups_create_localizations(http, dinfo);
     82 
     83   key.id = size->media;
     84   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL)
     85   {
     86     DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
     87     return (match->str);
     88   }
     89 
     90  /*
     91   * If not, get the localized size, source, and type strings...
     92   */
     93 
     94   lang = cupsLangDefault();
     95 
     96   snprintf(temp, sizeof(temp), "media.%s", size->media);
     97   if ((lsize = _cupsLangString(lang, temp)) != NULL && strcmp(lsize, temp))
     98   {
     99     DEBUG_printf(("1cupsLocalizeDestMedia: Returning standard localization \"%s\".", lsize));
    100     return (lsize);
    101   }
    102 
    103   pwg  = pwgMediaForSize(size->width, size->length);
    104 
    105   if (pwg->ppd)
    106     lsize = _cupsLangString(lang, pwg->ppd);
    107   else
    108     lsize = NULL;
    109 
    110   if (!lsize)
    111   {
    112     if ((size->width % 635) == 0 && (size->length % 635) == 0)
    113     {
    114      /*
    115       * Use inches since the size is a multiple of 1/4 inch.
    116       */
    117 
    118       snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g \"")), size->width / 2540.0, size->length / 2540.0);
    119     }
    120     else
    121     {
    122      /*
    123       * Use millimeters since the size is not a multiple of 1/4 inch.
    124       */
    125 
    126       snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%d x %d mm")), (size->width + 50) / 100, (size->length + 50) / 100);
    127     }
    128 
    129     lsize = temp;
    130   }
    131 
    132   if (flags & CUPS_MEDIA_FLAGS_READY)
    133     db = dinfo->ready_db;
    134   else
    135     db = dinfo->media_db;
    136 
    137   DEBUG_printf(("1cupsLocalizeDestMedia: size->media=\"%s\"", size->media));
    138 
    139   for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db))
    140   {
    141     if (mdb->key && !strcmp(mdb->key, size->media))
    142       break;
    143     else if (mdb->size_name && !strcmp(mdb->size_name, size->media))
    144       break;
    145   }
    146 
    147   if (!mdb)
    148   {
    149     for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db))
    150     {
    151       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)
    152 	break;
    153     }
    154   }
    155 
    156   if (mdb)
    157   {
    158     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));
    159 
    160     lsource = cupsLocalizeDestValue(http, dest, dinfo, "media-source", mdb->source);
    161     ltype   = cupsLocalizeDestValue(http, dest, dinfo, "media-type", mdb->type);
    162   }
    163   else
    164   {
    165     lsource = NULL;
    166     ltype   = NULL;
    167   }
    168 
    169   if (!lsource && !ltype)
    170   {
    171     if (size->bottom || size->left || size->right || size->top)
    172       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless)")), lsize);
    173     else
    174       strlcpy(name, lsize, sizeof(name));
    175   }
    176   else if (!lsource)
    177   {
    178     if (size->bottom || size->left || size->right || size->top)
    179       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, ltype);
    180     else
    181       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s)")), lsize, ltype);
    182   }
    183   else if (!ltype)
    184   {
    185     if (size->bottom || size->left || size->right || size->top)
    186       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, lsource);
    187     else
    188       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s)")), lsize, lsource);
    189   }
    190   else
    191   {
    192     if (size->bottom || size->left || size->right || size->top)
    193       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s, %s)")), lsize, ltype, lsource);
    194     else
    195       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s, %s)")), lsize, ltype, lsource);
    196   }
    197 
    198   if ((match = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
    199     return (NULL);
    200 
    201   match->id  = strdup(size->media);
    202   match->str = strdup(name);
    203 
    204   cupsArrayAdd(dinfo->localizations, match);
    205 
    206   DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
    207 
    208   return (match->str);
    209 }
    210 
    211 
    212 /*
    213  * 'cupsLocalizeDestOption()' - Get the localized string for a destination
    214  *                              option.
    215  *
    216  * The returned string is stored in the destination information and will become
    217  * invalid if the destination information is deleted.
    218  *
    219  * @since CUPS 1.6/macOS 10.8@
    220  */
    221 
    222 const char *				/* O - Localized string */
    223 cupsLocalizeDestOption(
    224     http_t       *http,			/* I - Connection to destination */
    225     cups_dest_t  *dest,			/* I - Destination */
    226     cups_dinfo_t *dinfo,		/* I - Destination information */
    227     const char   *option)		/* I - Option to localize */
    228 {
    229   _cups_message_t	key,		/* Search key */
    230 			*match;		/* Matching entry */
    231   const char            *localized;     /* Localized string */
    232 
    233 
    234   DEBUG_printf(("cupsLocalizeDestOption(http=%p, dest=%p, dinfo=%p, option=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option));
    235 
    236   if (!http || !dest || !dinfo)
    237     return (option);
    238 
    239   if (!dinfo->localizations)
    240     cups_create_localizations(http, dinfo);
    241 
    242   key.id = (char *)option;
    243   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
    244                                                 &key)) != NULL)
    245     return (match->str);
    246   else if ((localized = _cupsLangString(cupsLangDefault(), option)) != NULL)
    247     return (localized);
    248   else
    249     return (option);
    250 }
    251 
    252 
    253 /*
    254  * 'cupsLocalizeDestValue()' - Get the localized string for a destination
    255  *                             option+value pair.
    256  *
    257  * The returned string is stored in the destination information and will become
    258  * invalid if the destination information is deleted.
    259  *
    260  * @since CUPS 1.6/macOS 10.8@
    261  */
    262 
    263 const char *				/* O - Localized string */
    264 cupsLocalizeDestValue(
    265     http_t       *http,			/* I - Connection to destination */
    266     cups_dest_t  *dest,			/* I - Destination */
    267     cups_dinfo_t *dinfo,		/* I - Destination information */
    268     const char   *option,		/* I - Option to localize */
    269     const char   *value)		/* I - Value to localize */
    270 {
    271   _cups_message_t	key,		/* Search key */
    272 			*match;		/* Matching entry */
    273   char			pair[256];	/* option.value pair */
    274   const char            *localized;     /* Localized string */
    275 
    276 
    277   DEBUG_printf(("cupsLocalizeDestValue(http=%p, dest=%p, dinfo=%p, option=\"%s\", value=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option, value));
    278 
    279   if (!http || !dest || !dinfo)
    280     return (value);
    281 
    282   if (!strcmp(option, "media"))
    283   {
    284     pwg_media_t *media = pwgMediaForPWG(value);
    285     cups_size_t size;
    286 
    287     strlcpy(size.media, value, sizeof(size.media));
    288     size.width  = media ? media->width : 0;
    289     size.length = media ? media->length : 0;
    290     size.left   = 0;
    291     size.right  = 0;
    292     size.bottom = 0;
    293     size.top    = 0;
    294 
    295     return (cupsLocalizeDestMedia(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size));
    296   }
    297 
    298   if (!dinfo->localizations)
    299     cups_create_localizations(http, dinfo);
    300 
    301   snprintf(pair, sizeof(pair), "%s.%s", option, value);
    302   key.id = pair;
    303   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
    304                                                 &key)) != NULL)
    305     return (match->str);
    306   else if ((localized = _cupsLangString(cupsLangDefault(), pair)) != NULL && strcmp(localized, pair))
    307     return (localized);
    308   else
    309     return (value);
    310 }
    311 
    312 
    313 /*
    314  * 'cups_create_localizations()' - Create the localizations array for a
    315  *                                 destination.
    316  */
    317 
    318 static void
    319 cups_create_localizations(
    320     http_t       *http,			/* I - Connection to destination */
    321     cups_dinfo_t *dinfo)		/* I - Destination informations */
    322 {
    323   http_t		*http2;		/* Connection for strings file */
    324   http_status_t		status;		/* Request status */
    325   ipp_attribute_t	*attr;		/* "printer-strings-uri" attribute */
    326   char			scheme[32],	/* URI scheme */
    327   			userpass[256],	/* Username/password info */
    328   			hostname[256],	/* Hostname */
    329   			resource[1024],	/* Resource */
    330   			http_hostname[256],
    331   					/* Hostname of connection */
    332 			tempfile[1024];	/* Temporary filename */
    333   int			port;		/* Port number */
    334   http_encryption_t	encryption;	/* Encryption to use */
    335   cups_file_t		*temp;		/* Temporary file */
    336 
    337 
    338  /*
    339   * Create an empty message catalog...
    340   */
    341 
    342   dinfo->localizations = _cupsMessageNew(NULL);
    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...
    353     */
    354 
    355     DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) "
    356                "value.");
    357     return;				/* Nope */
    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     DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value "
    371                   "\"%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 
    421   DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource,
    422                 httpStatus(status)));
    423 
    424   if (status == HTTP_STATUS_OK)
    425   {
    426    /*
    427     * Got the file, read it...
    428     */
    429 
    430     char		buffer[8192],	/* Message buffer */
    431     			*id,		/* ID string */
    432     			*str;		/* Translated message */
    433     _cups_message_t	*m;		/* Current message */
    434 
    435     lseek(cupsFileNumber(temp), 0, SEEK_SET);
    436 
    437     while (cups_read_strings(temp, buffer, sizeof(buffer), &id, &str))
    438     {
    439       if ((m = malloc(sizeof(_cups_message_t))) == NULL)
    440         break;
    441 
    442       m->id  = strdup(id);
    443       m->str = strdup(str);
    444 
    445       if (m->id && m->str)
    446         cupsArrayAdd(dinfo->localizations, m);
    447       else
    448       {
    449         if (m->id)
    450           free(m->id);
    451 
    452         if (m->str)
    453           free(m->str);
    454 
    455         free(m);
    456         break;
    457       }
    458     }
    459   }
    460 
    461   DEBUG_printf(("4cups_create_localizations: %d messages loaded.",
    462                 cupsArrayCount(dinfo->localizations)));
    463 
    464  /*
    465   * Cleanup...
    466   */
    467 
    468   unlink(tempfile);
    469   cupsFileClose(temp);
    470 
    471   if (http2 != http)
    472     httpClose(http2);
    473 }
    474 
    475 
    476 /*
    477  * 'cups_read_strings()' - Read a pair of strings from a .strings file.
    478  */
    479 
    480 static int				/* O - 1 on success, 0 on failure */
    481 cups_read_strings(cups_file_t *strings,	/* I - .strings file */
    482                   char        *buffer,	/* I - Line buffer */
    483                   size_t      bufsize,	/* I - Size of line buffer */
    484 		  char        **id,	/* O - Pointer to ID string */
    485 		  char        **str)	/* O - Pointer to translation string */
    486 {
    487   char	*bufptr;			/* Pointer into buffer */
    488 
    489 
    490   while (cupsFileGets(strings, buffer, bufsize))
    491   {
    492     if (buffer[0] != '\"')
    493       continue;
    494 
    495     *id    = buffer + 1;
    496     bufptr = cups_scan_strings(buffer);
    497 
    498     if (*bufptr != '\"')
    499       continue;
    500 
    501     *bufptr++ = '\0';
    502 
    503     while (*bufptr && *bufptr != '\"')
    504       bufptr ++;
    505 
    506     if (!*bufptr)
    507       continue;
    508 
    509     *str   = bufptr + 1;
    510     bufptr = cups_scan_strings(bufptr);
    511 
    512     if (*bufptr != '\"')
    513       continue;
    514 
    515     *bufptr = '\0';
    516 
    517     return (1);
    518   }
    519 
    520   return (0);
    521 }
    522 
    523 
    524 /*
    525  * 'cups_scan_strings()' - Scan a quoted string.
    526  */
    527 
    528 static char *				/* O - End of string */
    529 cups_scan_strings(char *buffer)		/* I - Start of string */
    530 {
    531   char	*bufptr;			/* Pointer into string */
    532 
    533 
    534   for (bufptr = buffer + 1; *bufptr && *bufptr != '\"'; bufptr ++)
    535   {
    536     if (*bufptr == '\\')
    537     {
    538       if (bufptr[1] >= '0' && bufptr[1] <= '3' &&
    539 	  bufptr[2] >= '0' && bufptr[2] <= '7' &&
    540 	  bufptr[3] >= '0' && bufptr[3] <= '7')
    541       {
    542        /*
    543 	* Decode \nnn octal escape...
    544 	*/
    545 
    546 	*bufptr = (char)(((((bufptr[1] - '0') << 3) | (bufptr[2] - '0')) << 3) | (bufptr[3] - '0'));
    547 	_cups_strcpy(bufptr + 1, bufptr + 4);
    548       }
    549       else
    550       {
    551        /*
    552 	* Decode \C escape...
    553 	*/
    554 
    555 	_cups_strcpy(bufptr, bufptr + 1);
    556 	if (*bufptr == 'n')
    557 	  *bufptr = '\n';
    558 	else if (*bufptr == 'r')
    559 	  *bufptr = '\r';
    560 	else if (*bufptr == 't')
    561 	  *bufptr = '\t';
    562       }
    563     }
    564   }
    565 
    566   return (bufptr);
    567 }
    568