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