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