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