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