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  /*
     64   * Range check input...
     65   */
     66 
     67   if (!http || !dest || !dinfo || !size)
     68   {
     69     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
     70     return (NULL);
     71   }
     72 
     73  /*
     74   * See if the localization is cached...
     75   */
     76 
     77   if (!dinfo->localizations)
     78     cups_create_localizations(http, dinfo);
     79 
     80   key.id = size->media;
     81   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL)
     82     return (match->str);
     83 
     84  /*
     85   * If not, get the localized size, source, and type strings...
     86   */
     87 
     88   lang = cupsLangDefault();
     89   pwg  = pwgMediaForSize(size->width, size->length);
     90 
     91   if (pwg->ppd)
     92     lsize = _cupsLangString(lang, pwg->ppd);
     93   else
     94     lsize = NULL;
     95 
     96   if (!lsize)
     97   {
     98     if ((size->width % 635) == 0 && (size->length % 635) == 0)
     99     {
    100      /*
    101       * Use inches since the size is a multiple of 1/4 inch.
    102       */
    103 
    104       snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g")), size->width / 2540.0, size->length / 2540.0);
    105     }
    106     else
    107     {
    108      /*
    109       * Use millimeters since the size is not a multiple of 1/4 inch.
    110       */
    111 
    112       snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%d x %d mm")), (size->width + 50) / 100, (size->length + 50) / 100);
    113     }
    114 
    115     lsize = temp;
    116   }
    117 
    118   if (flags & CUPS_MEDIA_FLAGS_READY)
    119     db = dinfo->ready_db;
    120   else
    121     db = dinfo->media_db;
    122 
    123   DEBUG_printf(("1cupsLocalizeDestMedia: size->media=\"%s\"", size->media));
    124 
    125   for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db))
    126   {
    127     if (mdb->key && !strcmp(mdb->key, size->media))
    128       break;
    129     else if (mdb->size_name && !strcmp(mdb->size_name, size->media))
    130       break;
    131   }
    132 
    133   if (!mdb)
    134   {
    135     for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db))
    136     {
    137       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)
    138 	break;
    139     }
    140   }
    141 
    142   if (mdb)
    143   {
    144     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));
    145 
    146     lsource = cupsLocalizeDestValue(http, dest, dinfo, "media-source", mdb->source);
    147     ltype   = cupsLocalizeDestValue(http, dest, dinfo, "media-type", mdb->type);
    148   }
    149   else
    150   {
    151     lsource = NULL;
    152     ltype   = NULL;
    153   }
    154 
    155   if (!lsource && !ltype)
    156   {
    157     if (size->bottom || size->left || size->right || size->top)
    158       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless)")), lsize);
    159     else
    160       strlcpy(name, lsize, sizeof(name));
    161   }
    162   else if (!lsource)
    163   {
    164     if (size->bottom || size->left || size->right || size->top)
    165       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, ltype);
    166     else
    167       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s)")), lsize, ltype);
    168   }
    169   else if (!ltype)
    170   {
    171     if (size->bottom || size->left || size->right || size->top)
    172       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, lsource);
    173     else
    174       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s)")), lsize, lsource);
    175   }
    176   else
    177   {
    178     if (size->bottom || size->left || size->right || size->top)
    179       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s, %s)")), lsize, ltype, lsource);
    180     else
    181       snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s, %s)")), lsize, ltype, lsource);
    182   }
    183 
    184   if ((match = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
    185     return (NULL);
    186 
    187   match->id  = strdup(size->media);
    188   match->str = strdup(name);
    189 
    190   cupsArrayAdd(dinfo->localizations, match);
    191 
    192   return (match->str);
    193 }
    194 
    195 
    196 /*
    197  * 'cupsLocalizeDestOption()' - Get the localized string for a destination
    198  *                              option.
    199  *
    200  * The returned string is stored in the destination information and will become
    201  * invalid if the destination information is deleted.
    202  *
    203  * @since CUPS 1.6/macOS 10.8@
    204  */
    205 
    206 const char *				/* O - Localized string */
    207 cupsLocalizeDestOption(
    208     http_t       *http,			/* I - Connection to destination */
    209     cups_dest_t  *dest,			/* I - Destination */
    210     cups_dinfo_t *dinfo,		/* I - Destination information */
    211     const char   *option)		/* I - Option to localize */
    212 {
    213   _cups_message_t	key,		/* Search key */
    214 			*match;		/* Matching entry */
    215 
    216 
    217   if (!http || !dest || !dinfo)
    218     return (option);
    219 
    220   if (!dinfo->localizations)
    221     cups_create_localizations(http, dinfo);
    222 
    223   if (cupsArrayCount(dinfo->localizations) == 0)
    224     return (option);
    225 
    226   key.id = (char *)option;
    227   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
    228                                                 &key)) != NULL)
    229     return (match->str);
    230   else
    231     return (option);
    232 }
    233 
    234 
    235 /*
    236  * 'cupsLocalizeDestValue()' - Get the localized string for a destination
    237  *                             option+value pair.
    238  *
    239  * The returned string is stored in the destination information and will become
    240  * invalid if the destination information is deleted.
    241  *
    242  * @since CUPS 1.6/macOS 10.8@
    243  */
    244 
    245 const char *				/* O - Localized string */
    246 cupsLocalizeDestValue(
    247     http_t       *http,			/* I - Connection to destination */
    248     cups_dest_t  *dest,			/* I - Destination */
    249     cups_dinfo_t *dinfo,		/* I - Destination information */
    250     const char   *option,		/* I - Option to localize */
    251     const char   *value)		/* I - Value to localize */
    252 {
    253   _cups_message_t	key,		/* Search key */
    254 			*match;		/* Matching entry */
    255   char			pair[256];	/* option.value pair */
    256 
    257 
    258   if (!http || !dest || !dinfo)
    259     return (value);
    260 
    261   if (!dinfo->localizations)
    262     cups_create_localizations(http, dinfo);
    263 
    264   if (cupsArrayCount(dinfo->localizations) == 0)
    265     return (value);
    266 
    267   snprintf(pair, sizeof(pair), "%s.%s", option, value);
    268   key.id = pair;
    269   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
    270                                                 &key)) != NULL)
    271     return (match->str);
    272   else
    273     return (value);
    274 }
    275 
    276 
    277 /*
    278  * 'cups_create_localizations()' - Create the localizations array for a
    279  *                                 destination.
    280  */
    281 
    282 static void
    283 cups_create_localizations(
    284     http_t       *http,			/* I - Connection to destination */
    285     cups_dinfo_t *dinfo)		/* I - Destination informations */
    286 {
    287   http_t		*http2;		/* Connection for strings file */
    288   http_status_t		status;		/* Request status */
    289   ipp_attribute_t	*attr;		/* "printer-strings-uri" attribute */
    290   char			scheme[32],	/* URI scheme */
    291   			userpass[256],	/* Username/password info */
    292   			hostname[256],	/* Hostname */
    293   			resource[1024],	/* Resource */
    294   			http_hostname[256],
    295   					/* Hostname of connection */
    296 			tempfile[1024];	/* Temporary filename */
    297   int			port;		/* Port number */
    298   http_encryption_t	encryption;	/* Encryption to use */
    299   cups_file_t		*temp;		/* Temporary file */
    300 
    301 
    302  /*
    303   * Create an empty message catalog...
    304   */
    305 
    306   dinfo->localizations = _cupsMessageNew(NULL);
    307 
    308  /*
    309   * See if there are any localizations...
    310   */
    311 
    312   if ((attr = ippFindAttribute(dinfo->attrs, "printer-strings-uri",
    313                                IPP_TAG_URI)) == NULL)
    314   {
    315    /*
    316     * Nope...
    317     */
    318 
    319     DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) "
    320                "value.");
    321     return;				/* Nope */
    322   }
    323 
    324  /*
    325   * Pull apart the URI and determine whether we need to try a different
    326   * server...
    327   */
    328 
    329   if (httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text,
    330                       scheme, sizeof(scheme), userpass, sizeof(userpass),
    331                       hostname, sizeof(hostname), &port, resource,
    332                       sizeof(resource)) < HTTP_URI_STATUS_OK)
    333   {
    334     DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value "
    335                   "\"%s\".", attr->values[0].string.text));
    336     return;
    337   }
    338 
    339   httpGetHostname(http, http_hostname, sizeof(http_hostname));
    340 
    341   if (!_cups_strcasecmp(http_hostname, hostname) &&
    342       port == httpAddrPort(http->hostaddr))
    343   {
    344    /*
    345     * Use the same connection...
    346     */
    347 
    348     http2 = http;
    349   }
    350   else
    351   {
    352    /*
    353     * Connect to the alternate host...
    354     */
    355 
    356     if (!strcmp(scheme, "https"))
    357       encryption = HTTP_ENCRYPTION_ALWAYS;
    358     else
    359       encryption = HTTP_ENCRYPTION_IF_REQUESTED;
    360 
    361     if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1,
    362                               30000, NULL)) == NULL)
    363     {
    364       DEBUG_printf(("4cups_create_localizations: Unable to connect to "
    365                     "%s:%d: %s", hostname, port, cupsLastErrorString()));
    366       return;
    367     }
    368   }
    369 
    370  /*
    371   * Get a temporary file...
    372   */
    373 
    374   if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
    375   {
    376     DEBUG_printf(("4cups_create_localizations: Unable to create temporary "
    377                   "file: %s", cupsLastErrorString()));
    378     if (http2 != http)
    379       httpClose(http2);
    380     return;
    381   }
    382 
    383   status = cupsGetFd(http2, resource, cupsFileNumber(temp));
    384 
    385   DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource,
    386                 httpStatus(status)));
    387 
    388   if (status == HTTP_STATUS_OK)
    389   {
    390    /*
    391     * Got the file, read it...
    392     */
    393 
    394     char		buffer[8192],	/* Message buffer */
    395     			*id,		/* ID string */
    396     			*str;		/* Translated message */
    397     _cups_message_t	*m;		/* Current message */
    398 
    399     lseek(cupsFileNumber(temp), 0, SEEK_SET);
    400 
    401     while (cups_read_strings(temp, buffer, sizeof(buffer), &id, &str))
    402     {
    403       if ((m = malloc(sizeof(_cups_message_t))) == NULL)
    404         break;
    405 
    406       m->id  = strdup(id);
    407       m->str = strdup(str);
    408 
    409       if (m->id && m->str)
    410         cupsArrayAdd(dinfo->localizations, m);
    411       else
    412       {
    413         if (m->id)
    414           free(m->id);
    415 
    416         if (m->str)
    417           free(m->str);
    418 
    419         free(m);
    420         break;
    421       }
    422     }
    423   }
    424 
    425   DEBUG_printf(("4cups_create_localizations: %d messages loaded.",
    426                 cupsArrayCount(dinfo->localizations)));
    427 
    428  /*
    429   * Cleanup...
    430   */
    431 
    432   unlink(tempfile);
    433   cupsFileClose(temp);
    434 
    435   if (http2 != http)
    436     httpClose(http2);
    437 }
    438 
    439 
    440 /*
    441  * 'cups_read_strings()' - Read a pair of strings from a .strings file.
    442  */
    443 
    444 static int				/* O - 1 on success, 0 on failure */
    445 cups_read_strings(cups_file_t *strings,	/* I - .strings file */
    446                   char        *buffer,	/* I - Line buffer */
    447                   size_t      bufsize,	/* I - Size of line buffer */
    448 		  char        **id,	/* O - Pointer to ID string */
    449 		  char        **str)	/* O - Pointer to translation string */
    450 {
    451   char	*bufptr;			/* Pointer into buffer */
    452 
    453 
    454   while (cupsFileGets(strings, buffer, bufsize))
    455   {
    456     if (buffer[0] != '\"')
    457       continue;
    458 
    459     *id    = buffer + 1;
    460     bufptr = cups_scan_strings(buffer);
    461 
    462     if (*bufptr != '\"')
    463       continue;
    464 
    465     *bufptr++ = '\0';
    466 
    467     while (*bufptr && *bufptr != '\"')
    468       bufptr ++;
    469 
    470     if (!*bufptr)
    471       continue;
    472 
    473     *str   = bufptr + 1;
    474     bufptr = cups_scan_strings(bufptr);
    475 
    476     if (*bufptr != '\"')
    477       continue;
    478 
    479     *bufptr = '\0';
    480 
    481     return (1);
    482   }
    483 
    484   return (0);
    485 }
    486 
    487 
    488 /*
    489  * 'cups_scan_strings()' - Scan a quoted string.
    490  */
    491 
    492 static char *				/* O - End of string */
    493 cups_scan_strings(char *buffer)		/* I - Start of string */
    494 {
    495   char	*bufptr;			/* Pointer into string */
    496 
    497 
    498   for (bufptr = buffer + 1; *bufptr && *bufptr != '\"'; bufptr ++)
    499   {
    500     if (*bufptr == '\\')
    501     {
    502       if (bufptr[1] >= '0' && bufptr[1] <= '3' &&
    503 	  bufptr[2] >= '0' && bufptr[2] <= '7' &&
    504 	  bufptr[3] >= '0' && bufptr[3] <= '7')
    505       {
    506        /*
    507 	* Decode \nnn octal escape...
    508 	*/
    509 
    510 	*bufptr = (char)(((((bufptr[1] - '0') << 3) | (bufptr[2] - '0')) << 3) | (bufptr[3] - '0'));
    511 	_cups_strcpy(bufptr + 1, bufptr + 4);
    512       }
    513       else
    514       {
    515        /*
    516 	* Decode \C escape...
    517 	*/
    518 
    519 	_cups_strcpy(bufptr, bufptr + 1);
    520 	if (*bufptr == 'n')
    521 	  *bufptr = '\n';
    522 	else if (*bufptr == 'r')
    523 	  *bufptr = '\r';
    524 	else if (*bufptr == 't')
    525 	  *bufptr = '\t';
    526       }
    527     }
    528   }
    529 
    530   return (bufptr);
    531 }
    532