Coverage Report

Created: 2026-09-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cups/cups/dest-localization.c
Line
Count
Source
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
0
{
50
0
  cups_lang_t   *lang;    /* Standard localizations */
51
0
  _cups_message_t key,    /* Search key */
52
0
      *match;   /* Matching entry */
53
0
  pwg_media_t   *pwg;   /* PWG media information */
54
0
  cups_array_t    *db;    /* Media database */
55
0
  _cups_media_db_t  *mdb;   /* Media database entry */
56
0
  char      name[1024], /* Size name */
57
0
      temp[256];  /* Temporary string */
58
0
  const char    *lsize,   /* Localized media size */
59
0
      *lsource, /* Localized media source */
60
0
      *ltype;   /* Localized media type */
61
62
63
0
  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
0
  if (!http || !dest || !dinfo || !size)
70
0
  {
71
0
    DEBUG_puts("1cupsLocalizeDestMedia: Returning NULL.");
72
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
73
0
    return (NULL);
74
0
  }
75
76
 /*
77
  * See if the localization is cached...
78
  */
79
80
0
  if (!dinfo->localizations)
81
0
    cups_create_localizations(http, dinfo);
82
83
0
  key.id = size->media;
84
0
  if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL)
85
0
  {
86
0
    DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
87
0
    return (match->str);
88
0
  }
89
90
 /*
91
  * If not, get the localized size, source, and type strings...
92
  */
93
94
0
  lang = cupsLangDefault();
95
96
0
  snprintf(temp, sizeof(temp), "media.%s", size->media);
97
0
  if ((lsize = _cupsLangString(lang, temp)) != NULL && strcmp(lsize, temp))
98
0
  {
99
0
    DEBUG_printf(("1cupsLocalizeDestMedia: Returning standard localization \"%s\".", lsize));
100
0
    return (lsize);
101
0
  }
102
103
0
  pwg  = pwgMediaForSize(size->width, size->length);
104
105
0
  if (pwg->ppd)
106
0
    lsize = _cupsLangString(lang, pwg->ppd);
107
0
  else
108
0
    lsize = NULL;
109
110
0
  if (!lsize)
111
0
  {
112
0
    if ((size->width % 635) == 0 && (size->length % 635) == 0)
113
0
    {
114
     /*
115
      * Use inches since the size is a multiple of 1/4 inch.
116
      */
117
118
0
      snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g \"")), size->width / 2540.0, size->length / 2540.0);
119
0
    }
120
0
    else
121
0
    {
122
     /*
123
      * Use millimeters since the size is not a multiple of 1/4 inch.
124
      */
125
126
0
      snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%d x %d mm")), (size->width + 50) / 100, (size->length + 50) / 100);
127
0
    }
128
129
0
    lsize = temp;
130
0
  }
131
132
0
  if (flags & CUPS_MEDIA_FLAGS_READY)
133
0
    db = dinfo->ready_db;
134
0
  else
135
0
    db = dinfo->media_db;
136
137
0
  DEBUG_printf(("1cupsLocalizeDestMedia: size->media=\"%s\"", size->media));
138
139
0
  for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db))
140
0
  {
141
0
    if (mdb->key && !strcmp(mdb->key, size->media))
142
0
      break;
143
0
    else if (mdb->size_name && !strcmp(mdb->size_name, size->media))
144
0
      break;
145
0
  }
146
147
0
  if (!mdb)
148
0
  {
149
0
    for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); mdb; mdb = (_cups_media_db_t *)cupsArrayNext(db))
150
0
    {
151
0
      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
0
  break;
153
0
    }
154
0
  }
155
156
0
  if (mdb)
157
0
  {
158
0
    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
0
    lsource = cupsLocalizeDestValue(http, dest, dinfo, "media-source", mdb->source);
161
0
    ltype   = cupsLocalizeDestValue(http, dest, dinfo, "media-type", mdb->type);
162
0
  }
163
0
  else
164
0
  {
165
0
    lsource = NULL;
166
0
    ltype   = NULL;
167
0
  }
168
169
0
  if (!lsource && !ltype)
170
0
  {
171
0
    if (size->bottom || size->left || size->right || size->top)
172
0
      snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless)")), lsize);
173
0
    else
174
0
      strlcpy(name, lsize, sizeof(name));
175
0
  }
176
0
  else if (!lsource)
177
0
  {
178
0
    if (size->bottom || size->left || size->right || size->top)
179
0
      snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, ltype);
180
0
    else
181
0
      snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s)")), lsize, ltype);
182
0
  }
183
0
  else if (!ltype)
184
0
  {
185
0
    if (size->bottom || size->left || size->right || size->top)
186
0
      snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s)")), lsize, lsource);
187
0
    else
188
0
      snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s)")), lsize, lsource);
189
0
  }
190
0
  else
191
0
  {
192
0
    if (size->bottom || size->left || size->right || size->top)
193
0
      snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (Borderless, %s, %s)")), lsize, ltype, lsource);
194
0
    else
195
0
      snprintf(name, sizeof(name), _cupsLangString(lang, _("%s (%s, %s)")), lsize, ltype, lsource);
196
0
  }
