1 /*
2 * PPD/driver support for CUPS.
3 *
4 * This program handles listing and installing static PPD files, PPD files
5 * created from driver information files, and dynamically generated PPD files
6 * using driver helper programs.
7 *
8 * Copyright © 2020-2024 by OpenPrinting.
9 * Copyright © 2007-2019 by Apple Inc.
10 * Copyright © 1997-2007 by Easy Software Products.
11 *
12 * Licensed under Apache License v2.0. See the file "LICENSE" for more
13 * information.
14 */
15
16 /*
17 * Include necessary headers...
18 */
19
20 #include "util.h"
21 #include <cups/dir.h>
22 #include <cups/transcode.h>
23 #include <cups/ppd-private.h>
24 #include <ppdc/ppdc.h>
25 #include <regex.h>
26
27
28 /*
29 * Constants...
30 */
31
32 #define PPD_SYNC 0x50504441 /* Sync word for ppds.dat (PPDA) */
33 #define PPD_MAX_LANG 32 /* Maximum languages */
34 #define PPD_MAX_PROD 32 /* Maximum products */
35 #define PPD_MAX_VERS 32 /* Maximum versions */
36
37 #define PPD_TYPE_POSTSCRIPT 0 /* PostScript PPD */
38 #define PPD_TYPE_PDF 1 /* PDF PPD */
39 #define PPD_TYPE_RASTER 2 /* CUPS raster PPD */
40 #define PPD_TYPE_FAX 3 /* Facsimile/MFD PPD */
41 #define PPD_TYPE_UNKNOWN 4 /* Other/hybrid PPD */
42 #define PPD_TYPE_DRV 5 /* Driver info file */
43 #define PPD_TYPE_ARCHIVE 6 /* Archive file */
44
45 #define TAR_BLOCK 512 /* Number of bytes in a block */
46 #define TAR_BLOCKS 10 /* Blocking factor */
47
48 #define TAR_MAGIC "ustar" /* 5 chars and a null */
49 #define TAR_VERSION "00" /* POSIX tar version */
50
51 #define TAR_OLDNORMAL '\0' /* Normal disk file, Unix compat */
52 #define TAR_NORMAL '0' /* Normal disk file */
53 #define TAR_LINK '1' /* Link to previously dumped file */
54 #define TAR_SYMLINK '2' /* Symbolic link */
55 #define TAR_CHR '3' /* Character special file */
56 #define TAR_BLK '4' /* Block special file */
57 #define TAR_DIR '5' /* Directory */
58 #define TAR_FIFO '6' /* FIFO special file */
59 #define TAR_CONTIG '7' /* Contiguous file */
60
61
62 /*
63 * PPD information structures...
64 */
65
66 typedef struct /**** PPD record ****/
67 {
68 time_t mtime; /* Modification time */
69 off_t size; /* Size in bytes */
70 int model_number; /* cupsModelNumber */
71 int type; /* ppd-type */
72 char filename[512], /* Filename */
73 name[256], /* PPD name */
74 languages[PPD_MAX_LANG][6],
75 /* LanguageVersion/cupsLanguages */
76 products[PPD_MAX_PROD][128],
77 /* Product strings */
78 psversions[PPD_MAX_VERS][32],
79 /* PSVersion strings */
80 make[128], /* Manufacturer */
81 make_and_model[128], /* NickName/ModelName */
82 device_id[256], /* IEEE 1284 Device ID */
83 scheme[128]; /* PPD scheme */
84 } ppd_rec_t;
85
86 typedef struct /**** In-memory record ****/
87 {
88 int found; /* 1 if PPD is found */
89 int matches; /* Match count */
90 ppd_rec_t record; /* PPDs.dat record */
91 } ppd_info_t;
92
93 typedef union /**** TAR record format ****/
94 {
95 unsigned char all[TAR_BLOCK]; /* Raw data block */
96 struct
97 {
98 char pathname[100], /* Destination path */
99 mode[8], /* Octal file permissions */
100 uid[8], /* Octal user ID */
101 gid[8], /* Octal group ID */
102 size[12], /* Octal size in bytes */
103 mtime[12], /* Octal modification time */
104 chksum[8], /* Octal checksum value */
105 linkflag, /* File type */
106 linkname[100], /* Source path for link */
107 magic[6], /* Magic string */
108 version[2], /* Format version */
109 uname[32], /* User name */
110 gname[32], /* Group name */
111 devmajor[8], /* Octal device major number */
112 devminor[8], /* Octal device minor number */
113 prefix[155]; /* Prefix for long filenames */
114 } header;
115 } tar_rec_t;
116
117
118 /*
119 * Globals...
120 */
121
122 static cups_array_t *Inodes = NULL, /* Inodes of directories we've visited */
123 *PPDsByName = NULL,
124 /* PPD files sorted by filename and name */
125 *PPDsByMakeModel = NULL;
126 /* PPD files sorted by make and model */
127 static int ChangedPPD; /* Did we change the PPD database? */
128 static const char * const PPDTypes[] = /* ppd-type values */
129 {
130 "postscript",
131 "pdf",
132 "raster",
133 "fax",
134 "object",
135 "object-direct",
136 "object-storage",
137 "unknown",
138 "drv",
139 "archive"
140 };
141
142
143 /*
144 * Local functions...
145 */
146
147 static ppd_info_t *add_ppd(const char *filename, const char *name,
148 const char *language, const char *make,
149 const char *make_and_model,
150 const char *device_id, const char *product,
151 const char *psversion, time_t mtime,
152 size_t size, int model_number, int type,
153 const char *scheme);
154 static int cat_drv(const char *name, int request_id);
155 static void cat_ppd(const char *name, int request_id) _CUPS_NORETURN;
156 static int cat_static(const char *name, int request_id);
157 static int cat_tar(const char *name, int request_id);
158 static int compare_inodes(struct stat *a, struct stat *b);
159 static int compare_matches(const ppd_info_t *p0,
160 const ppd_info_t *p1);
161 static int compare_names(const ppd_info_t *p0,
162 const ppd_info_t *p1);
163 static int compare_ppds(const ppd_info_t *p0,
164 const ppd_info_t *p1);
165 static void dump_ppds_dat(const char *filename) _CUPS_NORETURN;
166 static void free_array(cups_array_t *a);
167 static cups_file_t *get_file(const char *name, int request_id,
168 const char *subdir, char *buffer,
169 size_t bufsize, char **subfile);
170 static void list_ppds(int request_id, int limit, const char *opt) _CUPS_NORETURN;
171 static int load_drivers(cups_array_t *include,
172 cups_array_t *exclude);
173 static int load_drv(const char *filename, const char *name,
174 cups_file_t *fp, time_t mtime, off_t size);
175 static void load_ppd(const char *filename, const char *name,
176 const char *scheme, struct stat *fileinfo,
177 ppd_info_t *ppd, cups_file_t *fp, off_t end);
178 static int load_ppds(const char *d, const char *p, int descend);
179 static void load_ppds_dat(char *filename, size_t filesize,
180 int verbose);
181 static int load_tar(const char *filename, const char *name,
182 cups_file_t *fp, time_t mtime, off_t size);
183 static int read_tar(cups_file_t *fp, char *name, size_t namesize,
184 struct stat *info);
185 static regex_t *regex_device_id(const char *device_id);
186 static regex_t *regex_string(const char *s);
187
188
189 /*
190 * 'main()' - Scan for drivers and return an IPP response.
191 *
192 * Usage:
193 *
194 * cups-driverd request_id limit options
195 */
196
197 int /* O - Exit code */
main(int argc,char * argv[])198 main(int argc, /* I - Number of command-line args */
199 char *argv[]) /* I - Command-line arguments */
200 {
201 /*
202 * Install or list PPDs...
203 */
204
205 if (argc == 3 && !strcmp(argv[1], "cat"))
206 cat_ppd(argv[2], 0);
207 else if ((argc == 2 || argc == 3) && !strcmp(argv[1], "dump"))
208 dump_ppds_dat(argv[2]);
209 else if (argc == 4 && !strcmp(argv[1], "get"))
210 cat_ppd(argv[3], atoi(argv[2]));
211 else if (argc == 5 && !strcmp(argv[1], "list"))
212 list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4]);
213 else
214 {
215 fputs("Usage: cups-driverd cat ppd-name\n", stderr);
216 fputs("Usage: cups-driverd dump\n", stderr);
217 fputs("Usage: cups-driverd get request_id ppd-name\n", stderr);
218 fputs("Usage: cups-driverd list request_id limit options\n", stderr);
219 return (1);
220 }
221 }
222
223
224 /*
225 * 'add_ppd()' - Add a PPD file.
226 */
227
228 static ppd_info_t * /* O - PPD */
add_ppd(const char * filename,const char * name,const char * language,const char * make,const char * make_and_model,const char * device_id,const char * product,const char * psversion,time_t mtime,size_t size,int model_number,int type,const char * scheme)229 add_ppd(const char *filename, /* I - PPD filename */
230 const char *name, /* I - PPD name */
231 const char *language, /* I - LanguageVersion */
232 const char *make, /* I - Manufacturer */
233 const char *make_and_model, /* I - NickName/ModelName */
234 const char *device_id, /* I - 1284DeviceID */
235 const char *product, /* I - Product */
236 const char *psversion, /* I - PSVersion */
237 time_t mtime, /* I - Modification time */
238 size_t size, /* I - File size */
239 int model_number, /* I - Model number */
240 int type, /* I - Driver type */
241 const char *scheme) /* I - PPD scheme */
242 {
243 ppd_info_t *ppd; /* PPD */
244 char *recommended; /* Foomatic driver string */
245
246
247 /*
248 * Add a new PPD file...
249 */
250
251 if ((ppd = (ppd_info_t *)calloc(1, sizeof(ppd_info_t))) == NULL)
252 {
253 fprintf(stderr,
254 "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
255 cupsArrayCount(PPDsByName));
256 return (NULL);
257 }
258
259 /*
260 * Zero-out the PPD data and copy the values over...
261 */
262
263 ppd->found = 1;
264 ppd->record.mtime = mtime;
265 ppd->record.size = (off_t)size;
266 ppd->record.model_number = model_number;
267 ppd->record.type = type;
268
269 strlcpy(ppd->record.filename, filename, sizeof(ppd->record.filename));
270 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
271 strlcpy(ppd->record.languages[0], language,
272 sizeof(ppd->record.languages[0]));
273 strlcpy(ppd->record.products[0], product, sizeof(ppd->record.products[0]));
274 strlcpy(ppd->record.psversions[0], psversion,
275 sizeof(ppd->record.psversions[0]));
276 strlcpy(ppd->record.make, make, sizeof(ppd->record.make));
277 strlcpy(ppd->record.make_and_model, make_and_model,
278 sizeof(ppd->record.make_and_model));
279 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
280 strlcpy(ppd->record.scheme, scheme, sizeof(ppd->record.scheme));
281
282 /*
283 * Strip confusing (and often wrong) "recommended" suffix added by
284 * Foomatic drivers...
285 */
286
287 if ((recommended = strstr(ppd->record.make_and_model,
288 " (recommended)")) != NULL)
289 *recommended = '\0';
290
291 /*
292 * Add the PPD to the PPD arrays...
293 */
294
295 cupsArrayAdd(PPDsByName, ppd);
296 cupsArrayAdd(PPDsByMakeModel, ppd);
297
298 /*
299 * Return the new PPD pointer...
300 */
301
302 return (ppd);
303 }
304
305
306 /*
307 * 'cat_drv()' - Generate a PPD from a driver info file.
308 */
309
310 static int /* O - Exit code */
cat_drv(const char * name,int request_id)311 cat_drv(const char *name, /* I - PPD name */
312 int request_id) /* I - Request ID for response? */
313 {
314 cups_file_t *fp; // File pointer
315 ppdcSource *src; // PPD source file data
316 ppdcDriver *d; // Current driver
317 cups_file_t *out; // Stdout via CUPS file API
318 char message[2048], // status-message
319 filename[1024], // Full path to .drv file(s)
320 scheme[32], // URI scheme ("drv")
321 userpass[256], // User/password info (unused)
322 host[2], // Hostname (unused)
323 resource[1024], // Resource path (/dir/to/filename.drv)
324 *pc_file_name; // Filename portion of URI
325 int port; // Port number (unused)
326
327
328 // Pull out the path to the .drv file...
329 if (httpSeparateURI(HTTP_URI_CODING_ALL, name, scheme, sizeof(scheme),
330 userpass, sizeof(userpass), host, sizeof(host), &port,
331 resource, sizeof(resource)) < HTTP_URI_OK)
332 {
333 fprintf(stderr, "ERROR: Bad PPD name \"%s\".\n", name);
334
335 if (request_id)
336 {
337 snprintf(message, sizeof(message), "Bad PPD name \"%s\".", name);
338
339 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
340 cupsdSendIPPGroup(IPP_TAG_OPERATION);
341 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
342 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
343 "en-US");
344 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
345 cupsdSendIPPTrailer();
346 }
347
348 return (1);
349 }
350
351 if ((fp = get_file(resource, request_id, "drv", filename, sizeof(filename), &pc_file_name)) == NULL || !pc_file_name)
352 return (1);
353
354 src = new ppdcSource(filename, fp);
355
356 for (d = (ppdcDriver *)src->drivers->first();
357 d;
358 d = (ppdcDriver *)src->drivers->next())
359 if (!strcmp(pc_file_name, d->pc_file_name->value) ||
360 (d->file_name && !strcmp(pc_file_name, d->file_name->value)))
361 break;
362
363 if (d)
364 {
365 ppdcArray *locales; // Locale names
366 ppdcCatalog *catalog; // Message catalog in .drv file
367
368
369 fprintf(stderr, "DEBUG2: [cups-driverd] %u locales defined in \"%s\"...\n", (unsigned)src->po_files->count, filename);
370
371 locales = new ppdcArray();
372 for (catalog = (ppdcCatalog *)src->po_files->first();
373 catalog;
374 catalog = (ppdcCatalog *)src->po_files->next())
375 {
376 fprintf(stderr, "DEBUG2: [cups-driverd] Adding locale \"%s\"...\n",
377 catalog->locale->value);
378 catalog->locale->retain();
379 locales->add(catalog->locale);
380 }
381
382 if (request_id)
383 {
384 cupsdSendIPPHeader(IPP_OK, request_id);
385 cupsdSendIPPGroup(IPP_TAG_OPERATION);
386 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
387 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
388 "en-US");
389 cupsdSendIPPTrailer();
390 fflush(stdout);
391 }
392
393 out = cupsFileStdout();
394 d->write_ppd_file(out, NULL, locales, src, PPDC_LFONLY);
395 cupsFileClose(out);
396
397 locales->release();
398 }
399 else
400 {
401 fprintf(stderr, "ERROR: PPD \"%s\" not found.\n", name);
402
403 if (request_id)
404 {
405 snprintf(message, sizeof(message), "PPD \"%s\" not found.", name);
406
407 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
408 cupsdSendIPPGroup(IPP_TAG_OPERATION);
409 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
410 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
411 "en-US");
412 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
413 cupsdSendIPPTrailer();
414 }
415 }
416
417 src->release();
418 cupsFileClose(fp);
419
420 return (!d);
421 }
422
423
424 /*
425 * 'cat_ppd()' - Copy a PPD file to stdout.
426 */
427
428 static void
cat_ppd(const char * name,int request_id)429 cat_ppd(const char *name, /* I - PPD name */
430 int request_id) /* I - Request ID for response? */
431 {
432 char scheme[256], /* Scheme from PPD name */
433 *sptr, /* Pointer into scheme */
434 line[1024], /* Line/filename */
435 message[2048]; /* status-message */
436
437
438 /*
439 * Figure out if this is a static or dynamic PPD file...
440 */
441
442 if (strstr(name, "../"))
443 {
444 fputs("ERROR: Invalid PPD name.\n", stderr);
445 exit(1);
446 }
447
448 strlcpy(scheme, name, sizeof(scheme));
449 if ((sptr = strchr(scheme, ':')) != NULL)
450 {
451 *sptr = '\0';
452
453 if (!strcmp(scheme, "file"))
454 {
455 /*
456 * "file:name" == "name"...
457 */
458
459 name += 5;
460
461 while (*name == '/')
462 name ++;
463
464 if (!strstr(name, ".tar/") && !strstr(name, ".tar.gz/"))
465 scheme[0] = '\0';
466 }
467 }
468 else
469 scheme[0] = '\0';
470
471 if (request_id > 0)
472 puts("Content-Type: application/ipp\n");
473
474 if (!scheme[0])
475 exit(cat_static(name, request_id));
476 else if (!strcmp(scheme, "drv"))
477 exit(cat_drv(name, request_id));
478 else if (!strcmp(scheme, "file"))
479 exit(cat_tar(name, request_id));
480 else
481 {
482 /*
483 * Dynamic PPD, see if we have a driver program to support it...
484 */
485
486 const char *serverbin; /* CUPS_SERVERBIN env var */
487 char *argv[4]; /* Arguments for program */
488
489
490 if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL)
491 serverbin = CUPS_SERVERBIN;
492
493 snprintf(line, sizeof(line), "%s/driver/%s", serverbin, scheme);
494 if (access(line, X_OK))
495 {
496 /*
497 * File does not exist or is not executable...
498 */
499
500 fprintf(stderr, "ERROR: [cups-driverd] Unable to access \"%s\" - %s\n",
501 line, strerror(errno));
502
503 if (request_id > 0)
504 {
505 snprintf(message, sizeof(message), "Unable to access \"%s\" - %s",
506 line, strerror(errno));
507
508 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
509 cupsdSendIPPGroup(IPP_TAG_OPERATION);
510 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
511 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
512 "en-US");
513 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
514 cupsdSendIPPTrailer();
515 }
516
517 exit(1);
518 }
519
520 /*
521 * Yes, let it cat the PPD file...
522 */
523
524 if (request_id)
525 {
526 cupsdSendIPPHeader(IPP_OK, request_id);
527 cupsdSendIPPGroup(IPP_TAG_OPERATION);
528 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
529 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
530 "en-US");
531 cupsdSendIPPTrailer();
532 }
533
534 argv[0] = scheme;
535 argv[1] = (char *)"cat";
536 argv[2] = (char *)name;
537 argv[3] = NULL;
538
539 if (cupsdExec(line, argv))
540 {
541 /*
542 * Unable to execute driver...
543 */
544
545 fprintf(stderr, "ERROR: [cups-driverd] Unable to execute \"%s\" - %s\n",
546 line, strerror(errno));
547 exit(1);
548 }
549 }
550
551 /*
552 * Exit with no errors...
553 */
554
555 exit(0);
556 }
557
558
559 /*
560 * 'copy_static()' - Copy a static PPD file to stdout.
561 */
562
563 static int /* O - Exit code */
cat_static(const char * name,int request_id)564 cat_static(const char *name, /* I - PPD name */
565 int request_id) /* I - Request ID for response? */
566 {
567 cups_file_t *fp; /* PPD file */
568 char filename[1024], /* PPD filename */
569 line[1024]; /* Line buffer */
570
571
572 if ((fp = get_file(name, request_id, "model", filename, sizeof(filename),
573 NULL)) == NULL)
574 return (1);
575
576 if (request_id)
577 {
578 cupsdSendIPPHeader(IPP_OK, request_id);
579 cupsdSendIPPGroup(IPP_TAG_OPERATION);
580 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
581 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
582 "en-US");
583 cupsdSendIPPTrailer();
584 }
585
586 /*
587 * Now copy the file to stdout...
588 */
589
590 while (cupsFileGets(fp, line, sizeof(line)))
591 puts(line);
592
593 cupsFileClose(fp);
594
595 return (0);
596 }
597
598
599 /*
600 * 'cat_tar()' - Copy an archived PPD file to stdout.
601 */
602
603 static int /* O - Exit code */
cat_tar(const char * name,int request_id)604 cat_tar(const char *name, /* I - PPD name */
605 int request_id) /* I - Request ID */
606 {
607 cups_file_t *fp; /* Archive file pointer */
608 char filename[1024], /* Archive filename */
609 *ppdname, /* PPD filename in archive */
610 curname[256], /* Current name in archive */
611 buffer[8192]; /* Copy buffer */
612 struct stat curinfo; /* Current file info in archive */
613 off_t total, /* Total bytes copied */
614 next; /* Offset for next record in archive */
615 ssize_t bytes; /* Bytes read */
616
617
618 /*
619 * Open the archive file...
620 */
621
622 if ((fp = get_file(name, request_id, "model", filename, sizeof(filename),
623 &ppdname)) == NULL || !ppdname)
624 return (1);
625
626 /*
627 * Scan the archive for the PPD...
628 */
629
630 while (read_tar(fp, curname, sizeof(curname), &curinfo))
631 {
632 next = cupsFileTell(fp) + ((curinfo.st_size + TAR_BLOCK - 1) &
633 ~(TAR_BLOCK - 1));
634
635 if (!strcmp(ppdname, curname))
636 {
637 if (request_id)
638 {
639 cupsdSendIPPHeader(IPP_OK, request_id);
640 cupsdSendIPPGroup(IPP_TAG_OPERATION);
641 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
642 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
643 "en-US");
644 cupsdSendIPPTrailer();
645 }
646
647 for (total = 0; total < curinfo.st_size; total += bytes)
648 {
649 if ((size_t)(bytes = (curinfo.st_size - total)) > sizeof(buffer))
650 bytes = sizeof(buffer);
651
652 if ((bytes = cupsFileRead(fp, buffer, (size_t)bytes)) < 0)
653 {
654 if (errno == EINTR || errno == EAGAIN)
655 {
656 bytes = 0;
657 }
658 else
659 {
660 perror("ERROR: [cups-driverd] Read error");
661 break;
662 }
663 }
664 else if (bytes > 0 && fwrite(buffer, (size_t)bytes, 1, stdout) != 1)
665 break;
666 }
667
668 cupsFileClose(fp);
669 return (0);
670 }
671
672 if (cupsFileTell(fp) != next)
673 {
674 if (cupsFileSeek(fp, next) != next)
675 break;
676 }
677 }
678
679 cupsFileClose(fp);
680
681 fprintf(stderr, "ERROR: PPD \"%s\" not found.\n", name);
682
683 if (request_id)
684 {
685 snprintf(buffer, sizeof(buffer), "PPD \"%s\" not found.", name);
686
687 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
688 cupsdSendIPPGroup(IPP_TAG_OPERATION);
689 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
690 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
691 "en-US");
692 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", buffer);
693 cupsdSendIPPTrailer();
694 }
695
696 return (1);
697 }
698
699
700 /*
701 * 'compare_inodes()' - Compare two inodes.
702 */
703
704 static int /* O - Result of comparison */
compare_inodes(struct stat * a,struct stat * b)705 compare_inodes(struct stat *a, /* I - First inode */
706 struct stat *b) /* I - Second inode */
707 {
708 if (a->st_dev != b->st_dev)
709 return (a->st_dev - b->st_dev);
710 else
711 return (a->st_ino - b->st_ino);
712 }
713
714
715 /*
716 * 'compare_matches()' - Compare PPD match scores for sorting.
717 */
718
719 static int
compare_matches(const ppd_info_t * p0,const ppd_info_t * p1)720 compare_matches(const ppd_info_t *p0, /* I - First PPD */
721 const ppd_info_t *p1) /* I - Second PPD */
722 {
723 if (p1->matches != p0->matches)
724 return (p1->matches - p0->matches);
725 else
726 return (cupsdCompareNames(p0->record.make_and_model,
727 p1->record.make_and_model));
728 }
729
730
731 /*
732 * 'compare_names()' - Compare PPD filenames for sorting.
733 */
734
735 static int /* O - Result of comparison */
compare_names(const ppd_info_t * p0,const ppd_info_t * p1)736 compare_names(const ppd_info_t *p0, /* I - First PPD file */
737 const ppd_info_t *p1) /* I - Second PPD file */
738 {
739 int diff; /* Difference between strings */
740
741
742 if ((diff = strcmp(p0->record.filename, p1->record.filename)) != 0)
743 return (diff);
744 else
745 return (strcmp(p0->record.name, p1->record.name));
746 }
747
748
749 /*
750 * 'compare_ppds()' - Compare PPD file make and model names for sorting.
751 */
752
753 static int /* O - Result of comparison */
compare_ppds(const ppd_info_t * p0,const ppd_info_t * p1)754 compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
755 const ppd_info_t *p1) /* I - Second PPD file */
756 {
757 int diff; /* Difference between strings */
758
759
760 /*
761 * First compare manufacturers...
762 */
763
764 if ((diff = _cups_strcasecmp(p0->record.make, p1->record.make)) != 0)
765 return (diff);
766 else if ((diff = cupsdCompareNames(p0->record.make_and_model,
767 p1->record.make_and_model)) != 0)
768 return (diff);
769 else if ((diff = strcmp(p0->record.languages[0],
770 p1->record.languages[0])) != 0)
771 return (diff);
772 else
773 return (compare_names(p0, p1));
774 }
775
776
777 /*
778 * 'dump_ppds_dat()' - Dump the contents of the ppds.dat file.
779 */
780
781 static void
dump_ppds_dat(const char * filename)782 dump_ppds_dat(const char *filename) /* I - Filename */
783 {
784 char temp[1024]; /* ppds.dat filename */
785 ppd_info_t *ppd; /* Current PPD */
786
787
788 /*
789 * See if we a PPD database file...
790 */
791
792 if (filename)
793 strlcpy(temp, filename, sizeof(temp));
794 else
795 temp[0] = '\0';
796
797 load_ppds_dat(temp, sizeof(temp), 0);
798
799 puts("mtime,size,model_number,type,filename,name,languages0,products0,"
800 "psversions0,make,make_and_model,device_id,scheme");
801 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByName);
802 ppd;
803 ppd = (ppd_info_t *)cupsArrayNext(PPDsByName))
804 printf("%d,%ld,%d,%d,\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\","
805 "\"%s\",\"%s\"\n",
806 (int)ppd->record.mtime, (long)ppd->record.size,
807 ppd->record.model_number, ppd->record.type, ppd->record.filename,
808 ppd->record.name, ppd->record.languages[0], ppd->record.products[0],
809 ppd->record.psversions[0], ppd->record.make,
810 ppd->record.make_and_model, ppd->record.device_id,
811 ppd->record.scheme);
812
813 exit(0);
814 }
815
816
817 /*
818 * 'free_array()' - Free an array of strings.
819 */
820
821 static void
free_array(cups_array_t * a)822 free_array(cups_array_t *a) /* I - Array to free */
823 {
824 char *ptr; /* Pointer to string */
825
826
827 for (ptr = (char *)cupsArrayFirst(a);
828 ptr;
829 ptr = (char *)cupsArrayNext(a))
830 free(ptr);
831
832 cupsArrayDelete(a);
833 }
834
835
836 /*
837 * 'get_file()' - Get the filename associated with a request.
838 */
839
840 static cups_file_t * /* O - File pointer or NULL */
get_file(const char * name,int request_id,const char * subdir,char * buffer,size_t bufsize,char ** subfile)841 get_file(const char *name, /* I - Name */
842 int request_id, /* I - Request ID */
843 const char *subdir, /* I - Subdirectory for file */
844 char *buffer, /* I - Filename buffer */
845 size_t bufsize, /* I - Size of filename buffer */
846 char **subfile) /* O - Sub-filename */
847 {
848 cups_file_t *fp; /* File pointer */
849 const char *datadir; /* CUPS_DATADIR env var */
850 char *bufptr, /* Pointer into filename buffer */
851 message[2048]; /* status-message */
852 #ifdef __APPLE__
853 const char *printerDriver, /* Pointer to .printerDriver extension */
854 *slash; /* Pointer to next slash */
855 #endif /* __APPLE__ */
856
857
858 if (subfile)
859 *subfile = NULL;
860
861 while (*name == '/')
862 name ++;
863
864 if (strstr(name, "../") || strstr(name, "/.."))
865 {
866 /*
867 * Bad name...
868 */
869
870 fprintf(stderr, "ERROR: [cups-driverd] Bad PPD name \"%s\".\n", name);
871
872 if (request_id)
873 {
874 snprintf(message, sizeof(message), "Bad PPD name \"%s\".", name);
875
876 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
877 cupsdSendIPPGroup(IPP_TAG_OPERATION);
878 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
879 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
880 "en-US");
881 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
882 cupsdSendIPPTrailer();
883 }
884
885 return (NULL);
886 }
887
888 /*
889 * Try opening the file...
890 */
891
892 #ifdef __APPLE__
893 if (!strncmp(name, "System/Library/Printers/PPDs/Contents/Resources/", 48) ||
894 !strncmp(name, "Library/Printers/PPDs/Contents/Resources/", 41) ||
895 (!strncmp(name, "System/Library/Printers/", 24) &&
896 (printerDriver =
897 strstr(name + 24,
898 ".printerDriver/Contents/Resources/PPDs")) != NULL &&
899 (slash = strchr(name + 24, '/')) != NULL &&
900 slash > printerDriver) ||
901 (!strncmp(name, "Library/Printers/", 17) &&
902 (printerDriver =
903 strstr(name + 17,
904 ".printerDriver/Contents/Resources/PPDs")) != NULL &&
905 (slash = strchr(name + 17, '/')) != NULL &&
906 slash > printerDriver))
907 {
908 /*
909 * Map ppd-name to macOS standard locations...
910 */
911
912 snprintf(buffer, bufsize, "/%s", name);
913 }
914 else
915
916 #elif defined(__linux)
917 if (!strncmp(name, "lsb/usr/", 8))
918 {
919 /*
920 * Map ppd-name to LSB standard /usr/share/ppd location...
921 */
922
923 snprintf(buffer, bufsize, "/usr/share/ppd/%s", name + 8);
924 }
925 else if (!strncmp(name, "lsb/opt/", 8))
926 {
927 /*
928 * Map ppd-name to LSB standard /opt/share/ppd location...
929 */
930
931 snprintf(buffer, bufsize, "/opt/share/ppd/%s", name + 8);
932 }
933 else if (!strncmp(name, "lsb/local/", 10))
934 {
935 /*
936 * Map ppd-name to LSB standard /usr/local/share/ppd location...
937 */
938
939 snprintf(buffer, bufsize, "/usr/local/share/ppd/%s", name + 10);
940 }
941 else
942
943 #endif /* __APPLE__ */
944 {
945 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
946 datadir = CUPS_DATADIR;
947
948 snprintf(buffer, bufsize, "%s/%s/%s", datadir, subdir, name);
949 }
950
951 /*
952 * Strip anything after ".drv/", ".drv.gz/", ".tar/", or ".tar.gz/"...
953 */
954
955 if (subfile)
956 {
957 if ((bufptr = strstr(buffer, ".drv/")) != NULL)
958 bufptr += 4;
959 else if ((bufptr = strstr(buffer, ".drv.gz/")) != NULL)
960 bufptr += 7;
961 else if ((bufptr = strstr(buffer, ".tar/")) != NULL)
962 bufptr += 4;
963 else if ((bufptr = strstr(buffer, ".tar.gz/")) != NULL)
964 bufptr += 7;
965
966 if (bufptr)
967 {
968 *bufptr++ = '\0';
969 *subfile = bufptr;
970 }
971 }
972
973 /*
974 * Try opening the file...
975 */
976
977 if ((fp = cupsFileOpen(buffer, "r")) == NULL)
978 {
979 fprintf(stderr, "ERROR: [cups-driverd] Unable to open \"%s\" - %s\n",
980 buffer, strerror(errno));
981
982 if (request_id)
983 {
984 snprintf(message, sizeof(message), "Unable to open \"%s\" - %s",
985 buffer, strerror(errno));
986
987 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
988 cupsdSendIPPGroup(IPP_TAG_OPERATION);
989 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
990 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
991 "en-US");
992 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
993 cupsdSendIPPTrailer();
994 }
995
996 return (NULL);
997 }
998
999 return (fp);
1000 }
1001
1002
1003 /*
1004 * 'list_ppds()' - List PPD files.
1005 */
1006
1007 static void
list_ppds(int request_id,int limit,const char * opt)1008 list_ppds(int request_id, /* I - Request ID */
1009 int limit, /* I - Limit */
1010 const char *opt) /* I - Option argument */
1011 {
1012 int i; /* Looping vars */
1013 int count; /* Number of PPDs to send */
1014 ppd_info_t *ppd; /* Current PPD file */
1015 cups_file_t *fp; /* ppds.dat file */
1016 char filename[1024], /* ppds.dat filename */
1017 model[1024]; /* Model directory */
1018 const char *cups_datadir; /* CUPS_DATADIR environment variable */
1019 int num_options; /* Number of options */
1020 cups_option_t *options; /* Options */
1021 cups_array_t *requested, /* requested-attributes values */
1022 *include, /* PPD schemes to include */
1023 *exclude; /* PPD schemes to exclude */
1024 const char *device_id, /* ppd-device-id option */
1025 *language, /* ppd-natural-language option */
1026 *make, /* ppd-make option */
1027 *make_and_model, /* ppd-make-and-model option */
1028 *model_number_str, /* ppd-model-number option */
1029 *product, /* ppd-product option */
1030 *psversion, /* ppd-psversion option */
1031 *type_str; /* ppd-type option */
1032 int model_number, /* ppd-model-number value */
1033 type, /* ppd-type value */
1034 send_device_id, /* Send ppd-device-id? */
1035 send_make, /* Send ppd-make? */
1036 send_make_and_model, /* Send ppd-make-and-model? */
1037 send_model_number, /* Send ppd-model-number? */
1038 send_name, /* Send ppd-name? */
1039 send_natural_language, /* Send ppd-natural-language? */
1040 send_product, /* Send ppd-product? */
1041 send_psversion, /* Send ppd-psversion? */
1042 send_type, /* Send ppd-type? */
1043 sent_header; /* Sent the IPP header? */
1044 size_t make_and_model_len, /* Length of ppd-make-and-model */
1045 product_len; /* Length of ppd-product */
1046 regex_t *device_id_re, /* Regular expression for matching device ID */
1047 *make_and_model_re; /* Regular expression for matching make and model */
1048 regmatch_t re_matches[6]; /* Regular expression matches */
1049 cups_array_t *matches; /* Matching PPDs */
1050
1051
1052 fprintf(stderr,
1053 "DEBUG2: [cups-driverd] list_ppds(request_id=%d, limit=%d, "
1054 "opt=\"%s\"\n", request_id, limit, opt);
1055
1056 /*
1057 * See if we a PPD database file...
1058 */
1059
1060 filename[0] = '\0';
1061 load_ppds_dat(filename, sizeof(filename), 1);
1062
1063 /*
1064 * Load all PPDs in the specified directory and below...
1065 */
1066
1067 if ((cups_datadir = getenv("CUPS_DATADIR")) == NULL)
1068 cups_datadir = CUPS_DATADIR;
1069
1070 Inodes = cupsArrayNew((cups_array_func_t)compare_inodes, NULL);
1071
1072 snprintf(model, sizeof(model), "%s/model", cups_datadir);
1073 load_ppds(model, "", 1);
1074
1075 snprintf(model, sizeof(model), "%s/drv", cups_datadir);
1076 load_ppds(model, "", 1);
1077
1078 #ifdef __APPLE__
1079 /*
1080 * Load PPDs from standard macOS locations...
1081 */
1082
1083 load_ppds("/Library/Printers",
1084 "Library/Printers", 0);
1085 load_ppds("/Library/Printers/PPDs/Contents/Resources",
1086 "Library/Printers/PPDs/Contents/Resources", 0);
1087 load_ppds("/Library/Printers/PPDs/Contents/Resources/en.lproj",
1088 "Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
1089 load_ppds("/System/Library/Printers",
1090 "System/Library/Printers", 0);
1091 load_ppds("/System/Library/Printers/PPDs/Contents/Resources",
1092 "System/Library/Printers/PPDs/Contents/Resources", 0);
1093 load_ppds("/System/Library/Printers/PPDs/Contents/Resources/en.lproj",
1094 "System/Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
1095
1096 #elif defined(__linux)
1097 /*
1098 * Load PPDs from LSB-defined locations...
1099 */
1100
1101 if (!access("/usr/local/share/ppd", 0))
1102 load_ppds("/usr/local/share/ppd", "lsb/local", 1);
1103 if (!access("/usr/share/ppd", 0))
1104 load_ppds("/usr/share/ppd", "lsb/usr", 1);
1105 if (!access("/opt/share/ppd", 0))
1106 load_ppds("/opt/share/ppd", "lsb/opt", 1);
1107 #endif /* __APPLE__ */
1108
1109 /*
1110 * Cull PPD files that are no longer present...
1111 */
1112
1113 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByName);
1114 ppd;
1115 ppd = (ppd_info_t *)cupsArrayNext(PPDsByName))
1116 if (!ppd->found)
1117 {
1118 /*
1119 * Remove this PPD file from the list...
1120 */
1121
1122 cupsArrayRemove(PPDsByName, ppd);
1123 cupsArrayRemove(PPDsByMakeModel, ppd);
1124 free(ppd);
1125
1126 ChangedPPD = 1;
1127 }
1128
1129 /*
1130 * Write the new ppds.dat file...
1131 */
1132
1133 fprintf(stderr, "DEBUG: [cups-driverd] ChangedPPD=%d\n", ChangedPPD);
1134
1135 if (ChangedPPD)
1136 {
1137 char newname[1024]; /* New filename */
1138
1139 snprintf(newname, sizeof(newname), "%s.%d", filename, (int)getpid());
1140
1141 if ((fp = cupsFileOpen(newname, "w")) != NULL)
1142 {
1143 unsigned ppdsync = PPD_SYNC; /* Sync word */
1144
1145 cupsFileWrite(fp, (char *)&ppdsync, sizeof(ppdsync));
1146
1147 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByName);
1148 ppd;
1149 ppd = (ppd_info_t *)cupsArrayNext(PPDsByName))
1150 cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
1151
1152 cupsFileClose(fp);
1153
1154 if (rename(newname, filename))
1155 fprintf(stderr, "ERROR: [cups-driverd] Unable to rename \"%s\" - %s\n",
1156 newname, strerror(errno));
1157 else
1158 fprintf(stderr, "INFO: [cups-driverd] Wrote \"%s\", %d PPDs...\n",
1159 filename, cupsArrayCount(PPDsByName));
1160 }
1161 else
1162 fprintf(stderr, "ERROR: [cups-driverd] Unable to write \"%s\" - %s\n",
1163 filename, strerror(errno));
1164 }
1165 else
1166 fputs("INFO: [cups-driverd] No new or changed PPDs...\n", stderr);
1167
1168 /*
1169 * Scan for dynamic PPD files...
1170 */
1171
1172 num_options = cupsParseOptions(opt, 0, &options);
1173 exclude = cupsdCreateStringsArray(cupsGetOption("exclude-schemes",
1174 num_options, options));
1175 include = cupsdCreateStringsArray(cupsGetOption("include-schemes",
1176 num_options, options));
1177
1178 load_drivers(include, exclude);
1179
1180 /*
1181 * Add the raw driver...
1182 */
1183
1184 add_ppd("", "raw", "en", "Raw", "Raw Queue", "", "", "", 0, 0, 0,
1185 PPD_TYPE_UNKNOWN, "raw");
1186
1187 /*
1188 * Send IPP attributes...
1189 */
1190
1191 requested = cupsdCreateStringsArray(
1192 cupsGetOption("requested-attributes", num_options,
1193 options));
1194 device_id = cupsGetOption("ppd-device-id", num_options, options);
1195 language = cupsGetOption("ppd-natural-language", num_options, options);
1196 make = cupsGetOption("ppd-make", num_options, options);
1197 make_and_model = cupsGetOption("ppd-make-and-model", num_options, options);
1198 model_number_str = cupsGetOption("ppd-model-number", num_options, options);
1199 product = cupsGetOption("ppd-product", num_options, options);
1200 psversion = cupsGetOption("ppd-psversion", num_options, options);
1201 type_str = cupsGetOption("ppd-type", num_options, options);
1202
1203 if (make_and_model)
1204 make_and_model_len = strlen(make_and_model);
1205 else
1206 make_and_model_len = 0;
1207
1208 if (product)
1209 product_len = strlen(product);
1210 else
1211 product_len = 0;
1212
1213 if (model_number_str)
1214 model_number = atoi(model_number_str);
1215 else
1216 model_number = 0;
1217
1218 if (type_str)
1219 {
1220 for (type = 0;
1221 type < (int)(sizeof(PPDTypes) / sizeof(PPDTypes[0]));
1222 type ++)
1223 if (!strcmp(type_str, PPDTypes[type]))
1224 break;
1225
1226 if (type >= (int)(sizeof(PPDTypes) / sizeof(PPDTypes[0])))
1227 {
1228 fprintf(stderr, "ERROR: [cups-driverd] Bad ppd-type=\"%s\" ignored!\n",
1229 type_str);
1230 type_str = NULL;
1231 }
1232 }
1233 else
1234 type = 0;
1235
1236 for (i = 0; i < num_options; i ++)
1237 fprintf(stderr, "DEBUG2: [cups-driverd] %s=\"%s\"\n", options[i].name,
1238 options[i].value);
1239
1240 if (!requested || cupsArrayFind(requested, (void *)"all") != NULL)
1241 {
1242 send_name = 1;
1243 send_make = 1;
1244 send_make_and_model = 1;
1245 send_model_number = 1;
1246 send_natural_language = 1;
1247 send_device_id = 1;
1248 send_product = 1;
1249 send_psversion = 1;
1250 send_type = 1;
1251 }
1252 else
1253 {
1254 send_name = cupsArrayFind(requested,
1255 (void *)"ppd-name") != NULL;
1256 send_make = cupsArrayFind(requested,
1257 (void *)"ppd-make") != NULL;
1258 send_make_and_model = cupsArrayFind(requested,
1259 (void *)"ppd-make-and-model") != NULL;
1260 send_model_number = cupsArrayFind(requested,
1261 (void *)"ppd-model-number") != NULL;
1262 send_natural_language = cupsArrayFind(requested,
1263 (void *)"ppd-natural-language") != NULL;
1264 send_device_id = cupsArrayFind(requested,
1265 (void *)"ppd-device-id") != NULL;
1266 send_product = cupsArrayFind(requested,
1267 (void *)"ppd-product") != NULL;
1268 send_psversion = cupsArrayFind(requested,
1269 (void *)"ppd-psversion") != NULL;
1270 send_type = cupsArrayFind(requested,
1271 (void *)"ppd-type") != NULL;
1272 }
1273
1274 /*
1275 * Send the content type header to the scheduler; request_id can only be
1276 * 0 when run manually since the scheduler enforces the IPP requirement for
1277 * a request ID from 1 to 2^31-1...
1278 */
1279
1280 if (request_id > 0)
1281 puts("Content-Type: application/ipp\n");
1282
1283 sent_header = 0;
1284
1285 if (limit <= 0 || limit > cupsArrayCount(PPDsByMakeModel))
1286 count = cupsArrayCount(PPDsByMakeModel);
1287 else
1288 count = limit;
1289
1290 if (device_id || language || make || make_and_model || model_number_str ||
1291 product)
1292 {
1293 matches = cupsArrayNew((cups_array_func_t)compare_matches, NULL);
1294
1295 if (device_id)
1296 device_id_re = regex_device_id(device_id);
1297 else
1298 device_id_re = NULL;
1299
1300 if (make_and_model)
1301 make_and_model_re = regex_string(make_and_model);
1302 else
1303 make_and_model_re = NULL;
1304
1305 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
1306 ppd;
1307 ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
1308 {
1309 /*
1310 * Filter PPDs based on make, model, product, language, model number,
1311 * and/or device ID using the "matches" score value. An exact match
1312 * for product, make-and-model, or device-id adds 3 to the score.
1313 * Partial matches for make-and-model yield 1 or 2 points, and matches
1314 * for the make and language add a single point. Results are then sorted
1315 * by score, highest score first.
1316 */
1317
1318 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1319 ppd->record.type >= PPD_TYPE_DRV)
1320 continue;
1321
1322 if (cupsArrayFind(exclude, ppd->record.scheme) ||
1323 (include && !cupsArrayFind(include, ppd->record.scheme)))
1324 continue;
1325
1326 ppd->matches = 0;
1327
1328 if (device_id_re &&
1329 !regexec(device_id_re, ppd->record.device_id,
1330 (size_t)(sizeof(re_matches) / sizeof(re_matches[0])),
1331 re_matches, 0))
1332 {
1333 /*
1334 * Add the number of matching values from the device ID - it will be
1335 * at least 2 (manufacturer and model), and as much as 3 (command set).
1336 */
1337
1338 for (i = 1; i < (int)(sizeof(re_matches) / sizeof(re_matches[0])); i ++)
1339 if (re_matches[i].rm_so >= 0)
1340 ppd->matches ++;
1341 }
1342
1343 if (language)
1344 {
1345 for (i = 0; i < PPD_MAX_LANG; i ++)
1346 if (!ppd->record.languages[i][0])
1347 break;
1348 else if (!strcmp(ppd->record.languages[i], language))
1349 {
1350 ppd->matches ++;
1351 break;
1352 }
1353 }
1354
1355 if (make && !_cups_strcasecmp(ppd->record.make, make))
1356 ppd->matches ++;
1357
1358 if (make_and_model_re &&
1359 !regexec(make_and_model_re, ppd->record.make_and_model,
1360 (size_t)(sizeof(re_matches) / sizeof(re_matches[0])),
1361 re_matches, 0))
1362 {
1363 // See how much of the make-and-model string we matched...
1364 if (re_matches[0].rm_so == 0)
1365 {
1366 if ((size_t)re_matches[0].rm_eo == make_and_model_len)
1367 ppd->matches += 3; // Exact match
1368 else
1369 ppd->matches += 2; // Prefix match
1370 }
1371 else
1372 ppd->matches ++; // Infix match
1373 }
1374
1375 if (model_number_str && ppd->record.model_number == model_number)
1376 ppd->matches ++;
1377
1378 if (product)
1379 {
1380 for (i = 0; i < PPD_MAX_PROD; i ++)
1381 if (!ppd->record.products[i][0])
1382 break;
1383 else if (!_cups_strcasecmp(ppd->record.products[i], product))
1384 {
1385 ppd->matches += 3;
1386 break;
1387 }
1388 else if (!_cups_strncasecmp(ppd->record.products[i], product,
1389 product_len))
1390 {
1391 ppd->matches += 2;
1392 break;
1393 }
1394 }
1395
1396 if (psversion)
1397 {
1398 for (i = 0; i < PPD_MAX_VERS; i ++)
1399 if (!ppd->record.psversions[i][0])
1400 break;
1401 else if (!_cups_strcasecmp(ppd->record.psversions[i], psversion))
1402 {
1403 ppd->matches ++;
1404 break;
1405 }
1406 }
1407
1408 if (type_str && ppd->record.type == type)
1409 ppd->matches ++;
1410
1411 if (ppd->matches)
1412 {
1413 fprintf(stderr, "DEBUG2: [cups-driverd] %s matches with score %d!\n",
1414 ppd->record.name, ppd->matches);
1415 cupsArrayAdd(matches, ppd);
1416 }
1417 }
1418 }
1419 else if (include || exclude)
1420 {
1421 matches = cupsArrayNew((cups_array_func_t)compare_ppds, NULL);
1422
1423 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
1424 ppd;
1425 ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
1426 {
1427 /*
1428 * Filter PPDs based on the include/exclude lists.
1429 */
1430
1431 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1432 ppd->record.type >= PPD_TYPE_DRV)
1433 continue;
1434
1435 if (cupsArrayFind(exclude, ppd->record.scheme) ||
1436 (include && !cupsArrayFind(include, ppd->record.scheme)))
1437 continue;
1438
1439 cupsArrayAdd(matches, ppd);
1440 }
1441 }
1442 else
1443 matches = PPDsByMakeModel;
1444
1445 for (ppd = (ppd_info_t *)cupsArrayFirst(matches);
1446 count > 0 && ppd;
1447 ppd = (ppd_info_t *)cupsArrayNext(matches))
1448 {
1449 /*
1450 * Skip invalid PPDs...
1451 */
1452
1453 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1454 ppd->record.type >= PPD_TYPE_DRV)
1455 continue;
1456
1457 /*
1458 * Send this PPD...
1459 */
1460
1461 if (!sent_header)
1462 {
1463 sent_header = 1;
1464
1465 if (request_id)
1466 {
1467 cupsdSendIPPHeader(IPP_OK, request_id);
1468 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1469 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
1470 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
1471 "en-US");
1472 }
1473 }
1474
1475 fprintf(stderr, "DEBUG2: [cups-driverd] Sending %s (%s)...\n",
1476 ppd->record.name, ppd->record.make_and_model);
1477
1478 count --;
1479
1480 if (request_id)
1481 {
1482 cupsdSendIPPGroup(IPP_TAG_PRINTER);
1483
1484 if (send_name)
1485 cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
1486
1487 if (send_natural_language)
1488 {
1489 cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
1490 ppd->record.languages[0]);
1491
1492 for (i = 1; i < PPD_MAX_LANG && ppd->record.languages[i][0]; i ++)
1493 cupsdSendIPPString(IPP_TAG_LANGUAGE, "", ppd->record.languages[i]);
1494 }
1495
1496 if (send_make)
1497 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
1498
1499 if (send_make_and_model)
1500 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model",
1501 ppd->record.make_and_model);
1502
1503 if (send_device_id)
1504 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-device-id",
1505 ppd->record.device_id);
1506
1507 if (send_product)
1508 {
1509 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-product",
1510 ppd->record.products[0]);
1511
1512 for (i = 1; i < PPD_MAX_PROD && ppd->record.products[i][0]; i ++)
1513 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.products[i]);
1514 }
1515
1516 if (send_psversion)
1517 {
1518 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-psversion",
1519 ppd->record.psversions[0]);
1520
1521 for (i = 1; i < PPD_MAX_VERS && ppd->record.psversions[i][0]; i ++)
1522 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.psversions[i]);
1523 }
1524
1525 if (send_type)
1526 {
1527 if (ppd->record.type < PPD_TYPE_POSTSCRIPT || ppd->record.type > PPD_TYPE_ARCHIVE)
1528 {
1529 /*
1530 * This cache file is corrupted, remove it!
1531 */
1532
1533 unlink(filename);
1534
1535 cupsdSendIPPString(IPP_TAG_KEYWORD, "ppd-type", PPDTypes[PPD_TYPE_UNKNOWN]);
1536 }
1537 else
1538 cupsdSendIPPString(IPP_TAG_KEYWORD, "ppd-type", PPDTypes[ppd->record.type]);
1539 }
1540
1541 if (send_model_number)
1542 cupsdSendIPPInteger(IPP_TAG_INTEGER, "ppd-model-number",
1543 ppd->record.model_number);
1544 }
1545 else
1546 printf("%s (%s)\n", ppd->record.name, ppd->record.make_and_model);
1547
1548 /*
1549 * If we have only requested the ppd-make attribute, then skip
1550 * the remaining PPDs with this make...
1551 */
1552
1553 if (cupsArrayFind(requested, (void *)"ppd-make") &&
1554 cupsArrayCount(requested) == 1)
1555 {
1556 const char *this_make; /* This ppd-make */
1557
1558
1559 for (this_make = ppd->record.make,
1560 ppd = (ppd_info_t *)cupsArrayNext(matches);
1561 ppd;
1562 ppd = (ppd_info_t *)cupsArrayNext(matches))
1563 if (_cups_strcasecmp(this_make, ppd->record.make))
1564 break;
1565
1566 cupsArrayPrev(matches);
1567 }
1568 }
1569
1570 if (!sent_header && request_id)
1571 {
1572 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
1573 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1574 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
1575 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
1576 }
1577
1578 if (request_id)
1579 cupsdSendIPPTrailer();
1580
1581 exit(0);
1582 }
1583
1584
1585 /*
1586 * 'load_drv()' - Load the PPDs from a driver information file.
1587 */
1588
1589 static int /* O - 1 on success, 0 on failure */
load_drv(const char * filename,const char * name,cups_file_t * fp,time_t mtime,off_t size)1590 load_drv(const char *filename, /* I - Actual filename */
1591 const char *name, /* I - Name to the rest of the world */
1592 cups_file_t *fp, /* I - File to read from */
1593 time_t mtime, /* I - Mod time of driver info file */
1594 off_t size) /* I - Size of driver info file */
1595 {
1596 ppdcSource *src; // Driver information file
1597 ppdcDriver *d; // Current driver
1598 ppdcAttr *device_id, // 1284DeviceID attribute
1599 *product, // Current product value
1600 *ps_version, // PSVersion attribute
1601 *cups_fax, // cupsFax attribute
1602 *nick_name; // NickName attribute
1603 ppdcFilter *filter; // Current filter
1604 ppd_info_t *ppd; // Current PPD
1605 int products_found; // Number of products found
1606 char uri[1024], // Driver URI
1607 make_model[1024]; // Make and model
1608 int type; // Driver type
1609
1610
1611 /*
1612 * Load the driver info file...
1613 */
1614
1615 src = new ppdcSource(filename, fp);
1616
1617 if (src->drivers->count == 0)
1618 {
1619 fprintf(stderr,
1620 "ERROR: [cups-driverd] Bad driver information file \"%s\"!\n",
1621 filename);
1622 src->release();
1623 return (0);
1624 }
1625
1626 /*
1627 * Add a dummy entry for the file...
1628 */
1629
1630 add_ppd(name, name, "", "", "", "", "", "", mtime, (size_t)size, 0, PPD_TYPE_DRV, "drv");
1631 ChangedPPD = 1;
1632
1633 /*
1634 * Then the drivers in the file...
1635 */
1636
1637 for (d = (ppdcDriver *)src->drivers->first();
1638 d;
1639 d = (ppdcDriver *)src->drivers->next())
1640 {
1641 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "drv", "", "", 0,
1642 "/%s/%s", name,
1643 d->file_name ? d->file_name->value :
1644 d->pc_file_name->value);
1645
1646 device_id = d->find_attr("1284DeviceID", NULL);
1647 ps_version = d->find_attr("PSVersion", NULL);
1648 nick_name = d->find_attr("NickName", NULL);
1649
1650 if (nick_name)
1651 strlcpy(make_model, nick_name->value->value, sizeof(make_model));
1652 else if (_cups_strncasecmp(d->model_name->value, d->manufacturer->value,
1653 strlen(d->manufacturer->value)))
1654 snprintf(make_model, sizeof(make_model), "%s %s, %s",
1655 d->manufacturer->value, d->model_name->value,
1656 d->version->value);
1657 else
1658 snprintf(make_model, sizeof(make_model), "%s, %s", d->model_name->value,
1659 d->version->value);
1660
1661 if ((cups_fax = d->find_attr("cupsFax", NULL)) != NULL &&
1662 !_cups_strcasecmp(cups_fax->value->value, "true"))
1663 type = PPD_TYPE_FAX;
1664 else if (d->type == PPDC_DRIVER_PS)
1665 type = PPD_TYPE_POSTSCRIPT;
1666 else if (d->type != PPDC_DRIVER_CUSTOM)
1667 type = PPD_TYPE_RASTER;
1668 else
1669 {
1670 for (filter = (ppdcFilter *)d->filters->first(),
1671 type = PPD_TYPE_POSTSCRIPT;
1672 filter;
1673 filter = (ppdcFilter *)d->filters->next())
1674 if (_cups_strcasecmp(filter->mime_type->value, "application/vnd.cups-raster"))
1675 type = PPD_TYPE_RASTER;
1676 else if (_cups_strcasecmp(filter->mime_type->value,
1677 "application/vnd.cups-pdf"))
1678 type = PPD_TYPE_PDF;
1679 }
1680
1681 for (product = (ppdcAttr *)d->attrs->first(), products_found = 0,
1682 ppd = NULL;
1683 product;
1684 product = (ppdcAttr *)d->attrs->next())
1685 if (!strcmp(product->name->value, "Product"))
1686 {
1687 if (!products_found)
1688 ppd = add_ppd(name, uri, "en", d->manufacturer->value, make_model, device_id ? device_id->value->value : "", product->value->value,
1689 ps_version ? ps_version->value->value : "(3010) 0", mtime, (size_t)size, d->model_number, type, "drv");
1690 else if (products_found < PPD_MAX_PROD)
1691 strlcpy(ppd->record.products[products_found], product->value->value, sizeof(ppd->record.products[0]));
1692 else
1693 break;
1694
1695 products_found ++;
1696 }
1697
1698 if (!products_found)
1699 add_ppd(name, uri, "en", d->manufacturer->value, make_model, device_id ? device_id->value->value : "", d->model_name->value, ps_version ? ps_version->value->value : "(3010) 0", mtime, (size_t)size, d->model_number, type, "drv");
1700 }
1701
1702 src->release();
1703
1704 return (1);
1705 }
1706
1707
1708 /*
1709 * 'load_drivers()' - Load driver-generated PPD files.
1710 */
1711
1712 static int /* O - 1 on success, 0 on failure */
load_drivers(cups_array_t * include,cups_array_t * exclude)1713 load_drivers(cups_array_t *include, /* I - Drivers to include */
1714 cups_array_t *exclude) /* I - Drivers to exclude */
1715 {
1716 int i; /* Looping var */
1717 char *start, /* Start of value */
1718 *ptr; /* Pointer into string */
1719 const char *server_bin, /* CUPS_SERVERBIN env variable */
1720 *scheme, /* Scheme for this driver */
1721 *scheme_end; /* Pointer to end of scheme */
1722 char drivers[1024]; /* Location of driver programs */
1723 int pid; /* Process ID for driver program */
1724 cups_file_t *fp; /* Pipe to driver program */
1725 cups_dir_t *dir; /* Directory pointer */
1726 cups_dentry_t *dent; /* Directory entry */
1727 char *argv[3], /* Arguments for command */
1728 filename[1024], /* Name of driver */
1729 line[2048], /* Line from driver */
1730 name[256], /* ppd-name */
1731 make[128], /* ppd-make */
1732 make_and_model[128], /* ppd-make-and-model */
1733 device_id[256], /* ppd-device-id */
1734 languages[128], /* ppd-natural-language */
1735 product[128], /* ppd-product */
1736 psversion[128], /* ppd-psversion */
1737 type_str[128]; /* ppd-type */
1738 int type; /* PPD type */
1739 ppd_info_t *ppd; /* Newly added PPD */
1740
1741
1742 /*
1743 * Try opening the driver directory...
1744 */
1745
1746 if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
1747 server_bin = CUPS_SERVERBIN;
1748
1749 snprintf(drivers, sizeof(drivers), "%s/driver", server_bin);
1750
1751 if ((dir = cupsDirOpen(drivers)) == NULL)
1752 {
1753 fprintf(stderr, "ERROR: [cups-driverd] Unable to open driver directory "
1754 "\"%s\": %s\n",
1755 drivers, strerror(errno));
1756 return (0);
1757 }
1758
1759 /*
1760 * Loop through all of the device drivers...
1761 */
1762
1763 argv[1] = (char *)"list";
1764 argv[2] = NULL;
1765
1766 while ((dent = cupsDirRead(dir)) != NULL)
1767 {
1768 /*
1769 * Only look at executable files...
1770 */
1771
1772 if (!(dent->fileinfo.st_mode & 0111) || !S_ISREG(dent->fileinfo.st_mode))
1773 continue;
1774
1775 /*
1776 * Include/exclude specific drivers...
1777 */
1778
1779 if (exclude)
1780 {
1781 /*
1782 * Look for "scheme" or "scheme*" (prefix match), and skip any matches.
1783 */
1784
1785 for (scheme = (char *)cupsArrayFirst(exclude);
1786 scheme;
1787 scheme = (char *)cupsArrayNext(exclude))
1788 {
1789 fprintf(stderr, "DEBUG: [cups-driverd] Exclude \"%s\" with \"%s\"?\n",
1790 dent->filename, scheme);
1791 scheme_end = scheme + strlen(scheme) - 1;
1792
1793 if ((scheme_end > scheme && *scheme_end == '*' &&
1794 !strncmp(scheme, dent->filename, (size_t)(scheme_end - scheme))) ||
1795 !strcmp(scheme, dent->filename))
1796 {
1797 fputs("DEBUG: [cups-driverd] Yes, exclude!\n", stderr);
1798 break;
1799 }
1800 }
1801
1802 if (scheme)
1803 continue;
1804 }
1805
1806 if (include)
1807 {
1808 /*
1809 * Look for "scheme" or "scheme*" (prefix match), and skip any non-matches.
1810 */
1811
1812 for (scheme = (char *)cupsArrayFirst(include);
1813 scheme;
1814 scheme = (char *)cupsArrayNext(include))
1815 {
1816 fprintf(stderr, "DEBUG: [cups-driverd] Include \"%s\" with \"%s\"?\n",
1817 dent->filename, scheme);
1818 scheme_end = scheme + strlen(scheme) - 1;
1819
1820 if ((scheme_end > scheme && *scheme_end == '*' &&
1821 !strncmp(scheme, dent->filename, (size_t)(scheme_end - scheme))) ||
1822 !strcmp(scheme, dent->filename))
1823 {
1824 fputs("DEBUG: [cups-driverd] Yes, include!\n", stderr);
1825 break;
1826 }
1827 }
1828
1829 if (!scheme)
1830 continue;
1831 }
1832 else
1833 scheme = dent->filename;
1834
1835 /*
1836 * Run the driver with no arguments and collect the output...
1837 */
1838
1839 snprintf(filename, sizeof(filename), "%s/%s", drivers, dent->filename);
1840
1841 if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_PROGRAM, !geteuid(),
1842 _cupsFileCheckFilter, NULL))
1843 continue;
1844
1845 argv[0] = dent->filename;
1846
1847 if ((fp = cupsdPipeCommand(&pid, filename, argv, 0)) != NULL)
1848 {
1849 while (cupsFileGets(fp, line, sizeof(line)))
1850 {
1851 /*
1852 * Each line is of the form:
1853 *
1854 * "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" \
1855 * "ppd-device-id" "ppd-product" "ppd-psversion"
1856 */
1857
1858 device_id[0] = '\0';
1859 product[0] = '\0';
1860 psversion[0] = '\0';
1861 strlcpy(type_str, "postscript", sizeof(type_str));
1862
1863 if (sscanf(line, "\"%255[^\"]\"%127s%*[ \t]\"%127[^\"]\""
1864 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%255[^\"]\""
1865 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
1866 "%*[ \t]\"%127[^\"]\"",
1867 name, languages, make, make_and_model,
1868 device_id, product, psversion, type_str) < 4)
1869 {
1870 /*
1871 * Bad format; strip trailing newline and write an error message.
1872 */
1873
1874 if (line[strlen(line) - 1] == '\n')
1875 line[strlen(line) - 1] = '\0';
1876
1877 fprintf(stderr, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n",
1878 dent->filename, line);
1879 break;
1880 }
1881 else
1882 {
1883 /*
1884 * Add the device to the array of available devices...
1885 */
1886
1887 if ((start = strchr(languages, ',')) != NULL)
1888 *start++ = '\0';
1889
1890 for (type = 0;
1891 type < (int)(sizeof(PPDTypes) / sizeof(PPDTypes[0]));
1892 type ++)
1893 if (!strcmp(type_str, PPDTypes[type]))
1894 break;
1895
1896 if (type >= (int)(sizeof(PPDTypes) / sizeof(PPDTypes[0])))
1897 {
1898 fprintf(stderr,
1899 "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n",
1900 type_str);
1901 type = PPD_TYPE_UNKNOWN;
1902 }
1903
1904 ppd = add_ppd(filename, name, languages, make, make_and_model,
1905 device_id, product, psversion, 0, 0, 0, type, scheme);
1906
1907 if (!ppd)
1908 {
1909 cupsDirClose(dir);
1910 cupsFileClose(fp);
1911 return (0);
1912 }
1913
1914 if (start && *start)
1915 {
1916 for (i = 1; i < PPD_MAX_LANG && *start; i ++)
1917 {
1918 if ((ptr = strchr(start, ',')) != NULL)
1919 *ptr++ = '\0';
1920 else
1921 ptr = start + strlen(start);
1922
1923 strlcpy(ppd->record.languages[i], start,
1924 sizeof(ppd->record.languages[0]));
1925
1926 start = ptr;
1927 }
1928 }
1929
1930 fprintf(stderr, "DEBUG2: [cups-driverd] Added dynamic PPD \"%s\"...\n",
1931 name);
1932 }
1933 }
1934
1935 cupsFileClose(fp);
1936 }
1937 else
1938 fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n",
1939 filename, strerror(errno));
1940 }
1941
1942 cupsDirClose(dir);
1943
1944 return (1);
1945 }
1946
1947
1948 /*
1949 * 'load_ppd()' - Load a PPD file.
1950 */
1951
1952 static void
load_ppd(const char * filename,const char * name,const char * scheme,struct stat * fileinfo,ppd_info_t * ppd,cups_file_t * fp,off_t end)1953 load_ppd(const char *filename, /* I - Real filename */
1954 const char *name, /* I - Virtual filename */
1955 const char *scheme, /* I - PPD scheme */
1956 struct stat *fileinfo, /* I - File information */
1957 ppd_info_t *ppd, /* I - Existing PPD file or NULL */
1958 cups_file_t *fp, /* I - File to read from */
1959 off_t end) /* I - End of file position or 0 */
1960 {
1961 int i; /* Looping var */
1962 char line[256], /* Line from file */
1963 *ptr, /* Pointer into line */
1964 lang_version[64], /* PPD LanguageVersion */
1965 lang_encoding[64], /* PPD LanguageEncoding */
1966 country[64], /* Country code */
1967 manufacturer[256], /* Manufacturer */
1968 make_model[256], /* Make and Model */
1969 model_name[256], /* ModelName */
1970 nick_name[256], /* NickName */
1971 device_id[256], /* 1284DeviceID */
1972 product[256], /* Product */
1973 psversion[256], /* PSVersion */
1974 temp[512]; /* Temporary make and model */
1975 int install_group, /* In the installable options group? */
1976 model_number, /* cupsModelNumber */
1977 type; /* ppd-type */
1978 cups_array_t *products, /* Product array */
1979 *psversions, /* PSVersion array */
1980 *cups_languages; /* cupsLanguages array */
1981 struct /* LanguageVersion translation table */
1982 {
1983 const char *version, /* LanguageVersion string */
1984 *language; /* Language code */
1985 } languages[] =
1986 {
1987 { "chinese", "zh" },
1988 { "czech", "cs" },
1989 { "danish", "da" },
1990 { "dutch", "nl" },
1991 { "english", "en" },
1992 { "finnish", "fi" },
1993 { "french", "fr" },
1994 { "german", "de" },
1995 { "greek", "el" },
1996 { "hungarian", "hu" },
1997 { "italian", "it" },
1998 { "japanese", "ja" },
1999 { "korean", "ko" },
2000 { "norwegian", "no" },
2001 { "polish", "pl" },
2002 { "portuguese", "pt" },
2003 { "russian", "ru" },
2004 { "simplified chinese", "zh_CN" },
2005 { "slovak", "sk" },
2006 { "spanish", "es" },
2007 { "swedish", "sv" },
2008 { "traditional chinese", "zh_TW" },
2009 { "turkish", "tr" }
2010 };
2011
2012
2013 /*
2014 * Now read until we get the required fields...
2015 */
2016
2017 cups_languages = cupsArrayNew(NULL, NULL);
2018 products = cupsArrayNew(NULL, NULL);
2019 psversions = cupsArrayNew(NULL, NULL);
2020
2021 model_name[0] = '\0';
2022 nick_name[0] = '\0';
2023 manufacturer[0] = '\0';
2024 device_id[0] = '\0';
2025 lang_encoding[0] = '\0';
2026 strlcpy(lang_version, "en", sizeof(lang_version));
2027 model_number = 0;
2028 install_group = 0;
2029 type = PPD_TYPE_POSTSCRIPT;
2030
2031 while ((end == 0 || cupsFileTell(fp) < end) &&
2032 cupsFileGets(fp, line, sizeof(line)))
2033 {
2034 if (!strncmp(line, "*Manufacturer:", 14))
2035 sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
2036 else if (!strncmp(line, "*ModelName:", 11))
2037 sscanf(line, "%*[^\"]\"%127[^\"]", model_name);
2038 else if (!strncmp(line, "*LanguageEncoding:", 18))
2039 sscanf(line, "%*[^:]:%63s", lang_encoding);
2040 else if (!strncmp(line, "*LanguageVersion:", 17))
2041 sscanf(line, "%*[^:]:%63s", lang_version);
2042 else if (!strncmp(line, "*NickName:", 10))
2043 sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
2044 else if (!_cups_strncasecmp(line, "*1284DeviceID:", 14))
2045 {
2046 sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
2047
2048 // Make sure device ID ends with a semicolon...
2049 if (device_id[0] && device_id[strlen(device_id) - 1] != ';')
2050 strlcat(device_id, ";", sizeof(device_id));
2051 }
2052 else if (!strncmp(line, "*Product:", 9))
2053 {
2054 if (sscanf(line, "%*[^\"]\"(%255[^\"]", product) == 1)
2055 {
2056 /*
2057 * Make sure the value ends with a right parenthesis - can't stop at
2058 * the first right paren since the product name may contain escaped
2059 * parenthesis...
2060 */
2061
2062 ptr = product + strlen(product) - 1;
2063 if (ptr > product && *ptr == ')')
2064 {
2065 /*
2066 * Yes, ends with a parenthesis, so remove it from the end and
2067 * add the product to the list...
2068 */
2069
2070 *ptr = '\0';
2071 cupsArrayAdd(products, strdup(product));
2072 }
2073 }
2074 }
2075 else if (!strncmp(line, "*PSVersion:", 11))
2076 {
2077 sscanf(line, "%*[^\"]\"%255[^\"]", psversion);
2078 cupsArrayAdd(psversions, strdup(psversion));
2079 }
2080 else if (!strncmp(line, "*cupsLanguages:", 15))
2081 {
2082 char *start; /* Start of language */
2083
2084
2085 for (start = line + 15; *start && isspace(*start & 255); start ++);
2086
2087 if (*start++ == '\"')
2088 {
2089 while (*start)
2090 {
2091 for (ptr = start + 1;
2092 *ptr && *ptr != '\"' && !isspace(*ptr & 255);
2093 ptr ++);
2094
2095 if (*ptr)
2096 {
2097 *ptr++ = '\0';
2098
2099 while (isspace(*ptr & 255))
2100 *ptr++ = '\0';
2101 }
2102
2103 cupsArrayAdd(cups_languages, strdup(start));
2104 start = ptr;
2105 }
2106 }
2107 }
2108 else if (!strncmp(line, "*cupsFax:", 9))
2109 {
2110 for (ptr = line + 9; isspace(*ptr & 255); ptr ++);
2111
2112 if (!_cups_strncasecmp(ptr, "true", 4))
2113 type = PPD_TYPE_FAX;
2114 }
2115 else if ((!strncmp(line, "*cupsFilter:", 12) || !strncmp(line, "*cupsFilter2:", 13)) && type == PPD_TYPE_POSTSCRIPT)
2116 {
2117 if (strstr(line + 12, "application/vnd.cups-raster"))
2118 type = PPD_TYPE_RASTER;
2119 else if (strstr(line + 12, "application/vnd.cups-pdf"))
2120 type = PPD_TYPE_PDF;
2121 }
2122 else if (!strncmp(line, "*cupsModelNumber:", 17))
2123 sscanf(line, "*cupsModelNumber:%d", &model_number);
2124 else if (!strncmp(line, "*OpenGroup: Installable", 23))
2125 install_group = 1;
2126 else if (!strncmp(line, "*CloseGroup:", 12))
2127 install_group = 0;
2128 else if (!strncmp(line, "*OpenUI", 7))
2129 {
2130 /*
2131 * Stop early if we have a NickName or ModelName attributes
2132 * before the first non-installable OpenUI...
2133 */
2134
2135 if (!install_group && (model_name[0] || nick_name[0]) &&
2136 cupsArrayCount(products) > 0 && cupsArrayCount(psversions) > 0)
2137 break;
2138 }
2139 }
2140
2141 /*
2142 * See if we got all of the required info...
2143 */
2144
2145 if (nick_name[0])
2146 cupsCharsetToUTF8((cups_utf8_t *)make_model, nick_name,
2147 sizeof(make_model), _ppdGetEncoding(lang_encoding));
2148 else
2149 strlcpy(make_model, model_name, sizeof(make_model));
2150
2151 while (isspace(make_model[0] & 255))
2152 _cups_strcpy(make_model, make_model + 1);
2153
2154 if (!make_model[0] || cupsArrayCount(products) == 0 ||
2155 cupsArrayCount(psversions) == 0)
2156 {
2157 /*
2158 * We don't have all the info needed, so skip this file...
2159 */
2160
2161 if (!make_model[0])
2162 fprintf(stderr, "WARNING: Missing NickName and ModelName in %s!\n",
2163 filename);
2164
2165 if (cupsArrayCount(products) == 0)
2166 fprintf(stderr, "WARNING: Missing Product in %s!\n", filename);
2167
2168 if (cupsArrayCount(psversions) == 0)
2169 fprintf(stderr, "WARNING: Missing PSVersion in %s!\n", filename);
2170
2171 free_array(products);
2172 free_array(psversions);
2173 free_array(cups_languages);
2174
2175 return;
2176 }
2177
2178 if (model_name[0])
2179 cupsArrayAdd(products, strdup(model_name));
2180
2181 /*
2182 * Normalize the make and model string...
2183 */
2184
2185 while (isspace(manufacturer[0] & 255))
2186 _cups_strcpy(manufacturer, manufacturer + 1);
2187
2188 if (!_cups_strncasecmp(make_model, manufacturer, strlen(manufacturer)))
2189 strlcpy(temp, make_model, sizeof(temp));
2190 else
2191 snprintf(temp, sizeof(temp), "%s %s", manufacturer, make_model);
2192
2193 _ppdNormalizeMakeAndModel(temp, make_model, sizeof(make_model));
2194
2195 /*
2196 * See if we got a manufacturer...
2197 */
2198
2199 if (!manufacturer[0] || !strcmp(manufacturer, "ESP"))
2200 {
2201 /*
2202 * Nope, copy the first part of the make and model then...
2203 */
2204
2205 strlcpy(manufacturer, make_model, sizeof(manufacturer));
2206
2207 /*
2208 * Truncate at the first space, dash, or slash, or make the
2209 * manufacturer "Other"...
2210 */
2211
2212 for (ptr = manufacturer; *ptr; ptr ++)
2213 if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
2214 break;
2215
2216 if (*ptr && ptr > manufacturer)
2217 *ptr = '\0';
2218 else
2219 strlcpy(manufacturer, "Other", sizeof(manufacturer));
2220 }
2221 else if (!_cups_strncasecmp(manufacturer, "LHAG", 4) ||
2222 !_cups_strncasecmp(manufacturer, "linotype", 8))
2223 strlcpy(manufacturer, "LHAG", sizeof(manufacturer));
2224 else if (!_cups_strncasecmp(manufacturer, "Hewlett", 7))
2225 strlcpy(manufacturer, "HP", sizeof(manufacturer));
2226
2227 /*
2228 * Fix the lang_version as needed...
2229 */
2230
2231 if ((ptr = strchr(lang_version, '-')) != NULL)
2232 *ptr++ = '\0';
2233 else if ((ptr = strchr(lang_version, '_')) != NULL)
2234 *ptr++ = '\0';
2235
2236 if (ptr)
2237 {
2238 /*
2239 * Set the country suffix...
2240 */
2241
2242 country[0] = '_';
2243 _cups_strcpy(country + 1, ptr);
2244 }
2245 else
2246 {
2247 /*
2248 * No country suffix...
2249 */
2250
2251 country[0] = '\0';
2252 }
2253
2254 for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
2255 if (!_cups_strcasecmp(languages[i].version, lang_version))
2256 break;
2257
2258 if (i < (int)(sizeof(languages) / sizeof(languages[0])))
2259 {
2260 /*
2261 * Found a known language...
2262 */
2263
2264 snprintf(lang_version, sizeof(lang_version), "%s%s",
2265 languages[i].language, country);
2266 }
2267 else
2268 {
2269 /*
2270 * Unknown language; use "xx"...
2271 */
2272
2273 strlcpy(lang_version, "xx", sizeof(lang_version));
2274 }
2275
2276 /*
2277 * Record the PPD file...
2278 */
2279
2280 if (!ppd)
2281 {
2282 /*
2283 * Add new PPD file...
2284 */
2285
2286 fprintf(stderr, "DEBUG2: [cups-driverd] Adding ppd \"%s\"...\n", name);
2287
2288 ppd = add_ppd(name, name, lang_version, manufacturer, make_model, device_id, (char *)cupsArrayFirst(products), (char *)cupsArrayFirst(psversions), fileinfo->st_mtime, (size_t)fileinfo->st_size, model_number, type, scheme);
2289
2290 if (!ppd)
2291 return;
2292 }
2293 else
2294 {
2295 /*
2296 * Update existing record...
2297 */
2298
2299 fprintf(stderr, "DEBUG2: [cups-driverd] Updating ppd \"%s\"...\n", name);
2300
2301 memset(ppd, 0, sizeof(ppd_info_t));
2302
2303 ppd->found = 1;
2304 ppd->record.mtime = fileinfo->st_mtime;
2305 ppd->record.size = fileinfo->st_size;
2306 ppd->record.model_number = model_number;
2307 ppd->record.type = type;
2308
2309 strlcpy(ppd->record.filename, name, sizeof(ppd->record.filename));
2310 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
2311 strlcpy(ppd->record.languages[0], lang_version,
2312 sizeof(ppd->record.languages[0]));
2313 strlcpy(ppd->record.products[0], (char *)cupsArrayFirst(products),
2314 sizeof(ppd->record.products[0]));
2315 strlcpy(ppd->record.psversions[0], (char *)cupsArrayFirst(psversions),
2316 sizeof(ppd->record.psversions[0]));
2317 strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make));
2318 strlcpy(ppd->record.make_and_model, make_model,
2319 sizeof(ppd->record.make_and_model));
2320 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
2321 strlcpy(ppd->record.scheme, scheme, sizeof(ppd->record.scheme));
2322 }
2323
2324 /*
2325 * Add remaining products, versions, and languages...
2326 */
2327
2328 for (i = 1;
2329 i < PPD_MAX_PROD && (ptr = (char *)cupsArrayNext(products)) != NULL;
2330 i ++)
2331 strlcpy(ppd->record.products[i], ptr,
2332 sizeof(ppd->record.products[0]));
2333
2334 for (i = 1;
2335 i < PPD_MAX_VERS && (ptr = (char *)cupsArrayNext(psversions)) != NULL;
2336 i ++)
2337 strlcpy(ppd->record.psversions[i], ptr,
2338 sizeof(ppd->record.psversions[0]));
2339
2340 for (i = 1, ptr = (char *)cupsArrayFirst(cups_languages);
2341 i < PPD_MAX_LANG && ptr;
2342 i ++, ptr = (char *)cupsArrayNext(cups_languages))
2343 strlcpy(ppd->record.languages[i], ptr,
2344 sizeof(ppd->record.languages[0]));
2345
2346 /*
2347 * Free products, versions, and languages...
2348 */
2349
2350 free_array(cups_languages);
2351 free_array(products);
2352 free_array(psversions);
2353
2354 ChangedPPD = 1;
2355 }
2356
2357
2358 /*
2359 * 'load_ppds()' - Load PPD files recursively.
2360 */
2361
2362 static int /* O - 1 on success, 0 on failure */
load_ppds(const char * d,const char * p,int descend)2363 load_ppds(const char *d, /* I - Actual directory */
2364 const char *p, /* I - Virtual path in name */
2365 int descend) /* I - Descend into directories? */
2366 {
2367 struct stat dinfo, /* Directory information */
2368 *dinfoptr; /* Pointer to match */
2369 cups_file_t *fp; /* Pointer to file */
2370 cups_dir_t *dir; /* Directory pointer */
2371 cups_dentry_t *dent; /* Directory entry */
2372 char filename[1024], /* Name of PPD or directory */
2373 line[256], /* Line from file */
2374 *ptr, /* Pointer into name */
2375 name[256]; /* Name of PPD file */
2376 ppd_info_t *ppd, /* New PPD file */
2377 key; /* Search key */
2378
2379
2380 /*
2381 * See if we've loaded this directory before...
2382 */
2383
2384 if (stat(d, &dinfo))
2385 {
2386 if (errno != ENOENT)
2387 fprintf(stderr, "ERROR: [cups-driverd] Unable to stat \"%s\": %s\n", d,
2388 strerror(errno));
2389
2390 return (0);
2391 }
2392 else if (cupsArrayFind(Inodes, &dinfo))
2393 {
2394 fprintf(stderr, "ERROR: [cups-driverd] Skipping \"%s\": loop detected!\n",
2395 d);
2396 return (1);
2397 }
2398
2399 /*
2400 * Nope, add it to the Inodes array and continue...
2401 */
2402
2403 if ((dinfoptr = (struct stat *)malloc(sizeof(struct stat))) == NULL)
2404 {
2405 fputs("ERROR: [cups-driverd] Unable to allocate memory for directory info.\n",
2406 stderr);
2407 exit(1);
2408 }
2409 memcpy(dinfoptr, &dinfo, sizeof(struct stat));
2410 cupsArrayAdd(Inodes, dinfoptr);
2411
2412 /*
2413 * Check permissions...
2414 */
2415
2416 if (_cupsFileCheck(d, _CUPS_FILE_CHECK_DIRECTORY, !geteuid(),
2417 _cupsFileCheckFilter, NULL))
2418 return (0);
2419
2420 if ((dir = cupsDirOpen(d)) == NULL)
2421 {
2422 if (errno != ENOENT)
2423 fprintf(stderr,
2424 "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
2425 d, strerror(errno));
2426
2427 return (0);
2428 }
2429
2430 fprintf(stderr, "DEBUG: [cups-driverd] Loading \"%s\"...\n", d);
2431
2432 while ((dent = cupsDirRead(dir)) != NULL)
2433 {
2434 /*
2435 * Skip files/directories starting with "."...
2436 */
2437
2438 if (dent->filename[0] == '.')
2439 continue;
2440
2441 /*
2442 * See if this is a file...
2443 */
2444
2445 snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename);
2446
2447 if (p[0])
2448 snprintf(name, sizeof(name), "%s/%s", p, dent->filename);
2449 else
2450 strlcpy(name, dent->filename, sizeof(name));
2451
2452 if (S_ISDIR(dent->fileinfo.st_mode))
2453 {
2454 /*
2455 * Do subdirectory...
2456 */
2457
2458 if (descend)
2459 {
2460 if (!load_ppds(filename, name, 1))
2461 {
2462 cupsDirClose(dir);
2463 return (1);
2464 }
2465 }
2466 else if ((ptr = filename + strlen(filename) - 14) > filename &&
2467 !strcmp(ptr, ".printerDriver"))
2468 {
2469 /*
2470 * Load PPDs in a printer driver bundle.
2471 */
2472
2473 if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_DIRECTORY, !geteuid(),
2474 _cupsFileCheckFilter, NULL))
2475 continue;
2476
2477 strlcat(filename, "/Contents/Resources/PPDs", sizeof(filename));
2478 strlcat(name, "/Contents/Resources/PPDs", sizeof(name));
2479
2480 load_ppds(filename, name, 0);
2481 }
2482
2483 continue;
2484 }
2485 else if (strstr(filename, ".plist"))
2486 {
2487 /*
2488 * Skip plist files in the PPDs directory...
2489 */
2490
2491 continue;
2492 }
2493 else if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_FILE_ONLY, !geteuid(),
2494 _cupsFileCheckFilter, NULL))
2495 continue;
2496
2497 /*
2498 * See if this file has been scanned before...
2499 */
2500
2501 strlcpy(key.record.filename, name, sizeof(key.record.filename));
2502 strlcpy(key.record.name, name, sizeof(key.record.name));
2503
2504 ppd = (ppd_info_t *)cupsArrayFind(PPDsByName, &key);
2505
2506 if (ppd &&
2507 ppd->record.size == dent->fileinfo.st_size &&
2508 ppd->record.mtime == dent->fileinfo.st_mtime)
2509 {
2510 /*
2511 * Rewind to the first entry for this file...
2512 */
2513
2514 while ((ppd = (ppd_info_t *)cupsArrayPrev(PPDsByName)) != NULL &&
2515 !strcmp(ppd->record.filename, name));
2516
2517 /*
2518 * Then mark all of the matches for this file as found...
2519 */
2520
2521 while ((ppd = (ppd_info_t *)cupsArrayNext(PPDsByName)) != NULL &&
2522 !strcmp(ppd->record.filename, name))
2523 ppd->found = 1;
2524
2525 continue;
2526 }
2527
2528 /*
2529 * No, file is new/changed, so re-scan it...
2530 */
2531
2532 if ((fp = cupsFileOpen(filename, "r")) == NULL)
2533 continue;
2534
2535 /*
2536 * Now see if this is a PPD file...
2537 */
2538
2539 line[0] = '\0';
2540 cupsFileGets(fp, line, sizeof(line));
2541
2542 if (!strncmp(line, "*PPD-Adobe:", 11))
2543 {
2544 /*
2545 * Yes, load it...
2546 */
2547
2548 load_ppd(filename, name, "file", &dent->fileinfo, ppd, fp, 0);
2549 }
2550 else
2551 {
2552 /*
2553 * Nope, treat it as a driver information file or archive...
2554 */
2555
2556 cupsFileRewind(fp);
2557
2558 if ((ptr = strstr(filename, ".tar")) != NULL &&
2559 (!strcmp(ptr, ".tar") || !strcmp(ptr, ".tar.gz")))
2560 load_tar(filename, name, fp, dent->fileinfo.st_mtime,
2561 dent->fileinfo.st_size);
2562 else
2563 load_drv(filename, name, fp, dent->fileinfo.st_mtime,
2564 dent->fileinfo.st_size);
2565 }
2566
2567 /*
2568 * Close the file...
2569 */
2570
2571 cupsFileClose(fp);
2572 }
2573
2574 cupsDirClose(dir);
2575
2576 return (1);
2577 }
2578
2579
2580 /*
2581 * 'load_ppds_dat()' - Load the ppds.dat file.
2582 */
2583
2584 static void
load_ppds_dat(char * filename,size_t filesize,int verbose)2585 load_ppds_dat(char *filename, /* I - Filename buffer */
2586 size_t filesize, /* I - Size of filename buffer */
2587 int verbose) /* I - Be verbose? */
2588 {
2589 ppd_info_t *ppd; /* Current PPD file */
2590 cups_file_t *fp; /* ppds.dat file */
2591 struct stat fileinfo; /* ppds.dat information */
2592 const char *cups_cachedir; /* CUPS_CACHEDIR environment variable */
2593
2594
2595 PPDsByName = cupsArrayNew((cups_array_func_t)compare_names, NULL);
2596 PPDsByMakeModel = cupsArrayNew((cups_array_func_t)compare_ppds, NULL);
2597 ChangedPPD = 0;
2598
2599 if (!filename[0])
2600 {
2601 if ((cups_cachedir = getenv("CUPS_CACHEDIR")) == NULL)
2602 cups_cachedir = CUPS_CACHEDIR;
2603
2604 snprintf(filename, filesize, "%s/ppds.dat", cups_cachedir);
2605 }
2606
2607 if ((fp = cupsFileOpen(filename, "r")) != NULL)
2608 {
2609 /*
2610 * See if we have the right sync word...
2611 */
2612
2613 unsigned ppdsync; /* Sync word */
2614 int num_ppds; /* Number of PPDs */
2615
2616 if ((size_t)cupsFileRead(fp, (char *)&ppdsync, sizeof(ppdsync)) == sizeof(ppdsync) &&
2617 ppdsync == PPD_SYNC &&
2618 !stat(filename, &fileinfo) &&
2619 (((size_t)fileinfo.st_size - sizeof(ppdsync)) % sizeof(ppd_rec_t)) == 0 &&
2620 (num_ppds = ((size_t)fileinfo.st_size - sizeof(ppdsync)) / sizeof(ppd_rec_t)) > 0)
2621 {
2622 /*
2623 * We have a ppds.dat file, so read it!
2624 */
2625
2626 for (; num_ppds > 0; num_ppds --)
2627 {
2628 if ((ppd = (ppd_info_t *)calloc(1, sizeof(ppd_info_t))) == NULL)
2629 {
2630 fputs("ERROR: [cups-driverd] Unable to allocate memory for PPD!\n",
2631 stderr);
2632 exit(1);
2633 }
2634
2635 if (cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t)) > 0)
2636 {
2637 cupsArrayAdd(PPDsByName, ppd);
2638 cupsArrayAdd(PPDsByMakeModel, ppd);
2639 }
2640 else
2641 {
2642 free(ppd);
2643 break;
2644 }
2645 }
2646
2647 if (verbose)
2648 fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
2649 filename, cupsArrayCount(PPDsByName));
2650 }
2651
2652 cupsFileClose(fp);
2653 }
2654 }
2655
2656
2657 /*
2658 * 'load_tar()' - Load archived PPD files.
2659 */
2660
2661 static int /* O - 1 on success, 0 on failure */
load_tar(const char * filename,const char * name,cups_file_t * fp,time_t mtime,off_t size)2662 load_tar(const char *filename, /* I - Actual filename */
2663 const char *name, /* I - Name to the rest of the world */
2664 cups_file_t *fp, /* I - File to read from */
2665 time_t mtime, /* I - Mod time of driver info file */
2666 off_t size) /* I - Size of driver info file */
2667 {
2668 char curname[256], /* Current archive file name */
2669 uri[1024]; /* Virtual file URI */
2670 const char *curext; /* Extension on file */
2671 struct stat curinfo; /* Current archive file information */
2672 off_t next; /* Position for next header */
2673
2674
2675 /*
2676 * Add a dummy entry for the file...
2677 */
2678
2679 (void)filename;
2680
2681 add_ppd(name, name, "", "", "", "", "", "", mtime, (size_t)size, 0, PPD_TYPE_ARCHIVE, "file");
2682 ChangedPPD = 1;
2683
2684 /*
2685 * Scan for PPDs in the archive...
2686 */
2687
2688 while (read_tar(fp, curname, sizeof(curname), &curinfo))
2689 {
2690 next = cupsFileTell(fp) + ((curinfo.st_size + TAR_BLOCK - 1) &
2691 ~(TAR_BLOCK - 1));
2692
2693 if ((curext = strrchr(curname, '.')) != NULL &&
2694 !_cups_strcasecmp(curext, ".ppd"))
2695 {
2696 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "file", "", "",
2697 0, "/%s/%s", name, curname);
2698 load_ppd(name, uri, "file", &curinfo, NULL, fp, next);
2699 }
2700
2701 if (cupsFileTell(fp) != next)
2702 cupsFileSeek(fp, next);
2703 }
2704
2705 return (1);
2706 }
2707
2708
2709 /*
2710 * 'read_tar()' - Read a file header from an archive.
2711 *
2712 * This function skips all directories and special files.
2713 */
2714
2715 static int /* O - 1 if found, 0 on EOF */
read_tar(cups_file_t * fp,char * name,size_t namesize,struct stat * info)2716 read_tar(cups_file_t *fp, /* I - Archive to read */
2717 char *name, /* I - Filename buffer */
2718 size_t namesize, /* I - Size of filename buffer */
2719 struct stat *info) /* O - File information */
2720 {
2721 tar_rec_t record; /* Record from file */
2722
2723
2724 while ((size_t)cupsFileRead(fp, (char *)&record, sizeof(record)) == sizeof(record))
2725 {
2726 /*
2727 * Check for a valid tar header...
2728 */
2729
2730 if (memcmp(record.header.magic, TAR_MAGIC, 6) ||
2731 memcmp(record.header.version, TAR_VERSION, 2))
2732 {
2733 if (record.header.magic[0] ||
2734 memcmp(record.header.magic, record.header.magic + 1, 5))
2735 fputs("ERROR: [cups-driverd] Bad tar magic/version.\n", stderr);
2736 break;
2737 }
2738
2739 /*
2740 * Ignore non-files...
2741 */
2742
2743 if (record.header.linkflag != TAR_OLDNORMAL &&
2744 record.header.linkflag != TAR_NORMAL)
2745 continue;
2746
2747 /*
2748 * Grab size and name from tar header and return...
2749 */
2750
2751 if (record.header.prefix[0])
2752 snprintf(name, namesize, "%s/%s", record.header.prefix,
2753 record.header.pathname);
2754 else
2755 strlcpy(name, record.header.pathname, namesize);
2756
2757 info->st_mtime = strtol(record.header.mtime, NULL, 8);
2758 info->st_size = strtoll(record.header.size, NULL, 8);
2759
2760 return (1);
2761 }
2762
2763 return (0);
2764 }
2765
2766
2767 /*
2768 * 'regex_device_id()' - Compile a regular expression based on the 1284 device
2769 * ID.
2770 */
2771
2772 static regex_t * /* O - Regular expression */
regex_device_id(const char * device_id)2773 regex_device_id(const char *device_id) /* I - IEEE-1284 device ID */
2774 {
2775 char res[2048], /* Regular expression string */
2776 *ptr; /* Pointer into string */
2777 regex_t *re; /* Regular expression */
2778 int cmd; /* Command set string? */
2779
2780
2781 fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id(\"%s\")\n", device_id);
2782
2783 /*
2784 * Scan the device ID string and insert class, command set, manufacturer, and
2785 * model attributes to match. We assume that the device ID in the PPD and the
2786 * device ID reported by the device itself use the same attribute names and
2787 * order of attributes.
2788 */
2789
2790 ptr = res;
2791
2792 while (*device_id && ptr < (res + sizeof(res) - 6))
2793 {
2794 cmd = !_cups_strncasecmp(device_id, "COMMAND SET:", 12) ||
2795 !_cups_strncasecmp(device_id, "CMD:", 4);
2796
2797 if (cmd || !_cups_strncasecmp(device_id, "MANUFACTURER:", 13) ||
2798 !_cups_strncasecmp(device_id, "MFG:", 4) ||
2799 !_cups_strncasecmp(device_id, "MFR:", 4) ||
2800 !_cups_strncasecmp(device_id, "MODEL:", 6) ||
2801 !_cups_strncasecmp(device_id, "MDL:", 4))
2802 {
2803 if (ptr > res)
2804 {
2805 *ptr++ = '.';
2806 *ptr++ = '*';
2807 }
2808
2809 *ptr++ = '(';
2810
2811 while (*device_id && *device_id != ';' && ptr < (res + sizeof(res) - 8))
2812 {
2813 if (strchr("[]{}().*\\|", *device_id))
2814 *ptr++ = '\\';
2815 if (*device_id == ':')
2816 {
2817 /*
2818 * KEY:.*value
2819 */
2820
2821 *ptr++ = *device_id++;
2822 *ptr++ = '.';
2823 *ptr++ = '*';
2824 }
2825 else
2826 *ptr++ = *device_id++;
2827 }
2828
2829 if (*device_id == ';' || !*device_id)
2830 {
2831 /*
2832 * KEY:.*value.*;
2833 */
2834
2835 *ptr++ = '.';
2836 *ptr++ = '*';
2837 *ptr++ = ';';
2838 }
2839 *ptr++ = ')';
2840 if (cmd)
2841 *ptr++ = '?';
2842 }
2843 else if ((device_id = strchr(device_id, ';')) == NULL)
2844 break;
2845 else
2846 device_id ++;
2847 }
2848
2849 *ptr = '\0';
2850
2851 fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id: \"%s\"\n", res);
2852
2853 /*
2854 * Compile the regular expression and return...
2855 */
2856
2857 if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
2858 {
2859 if (!regcomp(re, res, REG_EXTENDED | REG_ICASE))
2860 {
2861 fputs("DEBUG: [cups-driverd] regex_device_id: OK\n", stderr);
2862 return (re);
2863 }
2864
2865 free(re);
2866 }
2867
2868 return (NULL);
2869 }
2870
2871
2872 /*
2873 * 'regex_string()' - Construct a regular expression to compare a simple string.
2874 */
2875
2876 static regex_t * /* O - Regular expression */
regex_string(const char * s)2877 regex_string(const char *s) /* I - String to compare */
2878 {
2879 char res[2048], /* Regular expression string */
2880 *ptr; /* Pointer into string */
2881 regex_t *re; /* Regular expression */
2882
2883
2884 fprintf(stderr, "DEBUG: [cups-driverd] regex_string(\"%s\")\n", s);
2885
2886 /*
2887 * Convert the string to a regular expression, escaping special characters
2888 * as needed.
2889 */
2890
2891 ptr = res;
2892
2893 while (*s && ptr < (res + sizeof(res) - 2))
2894 {
2895 if (strchr("[]{}().*\\", *s))
2896 *ptr++ = '\\';
2897
2898 *ptr++ = *s++;
2899 }
2900
2901 *ptr = '\0';
2902
2903 fprintf(stderr, "DEBUG: [cups-driverd] regex_string: \"%s\"\n", res);
2904
2905 /*
2906 * Create a case-insensitive regular expression...
2907 */
2908
2909 if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
2910 {
2911 if (!regcomp(re, res, REG_ICASE))
2912 {
2913 fputs("DEBUG: [cups-driverd] regex_string: OK\n", stderr);
2914 return (re);
2915 }
2916
2917 free(re);
2918 }
2919
2920 return (NULL);
2921 }
2922