]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-driverd.cxx
Merge changes from CUPS 1.4svn-r7696.
[thirdparty/cups.git] / scheduler / cups-driverd.cxx
1 /*
2 * "$Id$"
3 *
4 * PPD/driver support for the Common UNIX Printing System (CUPS).
5 *
6 * This program handles listing and installing both static PPD files
7 * in CUPS_DATADIR/model and dynamically generated PPD files using
8 * the driver helper programs in CUPS_SERVERBIN/driver.
9 *
10 * Copyright 2007-2008 by Apple Inc.
11 * Copyright 1997-2007 by Easy Software Products.
12 *
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/".
18 *
19 * Contents:
20 *
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.
25 * copy_static() - Copy a static PPD file to stdout.
26 * compare_names() - Compare PPD filenames for sorting.
27 * compare_ppds() - Compare PPD file make and model names for sorting.
28 * free_array() - Free an array of strings.
29 * list_ppds() - List PPD files.
30 * load_ppds() - Load PPD files recursively.
31 * load_drv() - Load the PPDs from a driver information file.
32 * load_drivers() - Load driver-generated PPD files.
33 */
34
35 /*
36 * Include necessary headers...
37 */
38
39 #include "util.h"
40 #include <cups/dir.h>
41 #include <cups/transcode.h>
42 #include <cups/ppd-private.h>
43 #include <ppdc/ppdc.h>
44
45
46 /*
47 * Constants...
48 */
49
50 #define PPD_SYNC 0x50504434 /* Sync word for ppds.dat (PPD4) */
51 #define PPD_MAX_LANG 32 /* Maximum languages */
52 #define PPD_MAX_PROD 8 /* Maximum products */
53 #define PPD_MAX_VERS 8 /* Maximum versions */
54
55 #define PPD_TYPE_POSTSCRIPT 0 /* PostScript PPD */
56 #define PPD_TYPE_PDF 1 /* PDF PPD */
57 #define PPD_TYPE_RASTER 2 /* CUPS raster PPD */
58 #define PPD_TYPE_FAX 3 /* Facsimile/MFD PPD */
59 #define PPD_TYPE_UNKNOWN 4 /* Other/hybrid PPD */
60
61 static const char * const ppd_types[] = /* ppd-type values */
62 {
63 "postscript",
64 "pdf",
65 "raster",
66 "fax",
67 "unknown"
68 };
69
70
71 /*
72 * PPD information structures...
73 */
74
75 typedef struct /**** PPD record ****/
76 {
77 time_t mtime; /* Modification time */
78 size_t size; /* Size in bytes */
79 int model_number; /* cupsModelNumber */
80 int type; /* ppd-type */
81 char name[512], /* PPD name */
82 languages[PPD_MAX_LANG][6],
83 /* LanguageVersion/cupsLanguages */
84 products[PPD_MAX_PROD][128],
85 /* Product strings */
86 psversions[PPD_MAX_VERS][32],
87 /* PSVersion strings */
88 make[128], /* Manufacturer */
89 make_and_model[128], /* NickName/ModelName */
90 device_id[128]; /* IEEE 1284 Device ID */
91 } ppd_rec_t;
92
93 typedef struct /**** In-memory record ****/
94 {
95 int found; /* 1 if PPD is found */
96 ppd_rec_t record; /* PPDs.dat record */
97 } ppd_info_t;
98
99
100 /*
101 * Globals...
102 */
103
104 int NumPPDs, /* Number of PPD files */
105 SortedPPDs, /* Number of sorted PPD files */
106 AllocPPDs; /* Number of allocated entries */
107 ppd_info_t *PPDs; /* PPD file info */
108 int ChangedPPD; /* Did we change the PPD database? */
109
110
111 /*
112 * Local functions...
113 */
114
115 static ppd_info_t *add_ppd(const char *name, const char *language,
116 const char *make, const char *make_and_model,
117 const char *device_id, const char *product,
118 const char *psversion, time_t mtime,
119 size_t size, int model_number, int type);
120 static int cat_drv(const char *name, int request_id);
121 static int cat_ppd(const char *name, int request_id);
122 static int cat_static(const char *name, int request_id);
123 static int compare_names(const ppd_info_t *p0,
124 const ppd_info_t *p1);
125 static int compare_ppds(const ppd_info_t *p0,
126 const ppd_info_t *p1);
127 static void free_array(cups_array_t *a);
128 static int list_ppds(int request_id, int limit, const char *opt);
129 static int load_drivers(void);
130 static int load_drv(const char *filename, const char *name,
131 cups_file_t *fp, time_t mtime, off_t size);
132 static int load_ppds(const char *d, const char *p, int descend);
133
134
135 /*
136 * 'main()' - Scan for drivers and return an IPP response.
137 *
138 * Usage:
139 *
140 * cups-driverd request_id limit options
141 */
142
143 int /* O - Exit code */
144 main(int argc, /* I - Number of command-line args */
145 char *argv[]) /* I - Command-line arguments */
146 {
147 /*
148 * Install or list PPDs...
149 */
150
151 if (argc == 3 && !strcmp(argv[1], "cat"))
152 return (cat_ppd(argv[2], 0));
153 else if (argc == 4 && !strcmp(argv[1], "get"))
154 return (cat_ppd(argv[3], atoi(argv[2])));
155 else if (argc == 5 && !strcmp(argv[1], "list"))
156 return (list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4]));
157 else
158 {
159 fputs("Usage: cups-driverd cat ppd-name\n", stderr);
160 fputs("Usage: cups-driverd get request_id ppd-name\n", stderr);
161 fputs("Usage: cups-driverd list request_id limit options\n", stderr);
162 return (1);
163 }
164 }
165
166
167 /*
168 * 'add_ppd()' - Add a PPD file.
169 */
170
171 static ppd_info_t * /* O - PPD */
172 add_ppd(const char *name, /* I - PPD name */
173 const char *language, /* I - LanguageVersion */
174 const char *make, /* I - Manufacturer */
175 const char *make_and_model, /* I - NickName/ModelName */
176 const char *device_id, /* I - 1284DeviceID */
177 const char *product, /* I - Product */
178 const char *psversion, /* I - PSVersion */
179 time_t mtime, /* I - Modification time */
180 size_t size, /* I - File size */
181 int model_number, /* I - Model number */
182 int type) /* I - Driver type */
183 {
184 ppd_info_t *ppd; /* PPD */
185 char *recommended; /* Foomatic driver string */
186
187
188 /*
189 * Add a new PPD file...
190 */
191
192 if (NumPPDs >= AllocPPDs)
193 {
194 /*
195 * Allocate (more) memory for the PPD files...
196 */
197
198 AllocPPDs += 128;
199
200 if (!PPDs)
201 ppd = (ppd_info_t *)malloc(sizeof(ppd_info_t) * AllocPPDs);
202 else
203 ppd = (ppd_info_t *)realloc(PPDs, sizeof(ppd_info_t) * AllocPPDs);
204
205 if (ppd == NULL)
206 {
207 fprintf(stderr,
208 "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
209 AllocPPDs);
210 return (NULL);
211 }
212
213 PPDs = ppd;
214 }
215
216 ppd = PPDs + NumPPDs;
217 NumPPDs ++;
218
219 /*
220 * Zero-out the PPD data and copy the values over...
221 */
222
223 memset(ppd, 0, sizeof(ppd_info_t));
224
225 ppd->found = 1;
226 ppd->record.mtime = mtime;
227 ppd->record.size = size;
228 ppd->record.model_number = model_number;
229 ppd->record.type = type;
230
231 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
232 strlcpy(ppd->record.languages[0], language,
233 sizeof(ppd->record.languages[0]));
234 strlcpy(ppd->record.products[0], product, sizeof(ppd->record.products[0]));
235 strlcpy(ppd->record.psversions[0], psversion,
236 sizeof(ppd->record.psversions[0]));
237 strlcpy(ppd->record.make, make, sizeof(ppd->record.make));
238 strlcpy(ppd->record.make_and_model, make_and_model,
239 sizeof(ppd->record.make_and_model));
240 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
241
242 /*
243 * Strip confusing (and often wrong) "recommended" suffix added by
244 * Foomatic drivers...
245 */
246
247 if ((recommended = strstr(ppd->record.make_and_model,
248 " (recommended)")) != NULL)
249 *recommended = '\0';
250
251 /*
252 * Return the new PPD pointer...
253 */
254
255 return (ppd);
256 }
257
258
259 /*
260 * 'cat_drv()' - Generate a PPD from a driver info file.
261 */
262
263 static int /* O - Exit code */
264 cat_drv(const char *name, /* I - PPD name */
265 int request_id) /* I - Request ID for response? */
266 {
267 const char *datadir; // CUPS_DATADIR env var
268 ppdcSource *src; // PPD source file data
269 ppdcDriver *d; // Current driver
270 cups_file_t *out; // Stdout via CUPS file API
271 char message[2048], // status-message
272 filename[1024], // Full path to .drv file(s)
273 scheme[32], // URI scheme ("drv")
274 userpass[256], // User/password info (unused)
275 host[2], // Hostname (unused)
276 resource[1024], // Resource path (/dir/to/filename.drv)
277 *pc_file_name; // Filename portion of URI
278 int port; // Port number (unused)
279
280
281 // Determine where CUPS has installed the data files...
282 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
283 datadir = CUPS_DATADIR;
284
285 // Pull out the
286 if (httpSeparateURI(HTTP_URI_CODING_ALL, name, scheme, sizeof(scheme),
287 userpass, sizeof(userpass), host, sizeof(host), &port,
288 resource, sizeof(resource)) < HTTP_URI_OK ||
289 strstr(resource, "../") ||
290 (pc_file_name = strrchr(resource, '/')) == NULL ||
291 pc_file_name == resource)
292 {
293 fprintf(stderr, "ERROR: Bad PPD name \"%s\"!\n", name);
294
295 if (request_id)
296 {
297 snprintf(message, sizeof(message), "Bad PPD name \"%s\"!", name);
298
299 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
300 cupsdSendIPPGroup(IPP_TAG_OPERATION);
301 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
302 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
303 "en-US");
304 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
305 cupsdSendIPPTrailer();
306 }
307
308 return (1);
309 }
310
311 *pc_file_name++ = '\0';
312
313 #ifdef __APPLE__
314 if (!strncmp(resource, "/Library/Printers/PPDs.drv/", 27))
315 strlcpy(filename, resource, sizeof(filename));
316 else
317 #endif // __APPLE__
318 {
319 snprintf(filename, sizeof(filename), "%s/drv%s", datadir, resource);
320 if (access(filename, 0))
321 snprintf(filename, sizeof(filename), "%s/model%s", datadir, resource);
322 }
323
324 src = new ppdcSource(filename);
325
326 for (d = (ppdcDriver *)src->drivers->first();
327 d;
328 d = (ppdcDriver *)src->drivers->next())
329 if (!strcasecmp(pc_file_name, d->pc_file_name->value) ||
330 (d->file_name && !strcasecmp(pc_file_name, d->file_name->value)))
331 break;
332
333 if (d)
334 {
335 ppdcArray *locales; // Locale names
336 ppdcCatalog *catalog; // Message catalog in .drv file
337
338
339 fprintf(stderr, "DEBUG: %d locales defined in \"%s\"...\n",
340 src->po_files->count, filename);
341
342 locales = new ppdcArray();
343 for (catalog = (ppdcCatalog *)src->po_files->first();
344 catalog;
345 catalog = (ppdcCatalog *)src->po_files->next())
346 {
347 fprintf(stderr, "DEBUG: Adding locale \"%s\"...\n",
348 catalog->locale->value);
349 locales->add(catalog->locale);
350 }
351
352 if (request_id)
353 {
354 cupsdSendIPPHeader(IPP_OK, request_id);
355 cupsdSendIPPGroup(IPP_TAG_OPERATION);
356 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
357 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
358 "en-US");
359 cupsdSendIPPTrailer();
360 fflush(stdout);
361 }
362
363 out = cupsFileStdout();
364 d->write_ppd_file(out, NULL, locales, src, PPDC_LFONLY);
365 cupsFileClose(out);
366
367 delete locales;
368 }
369 else
370 {
371 fprintf(stderr, "ERROR: PPD \"%s\" not found!\n", name);
372
373 if (request_id)
374 {
375 snprintf(message, sizeof(message), "PPD \"%s\" not found!", name);
376
377 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
378 cupsdSendIPPGroup(IPP_TAG_OPERATION);
379 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
380 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
381 "en-US");
382 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
383 cupsdSendIPPTrailer();
384 }
385 }
386
387 delete src;
388
389 return (!d);
390 }
391
392
393 /*
394 * 'cat_ppd()' - Copy a PPD file to stdout.
395 */
396
397 static int /* O - Exit code */
398 cat_ppd(const char *name, /* I - PPD name */
399 int request_id) /* I - Request ID for response? */
400 {
401 char scheme[256], /* Scheme from PPD name */
402 *sptr, /* Pointer into scheme */
403 line[1024], /* Line/filename */
404 message[2048]; /* status-message */
405
406
407 /*
408 * Figure out if this is a static or dynamic PPD file...
409 */
410
411 strlcpy(scheme, name, sizeof(scheme));
412 if ((sptr = strchr(scheme, ':')) != NULL)
413 {
414 *sptr = '\0';
415
416 if (!strcmp(scheme, "file"))
417 {
418 /*
419 * "file:name" == "name"...
420 */
421
422 name += 5;
423 scheme[0] = '\0';
424 }
425 }
426 else
427 scheme[0] = '\0';
428
429 if (request_id > 0)
430 puts("Content-Type: application/ipp\n");
431
432 if (!scheme[0])
433 return (cat_static(name, request_id));
434 else if (!strcmp(scheme, "drv"))
435 return (cat_drv(name, request_id));
436 else
437 {
438 /*
439 * Dynamic PPD, see if we have a driver program to support it...
440 */
441
442 const char *serverbin; /* CUPS_SERVERBIN env var */
443 char *argv[4]; /* Arguments for program */
444
445
446 if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL)
447 serverbin = CUPS_SERVERBIN;
448
449 snprintf(line, sizeof(line), "%s/driver/%s", serverbin, scheme);
450 if (access(line, X_OK))
451 {
452 /*
453 * File does not exist or is not executable...
454 */
455
456 fprintf(stderr, "ERROR: [cups-driverd] Unable to access \"%s\" - %s\n",
457 line, strerror(errno));
458
459 if (request_id > 0)
460 {
461 snprintf(message, sizeof(message), "Unable to access \"%s\" - %s",
462 line, strerror(errno));
463
464 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
465 cupsdSendIPPGroup(IPP_TAG_OPERATION);
466 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
467 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
468 "en-US");
469 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
470 cupsdSendIPPTrailer();
471 }
472
473 return (1);
474 }
475
476 /*
477 * Yes, let it cat the PPD file...
478 */
479
480 if (request_id)
481 {
482 cupsdSendIPPHeader(IPP_OK, request_id);
483 cupsdSendIPPGroup(IPP_TAG_OPERATION);
484 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
485 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
486 "en-US");
487 cupsdSendIPPTrailer();
488 }
489
490 argv[0] = scheme;
491 argv[1] = (char *)"cat";
492 argv[2] = (char *)name;
493 argv[3] = NULL;
494
495 if (cupsdExec(line, argv))
496 {
497 /*
498 * Unable to execute driver...
499 */
500
501 fprintf(stderr, "ERROR: [cups-driverd] Unable to execute \"%s\" - %s\n",
502 line, strerror(errno));
503 return (1);
504 }
505 }
506
507 /*
508 * Return with no errors...
509 */
510
511 return (0);
512 }
513
514
515 /*
516 * 'copy_static()' - Copy a static PPD file to stdout.
517 */
518
519 static int /* O - Exit code */
520 cat_static(const char *name, /* I - PPD name */
521 int request_id) /* I - Request ID for response? */
522 {
523 cups_file_t *fp; /* PPD file */
524 const char *datadir; /* CUPS_DATADIR env var */
525 char line[1024], /* Line/filename */
526 message[2048]; /* status-message */
527
528
529 if (name[0] == '/' || strstr(name, "../") || strstr(name, "/.."))
530 {
531 /*
532 * Bad name...
533 */
534
535 fprintf(stderr, "ERROR: [cups-driverd] Bad PPD name \"%s\"!\n", name);
536
537 if (request_id)
538 {
539 snprintf(message, sizeof(message), "Bad PPD name \"%s\"!", name);
540
541 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
542 cupsdSendIPPGroup(IPP_TAG_OPERATION);
543 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
544 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
545 "en-US");
546 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
547 cupsdSendIPPTrailer();
548 }
549
550 return (1);
551 }
552
553 /*
554 * Try opening the file...
555 */
556
557 #ifdef __APPLE__
558 if (!strncmp(name, "System/Library/Printers/PPDs/Contents/Resources/", 48) ||
559 !strncmp(name, "Library/Printers/PPDs/Contents/Resources/", 41))
560 {
561 /*
562 * Map ppd-name to Mac OS X standard locations...
563 */
564
565 snprintf(line, sizeof(line), "/%s", name);
566 }
567 else
568
569 #elif defined(__linux)
570 if (!strncmp(name, "lsb/usr/", 8))
571 {
572 /*
573 * Map ppd-name to LSB standard /usr/share/ppd location...
574 */
575
576 snprintf(line, sizeof(line), "/usr/share/ppd/%s", name + 8);
577 }
578 else if (!strncmp(name, "lsb/opt/", 8))
579 {
580 /*
581 * Map ppd-name to LSB standard /opt/share/ppd location...
582 */
583
584 snprintf(line, sizeof(line), "/opt/share/ppd/%s", name + 8);
585 }
586 else if (!strncmp(name, "lsb/local/", 10))
587 {
588 /*
589 * Map ppd-name to LSB standard /usr/local/share/ppd location...
590 */
591
592 snprintf(line, sizeof(line), "/usr/local/share/ppd/%s", name + 10);
593 }
594 else
595
596 #endif /* __APPLE__ */
597 {
598 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
599 datadir = CUPS_DATADIR;
600
601 snprintf(line, sizeof(line), "%s/model/%s", datadir, name);
602 }
603
604 if ((fp = cupsFileOpen(line, "r")) == NULL)
605 {
606 fprintf(stderr, "ERROR: [cups-driverd] Unable to open \"%s\" - %s\n",
607 line, strerror(errno));
608
609 if (request_id)
610 {
611 snprintf(message, sizeof(message), "Unable to open \"%s\" - %s",
612 line, strerror(errno));
613
614 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
615 cupsdSendIPPGroup(IPP_TAG_OPERATION);
616 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
617 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
618 "en-US");
619 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
620 cupsdSendIPPTrailer();
621 }
622
623 return (1);
624 }
625
626 if (request_id)
627 {
628 cupsdSendIPPHeader(IPP_OK, request_id);
629 cupsdSendIPPGroup(IPP_TAG_OPERATION);
630 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
631 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
632 "en-US");
633 cupsdSendIPPTrailer();
634 }
635
636 /*
637 * Now copy the file to stdout...
638 */
639
640 while (cupsFileGets(fp, line, sizeof(line)))
641 puts(line);
642
643 cupsFileClose(fp);
644
645 return (0);
646 }
647
648
649 /*
650 * 'compare_names()' - Compare PPD filenames for sorting.
651 */
652
653 static int /* O - Result of comparison */
654 compare_names(const ppd_info_t *p0, /* I - First PPD file */
655 const ppd_info_t *p1) /* I - Second PPD file */
656 {
657 return (strcasecmp(p0->record.name, p1->record.name));
658 }
659
660
661 /*
662 * 'compare_ppds()' - Compare PPD file make and model names for sorting.
663 */
664
665 static int /* O - Result of comparison */
666 compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
667 const ppd_info_t *p1) /* I - Second PPD file */
668 {
669 int diff; /* Difference between strings */
670
671
672 /*
673 * First compare manufacturers...
674 */
675
676 if ((diff = strcasecmp(p0->record.make, p1->record.make)) != 0)
677 return (diff);
678 else if ((diff = cupsdCompareNames(p0->record.make_and_model,
679 p1->record.make_and_model)) != 0)
680 return (diff);
681 else
682 return (strcasecmp(p0->record.languages[0],
683 p1->record.languages[0]));
684 }
685
686
687 /*
688 * 'free_array()' - Free an array of strings.
689 */
690
691 static void
692 free_array(cups_array_t *a) /* I - Array to free */
693 {
694 char *ptr; /* Pointer to string */
695
696
697 for (ptr = (char *)cupsArrayFirst(a);
698 ptr;
699 ptr = (char *)cupsArrayNext(a))
700 free(ptr);
701
702 cupsArrayDelete(a);
703 }
704
705
706 /*
707 * 'list_ppds()' - List PPD files.
708 */
709
710 static int /* O - Exit code */
711 list_ppds(int request_id, /* I - Request ID */
712 int limit, /* I - Limit */
713 const char *opt) /* I - Option argument */
714 {
715 int i, j; /* Looping vars */
716 int count; /* Number of PPDs to send */
717 ppd_info_t *ppd; /* Current PPD file */
718 cups_file_t *fp; /* ppds.dat file */
719 struct stat fileinfo; /* ppds.dat information */
720 char filename[1024], /* ppds.dat filename */
721 model[1024]; /* Model directory */
722 const char *cups_cachedir; /* CUPS_CACHEDIR environment variable */
723 const char *cups_datadir; /* CUPS_DATADIR environment variable */
724 int num_options; /* Number of options */
725 cups_option_t *options; /* Options */
726 const char *requested, /* requested-attributes option */
727 *device_id, /* ppd-device-id option */
728 *language, /* ppd-natural-language option */
729 *make, /* ppd-make option */
730 *make_and_model, /* ppd-make-and-model option */
731 *model_number_str, /* ppd-model-number option */
732 *product, /* ppd-product option */
733 *psversion, /* ppd-psversion option */
734 *type_str; /* ppd-type option */
735 int model_number, /* ppd-model-number value */
736 type, /* ppd-type value */
737 mam_len, /* Length of ppd-make-and-model */
738 device_id_len, /* Length of ppd-device-id */
739 send_device_id, /* Send ppd-device-id? */
740 send_make, /* Send ppd-make? */
741 send_make_and_model, /* Send ppd-make-and-model? */
742 send_model_number, /* Send ppd-model-number? */
743 send_name, /* Send ppd-name? */
744 send_natural_language, /* Send ppd-natural-language? */
745 send_product, /* Send ppd-product? */
746 send_psversion, /* Send ppd-psversion? */
747 send_type, /* Send ppd-type? */
748 sent_header; /* Sent the IPP header? */
749
750
751 fprintf(stderr,
752 "DEBUG2: [cups-driverd] list_ppds(request_id=%d, limit=%d, "
753 "opt=\"%s\"\n", request_id, limit, opt);
754
755 /*
756 * See if we a PPD database file...
757 */
758
759 NumPPDs = 0;
760 AllocPPDs = 0;
761 PPDs = (ppd_info_t *)NULL;
762 ChangedPPD = 0;
763
764 if ((cups_cachedir = getenv("CUPS_CACHEDIR")) == NULL)
765 cups_cachedir = CUPS_CACHEDIR;
766
767 snprintf(filename, sizeof(filename), "%s/ppds.dat", cups_cachedir);
768 if ((fp = cupsFileOpen(filename, "r")) != NULL)
769 {
770 /*
771 * See if we have the right sync word...
772 */
773
774 unsigned ppdsync; /* Sync word */
775
776 if (cupsFileRead(fp, (char *)&ppdsync, sizeof(ppdsync))
777 == sizeof(ppdsync) &&
778 ppdsync == PPD_SYNC &&
779 !stat(filename, &fileinfo) &&
780 ((fileinfo.st_size - sizeof(ppdsync)) % sizeof(ppd_rec_t)) == 0 &&
781 (NumPPDs = (fileinfo.st_size - sizeof(ppdsync)) /
782 sizeof(ppd_rec_t)) > 0)
783 {
784 /*
785 * We have a ppds.dat file, so read it!
786 */
787
788 if ((PPDs = (ppd_info_t *)malloc(sizeof(ppd_info_t) * NumPPDs)) == NULL)
789 fprintf(stderr,
790 "ERROR: [cups-driverd] Unable to allocate memory for %d "
791 "PPD files!\n", NumPPDs);
792 else
793 {
794 AllocPPDs = NumPPDs;
795
796 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
797 {
798 cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
799 ppd->found = 0;
800 }
801
802 fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
803 filename, NumPPDs);
804 }
805 }
806
807 cupsFileClose(fp);
808 }
809
810 /*
811 * Load all PPDs in the specified directory and below...
812 */
813
814 SortedPPDs = NumPPDs;
815
816 if ((cups_datadir = getenv("CUPS_DATADIR")) == NULL)
817 cups_datadir = CUPS_DATADIR;
818
819 snprintf(model, sizeof(model), "%s/model", cups_datadir);
820 load_ppds(model, "", 1);
821
822 snprintf(model, sizeof(model), "%s/drv", cups_datadir);
823 load_ppds(model, "", 1);
824
825 #ifdef __APPLE__
826 /*
827 * Load PPDs from standard Mac OS X locations...
828 */
829
830 load_ppds("/Library/Printers/PPDs/Contents/Resources",
831 "Library/Printers/PPDs/Contents/Resources", 0);
832 load_ppds("/Library/Printers/PPDs/Contents/Resources/en.lproj",
833 "Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
834 load_ppds("/System/Library/Printers/PPDs/Contents/Resources",
835 "System/Library/Printers/PPDs/Contents/Resources", 0);
836 load_ppds("/System/Library/Printers/PPDs/Contents/Resources/en.lproj",
837 "System/Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
838
839 #elif defined(__linux)
840 /*
841 * Load PPDs from LSB-defined locations...
842 */
843
844 if (!access("/usr/local/share/ppd", 0))
845 load_ppds("/usr/local/share/ppd", "lsb/local", 1);
846 if (!access("/usr/share/ppd", 0))
847 load_ppds("/usr/share/ppd", "lsb/usr", 1);
848 if (!access("/opt/share/ppd", 0))
849 load_ppds("/opt/share/ppd", "lsb/opt", 1);
850 #endif /* __APPLE__ */
851
852 /*
853 * Cull PPD files that are no longer present...
854 */
855
856 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
857 if (!ppd->found)
858 {
859 /*
860 * Remove this PPD file from the list...
861 */
862
863 if (i > 1)
864 memmove(ppd, ppd + 1, (i - 1) * sizeof(ppd_info_t));
865
866 NumPPDs --;
867 ppd --;
868 }
869
870 /*
871 * Sort the PPDs by name...
872 */
873
874 if (NumPPDs > 1)
875 qsort(PPDs, NumPPDs, sizeof(ppd_info_t),
876 (int (*)(const void *, const void *))compare_names);
877
878 /*
879 * Write the new ppds.dat file...
880 */
881
882 if (ChangedPPD)
883 {
884 if ((fp = cupsFileOpen(filename, "w")) != NULL)
885 {
886 unsigned ppdsync = PPD_SYNC; /* Sync word */
887
888
889 cupsFileWrite(fp, (char *)&ppdsync, sizeof(ppdsync));
890
891 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
892 cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
893
894 cupsFileClose(fp);
895
896 fprintf(stderr, "INFO: [cups-driverd] Wrote \"%s\", %d PPDs...\n",
897 filename, NumPPDs);
898 }
899 else
900 fprintf(stderr, "ERROR: [cups-driverd] Unable to write \"%s\" - %s\n",
901 filename, strerror(errno));
902 }
903 else
904 fputs("INFO: [cups-driverd] No new or changed PPDs...\n", stderr);
905
906 /*
907 * Scan for dynamic PPD files...
908 */
909
910 load_drivers();
911
912 /*
913 * Add the raw driver...
914 */
915
916 add_ppd("raw", "en", "Raw", "Raw Queue", "", "", "", 0, 0, 0,
917 PPD_TYPE_UNKNOWN);
918
919 /*
920 * Sort the PPDs by make and model...
921 */
922
923 if (NumPPDs > 1)
924 qsort(PPDs, NumPPDs, sizeof(ppd_info_t),
925 (int (*)(const void *, const void *))compare_ppds);
926
927 /*
928 * Send IPP attributes...
929 */
930
931 num_options = cupsParseOptions(opt, 0, &options);
932 requested = cupsGetOption("requested-attributes", num_options, options);
933 device_id = cupsGetOption("ppd-device-id", num_options, options);
934 language = cupsGetOption("ppd-natural-language", num_options, options);
935 make = cupsGetOption("ppd-make", num_options, options);
936 make_and_model = cupsGetOption("ppd-make-and-model", num_options, options);
937 model_number_str = cupsGetOption("ppd-model-number", num_options, options);
938 product = cupsGetOption("ppd-product", num_options, options);
939 psversion = cupsGetOption("ppd-psversion", num_options, options);
940 type_str = cupsGetOption("ppd-type", num_options, options);
941
942 if (make_and_model)
943 mam_len = strlen(make_and_model);
944 else
945 mam_len = 0;
946
947 if (device_id)
948 device_id_len = strlen(device_id);
949 else
950 device_id_len = 0;
951
952 if (model_number_str)
953 model_number = atoi(model_number_str);
954 else
955 model_number = 0;
956
957 if (type_str)
958 {
959 for (type = 0;
960 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
961 type ++)
962 if (!strcmp(type_str, ppd_types[type]))
963 break;
964
965 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
966 {
967 fprintf(stderr, "ERROR: [cups-driverd] Bad ppd-type=\"%s\" ignored!\n",
968 type_str);
969 type_str = NULL;
970 }
971 }
972 else
973 type = 0;
974
975 if (requested)
976 fprintf(stderr, "DEBUG: [cups-driverd] requested-attributes=\"%s\"\n",
977 requested);
978 if (device_id)
979 fprintf(stderr, "DEBUG: [cups-driverd] ppd-device-id=\"%s\"\n",
980 device_id);
981 if (language)
982 fprintf(stderr, "DEBUG: [cups-driverd] ppd-natural-language=\"%s\"\n",
983 language);
984 if (make)
985 fprintf(stderr, "DEBUG: [cups-driverd] ppd-make=\"%s\"\n",
986 make);
987 if (make_and_model)
988 fprintf(stderr, "DEBUG: [cups-driverd] ppd-make-and-model=\"%s\"\n",
989 make_and_model);
990 if (model_number_str)
991 fprintf(stderr, "DEBUG: [cups-driverd] ppd-model-number=\"%s\"\n",
992 model_number_str);
993 if (product)
994 fprintf(stderr, "DEBUG: [cups-driverd] ppd-product=\"%s\"\n",
995 product);
996 if (psversion)
997 fprintf(stderr, "DEBUG: [cups-driverd] ppd-psversion=\"%s\"\n",
998 psversion);
999 if (type_str)
1000 fprintf(stderr, "DEBUG: [cups-driverd] ppd-type=\"%s\"\n", type_str);
1001
1002 if (!requested || strstr(requested, "all"))
1003 {
1004 send_name = 1;
1005 send_make = 1;
1006 send_make_and_model = 1;
1007 send_model_number = 1;
1008 send_natural_language = 1;
1009 send_device_id = 1;
1010 send_product = 1;
1011 send_psversion = 1;
1012 send_type = 1;
1013 }
1014 else
1015 {
1016 send_name = strstr(requested, "ppd-name") != NULL;
1017 send_make = strstr(requested, "ppd-make,") != NULL ||
1018 strstr(requested, ",ppd-make") != NULL ||
1019 !strcmp(requested, "ppd-make");
1020 send_make_and_model = strstr(requested, "ppd-make-and-model") != NULL;
1021 send_model_number = strstr(requested, "ppd-model-number") != NULL;
1022 send_natural_language = strstr(requested, "ppd-natural-language") != NULL;
1023 send_device_id = strstr(requested, "ppd-device-id") != NULL;
1024 send_product = strstr(requested, "ppd-product") != NULL;
1025 send_psversion = strstr(requested, "ppd-psversion") != NULL;
1026 send_type = strstr(requested, "ppd-type") != NULL;
1027 }
1028
1029 puts("Content-Type: application/ipp\n");
1030
1031 sent_header = 0;
1032
1033 if (limit <= 0 || limit > NumPPDs)
1034 count = NumPPDs;
1035 else
1036 count = limit;
1037
1038 for (i = NumPPDs, ppd = PPDs; count > 0 && i > 0; i --, ppd ++)
1039 {
1040 /*
1041 * Filter PPDs based on make, model, or device ID...
1042 */
1043
1044 if (device_id && strncasecmp(ppd->record.device_id, device_id,
1045 device_id_len))
1046 continue; /* TODO: implement smart compare */
1047
1048 if (language)
1049 {
1050 for (j = 0; j < PPD_MAX_LANG; j ++)
1051 if (!ppd->record.languages[j][0] ||
1052 !strcasecmp(ppd->record.languages[j], language))
1053 break;
1054
1055 if (j >= PPD_MAX_LANG || !ppd->record.languages[j][0])
1056 continue;
1057 }
1058
1059 if (make && strcasecmp(ppd->record.make, make))
1060 continue;
1061
1062 if (make_and_model && strncasecmp(ppd->record.make_and_model,
1063 make_and_model, mam_len))
1064 continue;
1065
1066 if (model_number_str && ppd->record.model_number != model_number)
1067 continue;
1068
1069 if (product)
1070 {
1071 for (j = 0; j < PPD_MAX_PROD; j ++)
1072 if (!ppd->record.products[j][0] ||
1073 !strcasecmp(ppd->record.products[j], product))
1074 break;
1075
1076 if (j >= PPD_MAX_PROD || !ppd->record.products[j][0])
1077 continue;
1078 }
1079
1080 if (psversion)
1081 {
1082 for (j = 0; j < PPD_MAX_VERS; j ++)
1083 if (!ppd->record.psversions[j][0] ||
1084 !strcasecmp(ppd->record.psversions[j], psversion))
1085 break;
1086
1087 if (j >= PPD_MAX_VERS || !ppd->record.psversions[j][0])
1088 continue;
1089 }
1090
1091 if (type_str && ppd->record.type != type)
1092 continue;
1093
1094 /*
1095 * Send this PPD...
1096 */
1097
1098 if (!sent_header)
1099 {
1100 sent_header = 1;
1101
1102 cupsdSendIPPHeader(IPP_OK, request_id);
1103 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1104 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
1105 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
1106 }
1107
1108 fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n",
1109 ppd->record.name, ppd->record.make_and_model);
1110
1111 count --;
1112
1113 cupsdSendIPPGroup(IPP_TAG_PRINTER);
1114
1115 if (send_name)
1116 cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
1117
1118 if (send_natural_language)
1119 {
1120 cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
1121 ppd->record.languages[0]);
1122
1123 for (j = 1; j < PPD_MAX_LANG && ppd->record.languages[j][0]; j ++)
1124 cupsdSendIPPString(IPP_TAG_LANGUAGE, "", ppd->record.languages[j]);
1125 }
1126
1127 if (send_make)
1128 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
1129
1130 if (send_make_and_model)
1131 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model",
1132 ppd->record.make_and_model);
1133
1134 if (send_device_id)
1135 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-device-id",
1136 ppd->record.device_id);
1137
1138 if (send_product)
1139 {
1140 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-product",
1141 ppd->record.products[0]);
1142
1143 for (j = 1; j < PPD_MAX_PROD && ppd->record.products[j][0]; j ++)
1144 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.products[j]);
1145 }
1146
1147 if (send_psversion)
1148 {
1149 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-psversion",
1150 ppd->record.psversions[0]);
1151
1152 for (j = 1; j < PPD_MAX_VERS && ppd->record.psversions[j][0]; j ++)
1153 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.psversions[j]);
1154 }
1155
1156 if (send_type)
1157 cupsdSendIPPString(IPP_TAG_KEYWORD, "ppd-type",
1158 ppd_types[ppd->record.type]);
1159
1160 if (send_model_number)
1161 cupsdSendIPPInteger(IPP_TAG_INTEGER, "ppd-model-number",
1162 ppd->record.model_number);
1163
1164 /*
1165 * If we have only requested the ppd-make attribute, then skip
1166 * the remaining PPDs with this make...
1167 */
1168
1169 if (requested && !strcmp(requested, "ppd-make"))
1170 {
1171 const char *this_make; /* This ppd-make */
1172
1173
1174 for (this_make = ppd->record.make, i --, ppd ++; i > 0; i --, ppd ++)
1175 if (strcasecmp(this_make, ppd->record.make))
1176 break;
1177
1178 i ++;
1179 ppd --;
1180 }
1181 }
1182
1183 if (!sent_header)
1184 {
1185 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
1186 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1187 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
1188 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
1189 }
1190
1191 cupsdSendIPPTrailer();
1192
1193 return (0);
1194 }
1195
1196
1197 /*
1198 * 'load_ppds()' - Load PPD files recursively.
1199 */
1200
1201 static int /* O - 1 on success, 0 on failure */
1202 load_ppds(const char *d, /* I - Actual directory */
1203 const char *p, /* I - Virtual path in name */
1204 int descend) /* I - Descend into directories? */
1205 {
1206 int i; /* Looping var */
1207 cups_file_t *fp; /* Pointer to file */
1208 cups_dir_t *dir; /* Directory pointer */
1209 cups_dentry_t *dent; /* Directory entry */
1210 char filename[1024], /* Name of PPD or directory */
1211 line[256], /* Line from backend */
1212 *ptr, /* Pointer into name */
1213 name[128], /* Name of PPD file */
1214 lang_version[64], /* PPD LanguageVersion */
1215 lang_encoding[64], /* PPD LanguageEncoding */
1216 country[64], /* Country code */
1217 manufacturer[256], /* Manufacturer */
1218 make_model[256], /* Make and Model */
1219 model_name[256], /* ModelName */
1220 nick_name[256], /* NickName */
1221 device_id[256], /* 1284DeviceID */
1222 product[256], /* Product */
1223 psversion[256], /* PSVersion */
1224 temp[512]; /* Temporary make and model */
1225 int model_number, /* cupsModelNumber */
1226 type; /* ppd-type */
1227 cups_array_t *products, /* Product array */
1228 *psversions, /* PSVersion array */
1229 *cups_languages; /* cupsLanguages array */
1230 ppd_info_t *ppd, /* New PPD file */
1231 key; /* Search key */
1232 int new_ppd; /* Is this a new PPD? */
1233 struct /* LanguageVersion translation table */
1234 {
1235 const char *version, /* LanguageVersion string */
1236 *language; /* Language code */
1237 } languages[] =
1238 {
1239 { "chinese", "zh" },
1240 { "czech", "cs" },
1241 { "danish", "da" },
1242 { "dutch", "nl" },
1243 { "english", "en" },
1244 { "finnish", "fi" },
1245 { "french", "fr" },
1246 { "german", "de" },
1247 { "greek", "el" },
1248 { "hungarian", "hu" },
1249 { "italian", "it" },
1250 { "japanese", "ja" },
1251 { "korean", "ko" },
1252 { "norwegian", "no" },
1253 { "polish", "pl" },
1254 { "portuguese", "pt" },
1255 { "russian", "ru" },
1256 { "simplified chinese", "zh_CN" },
1257 { "slovak", "sk" },
1258 { "spanish", "es" },
1259 { "swedish", "sv" },
1260 { "traditional chinese", "zh_TW" },
1261 { "turkish", "tr" }
1262 };
1263
1264
1265 fprintf(stderr, "DEBUG: [cups-driverd] Loading \"%s\"...\n", d);
1266
1267 if ((dir = cupsDirOpen(d)) == NULL)
1268 {
1269 if (errno != ENOENT)
1270 fprintf(stderr,
1271 "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
1272 d, strerror(errno));
1273
1274 return (0);
1275 }
1276
1277 while ((dent = cupsDirRead(dir)) != NULL)
1278 {
1279 /*
1280 * Skip files/directories starting with "."...
1281 */
1282
1283 if (dent->filename[0] == '.')
1284 continue;
1285
1286 /*
1287 * See if this is a file...
1288 */
1289
1290 snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename);
1291
1292 if (p[0])
1293 snprintf(name, sizeof(name), "%s/%s", p, dent->filename);
1294 else
1295 strlcpy(name, dent->filename, sizeof(name));
1296
1297 if (S_ISDIR(dent->fileinfo.st_mode))
1298 {
1299 /*
1300 * Do subdirectory...
1301 */
1302
1303 if (descend)
1304 if (!load_ppds(filename, name, 1))
1305 {
1306 cupsDirClose(dir);
1307 return (1);
1308 }
1309
1310 continue;
1311 }
1312
1313 /*
1314 * See if this file has been scanned before...
1315 */
1316
1317 if (SortedPPDs > 0)
1318 {
1319 strcpy(key.record.name, name);
1320
1321 ppd = (ppd_info_t *)bsearch(&key, PPDs, SortedPPDs, sizeof(ppd_info_t),
1322 (cupsd_compare_func_t)compare_names);
1323
1324 if (ppd &&
1325 ppd->record.size == dent->fileinfo.st_size &&
1326 ppd->record.mtime == dent->fileinfo.st_mtime)
1327 {
1328 ppd->found = 1;
1329 continue;
1330 }
1331 }
1332 else
1333 ppd = NULL;
1334
1335 /*
1336 * No, file is new/changed, so re-scan it...
1337 */
1338
1339 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1340 continue;
1341
1342 /*
1343 * Now see if this is a PPD file...
1344 */
1345
1346 line[0] = '\0';
1347 cupsFileGets(fp, line, sizeof(line));
1348
1349 if (strncmp(line, "*PPD-Adobe:", 11))
1350 {
1351 /*
1352 * Nope, treat it as a driver information file...
1353 */
1354
1355 load_drv(filename, name, fp, dent->fileinfo.st_mtime,
1356 dent->fileinfo.st_size);
1357 continue;
1358 }
1359
1360 /*
1361 * Now read until we get the NickName field...
1362 */
1363
1364 cups_languages = cupsArrayNew(NULL, NULL);
1365 products = cupsArrayNew(NULL, NULL);
1366 psversions = cupsArrayNew(NULL, NULL);
1367
1368 model_name[0] = '\0';
1369 nick_name[0] = '\0';
1370 manufacturer[0] = '\0';
1371 device_id[0] = '\0';
1372 lang_encoding[0] = '\0';
1373 strcpy(lang_version, "en");
1374 model_number = 0;
1375 type = PPD_TYPE_POSTSCRIPT;
1376
1377 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
1378 {
1379 if (!strncmp(line, "*Manufacturer:", 14))
1380 sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
1381 else if (!strncmp(line, "*ModelName:", 11))
1382 sscanf(line, "%*[^\"]\"%127[^\"]", model_name);
1383 else if (!strncmp(line, "*LanguageEncoding:", 18))
1384 sscanf(line, "%*[^:]:%63s", lang_encoding);
1385 else if (!strncmp(line, "*LanguageVersion:", 17))
1386 sscanf(line, "%*[^:]:%63s", lang_version);
1387 else if (!strncmp(line, "*NickName:", 10))
1388 sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
1389 else if (!strncasecmp(line, "*1284DeviceID:", 14))
1390 sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
1391 else if (!strncmp(line, "*Product:", 9))
1392 {
1393 if (sscanf(line, "%*[^\"]\"(%255[^)]", product) == 1)
1394 cupsArrayAdd(products, strdup(product));
1395 }
1396 else if (!strncmp(line, "*PSVersion:", 11))
1397 {
1398 sscanf(line, "%*[^\"]\"%255[^\"]", psversion);
1399 cupsArrayAdd(psversions, strdup(psversion));
1400 }
1401 else if (!strncmp(line, "*cupsLanguages:", 15))
1402 {
1403 char *start; /* Start of language */
1404
1405
1406 for (start = line + 15; *start && isspace(*start & 255); start ++);
1407
1408 if (*start++ == '\"')
1409 {
1410 while (*start)
1411 {
1412 for (ptr = start + 1;
1413 *ptr && *ptr != '\"' && !isspace(*ptr & 255);
1414 ptr ++);
1415
1416 if (*ptr)
1417 {
1418 *ptr++ = '\0';
1419
1420 while (isspace(*ptr & 255))
1421 *ptr++ = '\0';
1422 }
1423
1424 cupsArrayAdd(cups_languages, strdup(start));
1425 start = ptr;
1426 }
1427 }
1428 }
1429 else if (!strncmp(line, "*cupsFax:", 9))
1430 {
1431 for (ptr = line + 9; isspace(*ptr & 255); ptr ++);
1432
1433 if (!strncasecmp(ptr, "true", 4))
1434 type = PPD_TYPE_FAX;
1435 }
1436 else if (!strncmp(line, "*cupsFilter:", 12) && type == PPD_TYPE_POSTSCRIPT)
1437 {
1438 if (strstr(line + 12, "application/vnd.cups-raster"))
1439 type = PPD_TYPE_RASTER;
1440 else if (strstr(line + 12, "application/vnd.cups-pdf"))
1441 type = PPD_TYPE_PDF;
1442 }
1443 else if (!strncmp(line, "*cupsModelNumber:", 17))
1444 sscanf(line, "*cupsModelNumber:%d", &model_number);
1445 else if (!strncmp(line, "*OpenUI", 7))
1446 {
1447 /*
1448 * Stop early if we have a NickName or ModelName attributes
1449 * before the first OpenUI...
1450 */
1451
1452 if ((model_name[0] || nick_name[0]) && cupsArrayCount(products) > 0 &&
1453 cupsArrayCount(psversions) > 0)
1454 break;
1455 }
1456 }
1457
1458 /*
1459 * Close the file...
1460 */
1461
1462 cupsFileClose(fp);
1463
1464 /*
1465 * See if we got all of the required info...
1466 */
1467
1468 if (nick_name[0])
1469 cupsCharsetToUTF8((cups_utf8_t *)make_model, nick_name,
1470 sizeof(make_model), _ppdGetEncoding(lang_encoding));
1471 else
1472 strcpy(make_model, model_name);
1473
1474 while (isspace(make_model[0] & 255))
1475 _cups_strcpy(make_model, make_model + 1);
1476
1477 if (!make_model[0] || cupsArrayCount(products) == 0 ||
1478 cupsArrayCount(psversions) == 0)
1479 {
1480 /*
1481 * We don't have all the info needed, so skip this file...
1482 */
1483
1484 if (!make_model[0])
1485 fprintf(stderr, "WARNING: Missing NickName and ModelName in %s!\n",
1486 filename);
1487
1488 if (cupsArrayCount(products) == 0)
1489 fprintf(stderr, "WARNING: Missing Product in %s!\n", filename);
1490
1491 if (cupsArrayCount(psversions) == 0)
1492 fprintf(stderr, "WARNING: Missing PSVersion in %s!\n", filename);
1493
1494 free_array(products);
1495 free_array(psversions);
1496 free_array(cups_languages);
1497
1498 continue;
1499 }
1500
1501 if (model_name[0])
1502 cupsArrayAdd(products, strdup(model_name));
1503
1504 /*
1505 * Normalize the make and model string...
1506 */
1507
1508 while (isspace(manufacturer[0] & 255))
1509 _cups_strcpy(manufacturer, manufacturer + 1);
1510
1511 if (!strncasecmp(make_model, manufacturer, strlen(manufacturer)))
1512 strlcpy(temp, make_model, sizeof(temp));
1513 else
1514 snprintf(temp, sizeof(temp), "%s %s", manufacturer, make_model);
1515
1516 _ppdNormalizeMakeAndModel(temp, make_model, sizeof(make_model));
1517
1518 /*
1519 * See if we got a manufacturer...
1520 */
1521
1522 if (!manufacturer[0] || !strcmp(manufacturer, "ESP"))
1523 {
1524 /*
1525 * Nope, copy the first part of the make and model then...
1526 */
1527
1528 strlcpy(manufacturer, make_model, sizeof(manufacturer));
1529
1530 /*
1531 * Truncate at the first space, dash, or slash, or make the
1532 * manufacturer "Other"...
1533 */
1534
1535 for (ptr = manufacturer; *ptr; ptr ++)
1536 if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
1537 break;
1538
1539 if (*ptr && ptr > manufacturer)
1540 *ptr = '\0';
1541 else
1542 strcpy(manufacturer, "Other");
1543 }
1544 else if (!strncasecmp(manufacturer, "LHAG", 4) ||
1545 !strncasecmp(manufacturer, "linotype", 8))
1546 strcpy(manufacturer, "LHAG");
1547 else if (!strncasecmp(manufacturer, "Hewlett", 7))
1548 strcpy(manufacturer, "HP");
1549
1550 /*
1551 * Fix the lang_version as needed...
1552 */
1553
1554 if ((ptr = strchr(lang_version, '-')) != NULL)
1555 *ptr++ = '\0';
1556 else if ((ptr = strchr(lang_version, '_')) != NULL)
1557 *ptr++ = '\0';
1558
1559 if (ptr)
1560 {
1561 /*
1562 * Setup the country suffix...
1563 */
1564
1565 country[0] = '_';
1566 _cups_strcpy(country + 1, ptr);
1567 }
1568 else
1569 {
1570 /*
1571 * No country suffix...
1572 */
1573
1574 country[0] = '\0';
1575 }
1576
1577 for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
1578 if (!strcasecmp(languages[i].version, lang_version))
1579 break;
1580
1581 if (i < (int)(sizeof(languages) / sizeof(languages[0])))
1582 {
1583 /*
1584 * Found a known language...
1585 */
1586
1587 snprintf(lang_version, sizeof(lang_version), "%s%s",
1588 languages[i].language, country);
1589 }
1590 else
1591 {
1592 /*
1593 * Unknown language; use "xx"...
1594 */
1595
1596 strcpy(lang_version, "xx");
1597 }
1598
1599 /*
1600 * Record the PPD file...
1601 */
1602
1603 new_ppd = !ppd;
1604
1605 if (new_ppd)
1606 {
1607 /*
1608 * Add new PPD file...
1609 */
1610
1611 fprintf(stderr, "DEBUG: [cups-driverd] Adding ppd \"%s\"...\n", name);
1612
1613 ppd = add_ppd(name, lang_version, manufacturer, make_model, device_id,
1614 (char *)cupsArrayFirst(products),
1615 (char *)cupsArrayFirst(psversions),
1616 dent->fileinfo.st_mtime, dent->fileinfo.st_size,
1617 model_number, type);
1618
1619 if (!ppd)
1620 {
1621 cupsDirClose(dir);
1622 return (0);
1623 }
1624 }
1625 else
1626 {
1627 /*
1628 * Update existing record...
1629 */
1630
1631 fprintf(stderr, "DEBUG: [cups-driverd] Updating ppd \"%s\"...\n", name);
1632
1633 memset(ppd, 0, sizeof(ppd_info_t));
1634
1635 ppd->found = 1;
1636 ppd->record.mtime = dent->fileinfo.st_mtime;
1637 ppd->record.size = dent->fileinfo.st_size;
1638 ppd->record.model_number = model_number;
1639 ppd->record.type = type;
1640
1641 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
1642 strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make));
1643 strlcpy(ppd->record.make_and_model, make_model,
1644 sizeof(ppd->record.make_and_model));
1645 strlcpy(ppd->record.languages[0], lang_version,
1646 sizeof(ppd->record.languages[0]));
1647 strlcpy(ppd->record.products[0], (char *)cupsArrayFirst(products),
1648 sizeof(ppd->record.products[0]));
1649 strlcpy(ppd->record.psversions[0], (char *)cupsArrayFirst(psversions),
1650 sizeof(ppd->record.psversions[0]));
1651 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
1652 }
1653
1654 /*
1655 * Add remaining products, versions, and languages...
1656 */
1657
1658 for (i = 1;
1659 i < PPD_MAX_PROD && (ptr = (char *)cupsArrayNext(products)) != NULL;
1660 i ++)
1661 strlcpy(ppd->record.products[i], ptr,
1662 sizeof(ppd->record.products[0]));
1663
1664 for (i = 1;
1665 i < PPD_MAX_VERS && (ptr = (char *)cupsArrayNext(psversions)) != NULL;
1666 i ++)
1667 strlcpy(ppd->record.psversions[i], ptr,
1668 sizeof(ppd->record.psversions[0]));
1669
1670 for (i = 1, ptr = (char *)cupsArrayFirst(cups_languages);
1671 i < PPD_MAX_LANG && ptr;
1672 i ++, ptr = (char *)cupsArrayNext(cups_languages))
1673 strlcpy(ppd->record.languages[i], ptr,
1674 sizeof(ppd->record.languages[0]));
1675
1676 /*
1677 * Free products, versions, and languages...
1678 */
1679
1680 free_array(cups_languages);
1681 free_array(products);
1682 free_array(psversions);
1683
1684 ChangedPPD = 1;
1685 }
1686
1687 cupsDirClose(dir);
1688
1689 return (1);
1690 }
1691
1692
1693 /*
1694 * 'load_drv()' - Load the PPDs from a driver information file.
1695 */
1696
1697 static int /* O - 1 on success, 0 on failure */
1698 load_drv(const char *filename, /* I - Actual filename */
1699 const char *name, /* I - Name to the rest of the world */
1700 cups_file_t *fp, /* I - File to read from */
1701 time_t mtime, /* I - Mod time of driver info file */
1702 off_t size) /* I - Size of driver info file */
1703 {
1704 ppdcSource *src; // Driver information file
1705 ppdcDriver *d; // Current driver
1706 ppdcAttr *device_id, // 1284DeviceID attribute
1707 *product, // Current product value
1708 *ps_version, // PSVersion attribute
1709 *cups_fax, // cupsFax attribute
1710 *nick_name; // NickName attribute
1711 ppdcFilter *filter; // Current filter
1712 bool product_found; // Found product?
1713 char uri[1024], // Driver URI
1714 make_model[1024]; // Make and model
1715 int type; // Driver type
1716
1717
1718 src = new ppdcSource(filename, fp);
1719
1720 if (src->drivers->count == 0)
1721 {
1722 fprintf(stderr,
1723 "ERROR: [cups-driverd] Bad driver information file \"%s\"!\n",
1724 filename);
1725 delete src;
1726 return (0);
1727 }
1728
1729 for (d = (ppdcDriver *)src->drivers->first();
1730 d;
1731 d = (ppdcDriver *)src->drivers->next())
1732 {
1733 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "drv", "", "", 0,
1734 "/%s/%s", name,
1735 d->file_name ? d->file_name->value :
1736 d->pc_file_name->value);
1737
1738 device_id = d->find_attr("1284DeviceID", NULL);
1739 ps_version = d->find_attr("PSVersion", NULL);
1740 nick_name = d->find_attr("NickName", NULL);
1741
1742 if (nick_name)
1743 strlcpy(make_model, nick_name->value->value, sizeof(make_model));
1744 else if (strncasecmp(d->model_name->value, d->manufacturer->value,
1745 strlen(d->manufacturer->value)))
1746 snprintf(make_model, sizeof(make_model), "%s %s, %s",
1747 d->manufacturer->value, d->model_name->value,
1748 d->version->value);
1749 else
1750 snprintf(make_model, sizeof(make_model), "%s, %s", d->model_name->value,
1751 d->version->value);
1752
1753 if ((cups_fax = d->find_attr("cupsFax", NULL)) != NULL &&
1754 !strcasecmp(cups_fax->value->value, "true"))
1755 type = PPD_TYPE_FAX;
1756 else if (d->type == PPDC_DRIVER_PS)
1757 type = PPD_TYPE_POSTSCRIPT;
1758 else if (d->type != PPDC_DRIVER_CUSTOM)
1759 type = PPD_TYPE_RASTER;
1760 else
1761 {
1762 for (filter = (ppdcFilter *)d->filters->first(),
1763 type = PPD_TYPE_POSTSCRIPT;
1764 filter;
1765 filter = (ppdcFilter *)d->filters->next())
1766 if (strcasecmp(filter->mime_type->value, "application/vnd.cups-raster"))
1767 type = PPD_TYPE_RASTER;
1768 else if (strcasecmp(filter->mime_type->value,
1769 "application/vnd.cups-pdf"))
1770 type = PPD_TYPE_PDF;
1771 }
1772
1773 for (product = (ppdcAttr *)d->attrs->first(), product_found = false;
1774 product;
1775 product = (ppdcAttr *)d->attrs->next())
1776 if (!strcmp(product->name->value, "Product"))
1777 {
1778 product_found = true;
1779
1780 add_ppd(uri, "en", d->manufacturer->value, make_model,
1781 device_id ? device_id->value->value : "",
1782 product->value->value,
1783 ps_version ? ps_version->value->value : "(3010) 0",
1784 mtime, size, d->model_number, type);
1785 }
1786
1787 if (!product_found)
1788 add_ppd(uri, "en", d->manufacturer->value, make_model,
1789 device_id ? device_id->value->value : "",
1790 d->model_name->value,
1791 ps_version ? ps_version->value->value : "(3010) 0",
1792 mtime, size, d->model_number, type);
1793 }
1794
1795 delete src;
1796
1797 return (1);
1798 }
1799
1800
1801 /*
1802 * 'load_drivers()' - Load driver-generated PPD files.
1803 */
1804
1805 static int /* O - 1 on success, 0 on failure */
1806 load_drivers(void)
1807 {
1808 int i; /* Looping var */
1809 char *start, /* Start of value */
1810 *ptr; /* Pointer into string */
1811 const char *server_bin; /* CUPS_SERVERBIN env variable */
1812 char drivers[1024]; /* Location of driver programs */
1813 int pid; /* Process ID for driver program */
1814 cups_file_t *fp; /* Pipe to driver program */
1815 cups_dir_t *dir; /* Directory pointer */
1816 cups_dentry_t *dent; /* Directory entry */
1817 char *argv[3], /* Arguments for command */
1818 filename[1024], /* Name of driver */
1819 line[2048], /* Line from driver */
1820 name[512], /* ppd-name */
1821 make[128], /* ppd-make */
1822 make_and_model[128], /* ppd-make-and-model */
1823 device_id[128], /* ppd-device-id */
1824 languages[128], /* ppd-natural-language */
1825 product[128], /* ppd-product */
1826 psversion[128], /* ppd-psversion */
1827 type_str[128]; /* ppd-type */
1828 int type; /* PPD type */
1829 ppd_info_t *ppd; /* Newly added PPD */
1830
1831
1832 /*
1833 * Try opening the driver directory...
1834 */
1835
1836 if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
1837 server_bin = CUPS_SERVERBIN;
1838
1839 snprintf(drivers, sizeof(drivers), "%s/driver", server_bin);
1840
1841 if ((dir = cupsDirOpen(drivers)) == NULL)
1842 {
1843 fprintf(stderr, "ERROR: [cups-driverd] Unable to open driver directory "
1844 "\"%s\": %s\n",
1845 drivers, strerror(errno));
1846 return (0);
1847 }
1848
1849 /*
1850 * Loop through all of the device drivers...
1851 */
1852
1853 argv[1] = (char *)"list";
1854 argv[2] = NULL;
1855
1856 while ((dent = cupsDirRead(dir)) != NULL)
1857 {
1858 /*
1859 * Only look at executable files...
1860 */
1861
1862 if (!(dent->fileinfo.st_mode & 0111) || !S_ISREG(dent->fileinfo.st_mode))
1863 continue;
1864
1865 /*
1866 * Run the driver with no arguments and collect the output...
1867 */
1868
1869 argv[0] = dent->filename;
1870 snprintf(filename, sizeof(filename), "%s/%s", drivers, dent->filename);
1871
1872 if ((fp = cupsdPipeCommand(&pid, filename, argv, 0)) != NULL)
1873 {
1874 while (cupsFileGets(fp, line, sizeof(line)))
1875 {
1876 /*
1877 * Each line is of the form:
1878 *
1879 * "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" \
1880 * "ppd-device-id" "ppd-product" "ppd-psversion"
1881 */
1882
1883 device_id[0] = '\0';
1884 product[0] = '\0';
1885 psversion[0] = '\0';
1886 strcpy(type_str, "postscript");
1887
1888 if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\""
1889 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
1890 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
1891 "%*[ \t]\"%127[^\"]\"",
1892 name, languages, make, make_and_model,
1893 device_id, product, psversion, type_str) < 4)
1894 {
1895 /*
1896 * Bad format; strip trailing newline and write an error message.
1897 */
1898
1899 if (line[strlen(line) - 1] == '\n')
1900 line[strlen(line) - 1] = '\0';
1901
1902 fprintf(stderr, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n",
1903 dent->filename, line);
1904 break;
1905 }
1906 else
1907 {
1908 /*
1909 * Add the device to the array of available devices...
1910 */
1911
1912 if ((start = strchr(languages, ',')) != NULL)
1913 *start++ = '\0';
1914
1915 for (type = 0;
1916 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
1917 type ++)
1918 if (!strcmp(type_str, ppd_types[type]))
1919 break;
1920
1921 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
1922 {
1923 fprintf(stderr,
1924 "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n",
1925 type_str);
1926 type = PPD_TYPE_UNKNOWN;
1927 }
1928
1929 ppd = add_ppd(name, languages, make, make_and_model, device_id,
1930 product, psversion, 0, 0, 0, type);
1931
1932 if (!ppd)
1933 {
1934 cupsDirClose(dir);
1935 cupsFileClose(fp);
1936 return (0);
1937 }
1938
1939 if (start && *start)
1940 {
1941 for (i = 1; i < PPD_MAX_LANG && *start; i ++)
1942 {
1943 if ((ptr = strchr(start, ',')) != NULL)
1944 *ptr++ = '\0';
1945 else
1946 ptr = start + strlen(start);
1947
1948 strlcpy(ppd->record.languages[i], start,
1949 sizeof(ppd->record.languages[0]));
1950
1951 start = ptr;
1952 }
1953 }
1954
1955 fprintf(stderr, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n",
1956 name);
1957 }
1958 }
1959
1960 cupsFileClose(fp);
1961 }
1962 else
1963 fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n",
1964 filename, strerror(errno));
1965 }
1966
1967 cupsDirClose(dir);
1968
1969 return (1);
1970 }
1971
1972
1973 /*
1974 * End of "$Id$".
1975 */