197
198
0
  if ((match = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
199
0
    return (NULL);
200
201
0
  match->id  = strdup(size->media);
202
0
  match->str = strdup(name);
203
204
0
  cupsArrayAdd(dinfo->localizations, match);
205
206
0
  DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
207
208
0
  return (match->str);
209
0
}
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
0
{
229
0
  _cups_message_t key,    /* Search key */
230
0
      *match;   /* Matching entry */
231
0
  const char            *localized;     /* Localized string */
232
233
234
0
  DEBUG_printf(("cupsLocalizeDestOption(http=%p, dest=%p, dinfo=%p, option=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option));
235
236
0
  if (!http || !dest || !dinfo)
237
0
    return (option);
238
239
0
  if (!dinfo->localizations)
240
0
    cups_create_localizations(http, dinfo);
241
242
0
  key.id = (char *)option;
243
0
  if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
244
0
                                                &key)) != NULL)
245
0
    return (match->str);
246
0
  else if ((localized = _cupsLangString(cupsLangDefault(), option)) != NULL)
247
0
    return (localized);
248
0
  else
249
0
    return (option);
250
0
}
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
0
{
271
0
  _cups_message_t key,    /* Search key */
272
0
      *match;   /* Matching entry */
273
0
  char      pair[256];  /* option.value pair */
274
0
  const char            *localized;     /* Localized string */
275
276
277
0
  DEBUG_printf(("cupsLocalizeDestValue(http=%p, dest=%p, dinfo=%p, option=\"%s\", value=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option, value));
278
279
0
  if (!http || !dest || !dinfo)
280
0
    return (value);
281
282
0
  if (!strcmp(option, "media"))
283
0
  {
284
0
    pwg_media_t *media = pwgMediaForPWG(value);
285
0
    cups_size_t size;
286
287
0
    strlcpy(size.media, value, sizeof(size.media));
288
0
    size.width  = media ? media->width : 0;
289
0
    size.length = media ? media->length : 0;
290
0
    size.left   = 0;
291
0
    size.right  = 0;
292
0
    size.bottom = 0;
293
0
    size.top    = 0;
294
295
0
    return (cupsLocalizeDestMedia(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size));
296
0
  }
297
298
0
  if (!dinfo->localizations)
299
0
    cups_create_localizations(http, dinfo);
300
301
0
  snprintf(pair, sizeof(pair), "%s.%s", option, value);
302
0
  key.id = pair;
303
0
  if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations,
304
0
                                                &key)) != NULL)
305
0
    return (match->str);
306
0
  else if ((localized = _cupsLangString(cupsLangDefault(), pair)) != NULL && strcmp(localized, pair))
307
0
    return (localized);
308
0
  else
309
0
    return (value);
310
0
}
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
0
{
323
0
  http_t    *http2;   /* Connection for strings file */
324
0
  http_status_t   status;   /* Request status */
325
0
  ipp_attribute_t *attr;    /* "printer-strings-uri" attribute */
326
0
  char      scheme[32], /* URI scheme */
327
0
        userpass[256],  /* Username/password info */
328
0
        hostname[256],  /* Hostname */
329
0
        resource[1024], /* Resource */
330
0
        http_hostname[256],
331
            /* Hostname of connection */
332
0
      tempfile[1024]; /* Temporary filename */
333
0
  int     port;   /* Port number */
334
0
  http_encryption_t encryption; /* Encryption to use */
335
0
  cups_file_t   *temp;    /* Temporary file */
336
337
338
 /*
339
  * Create an empty message catalog...
340
  */
341
342
0
  dinfo->localizations = _cupsMessageNew(NULL);
343
344
 /*
345
  * See if there are any localizations...
346
  */
347
348
0
  if ((attr = ippFindAttribute(dinfo->attrs, "printer-strings-uri",
349
0
                               IPP_TAG_URI)) == NULL)
350
0
  {
351
   /*
352
    * Nope...
353
    */
354
355
0
    DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) "
356
0
               "value.");
357
0
    return;       /* Nope */
358
0
  }
359
360
 /*
361
  * Pull apart the URI and determine whether we need to try a different
362
  * server...
363
  */
364
365
0
  if (httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text,
366
0
                      scheme, sizeof(scheme), userpass, sizeof(userpass),
367
0
                      hostname, sizeof(hostname), &port, resource,
368
0
                      sizeof(resource)) < HTTP_URI_STATUS_OK)
369
0
  {
370
0
    DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value "
371
0
                  "\"%s\".", attr->values[0].string.text));
372
0
    return;
373
0
  }
374
375
0
  httpGetHostname(http, http_hostname, sizeof(http_hostname));
376
377
0
  if (!_cups_strcasecmp(http_hostname, hostname) &&
378
0
      port == httpAddrPort(http->hostaddr))
379
0
  {
380
   /*
381
    * Use the same connection...
382
    */
383
384
0
    http2 = http;
385
0
  }
386
0
  else
