• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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