387
0
  {
388
   /*
389
    * Connect to the alternate host...
390
    */
391
392
0
    if (!strcmp(scheme, "https"))
393
0
      encryption = HTTP_ENCRYPTION_ALWAYS;
394
0
    else
395
0
      encryption = HTTP_ENCRYPTION_IF_REQUESTED;
396
397
0
    if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1,
398
0
                              30000, NULL)) == NULL)
399
0
    {
400
0
      DEBUG_printf(("4cups_create_localizations: Unable to connect to "
401
0
                    "%s:%d: %s", hostname, port, cupsLastErrorString()));
402
0
      return;
403
0
    }
404
0
  }
405
406
 /*
407
  * Get a temporary file...
408
  */
409
410
0
  if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
411
0
  {
412
0
    DEBUG_printf(("4cups_create_localizations: Unable to create temporary "
413
0
                  "file: %s", cupsLastErrorString()));
414
0
    if (http2 != http)
415
0
      httpClose(http2);
416
0
    return;
417
0
  }
418
419
0
  status = cupsGetFd(http2, resource, cupsFileNumber(temp));
420
421
0
  DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource,
422
0
                httpStatus(status)));
423
424
0
  if (status == HTTP_STATUS_OK)
425
0
  {
426
   /*
427
    * Got the file, read it...
428
    */
429
430
0
    char    buffer[8192], /* Message buffer */
431
0
          *id,    /* ID string */
432
0
          *str;   /* Translated message */
433
0
    _cups_message_t *m;   /* Current message */
434
435
0
    lseek(cupsFileNumber(temp), 0, SEEK_SET);
436
437
0
    while (cups_read_strings(temp, buffer, sizeof(buffer), &id, &str))
438
0
    {
439
0
      if ((m = malloc(sizeof(_cups_message_t))) == NULL)
440
0
        break;
441
442
0
      m->id  = strdup(id);
443
0
      m->str = strdup(str);
444
445
0
      if (m->id && m->str)
446
0
        cupsArrayAdd(dinfo->localizations, m);
447
0
      else
448
0
      {
449
0
        if (m->id)
450
0
          free(m->id);
451
452
0
        if (m->str)
453
0
          free(m->str);
454
455
0
        free(m);
456
0
        break;
457
0
      }
458
0
    }
459
0
  }
460
461
0
  DEBUG_printf(("4cups_create_localizations: %d messages loaded.",
462
0
                cupsArrayCount(dinfo->localizations)));
463
464
 /*
465
  * Cleanup...
466
  */
467
468
0
  unlink(tempfile);
469
0
  cupsFileClose(temp);
470
471
0
  if (http2 != http)
472
0
    httpClose(http2);
473
0
}
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
0
{
487
0
  char  *bufptr;      /* Pointer into buffer */
488
489
490
0
  while (cupsFileGets(strings, buffer, bufsize))
491
0
  {
492
0
    if (buffer[0] != '\"')
493
0
      continue;
494
495
0
    *id    = buffer + 1;
496
0
    bufptr = cups_scan_strings(buffer);
497
498
0
    if (*bufptr != '\"')
499
0
      continue;
500
501
0
    *bufptr++ = '\0';
502
503
0
    while (*bufptr && *bufptr != '\"')
504
0
      bufptr ++;
505
506
0
    if (!*bufptr)
507
0
      continue;
508
509
0
    *str   = bufptr + 1;
510
0
    bufptr = cups_scan_strings(bufptr);
511
512
0
    if (*bufptr != '\"')
513
0
      continue;
514
515
0
    *bufptr = '\0';
516
517
0
    return (1);
518
0
  }
519
520
0
  return (0);
521
0
}
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
0
{
531
0
  char  *bufptr;      /* Pointer into string */
532
533
534
0
  for (bufptr = buffer + 1; *bufptr && *bufptr != '\"'; bufptr ++)
535
0
  {
536
0
    if (*bufptr == '\\')
537
0
    {
538
0
      if (bufptr[1] >= '0' && bufptr[1] <= '3' &&
539
0
    bufptr[2] >= '0' && bufptr[2] <= '7' &&
540
0
    bufptr[3] >= '0' && bufptr[3] <= '7')
541
0
      {
542
       /*
543
  * Decode \nnn octal escape...
544
  */
545
546
0
  *bufptr = (char)(((((bufptr[1] - '0') << 3) | (bufptr[2] - '0')) << 3) | (bufptr[3] - '0'));
547
0
  _cups_strcpy(bufptr + 1, bufptr + 4);
548
0
      }
549
0
      else
550
0
      {
551
       /*
552
  * Decode \C escape...
553
  */
554
555
0
  _cups_strcpy(bufptr, bufptr + 1);
556
0
  if (*bufptr == 'n')
557
0
    *bufptr = '\n';
558
0
  else if (*bufptr == 'r')
559
0
    *bufptr = '\r';
560
0
  else if (*bufptr == 't')
561
0
    *bufptr = '\t';
562
0
      }
563
0
    }
564
0
  }
565
566
0
  return (bufptr);
567
0
}