Revert manifest to default one
[external/cups.git] / scheduler / printers.c
1 /*
2  * "$Id: printers.c 10295 2012-02-15 23:21:06Z mike $"
3  *
4  *   Printer routines for the CUPS scheduler.
5  *
6  *   Copyright 2007-2012 by Apple Inc.
7  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   which should have been included with this file.  If this file is
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  * Contents:
16  *
17  *   cupsdAddPrinter()          - Add a printer to the system.
18  *   cupsdCreateCommonData()    - Create the common printer data.
19  *   cupsdDeleteAllPrinters()   - Delete all printers from the system.
20  *   cupsdDeletePrinter()       - Delete a printer from the system.
21  *   cupsdFindDest()            - Find a destination in the list.
22  *   cupsdFindPrinter()         - Find a printer in the list.
23  *   cupsdLoadAllPrinters()     - Load printers from the printers.conf file.
24  *   cupsdRenamePrinter()       - Rename a printer.
25  *   cupsdSaveAllPrinters()     - Save all printer definitions to the
26  *                                printers.conf file.
27  *   cupsdSetAuthInfoRequired() - Set the required authentication info.
28  *   cupsdSetDeviceURI()        - Set the device URI for a printer.
29  *   cupsdSetPrinterAttr()      - Set a printer attribute.
30  *   cupsdSetPrinterAttrs()     - Set printer attributes based upon the PPD
31  *                                file.
32  *   cupsdSetPrinterReasons()   - Set/update the reasons strings.
33  *   cupsdSetPrinterState()     - Update the current state of a printer.
34  *   cupsdStopPrinter()         - Stop a printer from printing any jobs...
35  *   cupsdUpdatePrinterPPD()    - Update keywords in a printer's PPD file.
36  *   cupsdUpdatePrinters()      - Update printers after a partial reload.
37  *   cupsdValidateDest()        - Validate a printer/class destination.
38  *   cupsdWritePrintcap()       - Write a pseudo-printcap file for older
39  *                                applications that need it...
40  *   add_printer_defaults()     - Add name-default attributes to the printer
41  *                                attributes.
42  *   add_printer_filter()       - Add a MIME filter for a printer.
43  *   add_printer_formats()      - Add document-format-supported values for a
44  *                                printer.
45  *   compare_printers()         - Compare two printers.
46  *   delete_printer_filters()   - Delete all MIME filters for a printer.
47  *   dirty_printer()            - Mark config and state files dirty for the
48  *                                specified printer.
49  *   load_ppd()                 - Load a cached PPD file, updating the cache as
50  *                                needed.
51  *   new_media_col()            - Create a media-col collection value.
52  *   write_irix_config()        - Update the config files used by the IRIX
53  *                                desktop tools.
54  *   write_irix_state()         - Update the status files used by IRIX printing
55  *                                desktop tools.
56  *   write_xml_string()         - Write a string with XML escaping.
57  */
58
59 /*
60  * Include necessary headers...
61  */
62
63 #include "cupsd.h"
64 #include <cups/dir.h>
65 #ifdef HAVE_APPLICATIONSERVICES_H
66 #  include <ApplicationServices/ApplicationServices.h>
67 #endif /* HAVE_APPLICATIONSERVICES_H */
68 #ifdef HAVE_SYS_MOUNT_H
69 #  include <sys/mount.h>
70 #endif /* HAVE_SYS_MOUNT_H */
71 #ifdef HAVE_SYS_STATVFS_H
72 #  include <sys/statvfs.h>
73 #elif defined(HAVE_SYS_STATFS_H)
74 #  include <sys/statfs.h>
75 #endif /* HAVE_SYS_STATVFS_H */
76 #ifdef HAVE_SYS_VFS_H
77 #  include <sys/vfs.h>
78 #endif /* HAVE_SYS_VFS_H */
79 #ifdef __APPLE__
80 #  include <asl.h>
81 #endif /* __APPLE__ */
82
83 #ifdef HAVE_DBUS
84 # include "colord.h"
85 #endif /* HAVE_DBUS */
86
87 /*
88  * Local functions...
89  */
90
91 static void     add_printer_defaults(cupsd_printer_t *p);
92 static void     add_printer_filter(cupsd_printer_t *p, mime_type_t *type,
93                                    const char *filter);
94 static void     add_printer_formats(cupsd_printer_t *p);
95 static int      compare_printers(void *first, void *second, void *data);
96 static void     delete_printer_filters(cupsd_printer_t *p);
97 static void     dirty_printer(cupsd_printer_t *p);
98 static void     load_ppd(cupsd_printer_t *p);
99 static void     log_ipp_conformance(cupsd_printer_t *p, const char *reason);
100 static ipp_t    *new_media_col(_pwg_size_t *size, const char *source,
101                                const char *type);
102 #ifdef __sgi
103 static void     write_irix_config(cupsd_printer_t *p);
104 static void     write_irix_state(cupsd_printer_t *p);
105 #endif /* __sgi */
106 static void     write_xml_string(cups_file_t *fp, const char *s);
107
108
109 /*
110  * 'cupsdAddPrinter()' - Add a printer to the system.
111  */
112
113 cupsd_printer_t *                       /* O - New printer */
114 cupsdAddPrinter(const char *name)       /* I - Name of printer */
115 {
116   cupsd_printer_t       *p;             /* New printer */
117   char                  uri[1024],      /* Printer URI */
118                         uuid[64];       /* Printer UUID */
119
120
121  /*
122   * Range check input...
123   */
124
125   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddPrinter(\"%s\")", name);
126
127  /*
128   * Create a new printer entity...
129   */
130
131   if ((p = calloc(1, sizeof(cupsd_printer_t))) == NULL)
132   {
133     cupsdLogMessage(CUPSD_LOG_CRIT, "Unable to allocate memory for printer - %s",
134                     strerror(errno));
135     return (NULL);
136   }
137
138   cupsdSetString(&p->name, name);
139   cupsdSetString(&p->info, name);
140   cupsdSetString(&p->hostname, ServerName);
141
142   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
143                    ServerName, RemotePort, "/printers/%s", name);
144   cupsdSetString(&p->uri, uri);
145   cupsdSetString(&p->uuid, _httpAssembleUUID(ServerName, RemotePort, name, 0,
146                                              uuid, sizeof(uuid)));
147   cupsdSetDeviceURI(p, "file:///dev/null");
148
149   p->state      = IPP_PRINTER_STOPPED;
150   p->state_time = time(NULL);
151   p->accepting  = 0;
152   p->shared     = DefaultShared;
153   p->filetype   = mimeAddType(MimeDatabase, "printer", name);
154
155   cupsdSetString(&p->job_sheets[0], "none");
156   cupsdSetString(&p->job_sheets[1], "none");
157
158   cupsdSetString(&p->error_policy, ErrorPolicy);
159   cupsdSetString(&p->op_policy, DefaultPolicy);
160
161   p->op_policy_ptr = DefaultPolicyPtr;
162
163  /*
164   * Insert the printer in the printer list alphabetically...
165   */
166
167   if (!Printers)
168     Printers = cupsArrayNew(compare_printers, NULL);
169
170   cupsdLogMessage(CUPSD_LOG_DEBUG2,
171                   "cupsdAddPrinter: Adding %s to Printers", p->name);
172   cupsArrayAdd(Printers, p);
173
174   if (!ImplicitPrinters)
175     ImplicitPrinters = cupsArrayNew(compare_printers, NULL);
176
177  /*
178   * Return the new printer...
179   */
180
181   return (p);
182 }
183
184
185 /*
186  * 'cupsdCreateCommonData()' - Create the common printer data.
187  */
188
189 void
190 cupsdCreateCommonData(void)
191 {
192   int                   i;              /* Looping var */
193   ipp_attribute_t       *attr;          /* Attribute data */
194   cups_dir_t            *dir;           /* Notifier directory */
195   cups_dentry_t         *dent;          /* Notifier directory entry */
196   cups_array_t          *notifiers;     /* Notifier array */
197   char                  filename[1024], /* Filename */
198                         *notifier;      /* Current notifier */
199   cupsd_policy_t        *p;             /* Current policy */
200   int                   k_supported;    /* Maximum file size supported */
201 #ifdef HAVE_STATVFS
202   struct statvfs        spoolinfo;      /* FS info for spool directory */
203   double                spoolsize;      /* FS size */
204 #elif defined(HAVE_STATFS)
205   struct statfs         spoolinfo;      /* FS info for spool directory */
206   double                spoolsize;      /* FS size */
207 #endif /* HAVE_STATVFS */
208   static const int nups[] =             /* number-up-supported values */
209                 { 1, 2, 4, 6, 9, 16 };
210   static const int orients[4] =/* orientation-requested-supported values */
211                 {
212                   IPP_PORTRAIT,
213                   IPP_LANDSCAPE,
214                   IPP_REVERSE_LANDSCAPE,
215                   IPP_REVERSE_PORTRAIT
216                 };
217   static const char * const holds[] =   /* job-hold-until-supported values */
218                 {
219                   "no-hold",
220                   "indefinite",
221                   "day-time",
222                   "evening",
223                   "night",
224                   "second-shift",
225                   "third-shift",
226                   "weekend"
227                 };
228   static const char * const versions[] =/* ipp-versions-supported values */
229                 {
230                   "1.0",
231                   "1.1",
232                   "2.0",
233                   "2.1"
234                 };
235   static const int      ops[] =         /* operations-supported values */
236                 {
237                   IPP_PRINT_JOB,
238                   IPP_VALIDATE_JOB,
239                   IPP_CREATE_JOB,
240                   IPP_SEND_DOCUMENT,
241                   IPP_CANCEL_JOB,
242                   IPP_GET_JOB_ATTRIBUTES,
243                   IPP_GET_JOBS,
244                   IPP_GET_PRINTER_ATTRIBUTES,
245                   IPP_HOLD_JOB,
246                   IPP_RELEASE_JOB,
247                   IPP_RESTART_JOB,
248                   IPP_PAUSE_PRINTER,
249                   IPP_RESUME_PRINTER,
250                   IPP_PURGE_JOBS,
251                   IPP_SET_PRINTER_ATTRIBUTES,
252                   IPP_SET_JOB_ATTRIBUTES,
253                   IPP_GET_PRINTER_SUPPORTED_VALUES,
254                   IPP_CREATE_PRINTER_SUBSCRIPTION,
255                   IPP_CREATE_JOB_SUBSCRIPTION,
256                   IPP_GET_SUBSCRIPTION_ATTRIBUTES,
257                   IPP_GET_SUBSCRIPTIONS,
258                   IPP_RENEW_SUBSCRIPTION,
259                   IPP_CANCEL_SUBSCRIPTION,
260                   IPP_GET_NOTIFICATIONS,
261                   IPP_ENABLE_PRINTER,
262                   IPP_DISABLE_PRINTER,
263                   IPP_HOLD_NEW_JOBS,
264                   IPP_RELEASE_HELD_NEW_JOBS,
265                   IPP_CANCEL_JOBS,
266                   IPP_CANCEL_MY_JOBS,
267                   IPP_CLOSE_JOB,
268                   CUPS_GET_DEFAULT,
269                   CUPS_GET_PRINTERS,
270                   CUPS_ADD_PRINTER,
271                   CUPS_DELETE_PRINTER,
272                   CUPS_GET_CLASSES,
273                   CUPS_ADD_CLASS,
274                   CUPS_DELETE_CLASS,
275                   CUPS_ACCEPT_JOBS,
276                   CUPS_REJECT_JOBS,
277                   CUPS_SET_DEFAULT,
278                   CUPS_GET_DEVICES,
279                   CUPS_GET_PPDS,
280                   CUPS_MOVE_JOB,
281                   CUPS_AUTHENTICATE_JOB,
282                   CUPS_GET_PPD,
283                   CUPS_GET_DOCUMENT,
284                   IPP_RESTART_JOB
285                 };
286   static const char * const charsets[] =/* charset-supported values */
287                 {
288                   "us-ascii",
289                   "utf-8"
290                 };
291   static const char * const compressions[] =
292                 {                       /* document-compression-supported values */
293                   "none"
294 #ifdef HAVE_LIBZ
295                   ,"gzip"
296 #endif /* HAVE_LIBZ */
297                 };
298   static const char * const media_col_supported[] =
299                 {                       /* media-col-supported values */
300                   "media-bottom-margin",
301                   "media-left-margin",
302                   "media-right-margin",
303                   "media-size",
304                   "media-source",
305                   "media-top-margin",
306                   "media-type"
307                 };
308   static const char * const multiple_document_handling[] =
309                 {                       /* multiple-document-handling-supported values */
310                   "separate-documents-uncollated-copies",
311                   "separate-documents-collated-copies"
312                 };
313   static const char * const notify_attrs[] =
314                 {                       /* notify-attributes-supported values */
315                   "printer-state-change-time",
316                   "notify-lease-expiration-time",
317                   "notify-subscriber-user-name"
318                 };
319   static const char * const notify_events[] =
320                 {                       /* notify-events-supported values */
321                   "job-completed",
322                   "job-config-changed",
323                   "job-created",
324                   "job-progress",
325                   "job-state-changed",
326                   "job-stopped",
327                   "printer-added",
328                   "printer-changed",
329                   "printer-config-changed",
330                   "printer-deleted",
331                   "printer-finishings-changed",
332                   "printer-media-changed",
333                   "printer-modified",
334                   "printer-restarted",
335                   "printer-shutdown",
336                   "printer-state-changed",
337                   "printer-stopped",
338                   "server-audit",
339                   "server-restarted",
340                   "server-started",
341                   "server-stopped"
342                 };
343   static const char * const job_creation[] =
344                 {                       /* job-creation-attributes-supported */
345                   "copies",
346                   "finishings",
347                   "ipp-attribute-fidelity",
348                   "job-hold-until",
349                   "job-name",
350                   "job-priority",
351                   "job-sheets",
352                   "media",
353                   "media-col",
354                   "multiple-document-handling",
355                   "number-up",
356                   "output-bin",
357                   "output-mode",
358                   "orientation-requested",
359                   "page-ranges",
360                   "print-quality",
361                   "printer-resolution",
362                   "sides"
363                 };
364   static const char * const job_settable[] =
365                 {                       /* job-settable-attributes-supported */
366                   "copies",
367                   "finishings",
368                   "job-hold-until",
369                   "job-name",
370                   "job-priority",
371                   "media",
372                   "media-col",
373                   "multiple-document-handling",
374                   "number-up",
375                   "output-bin",
376                   "output-mode",
377                   "orientation-requested",
378                   "page-ranges",
379                   "print-quality",
380                   "printer-resolution",
381                   "sides"
382                 };
383   static const char * const pdf_versions[] =
384                 {                       /* pdf-versions-supported */
385                   "adobe-1.2",
386                   "adobe-1.3",
387                   "adobe-1.4",
388                   "adobe-1.5",
389                   "adobe-1.6",
390                   "adobe-1.7",
391                   "iso-19005-1_2005",
392                   "iso-32000-1_2008",
393                   "pwg-5102.3"
394                 };
395   static const char * const printer_settable[] =
396                 {                       /* printer-settable-attributes-supported */
397                   "printer-info",
398                   "printer-location"
399                 };
400   static const char * const which_jobs[] =
401                 {                       /* which-jobs-supported values */
402                   "completed",
403                   "not-completed",
404                   "aborted",
405                   "all",
406                   "canceled",
407                   "pending",
408                   "pending-held",
409                   "processing",
410                   "processing-stopped"
411                 };
412
413
414   if (CommonData)
415     ippDelete(CommonData);
416
417   CommonData = ippNew();
418
419  /*
420   * Get the maximum spool size based on the size of the filesystem used for
421   * the RequestRoot directory.  If the host OS doesn't support the statfs call
422   * or the filesystem is larger than 2TiB, always report INT_MAX.
423   */
424
425 #ifdef HAVE_STATVFS
426   if (statvfs(RequestRoot, &spoolinfo))
427     k_supported = INT_MAX;
428   else if ((spoolsize = (double)spoolinfo.f_frsize * spoolinfo.f_blocks / 1024) >
429                INT_MAX)
430     k_supported = INT_MAX;
431   else
432     k_supported = (int)spoolsize;
433
434 #elif defined(HAVE_STATFS)
435   if (statfs(RequestRoot, &spoolinfo))
436     k_supported = INT_MAX;
437   else if ((spoolsize = (double)spoolinfo.f_bsize * spoolinfo.f_blocks / 1024) >
438                INT_MAX)
439     k_supported = INT_MAX;
440   else
441     k_supported = (int)spoolsize;
442
443 #else
444   k_supported = INT_MAX;
445 #endif /* HAVE_STATVFS */
446
447  /*
448   * This list of attributes is sorted to improve performance when the
449   * client provides a requested-attributes attribute...
450   */
451
452   /* charset-configured */
453   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
454                "charset-configured", NULL, "utf-8");
455
456   /* charset-supported */
457   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
458                 "charset-supported", sizeof(charsets) / sizeof(charsets[0]),
459                 NULL, charsets);
460
461   /* compression-supported */
462   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
463                 "compression-supported",
464                 sizeof(compressions) / sizeof(compressions[0]),
465                 NULL, compressions);
466
467   /* copies-supported */
468   ippAddRange(CommonData, IPP_TAG_PRINTER, "copies-supported", 1, MaxCopies);
469
470   /* cups-version */
471   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_TEXT | IPP_TAG_COPY,
472                "cups-version", NULL, CUPS_SVERSION + 6);
473
474   /* generated-natural-language-supported (no IPP_TAG_COPY) */
475   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
476                "generated-natural-language-supported", NULL, DefaultLanguage);
477
478   /* ipp-versions-supported */
479   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
480                 "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]),
481                 NULL, versions);
482
483   /* ippget-event-life */
484   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
485                 "ippget-event-life", 15);
486
487   /* job-creation-attributes-supported */
488   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
489                 "job-creation-attributes-supported",
490                 sizeof(job_creation) / sizeof(job_creation[0]),
491                 NULL, job_creation);
492
493   /* job-hold-until-supported */
494   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
495                 "job-hold-until-supported", sizeof(holds) / sizeof(holds[0]),
496                 NULL, holds);
497
498   /* job-ids-supported */
499   ippAddBoolean(CommonData, IPP_TAG_PRINTER, "job-ids-supported", 1);
500
501   /* job-k-octets-supported */
502   ippAddRange(CommonData, IPP_TAG_PRINTER, "job-k-octets-supported", 0,
503               k_supported);
504
505   /* job-priority-supported */
506   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
507                 "job-priority-supported", 100);
508
509   /* job-settable-attributes-supported */
510   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
511                 "job-settable-attributes-supported",
512                 sizeof(job_settable) / sizeof(job_settable[0]),
513                 NULL, job_settable);
514
515   /* job-sheets-supported */
516   if (cupsArrayCount(Banners) > 0)
517   {
518    /*
519     * Setup the job-sheets-supported attribute...
520     */
521
522     if (Classification && !ClassifyOverride)
523       attr = ippAddString(CommonData, IPP_TAG_PRINTER,
524                           IPP_TAG_NAME | IPP_TAG_COPY,
525                           "job-sheets-supported", NULL, Classification);
526     else
527       attr = ippAddStrings(CommonData, IPP_TAG_PRINTER,
528                            IPP_TAG_NAME | IPP_TAG_COPY,
529                            "job-sheets-supported", cupsArrayCount(Banners) + 1,
530                            NULL, NULL);
531
532     if (attr == NULL)
533       cupsdLogMessage(CUPSD_LOG_EMERG,
534                       "Unable to allocate memory for "
535                       "job-sheets-supported attribute: %s!", strerror(errno));
536     else if (!Classification || ClassifyOverride)
537     {
538       cupsd_banner_t    *banner;        /* Current banner */
539
540
541       attr->values[0].string.text = _cupsStrAlloc("none");
542
543       for (i = 1, banner = (cupsd_banner_t *)cupsArrayFirst(Banners);
544            banner;
545            i ++, banner = (cupsd_banner_t *)cupsArrayNext(Banners))
546         attr->values[i].string.text = banner->name;
547     }
548   }
549   else
550     ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
551                  "job-sheets-supported", NULL, "none");
552
553   /* jpeg-k-octets-supported */
554   ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-k-octets-supported", 0,
555               k_supported);
556
557   /* jpeg-x-dimension-supported */
558   ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-x-dimension-supported", 0,
559               65535);
560
561   /* jpeg-y-dimension-supported */
562   ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-y-dimension-supported", 1,
563               65535);
564
565   /* media-col-supported */
566   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
567                 "media-col-supported",
568                 sizeof(media_col_supported) /
569                     sizeof(media_col_supported[0]), NULL,
570                 media_col_supported);
571
572   /* multiple-document-handling-supported */
573   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
574                 "multiple-document-handling-supported",
575                 sizeof(multiple_document_handling) /
576                     sizeof(multiple_document_handling[0]), NULL,
577                 multiple_document_handling);
578
579   /* multiple-document-jobs-supported */
580   ippAddBoolean(CommonData, IPP_TAG_PRINTER,
581                 "multiple-document-jobs-supported", 1);
582
583   /* multiple-operation-time-out */
584   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
585                 "multiple-operation-time-out", MultipleOperationTimeout);
586
587   /* natural-language-configured (no IPP_TAG_COPY) */
588   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
589                "natural-language-configured", NULL, DefaultLanguage);
590
591   /* notify-attributes-supported */
592   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
593                 "notify-attributes-supported",
594                 (int)(sizeof(notify_attrs) / sizeof(notify_attrs[0])),
595                 NULL, notify_attrs);
596
597   /* notify-lease-duration-supported */
598   ippAddRange(CommonData, IPP_TAG_PRINTER,
599               "notify-lease-duration-supported", 0,
600               MaxLeaseDuration ? MaxLeaseDuration : 2147483647);
601
602   /* notify-max-events-supported */
603   ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
604                "notify-max-events-supported", MaxEvents);
605
606   /* notify-events-supported */
607   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
608                 "notify-events-supported",
609                 (int)(sizeof(notify_events) / sizeof(notify_events[0])),
610                 NULL, notify_events);
611
612   /* notify-pull-method-supported */
613   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
614                "notify-pull-method-supported", NULL, "ippget");
615
616   /* notify-schemes-supported */
617   snprintf(filename, sizeof(filename), "%s/notifier", ServerBin);
618   if ((dir = cupsDirOpen(filename)) != NULL)
619   {
620     notifiers = cupsArrayNew((cups_array_func_t)strcmp, NULL);
621
622     while ((dent = cupsDirRead(dir)) != NULL)
623       if (S_ISREG(dent->fileinfo.st_mode) &&
624           (dent->fileinfo.st_mode & S_IXOTH) != 0)
625         cupsArrayAdd(notifiers, _cupsStrAlloc(dent->filename));
626
627     if (cupsArrayCount(notifiers) > 0)
628     {
629       attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
630                            "notify-schemes-supported",
631                            cupsArrayCount(notifiers), NULL, NULL);
632
633       for (i = 0, notifier = (char *)cupsArrayFirst(notifiers);
634            notifier;
635            i ++, notifier = (char *)cupsArrayNext(notifiers))
636         attr->values[i].string.text = notifier;
637     }
638
639     cupsArrayDelete(notifiers);
640     cupsDirClose(dir);
641   }
642
643   /* number-up-supported */
644   ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
645                  "number-up-supported", sizeof(nups) / sizeof(nups[0]), nups);
646
647   /* operations-supported */
648   ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
649                  "operations-supported",
650                  sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, ops);
651
652   /* orientation-requested-supported */
653   ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
654                  "orientation-requested-supported", 4, orients);
655
656   /* page-ranges-supported */
657   ippAddBoolean(CommonData, IPP_TAG_PRINTER, "page-ranges-supported", 1);
658
659   /* pdf-k-octets-supported */
660   ippAddRange(CommonData, IPP_TAG_PRINTER, "pdf-k-octets-supported", 0,
661               k_supported);
662
663   /* pdf-versions-supported */
664   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
665                 "pdf-versions-supported",
666                 sizeof(pdf_versions) / sizeof(pdf_versions[0]), NULL,
667                 pdf_versions);
668
669   /* pdl-override-supported */
670   ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
671                "pdl-override-supported", NULL, "attempted");
672
673   /* printer-op-policy-supported */
674   attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
675                        "printer-op-policy-supported", cupsArrayCount(Policies),
676                        NULL, NULL);
677   for (i = 0, p = (cupsd_policy_t *)cupsArrayFirst(Policies);
678        p;
679        i ++, p = (cupsd_policy_t *)cupsArrayNext(Policies))
680     attr->values[i].string.text = p->name;
681
682   /* printer-settable-attributes-supported */
683   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
684                 "printer-settable-attributes-supported",
685                 sizeof(printer_settable) / sizeof(printer_settable[0]),
686                 NULL, printer_settable);
687
688   /* server-is-sharing-printers */
689   ippAddBoolean(CommonData, IPP_TAG_PRINTER, "server-is-sharing-printers",
690                 BrowseLocalProtocols != 0 && Browsing);
691
692   /* which-jobs-supported */
693   ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
694                 "which-jobs-supported",
695                 sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
696 }
697
698
699 /*
700  * 'cupsdDeleteAllPrinters()' - Delete all printers from the system.
701  */
702
703 void
704 cupsdDeleteAllPrinters(void)
705 {
706   cupsd_printer_t       *p;             /* Pointer to current printer/class */
707
708
709   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
710        p;
711        p = (cupsd_printer_t *)cupsArrayNext(Printers))
712   {
713     p->op_policy_ptr = DefaultPolicyPtr;
714     cupsdDeletePrinter(p, 0);
715   }
716 }
717
718 /*
719  * 'cupsdCmsRegisterPrinter()' - Registers a printer and profiles with the CMS
720  */
721
722 void
723 cupsdCmsRegisterPrinter(cupsd_printer_t *p)
724 {
725 #if defined(HAVE_DBUS)
726   colordRegisterPrinter(p);
727 #endif /* defined(HAVE_DBUS) */
728 }
729
730 /*
731  * 'cupsdCmsUnregisterPrinter()' - Unregisters a printer and profiles with the CMS
732  */
733
734 void
735 cupsdCmsUnregisterPrinter(cupsd_printer_t *p)
736 {
737 #if defined(HAVE_DBUS)
738   colordUnregisterPrinter(p);
739 #endif /* defined(HAVE_DBUS) */
740 }
741
742 /*
743  * 'cupsdCmsStart()' - Starts the CMS
744  */
745
746 void
747 cupsdCmsStart(void)
748 {
749 #if defined(HAVE_DBUS)
750   colordStart();
751 #endif /* defined(HAVE_DBUS) */
752 }
753
754 /*
755  * 'cupsdCmsStop()' - Stops the CMS
756  */
757
758 void
759 cupsdCmsStop(void)
760 {
761 #if defined(HAVE_DBUS)
762   colordStop();
763 #endif /* defined(HAVE_DBUS) */
764 }
765
766 /*
767  * 'cupsdDeletePrinter()' - Delete a printer from the system.
768  */
769
770 int                                     /* O - 1 if classes affected, 0 otherwise */
771 cupsdDeletePrinter(
772     cupsd_printer_t *p,                 /* I - Printer to delete */
773     int             update)             /* I - Update printers.conf? */
774 {
775   int   i,                              /* Looping var */
776         changed = 0;                    /* Class changed? */
777 #ifdef __sgi
778   char  filename[1024];                 /* Interface script filename */
779 #endif /* __sgi */
780
781
782   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeletePrinter(p=%p(%s), update=%d)",
783                   p, p->name, update);
784
785  /*
786   * Save the current position in the Printers array...
787   */
788
789   cupsArraySave(Printers);
790
791  /*
792   * Stop printing on this printer...
793   */
794
795   cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, update);
796
797   p->state = IPP_PRINTER_STOPPED;       /* Force for browsed printers */
798
799   if (p->job)
800     cupsdSetJobState(p->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
801                      update ? "Job stopped due to printer being deleted." :
802                               "Job stopped.");
803
804  /*
805   * Unregister profiles...
806   */
807
808   cupsdCmsUnregisterPrinter(p);
809
810  /*
811   * If this printer is the next for browsing, point to the next one...
812   */
813
814   if (p == BrowseNext)
815   {
816     cupsArrayFind(Printers, p);
817     BrowseNext = (cupsd_printer_t *)cupsArrayNext(Printers);
818   }
819
820  /*
821   * Remove the printer from the list...
822   */
823
824   cupsdLogMessage(CUPSD_LOG_DEBUG2,
825                   "cupsdDeletePrinter: Removing %s from Printers", p->name);
826   cupsArrayRemove(Printers, p);
827
828   if (p->type & CUPS_PRINTER_IMPLICIT)
829   {
830     cupsdLogMessage(CUPSD_LOG_DEBUG2,
831                     "cupsdDeletePrinter: Removing %s from ImplicitPrinters",
832                     p->name);
833     cupsArrayRemove(ImplicitPrinters, p);
834   }
835
836  /*
837   * Remove the dummy interface/icon/option files under IRIX...
838   */
839
840 #ifdef __sgi
841   snprintf(filename, sizeof(filename), "/var/spool/lp/interface/%s", p->name);
842   unlink(filename);
843
844   snprintf(filename, sizeof(filename), "/var/spool/lp/gui_interface/ELF/%s.gui",
845            p->name);
846   unlink(filename);
847
848   snprintf(filename, sizeof(filename), "/var/spool/lp/activeicons/%s", p->name);
849   unlink(filename);
850
851   snprintf(filename, sizeof(filename), "/var/spool/lp/pod/%s.config", p->name);
852   unlink(filename);
853
854   snprintf(filename, sizeof(filename), "/var/spool/lp/pod/%s.status", p->name);
855   unlink(filename);
856
857   snprintf(filename, sizeof(filename), "/var/spool/lp/member/%s", p->name);
858   unlink(filename);
859 #endif /* __sgi */
860
861  /*
862   * If p is the default printer, assign a different one...
863   */
864
865   if (p == DefaultPrinter)
866   {
867     DefaultPrinter = NULL;
868
869     if (UseNetworkDefault)
870     {
871      /*
872       * Find the first network default printer and use it...
873       */
874
875       cupsd_printer_t   *dp;            /* New default printer */
876
877
878       for (dp = (cupsd_printer_t *)cupsArrayFirst(Printers);
879            dp;
880            dp = (cupsd_printer_t *)cupsArrayNext(Printers))
881         if (dp != p && (dp->type & CUPS_PRINTER_DEFAULT))
882         {
883           DefaultPrinter = dp;
884           break;
885         }
886     }
887   }
888
889  /*
890   * Remove this printer from any classes...
891   */
892
893   if (!(p->type & CUPS_PRINTER_IMPLICIT))
894   {
895     changed = cupsdDeletePrinterFromClasses(p);
896
897    /*
898     * Deregister from any browse protocols...
899     */
900
901     cupsdDeregisterPrinter(p, 1);
902   }
903
904  /*
905   * Free all memory used by the printer...
906   */
907
908   if (p->printers != NULL)
909     free(p->printers);
910
911   delete_printer_filters(p);
912
913   for (i = 0; i < p->num_reasons; i ++)
914     _cupsStrFree(p->reasons[i]);
915
916   ippDelete(p->attrs);
917   ippDelete(p->ppd_attrs);
918
919   mimeDeleteType(MimeDatabase, p->filetype);
920   mimeDeleteType(MimeDatabase, p->prefiltertype);
921
922   cupsdFreeStrings(&(p->users));
923   cupsdFreeQuotas(p);
924
925   cupsdClearString(&p->uri);
926   cupsdClearString(&p->hostname);
927   cupsdClearString(&p->name);
928   cupsdClearString(&p->location);
929   cupsdClearString(&p->make_model);
930   cupsdClearString(&p->info);
931   cupsdClearString(&p->job_sheets[0]);
932   cupsdClearString(&p->job_sheets[1]);
933   cupsdClearString(&p->device_uri);
934   cupsdClearString(&p->sanitized_device_uri);
935   cupsdClearString(&p->port_monitor);
936   cupsdClearString(&p->op_policy);
937   cupsdClearString(&p->error_policy);
938
939   cupsdClearString(&p->alert);
940   cupsdClearString(&p->alert_description);
941
942 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
943   cupsdClearString(&p->pdl);
944 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
945
946   cupsArrayDelete(p->filetypes);
947
948   if (p->browse_attrs)
949     free(p->browse_attrs);
950
951   cupsFreeOptions(p->num_options, p->options);
952
953   free(p);
954
955  /*
956   * Restore the previous position in the Printers array...
957   */
958
959   cupsArrayRestore(Printers);
960
961   return (changed);
962 }
963
964
965 /*
966  * 'cupsdFindDest()' - Find a destination in the list.
967  */
968
969 cupsd_printer_t *                       /* O - Destination in list */
970 cupsdFindDest(const char *name)         /* I - Name of printer or class to find */
971 {
972   cupsd_printer_t       key;            /* Search key */
973
974
975   key.name = (char *)name;
976   return ((cupsd_printer_t *)cupsArrayFind(Printers, &key));
977 }
978
979
980 /*
981  * 'cupsdFindPrinter()' - Find a printer in the list.
982  */
983
984 cupsd_printer_t *                       /* O - Printer in list */
985 cupsdFindPrinter(const char *name)      /* I - Name of printer to find */
986 {
987   cupsd_printer_t       *p;             /* Printer in list */
988
989
990   if ((p = cupsdFindDest(name)) != NULL && (p->type & CUPS_PRINTER_CLASS))
991     return (NULL);
992   else
993     return (p);
994 }
995
996
997 /*
998  * 'cupsdLoadAllPrinters()' - Load printers from the printers.conf file.
999  */
1000
1001 void
1002 cupsdLoadAllPrinters(void)
1003 {
1004   int                   i;              /* Looping var */
1005   cups_file_t           *fp;            /* printers.conf file */
1006   int                   linenum;        /* Current line number */
1007   char                  line[4096],     /* Line from file */
1008                         *value,         /* Pointer to value */
1009                         *valueptr;      /* Pointer into value */
1010   cupsd_printer_t       *p;             /* Current printer */
1011
1012
1013  /*
1014   * Open the printers.conf file...
1015   */
1016
1017   snprintf(line, sizeof(line), "%s/printers.conf", ServerRoot);
1018   if ((fp = cupsdOpenConfFile(line)) == NULL)
1019     return;
1020
1021  /*
1022   * Read printer configurations until we hit EOF...
1023   */
1024
1025   linenum = 0;
1026   p       = NULL;
1027
1028   while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1029   {
1030    /*
1031     * Decode the directive...
1032     */
1033
1034     if (!_cups_strcasecmp(line, "<Printer") ||
1035         !_cups_strcasecmp(line, "<DefaultPrinter"))
1036     {
1037      /*
1038       * <Printer name> or <DefaultPrinter name>
1039       */
1040
1041       if (p == NULL && value)
1042       {
1043        /*
1044         * Add the printer and a base file type...
1045         */
1046
1047         cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading printer %s...", value);
1048
1049         p = cupsdAddPrinter(value);
1050         p->accepting = 1;
1051         p->state     = IPP_PRINTER_IDLE;
1052
1053        /*
1054         * Set the default printer as needed...
1055         */
1056
1057         if (!_cups_strcasecmp(line, "<DefaultPrinter"))
1058           DefaultPrinter = p;
1059       }
1060       else
1061         cupsdLogMessage(CUPSD_LOG_ERROR,
1062                         "Syntax error on line %d of printers.conf.", linenum);
1063     }
1064     else if (!_cups_strcasecmp(line, "</Printer>"))
1065     {
1066       if (p != NULL)
1067       {
1068        /*
1069         * Close out the current printer...
1070         */
1071
1072         cupsdSetPrinterAttrs(p);
1073
1074         if (strncmp(p->device_uri, "file:", 5) &&
1075             p->state != IPP_PRINTER_STOPPED)
1076         {
1077          /*
1078           * See if the backend exists...
1079           */
1080
1081           snprintf(line, sizeof(line), "%s/backend/%s", ServerBin,
1082                    p->device_uri);
1083
1084           if ((valueptr = strchr(line + strlen(ServerBin), ':')) != NULL)
1085             *valueptr = '\0';           /* Chop everything but URI scheme */
1086
1087           if (access(line, 0))
1088           {
1089            /*
1090             * Backend does not exist, stop printer...
1091             */
1092
1093             p->state = IPP_PRINTER_STOPPED;
1094             snprintf(p->state_message, sizeof(p->state_message),
1095                      "Backend %s does not exist!", line);
1096           }
1097         }
1098
1099         p = NULL;
1100       }
1101       else
1102         cupsdLogMessage(CUPSD_LOG_ERROR,
1103                         "Syntax error on line %d of printers.conf.", linenum);
1104     }
1105     else if (!p)
1106     {
1107       cupsdLogMessage(CUPSD_LOG_ERROR,
1108                       "Syntax error on line %d of printers.conf.", linenum);
1109     }
1110     else if (!_cups_strcasecmp(line, "UUID"))
1111     {
1112       if (value && !strncmp(value, "urn:uuid:", 9))
1113         cupsdSetString(&(p->uuid), value);
1114       else
1115         cupsdLogMessage(CUPSD_LOG_ERROR,
1116                         "Bad UUID on line %d of printers.conf.", linenum);
1117     }
1118     else if (!_cups_strcasecmp(line, "AuthInfoRequired"))
1119     {
1120       if (!cupsdSetAuthInfoRequired(p, value, NULL))
1121         cupsdLogMessage(CUPSD_LOG_ERROR,
1122                         "Bad AuthInfoRequired on line %d of printers.conf.",
1123                         linenum);
1124     }
1125     else if (!_cups_strcasecmp(line, "Info"))
1126     {
1127       if (value)
1128         cupsdSetString(&p->info, value);
1129     }
1130     else if (!_cups_strcasecmp(line, "MakeModel"))
1131     {
1132       if (value)
1133         cupsdSetString(&p->make_model, value);
1134     }
1135     else if (!_cups_strcasecmp(line, "Location"))
1136     {
1137       if (value)
1138         cupsdSetString(&p->location, value);
1139     }
1140     else if (!_cups_strcasecmp(line, "DeviceURI"))
1141     {
1142       if (value)
1143         cupsdSetDeviceURI(p, value);
1144       else
1145         cupsdLogMessage(CUPSD_LOG_ERROR,
1146                         "Syntax error on line %d of printers.conf.", linenum);
1147     }
1148     else if (!_cups_strcasecmp(line, "Option") && value)
1149     {
1150      /*
1151       * Option name value
1152       */
1153
1154       for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
1155
1156       if (!*valueptr)
1157         cupsdLogMessage(CUPSD_LOG_ERROR,
1158                         "Syntax error on line %d of printers.conf.", linenum);
1159       else
1160       {
1161         for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
1162
1163         p->num_options = cupsAddOption(value, valueptr, p->num_options,
1164                                        &(p->options));
1165       }
1166     }
1167     else if (!_cups_strcasecmp(line, "PortMonitor"))
1168     {
1169       if (value && strcmp(value, "none"))
1170         cupsdSetString(&p->port_monitor, value);
1171       else if (value)
1172         cupsdClearString(&p->port_monitor);
1173       else
1174         cupsdLogMessage(CUPSD_LOG_ERROR,
1175                         "Syntax error on line %d of printers.conf.", linenum);
1176     }
1177     else if (!_cups_strcasecmp(line, "Reason"))
1178     {
1179       if (value &&
1180           strcmp(value, "connecting-to-device") &&
1181           strcmp(value, "cups-insecure-filter-warning") &&
1182           strcmp(value, "cups-missing-filter-warning"))
1183       {
1184         for (i = 0 ; i < p->num_reasons; i ++)
1185           if (!strcmp(value, p->reasons[i]))
1186             break;
1187
1188         if (i >= p->num_reasons &&
1189             p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
1190         {
1191           p->reasons[p->num_reasons] = _cupsStrAlloc(value);
1192           p->num_reasons ++;
1193         }
1194       }
1195       else
1196         cupsdLogMessage(CUPSD_LOG_ERROR,
1197                         "Syntax error on line %d of printers.conf.", linenum);
1198     }
1199     else if (!_cups_strcasecmp(line, "State"))
1200     {
1201      /*
1202       * Set the initial queue state...
1203       */
1204
1205       if (value && !_cups_strcasecmp(value, "idle"))
1206         p->state = IPP_PRINTER_IDLE;
1207       else if (value && !_cups_strcasecmp(value, "stopped"))
1208       {
1209         p->state = IPP_PRINTER_STOPPED;
1210
1211         for (i = 0 ; i < p->num_reasons; i ++)
1212           if (!strcmp("paused", p->reasons[i]))
1213             break;
1214
1215         if (i >= p->num_reasons &&
1216             p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
1217         {
1218           p->reasons[p->num_reasons] = _cupsStrAlloc("paused");
1219           p->num_reasons ++;
1220         }
1221       }
1222       else
1223         cupsdLogMessage(CUPSD_LOG_ERROR,
1224                         "Syntax error on line %d of printers.conf.", linenum);
1225     }
1226     else if (!_cups_strcasecmp(line, "StateMessage"))
1227     {
1228      /*
1229       * Set the initial queue state message...
1230       */
1231
1232       if (value)
1233         strlcpy(p->state_message, value, sizeof(p->state_message));
1234     }
1235     else if (!_cups_strcasecmp(line, "StateTime"))
1236     {
1237      /*
1238       * Set the state time...
1239       */
1240
1241       if (value)
1242         p->state_time = atoi(value);
1243     }
1244     else if (!_cups_strcasecmp(line, "Accepting"))
1245     {
1246      /*
1247       * Set the initial accepting state...
1248       */
1249
1250       if (value &&
1251           (!_cups_strcasecmp(value, "yes") ||
1252            !_cups_strcasecmp(value, "on") ||
1253            !_cups_strcasecmp(value, "true")))
1254         p->accepting = 1;
1255       else if (value &&
1256                (!_cups_strcasecmp(value, "no") ||
1257                 !_cups_strcasecmp(value, "off") ||
1258                 !_cups_strcasecmp(value, "false")))
1259         p->accepting = 0;
1260       else
1261         cupsdLogMessage(CUPSD_LOG_ERROR,
1262                         "Syntax error on line %d of printers.conf.", linenum);
1263     }
1264     else if (!_cups_strcasecmp(line, "Type"))
1265     {
1266       if (value)
1267         p->type = atoi(value);
1268       else
1269         cupsdLogMessage(CUPSD_LOG_ERROR,
1270                         "Syntax error on line %d of printers.conf.", linenum);
1271     }
1272     else if (!_cups_strcasecmp(line, "Shared"))
1273     {
1274      /*
1275       * Set the initial shared state...
1276       */
1277
1278       if (value &&
1279           (!_cups_strcasecmp(value, "yes") ||
1280            !_cups_strcasecmp(value, "on") ||
1281            !_cups_strcasecmp(value, "true")))
1282         p->shared = 1;
1283       else if (value &&
1284                (!_cups_strcasecmp(value, "no") ||
1285                 !_cups_strcasecmp(value, "off") ||
1286                 !_cups_strcasecmp(value, "false")))
1287         p->shared = 0;
1288       else
1289         cupsdLogMessage(CUPSD_LOG_ERROR,
1290                         "Syntax error on line %d of printers.conf.", linenum);
1291     }
1292     else if (!_cups_strcasecmp(line, "JobSheets"))
1293     {
1294      /*
1295       * Set the initial job sheets...
1296       */
1297
1298       if (value)
1299       {
1300         for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
1301
1302         if (*valueptr)
1303           *valueptr++ = '\0';
1304
1305         cupsdSetString(&p->job_sheets[0], value);
1306
1307         while (isspace(*valueptr & 255))
1308           valueptr ++;
1309
1310         if (*valueptr)
1311         {
1312           for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++);
1313
1314           if (*valueptr)
1315             *valueptr = '\0';
1316
1317           cupsdSetString(&p->job_sheets[1], value);
1318         }
1319       }
1320       else
1321         cupsdLogMessage(CUPSD_LOG_ERROR,
1322                         "Syntax error on line %d of printers.conf.", linenum);
1323     }
1324     else if (!_cups_strcasecmp(line, "AllowUser"))
1325     {
1326       if (value)
1327       {
1328         p->deny_users = 0;
1329         cupsdAddString(&(p->users), value);
1330       }
1331       else
1332         cupsdLogMessage(CUPSD_LOG_ERROR,
1333                         "Syntax error on line %d of printers.conf.", linenum);
1334     }
1335     else if (!_cups_strcasecmp(line, "DenyUser"))
1336     {
1337       if (value)
1338       {
1339         p->deny_users = 1;
1340         cupsdAddString(&(p->users), value);
1341       }
1342       else
1343         cupsdLogMessage(CUPSD_LOG_ERROR,
1344                         "Syntax error on line %d of printers.conf.", linenum);
1345     }
1346     else if (!_cups_strcasecmp(line, "QuotaPeriod"))
1347     {
1348       if (value)
1349         p->quota_period = atoi(value);
1350       else
1351         cupsdLogMessage(CUPSD_LOG_ERROR,
1352                         "Syntax error on line %d of printers.conf.", linenum);
1353     }
1354     else if (!_cups_strcasecmp(line, "PageLimit"))
1355     {
1356       if (value)
1357         p->page_limit = atoi(value);
1358       else
1359         cupsdLogMessage(CUPSD_LOG_ERROR,
1360                         "Syntax error on line %d of printers.conf.", linenum);
1361     }
1362     else if (!_cups_strcasecmp(line, "KLimit"))
1363     {
1364       if (value)
1365         p->k_limit = atoi(value);
1366       else
1367         cupsdLogMessage(CUPSD_LOG_ERROR,
1368                         "Syntax error on line %d of printers.conf.", linenum);
1369     }
1370     else if (!_cups_strcasecmp(line, "OpPolicy"))
1371     {
1372       if (value)
1373       {
1374         cupsd_policy_t *pol;            /* Policy */
1375
1376
1377         if ((pol = cupsdFindPolicy(value)) != NULL)
1378         {
1379           cupsdSetString(&p->op_policy, value);
1380           p->op_policy_ptr = pol;
1381         }
1382         else
1383           cupsdLogMessage(CUPSD_LOG_ERROR,
1384                           "Bad policy \"%s\" on line %d of printers.conf",
1385                           value, linenum);
1386       }
1387       else
1388         cupsdLogMessage(CUPSD_LOG_ERROR,
1389                         "Syntax error on line %d of printers.conf.", linenum);
1390     }
1391     else if (!_cups_strcasecmp(line, "ErrorPolicy"))
1392     {
1393       if (value)
1394         cupsdSetString(&p->error_policy, value);
1395       else
1396         cupsdLogMessage(CUPSD_LOG_ERROR,
1397                         "Syntax error on line %d of printers.conf.", linenum);
1398     }
1399     else if (!_cups_strcasecmp(line, "Attribute") && value)
1400     {
1401       for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
1402
1403       if (!*valueptr)
1404         cupsdLogMessage(CUPSD_LOG_ERROR,
1405                         "Syntax error on line %d of printers.conf.", linenum);
1406       else
1407       {
1408         for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
1409
1410         if (!p->attrs)
1411           cupsdSetPrinterAttrs(p);
1412
1413         if (!strcmp(value, "marker-change-time"))
1414           p->marker_time = atoi(valueptr);
1415         else
1416           cupsdSetPrinterAttr(p, value, valueptr);
1417       }
1418     }
1419     else if (_cups_strcasecmp(line, "Filter") &&
1420              _cups_strcasecmp(line, "Prefilter") &&
1421              _cups_strcasecmp(line, "Product"))
1422     {
1423      /*
1424       * Something else we don't understand (and that wasn't used in a prior
1425       * release of CUPS...
1426       */
1427
1428       cupsdLogMessage(CUPSD_LOG_ERROR,
1429                       "Unknown configuration directive %s on line %d of "
1430                       "printers.conf.", line, linenum);
1431     }
1432   }
1433
1434   cupsFileClose(fp);
1435 }
1436
1437
1438 /*
1439  * 'cupsdRenamePrinter()' - Rename a printer.
1440  */
1441
1442 void
1443 cupsdRenamePrinter(
1444     cupsd_printer_t *p,                 /* I - Printer */
1445     const char      *name)              /* I - New name */
1446 {
1447  /*
1448   * Remove the printer from the array(s) first...
1449   */
1450
1451   cupsdLogMessage(CUPSD_LOG_DEBUG2,
1452                   "cupsdRenamePrinter: Removing %s from Printers", p->name);
1453   cupsArrayRemove(Printers, p);
1454
1455   if (p->type & CUPS_PRINTER_IMPLICIT)
1456   {
1457     cupsdLogMessage(CUPSD_LOG_DEBUG2,
1458                     "cupsdRenamePrinter: Removing %s from ImplicitPrinters",
1459                     p->name);
1460     cupsArrayRemove(ImplicitPrinters, p);
1461   }
1462
1463  /*
1464   * Rename the printer type...
1465   */
1466
1467   mimeDeleteType(MimeDatabase, p->filetype);
1468   p->filetype = mimeAddType(MimeDatabase, "printer", name);
1469
1470   if (p->prefiltertype)
1471   {
1472     mimeDeleteType(MimeDatabase, p->prefiltertype);
1473     p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", name);
1474   }
1475
1476  /*
1477   * Unregister profiles...
1478   */
1479
1480   cupsdCmsUnregisterPrinter(p);
1481
1482  /*
1483   * Rename the printer...
1484   */
1485
1486   cupsdSetString(&p->name, name);
1487
1488  /*
1489   * Reset printer attributes...
1490   */
1491
1492   cupsdSetPrinterAttrs(p);
1493
1494  /*
1495   * Add the printer back to the printer array(s)...
1496   */
1497
1498   cupsdLogMessage(CUPSD_LOG_DEBUG2,
1499                   "cupsdRenamePrinter: Adding %s to Printers", p->name);
1500   cupsArrayAdd(Printers, p);
1501
1502   if (p->type & CUPS_PRINTER_IMPLICIT)
1503   {
1504     cupsdLogMessage(CUPSD_LOG_DEBUG2,
1505                     "cupsdRenamePrinter: Adding %s to ImplicitPrinters",
1506                     p->name);
1507     cupsArrayAdd(ImplicitPrinters, p);
1508   }
1509 }
1510
1511
1512 /*
1513  * 'cupsdSaveAllPrinters()' - Save all printer definitions to the printers.conf
1514  *                            file.
1515  */
1516
1517 void
1518 cupsdSaveAllPrinters(void)
1519 {
1520   int                   i;              /* Looping var */
1521   cups_file_t           *fp;            /* printers.conf file */
1522   char                  filename[1024], /* printers.conf filename */
1523                         temp[1024],     /* Temporary string */
1524                         value[2048],    /* Value string */
1525                         *ptr,           /* Pointer into value */
1526                         *name;          /* Current user/group name */
1527   cupsd_printer_t       *printer;       /* Current printer class */
1528   time_t                curtime;        /* Current time */
1529   struct tm             *curdate;       /* Current date */
1530   cups_option_t         *option;        /* Current option */
1531   ipp_attribute_t       *marker;        /* Current marker attribute */
1532
1533
1534  /*
1535   * Create the printers.conf file...
1536   */
1537
1538   snprintf(filename, sizeof(filename), "%s/printers.conf", ServerRoot);
1539
1540   if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm & 0600)) == NULL)
1541     return;
1542
1543   cupsdLogMessage(CUPSD_LOG_INFO, "Saving printers.conf...");
1544
1545  /*
1546   * Write a small header to the file...
1547   */
1548
1549   curtime = time(NULL);
1550   curdate = localtime(&curtime);
1551   strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1552
1553   cupsFilePuts(fp, "# Printer configuration file for " CUPS_SVERSION "\n");
1554   cupsFilePrintf(fp, "# Written by cupsd\n");
1555   cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
1556
1557  /*
1558   * Write each local printer known to the system...
1559   */
1560
1561   for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
1562        printer;
1563        printer = (cupsd_printer_t *)cupsArrayNext(Printers))
1564   {
1565    /*
1566     * Skip remote destinations and printer classes...
1567     */
1568
1569     if ((printer->type & CUPS_PRINTER_DISCOVERED) ||
1570         (printer->type & CUPS_PRINTER_CLASS) ||
1571         (printer->type & CUPS_PRINTER_IMPLICIT))
1572       continue;
1573
1574    /*
1575     * Write printers as needed...
1576     */
1577
1578     if (printer == DefaultPrinter)
1579       cupsFilePrintf(fp, "<DefaultPrinter %s>\n", printer->name);
1580     else
1581       cupsFilePrintf(fp, "<Printer %s>\n", printer->name);
1582
1583     cupsFilePrintf(fp, "UUID %s\n", printer->uuid);
1584
1585     if (printer->num_auth_info_required > 0)
1586     {
1587       switch (printer->num_auth_info_required)
1588       {
1589         case 1 :
1590             strlcpy(value, printer->auth_info_required[0], sizeof(value));
1591             break;
1592
1593         case 2 :
1594             snprintf(value, sizeof(value), "%s,%s",
1595                      printer->auth_info_required[0],
1596                      printer->auth_info_required[1]);
1597             break;
1598
1599         case 3 :
1600         default :
1601             snprintf(value, sizeof(value), "%s,%s,%s",
1602                      printer->auth_info_required[0],
1603                      printer->auth_info_required[1],
1604                      printer->auth_info_required[2]);
1605             break;
1606       }
1607
1608       cupsFilePutConf(fp, "AuthInfoRequired", value);
1609     }
1610
1611     if (printer->info)
1612       cupsFilePutConf(fp, "Info", printer->info);
1613
1614     if (printer->location)
1615       cupsFilePutConf(fp, "Location", printer->location);
1616
1617     if (printer->make_model)
1618       cupsFilePutConf(fp, "MakeModel", printer->make_model);
1619
1620     cupsFilePutConf(fp, "DeviceURI", printer->device_uri);
1621
1622     if (printer->port_monitor)
1623       cupsFilePutConf(fp, "PortMonitor", printer->port_monitor);
1624
1625     if (printer->state == IPP_PRINTER_STOPPED)
1626     {
1627       cupsFilePuts(fp, "State Stopped\n");
1628
1629       if (printer->state_message)
1630         cupsFilePutConf(fp, "StateMessage", printer->state_message);
1631     }
1632     else
1633       cupsFilePuts(fp, "State Idle\n");
1634
1635     cupsFilePrintf(fp, "StateTime %d\n", (int)printer->state_time);
1636
1637     for (i = 0; i < printer->num_reasons; i ++)
1638       if (strcmp(printer->reasons[i], "connecting-to-device") &&
1639           strcmp(printer->reasons[i], "cups-insecure-filter-warning") &&
1640           strcmp(printer->reasons[i], "cups-missing-filter-warning"))
1641         cupsFilePutConf(fp, "Reason", printer->reasons[i]);
1642
1643     cupsFilePrintf(fp, "Type %d\n", printer->type);
1644
1645     if (printer->accepting)
1646       cupsFilePuts(fp, "Accepting Yes\n");
1647     else
1648       cupsFilePuts(fp, "Accepting No\n");
1649
1650     if (printer->shared)
1651       cupsFilePuts(fp, "Shared Yes\n");
1652     else
1653       cupsFilePuts(fp, "Shared No\n");
1654
1655     snprintf(value, sizeof(value), "%s %s", printer->job_sheets[0],
1656              printer->job_sheets[1]);
1657     cupsFilePutConf(fp, "JobSheets", value);
1658
1659     cupsFilePrintf(fp, "QuotaPeriod %d\n", printer->quota_period);
1660     cupsFilePrintf(fp, "PageLimit %d\n", printer->page_limit);
1661     cupsFilePrintf(fp, "KLimit %d\n", printer->k_limit);
1662
1663     for (name = (char *)cupsArrayFirst(printer->users);
1664          name;
1665          name = (char *)cupsArrayNext(printer->users))
1666       cupsFilePutConf(fp, printer->deny_users ? "DenyUser" : "AllowUser", name);
1667
1668     if (printer->op_policy)
1669       cupsFilePutConf(fp, "OpPolicy", printer->op_policy);
1670     if (printer->error_policy)
1671       cupsFilePutConf(fp, "ErrorPolicy", printer->error_policy);
1672
1673     for (i = printer->num_options, option = printer->options;
1674          i > 0;
1675          i --, option ++)
1676     {
1677       snprintf(value, sizeof(value), "%s %s", option->name, option->value);
1678       cupsFilePutConf(fp, "Option", value);
1679     }
1680
1681     if ((marker = ippFindAttribute(printer->attrs, "marker-colors",
1682                                    IPP_TAG_NAME)) != NULL)
1683     {
1684       snprintf(value, sizeof(value), "%s ", marker->name);
1685
1686       for (i = 0, ptr = value + strlen(value);
1687            i < marker->num_values && ptr < (value + sizeof(value) - 1);
1688            i ++)
1689       {
1690         if (i)
1691           *ptr++ = ',';
1692
1693         strlcpy(ptr, marker->values[i].string.text,
1694                 value + sizeof(value) - ptr);
1695         ptr += strlen(ptr);
1696       }
1697
1698       *ptr = '\0';
1699       cupsFilePutConf(fp, "Attribute", value);
1700     }
1701
1702     if ((marker = ippFindAttribute(printer->attrs, "marker-levels",
1703                                    IPP_TAG_INTEGER)) != NULL)
1704     {
1705       cupsFilePrintf(fp, "Attribute %s %d", marker->name,
1706                      marker->values[0].integer);
1707       for (i = 1; i < marker->num_values; i ++)
1708         cupsFilePrintf(fp, ",%d", marker->values[i].integer);
1709       cupsFilePuts(fp, "\n");
1710     }
1711
1712     if ((marker = ippFindAttribute(printer->attrs, "marker-low-levels",
1713                                    IPP_TAG_INTEGER)) != NULL)
1714     {
1715       cupsFilePrintf(fp, "Attribute %s %d", marker->name,
1716                      marker->values[0].integer);
1717       for (i = 1; i < marker->num_values; i ++)
1718         cupsFilePrintf(fp, ",%d", marker->values[i].integer);
1719       cupsFilePuts(fp, "\n");
1720     }
1721
1722     if ((marker = ippFindAttribute(printer->attrs, "marker-high-levels",
1723                                    IPP_TAG_INTEGER)) != NULL)
1724     {
1725       cupsFilePrintf(fp, "Attribute %s %d", marker->name,
1726                      marker->values[0].integer);
1727       for (i = 1; i < marker->num_values; i ++)
1728         cupsFilePrintf(fp, ",%d", marker->values[i].integer);
1729       cupsFilePuts(fp, "\n");
1730     }
1731
1732     if ((marker = ippFindAttribute(printer->attrs, "marker-message",
1733                                    IPP_TAG_TEXT)) != NULL)
1734     {
1735       snprintf(value, sizeof(value), "%s %s", marker->name,
1736                marker->values[0].string.text);
1737
1738       cupsFilePutConf(fp, "Attribute", value);
1739     }
1740
1741     if ((marker = ippFindAttribute(printer->attrs, "marker-names",
1742                                    IPP_TAG_NAME)) != NULL)
1743     {
1744       snprintf(value, sizeof(value), "%s ", marker->name);
1745
1746       for (i = 0, ptr = value + strlen(value);
1747            i < marker->num_values && ptr < (value + sizeof(value) - 1);
1748            i ++)
1749       {
1750         if (i)
1751           *ptr++ = ',';
1752
1753         strlcpy(ptr, marker->values[i].string.text,
1754                 value + sizeof(value) - ptr);
1755         ptr += strlen(ptr);
1756       }
1757
1758       *ptr = '\0';
1759       cupsFilePutConf(fp, "Attribute", value);
1760     }
1761
1762     if ((marker = ippFindAttribute(printer->attrs, "marker-types",
1763                                    IPP_TAG_KEYWORD)) != NULL)
1764     {
1765       snprintf(value, sizeof(value), "%s ", marker->name);
1766
1767       for (i = 0, ptr = value + strlen(value);
1768            i < marker->num_values && ptr < (value + sizeof(value) - 1);
1769            i ++)
1770       {
1771         if (i)
1772           *ptr++ = ',';
1773
1774         strlcpy(ptr, marker->values[i].string.text,
1775                 value + sizeof(value) - ptr);
1776         ptr += strlen(ptr);
1777       }
1778
1779       *ptr = '\0';
1780       cupsFilePutConf(fp, "Attribute", value);
1781     }
1782
1783     if (printer->marker_time)
1784       cupsFilePrintf(fp, "Attribute marker-change-time %ld\n",
1785                      (long)printer->marker_time);
1786
1787     cupsFilePuts(fp, "</Printer>\n");
1788
1789 #ifdef __sgi
1790     /*
1791      * Make IRIX desktop & printer status happy
1792      */
1793
1794     write_irix_state(printer);
1795 #endif /* __sgi */
1796   }
1797
1798   cupsdCloseCreatedConfFile(fp, filename);
1799 }
1800
1801
1802 /*
1803  * 'cupsdSetAuthInfoRequired()' - Set the required authentication info.
1804  */
1805
1806 int                                     /* O - 1 if value OK, 0 otherwise */
1807 cupsdSetAuthInfoRequired(
1808     cupsd_printer_t *p,                 /* I - Printer */
1809     const char      *values,            /* I - Plain text value (or NULL) */
1810     ipp_attribute_t *attr)              /* I - IPP attribute value (or NULL) */
1811 {
1812   int   i;                              /* Looping var */
1813
1814
1815   p->num_auth_info_required = 0;
1816
1817  /*
1818   * Do we have a plain text value?
1819   */
1820
1821   if (values)
1822   {
1823    /*
1824     * Yes, grab the keywords...
1825     */
1826
1827     const char  *end;                   /* End of current value */
1828
1829
1830     while (*values && p->num_auth_info_required < 4)
1831     {
1832       if ((end = strchr(values, ',')) == NULL)
1833         end = values + strlen(values);
1834
1835       if ((end - values) == 4 && !strncmp(values, "none", 4))
1836       {
1837         if (p->num_auth_info_required != 0 || *end)
1838           return (0);
1839
1840         p->auth_info_required[p->num_auth_info_required] = "none";
1841         p->num_auth_info_required ++;
1842
1843         return (1);
1844       }
1845       else if ((end - values) == 9 && !strncmp(values, "negotiate", 9))
1846       {
1847         if (p->num_auth_info_required != 0 || *end)
1848           return (0);
1849
1850         p->auth_info_required[p->num_auth_info_required] = "negotiate";
1851         p->num_auth_info_required ++;
1852
1853        /*
1854         * Don't allow sharing of queues that require Kerberos authentication.
1855         */
1856
1857         if (p->shared)
1858         {
1859           cupsdDeregisterPrinter(p, 1);
1860           p->shared = 0;
1861         }
1862       }
1863       else if ((end - values) == 6 && !strncmp(values, "domain", 6))
1864       {
1865         p->auth_info_required[p->num_auth_info_required] = "domain";
1866         p->num_auth_info_required ++;
1867       }
1868       else if ((end - values) == 8 && !strncmp(values, "password", 8))
1869       {
1870         p->auth_info_required[p->num_auth_info_required] = "password";
1871         p->num_auth_info_required ++;
1872       }
1873       else if ((end - values) == 8 && !strncmp(values, "username", 8))
1874       {
1875         p->auth_info_required[p->num_auth_info_required] = "username";
1876         p->num_auth_info_required ++;
1877       }
1878       else
1879         return (0);
1880
1881       values = (*end) ? end + 1 : end;
1882     }
1883
1884     if (p->num_auth_info_required == 0)
1885     {
1886       p->auth_info_required[0]  = "none";
1887       p->num_auth_info_required = 1;
1888     }
1889
1890    /*
1891     * Update the printer-type value as needed...
1892     */
1893
1894     if (p->num_auth_info_required > 1 ||
1895         strcmp(p->auth_info_required[0], "none"))
1896       p->type |= CUPS_PRINTER_AUTHENTICATED;
1897     else
1898       p->type &= ~CUPS_PRINTER_AUTHENTICATED;
1899
1900     return (1);
1901   }
1902
1903  /*
1904   * Grab values from an attribute instead...
1905   */
1906
1907   if (!attr || attr->num_values > 4)
1908     return (0);
1909
1910   for (i = 0; i < attr->num_values; i ++)
1911   {
1912     if (!strcmp(attr->values[i].string.text, "none"))
1913     {
1914       if (p->num_auth_info_required != 0 || attr->num_values != 1)
1915         return (0);
1916
1917       p->auth_info_required[p->num_auth_info_required] = "none";
1918       p->num_auth_info_required ++;
1919
1920       return (1);
1921     }
1922     else if (!strcmp(attr->values[i].string.text, "negotiate"))
1923     {
1924       if (p->num_auth_info_required != 0 || attr->num_values != 1)
1925         return (0);
1926
1927       p->auth_info_required[p->num_auth_info_required] = "negotiate";
1928       p->num_auth_info_required ++;
1929
1930      /*
1931       * Don't allow sharing of queues that require Kerberos authentication.
1932       */
1933
1934       if (p->shared)
1935       {
1936         cupsdDeregisterPrinter(p, 1);
1937         p->shared = 0;
1938       }
1939
1940       return (1);
1941     }
1942     else if (!strcmp(attr->values[i].string.text, "domain"))
1943     {
1944       p->auth_info_required[p->num_auth_info_required] = "domain";
1945       p->num_auth_info_required ++;
1946     }
1947     else if (!strcmp(attr->values[i].string.text, "password"))
1948     {
1949       p->auth_info_required[p->num_auth_info_required] = "password";
1950       p->num_auth_info_required ++;
1951     }
1952     else if (!strcmp(attr->values[i].string.text, "username"))
1953     {
1954       p->auth_info_required[p->num_auth_info_required] = "username";
1955       p->num_auth_info_required ++;
1956     }
1957     else
1958       return (0);
1959   }
1960
1961   return (1);
1962 }
1963
1964
1965 /*
1966  * 'cupsdSetDeviceURI()' - Set the device URI for a printer.
1967  */
1968
1969 void
1970 cupsdSetDeviceURI(cupsd_printer_t *p,   /* I - Printer */
1971                   const char      *uri) /* I - Device URI */
1972 {
1973   char  buffer[1024],                   /* URI buffer */
1974         *start,                         /* Start of data after scheme */
1975         *slash,                         /* First slash after scheme:// */
1976         *ptr;                           /* Pointer into user@host:port part */
1977
1978
1979  /*
1980   * Set the full device URI..
1981   */
1982
1983   cupsdSetString(&(p->device_uri), uri);
1984
1985  /*
1986   * Copy the device URI to a temporary buffer so we can sanitize any auth
1987   * info in it...
1988   */
1989
1990   strlcpy(buffer, uri, sizeof(buffer));
1991
1992  /*
1993   * Find the end of the scheme:// part...
1994   */
1995
1996   if ((ptr = strchr(buffer, ':')) != NULL)
1997   {
1998     for (start = ptr + 1; *start; start ++)
1999       if (*start != '/')
2000         break;
2001
2002    /*
2003     * Find the next slash (/) in the URI...
2004     */
2005
2006     if ((slash = strchr(start, '/')) == NULL)
2007       slash = start + strlen(start);    /* No slash, point to the end */
2008
2009    /*
2010     * Check for an @ sign before the slash...
2011     */
2012
2013     if ((ptr = strchr(start, '@')) != NULL && ptr < slash)
2014     {
2015      /*
2016       * Found an @ sign and it is before the resource part, so we have
2017       * an authentication string.  Copy the remaining URI over the
2018       * authentication string...
2019       */
2020
2021       _cups_strcpy(start, ptr + 1);
2022     }
2023   }
2024
2025  /*
2026   * Save the sanitized URI...
2027   */
2028
2029   cupsdSetString(&(p->sanitized_device_uri), buffer);
2030 }
2031
2032
2033 /*
2034  * 'cupsdSetPrinterAttr()' - Set a printer attribute.
2035  */
2036
2037 void
2038 cupsdSetPrinterAttr(
2039     cupsd_printer_t *p,                 /* I - Printer */
2040     const char      *name,              /* I - Attribute name */
2041     char            *value)             /* I - Attribute value string */
2042 {
2043   ipp_attribute_t       *attr;          /* Attribute */
2044   int                   i,              /* Looping var */
2045                         count;          /* Number of values */
2046   char                  *ptr,           /* Pointer into value */
2047                         *start,         /* Start of value */
2048                         quote;          /* Quote character */
2049   ipp_tag_t             value_tag;      /* Value tag for this attribute */
2050
2051
2052  /*
2053   * Don't allow empty values...
2054   */
2055
2056   if (!*value && strcmp(name, "marker-message"))
2057   {
2058     cupsdLogMessage(CUPSD_LOG_ERROR, "Ignoring empty \"%s\" attribute", name);
2059     return;
2060   }
2061
2062  /*
2063   * Count the number of values...
2064   */
2065
2066   for (count = 1, quote = '\0', ptr = value;
2067        *ptr;
2068        ptr ++)
2069   {
2070     if (*ptr == quote)
2071       quote = '\0';
2072     else if (quote)
2073       continue;
2074     else if (*ptr == '\\' && ptr[1])
2075       ptr ++;
2076     else if (*ptr == '\'' || *ptr == '\"')
2077       quote = *ptr;
2078     else if (*ptr == ',')
2079       count ++;
2080   }
2081
2082  /*
2083   * Then add or update the attribute as needed...
2084   */
2085
2086   if (!strcmp(name, "marker-levels") || !strcmp(name, "marker-low-levels") ||
2087       !strcmp(name, "marker-high-levels"))
2088   {
2089    /*
2090     * Integer values...
2091     */
2092
2093     if ((attr = ippFindAttribute(p->attrs, name, IPP_TAG_INTEGER)) != NULL &&
2094         attr->num_values < count)
2095     {
2096       ippDeleteAttribute(p->attrs, attr);
2097       attr = NULL;
2098     }
2099
2100     if (attr)
2101       attr->num_values = count;
2102     else
2103       attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, name,
2104                             count, NULL);
2105
2106     if (!attr)
2107     {
2108       cupsdLogMessage(CUPSD_LOG_ERROR,
2109                       "Unable to allocate memory for printer attribute "
2110                       "(%d values)", count);
2111       return;
2112     }
2113
2114     for (i = 0; i < count; i ++)
2115     {
2116       if ((ptr = strchr(value, ',')) != NULL)
2117         *ptr++ = '\0';
2118
2119       attr->values[i].integer = strtol(value, NULL, 10);
2120
2121       if (ptr)
2122         value = ptr;
2123     }
2124   }
2125   else
2126   {
2127    /*
2128     * Name or keyword values...
2129     */
2130
2131     if (!strcmp(name, "marker-types"))
2132       value_tag = IPP_TAG_KEYWORD;
2133     else if (!strcmp(name, "marker-message"))
2134       value_tag = IPP_TAG_TEXT;
2135     else
2136       value_tag = IPP_TAG_NAME;
2137
2138     if ((attr = ippFindAttribute(p->attrs, name, value_tag)) != NULL &&
2139         attr->num_values < count)
2140     {
2141       ippDeleteAttribute(p->attrs, attr);
2142       attr = NULL;
2143     }
2144
2145     if (attr)
2146     {
2147       for (i = 0; i < attr->num_values; i ++)
2148         _cupsStrFree(attr->values[i].string.text);
2149
2150       attr->num_values = count;
2151     }
2152     else
2153       attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, value_tag, name,
2154                            count, NULL, NULL);
2155
2156     if (!attr)
2157     {
2158       cupsdLogMessage(CUPSD_LOG_ERROR,
2159                       "Unable to allocate memory for printer attribute "
2160                       "(%d values)", count);
2161       return;
2162     }
2163
2164     for (i = 0, quote = '\0', ptr = value; i < count; i ++)
2165     {
2166       for (start = ptr; *ptr; ptr ++)
2167       {
2168         if (*ptr == quote)
2169           *ptr = quote = '\0';
2170         else if (quote)
2171           continue;
2172         else if (*ptr == '\\' && ptr[1])
2173           _cups_strcpy(ptr, ptr + 1);
2174         else if (*ptr == '\'' || *ptr == '\"')
2175         {
2176           quote = *ptr;
2177
2178           if (ptr == start)
2179             start ++;
2180           else
2181             _cups_strcpy(ptr, ptr + 1);
2182         }
2183         else if (*ptr == ',')
2184         {
2185           *ptr++ = '\0';
2186           break;
2187         }
2188       }
2189
2190       attr->values[i].string.text = _cupsStrAlloc(start);
2191     }
2192   }
2193 }
2194
2195
2196 /*
2197  * 'cupsdSetPrinterAttrs()' - Set printer attributes based upon the PPD file.
2198  */
2199
2200 void
2201 cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
2202 {
2203   int           i,                      /* Looping var */
2204                 length;                 /* Length of browse attributes */
2205   char          resource[HTTP_MAX_URI]; /* Resource portion of URI */
2206   int           num_air;                /* Number of auth-info-required values */
2207   const char    * const *air;           /* auth-info-required values */
2208   cupsd_location_t *auth;               /* Pointer to authentication element */
2209   const char    *auth_supported;        /* Authentication supported */
2210   ipp_t         *oldattrs;              /* Old printer attributes */
2211   ipp_attribute_t *attr;                /* Attribute data */
2212   cups_option_t *option;                /* Current printer option */
2213   char          *name,                  /* Current user/group name */
2214                 *filter;                /* Current filter */
2215   static const char * const air_none[] =
2216                 {                       /* No authentication */
2217                   "none"
2218                 };
2219   static const char * const air_userpass[] =
2220                 {                       /* Basic/Digest authentication */
2221                   "username",
2222                   "password"
2223                 };
2224
2225
2226   DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name,
2227                 p->type));
2228
2229  /*
2230   * Make sure that we have the common attributes defined...
2231   */
2232
2233   if (!CommonData)
2234     cupsdCreateCommonData();
2235
2236  /*
2237   * Clear out old filters, if any...
2238   */
2239
2240   delete_printer_filters(p);
2241
2242  /*
2243   * Figure out the authentication that is required for the printer.
2244   */
2245
2246   auth_supported = "requesting-user-name";
2247   num_air        = 1;
2248   air            = air_none;
2249
2250   if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none"))
2251   {
2252     num_air = p->num_auth_info_required;
2253     air     = p->auth_info_required;
2254   }
2255   else if ((p->type & CUPS_PRINTER_AUTHENTICATED) &&
2256            (p->type & CUPS_PRINTER_DISCOVERED))
2257   {
2258     num_air = 2;
2259     air     = air_userpass;
2260   }
2261
2262   if (p->type & CUPS_PRINTER_CLASS)
2263     snprintf(resource, sizeof(resource), "/classes/%s", p->name);
2264   else
2265     snprintf(resource, sizeof(resource), "/printers/%s", p->name);
2266
2267   if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
2268       auth->type == CUPSD_AUTH_NONE)
2269     auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
2270
2271   if (auth)
2272   {
2273     int auth_type;              /* Authentication type */
2274
2275
2276     if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT)
2277       auth_type = DefaultAuthType;
2278
2279     if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
2280       auth_supported = "basic";
2281     else if (auth_type == CUPSD_AUTH_DIGEST)
2282       auth_supported = "digest";
2283 #ifdef HAVE_GSSAPI
2284     else if (auth_type == CUPSD_AUTH_NEGOTIATE)
2285       auth_supported = "negotiate";
2286 #endif /* HAVE_GSSAPI */
2287
2288     if (!(p->type & CUPS_PRINTER_DISCOVERED))
2289     {
2290       if (auth_type != CUPSD_AUTH_NONE)
2291         p->type |= CUPS_PRINTER_AUTHENTICATED;
2292       else
2293         p->type &= ~CUPS_PRINTER_AUTHENTICATED;
2294     }
2295   }
2296   else if (!(p->type & CUPS_PRINTER_DISCOVERED))
2297     p->type &= ~CUPS_PRINTER_AUTHENTICATED;
2298
2299  /*
2300   * Create the required IPP attributes for a printer...
2301   */
2302
2303   oldattrs = p->attrs;
2304   p->attrs = ippNew();
2305
2306   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2307                "uri-authentication-supported", NULL, auth_supported);
2308   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2309                "uri-security-supported", NULL, "none");
2310   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL,
2311                p->name);
2312   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
2313                NULL, p->location ? p->location : "");
2314   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
2315                NULL, p->info ? p->info : "");
2316   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL,
2317                p->uuid);
2318
2319   if (cupsArrayCount(p->users) > 0)
2320   {
2321     if (p->deny_users)
2322       attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2323                            "requesting-user-name-denied",
2324                            cupsArrayCount(p->users), NULL, NULL);
2325     else
2326       attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2327                            "requesting-user-name-allowed",
2328                            cupsArrayCount(p->users), NULL, NULL);
2329
2330     for (i = 0, name = (char *)cupsArrayFirst(p->users);
2331          name;
2332          i ++, name = (char *)cupsArrayNext(p->users))
2333       attr->values[i].string.text = _cupsStrRetain(name);
2334   }
2335
2336   ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2337                 "job-quota-period", p->quota_period);
2338   ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2339                 "job-k-limit", p->k_limit);
2340   ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2341                 "job-page-limit", p->page_limit);
2342   ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2343                 "auth-info-required", num_air, NULL, air);
2344
2345   if (cupsArrayCount(Banners) > 0 && !(p->type & CUPS_PRINTER_DISCOVERED))
2346   {
2347    /*
2348     * Setup the job-sheets-default attribute...
2349     */
2350
2351     attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2352                          "job-sheets-default", 2, NULL, NULL);
2353
2354     if (attr != NULL)
2355     {
2356       attr->values[0].string.text = _cupsStrAlloc(Classification ?
2357                                            Classification : p->job_sheets[0]);
2358       attr->values[1].string.text = _cupsStrAlloc(Classification ?
2359                                            Classification : p->job_sheets[1]);
2360     }
2361   }
2362
2363   p->raw    = 0;
2364   p->remote = 0;
2365
2366   if (p->type & CUPS_PRINTER_DISCOVERED)
2367   {
2368    /*
2369     * Tell the client this is a remote printer of some type...
2370     */
2371
2372     if (strchr(p->uri, '?'))
2373     {
2374      /*
2375       * Strip trailing "?options" from URI...
2376       */
2377
2378       char *ptr;                        /* Pointer into URI */
2379
2380       strlcpy(resource, p->uri, sizeof(resource));
2381       if ((ptr = strchr(resource, '?')) != NULL)
2382         *ptr = '\0';
2383
2384       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
2385                    "printer-uri-supported", NULL, resource);
2386     }
2387     else
2388       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
2389                    "printer-uri-supported", NULL, p->uri);
2390
2391     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info",
2392                  NULL, p->uri);
2393
2394     if (p->make_model)
2395       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2396                    "printer-make-and-model", NULL, p->make_model);
2397
2398     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
2399                  p->uri);
2400
2401     p->raw    = 1;
2402     p->remote = 1;
2403   }
2404   else
2405   {
2406    /*
2407     * Assign additional attributes depending on whether this is a printer
2408     * or class...
2409     */
2410
2411     if (p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
2412     {
2413       p->raw = 1;
2414       p->type &= ~CUPS_PRINTER_OPTIONS;
2415
2416      /*
2417       * Add class-specific attributes...
2418       */
2419
2420       if ((p->type & CUPS_PRINTER_IMPLICIT) && p->num_printers > 0 &&
2421           p->printers[0]->make_model)
2422         ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2423                      "printer-make-and-model", NULL, p->printers[0]->make_model);
2424       else
2425         ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2426                      "printer-make-and-model", NULL, "Local Printer Class");
2427
2428       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
2429                    "file:///dev/null");
2430
2431       if (p->num_printers > 0)
2432       {
2433        /*
2434         * Add a list of member names; URIs are added in copy_printer_attrs...
2435         */
2436
2437         attr    = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2438                                 "member-names", p->num_printers, NULL, NULL);
2439         p->type |= CUPS_PRINTER_OPTIONS;
2440
2441         for (i = 0; i < p->num_printers; i ++)
2442         {
2443           if (attr != NULL)
2444             attr->values[i].string.text = _cupsStrRetain(p->printers[i]->name);
2445
2446           p->type &= ~CUPS_PRINTER_OPTIONS | p->printers[i]->type;
2447         }
2448       }
2449     }
2450     else
2451     {
2452      /*
2453       * Add printer-specific attributes...
2454       */
2455
2456       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
2457                    p->sanitized_device_uri);
2458
2459      /*
2460       * Assign additional attributes from the PPD file (if any)...
2461       */
2462
2463       load_ppd(p);
2464
2465      /*
2466       * Add filters for printer...
2467       */
2468
2469       cupsdSetPrinterReasons(p, "-cups-missing-filter-warning,"
2470                                 "cups-insecure-filter-warning");
2471
2472       if (p->pc && p->pc->filters)
2473       {
2474         for (filter = (char *)cupsArrayFirst(p->pc->filters);
2475              filter;
2476              filter = (char *)cupsArrayNext(p->pc->filters))
2477           add_printer_filter(p, p->filetype, filter);
2478       }
2479       else if (!(p->type & CUPS_PRINTER_REMOTE))
2480       {
2481         char    interface[1024];        /* Interface script */
2482
2483
2484         snprintf(interface, sizeof(interface), "%s/interfaces/%s", ServerRoot,
2485                  p->name);
2486         if (!access(interface, X_OK))
2487         {
2488          /*
2489           * Yes, we have a System V style interface script; use it!
2490           */
2491
2492           snprintf(interface, sizeof(interface), "*/* 0 %s/interfaces/%s",
2493                    ServerRoot, p->name);
2494           add_printer_filter(p, p->filetype, interface);
2495         }
2496         else
2497         {
2498          /*
2499           * Add a filter from application/vnd.cups-raw to printer/name to
2500           * handle "raw" printing by users.
2501           */
2502
2503           add_printer_filter(p, p->filetype, "application/vnd.cups-raw 0 -");
2504
2505          /*
2506           * Add a PostScript filter, since this is still possibly PS printer.
2507           */
2508
2509           add_printer_filter(p, p->filetype,
2510                              "application/vnd.cups-postscript 0 -");
2511         }
2512       }
2513
2514       if (p->pc && p->pc->prefilters)
2515       {
2516         if (!p->prefiltertype)
2517           p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", p->name);
2518
2519         for (filter = (char *)cupsArrayFirst(p->pc->prefilters);
2520              filter;
2521              filter = (char *)cupsArrayNext(p->pc->prefilters))
2522           add_printer_filter(p, p->prefiltertype, filter);
2523       }
2524     }
2525   }
2526
2527  /*
2528   * Copy marker attributes as needed...
2529   */
2530
2531   if (oldattrs)
2532   {
2533     ipp_attribute_t *oldattr;           /* Old attribute */
2534
2535
2536     if ((oldattr = ippFindAttribute(oldattrs, "marker-colors",
2537                                     IPP_TAG_NAME)) != NULL)
2538     {
2539       if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2540                                 "marker-colors", oldattr->num_values, NULL,
2541                                 NULL)) != NULL)
2542       {
2543         for (i = 0; i < oldattr->num_values; i ++)
2544           attr->values[i].string.text =
2545               _cupsStrRetain(oldattr->values[i].string.text);
2546       }
2547     }
2548
2549     if ((oldattr = ippFindAttribute(oldattrs, "marker-levels",
2550                                     IPP_TAG_INTEGER)) != NULL)
2551     {
2552       if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2553                                  "marker-levels", oldattr->num_values,
2554                                  NULL)) != NULL)
2555       {
2556         for (i = 0; i < oldattr->num_values; i ++)
2557           attr->values[i].integer = oldattr->values[i].integer;
2558       }
2559     }
2560
2561     if ((oldattr = ippFindAttribute(oldattrs, "marker-message",
2562                                     IPP_TAG_TEXT)) != NULL)
2563       ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "marker-message",
2564                    NULL, oldattr->values[0].string.text);
2565
2566     if ((oldattr = ippFindAttribute(oldattrs, "marker-low-levels",
2567                                     IPP_TAG_INTEGER)) != NULL)
2568     {
2569       if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2570                                  "marker-low-levels", oldattr->num_values,
2571                                  NULL)) != NULL)
2572       {
2573         for (i = 0; i < oldattr->num_values; i ++)
2574           attr->values[i].integer = oldattr->values[i].integer;
2575       }
2576     }
2577
2578     if ((oldattr = ippFindAttribute(oldattrs, "marker-high-levels",
2579                                     IPP_TAG_INTEGER)) != NULL)
2580     {
2581       if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2582                                  "marker-high-levels", oldattr->num_values,
2583                                  NULL)) != NULL)
2584       {
2585         for (i = 0; i < oldattr->num_values; i ++)
2586           attr->values[i].integer = oldattr->values[i].integer;
2587       }
2588     }
2589
2590     if ((oldattr = ippFindAttribute(oldattrs, "marker-names",
2591                                     IPP_TAG_NAME)) != NULL)
2592     {
2593       if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2594                                 "marker-names", oldattr->num_values, NULL,
2595                                 NULL)) != NULL)
2596       {
2597         for (i = 0; i < oldattr->num_values; i ++)
2598           attr->values[i].string.text =
2599               _cupsStrRetain(oldattr->values[i].string.text);
2600       }
2601     }
2602
2603     if ((oldattr = ippFindAttribute(oldattrs, "marker-types",
2604                                     IPP_TAG_KEYWORD)) != NULL)
2605     {
2606       if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2607                                 "marker-types", oldattr->num_values, NULL,
2608                                 NULL)) != NULL)
2609       {
2610         for (i = 0; i < oldattr->num_values; i ++)
2611           attr->values[i].string.text =
2612               _cupsStrRetain(oldattr->values[i].string.text);
2613       }
2614     }
2615
2616     ippDelete(oldattrs);
2617   }
2618
2619  /*
2620   * Force sharing off for remote queues...
2621   */
2622
2623   if (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
2624     p->shared = 0;
2625   else
2626   {
2627    /*
2628     * Copy the printer options into a browse attributes string we can re-use.
2629     */
2630
2631     const char  *valptr;                /* Pointer into value */
2632     char        *attrptr;               /* Pointer into attribute string */
2633
2634
2635    /*
2636     * Free the old browse attributes as needed...
2637     */
2638
2639     if (p->browse_attrs)
2640       free(p->browse_attrs);
2641
2642    /*
2643     * Compute the length of all attributes + job-sheets, lease-duration,
2644     * and BrowseLocalOptions.
2645     */
2646
2647     for (length = 1, i = p->num_options, option = p->options;
2648          i > 0;
2649          i --, option ++)
2650     {
2651       length += strlen(option->name) + 2;
2652
2653       if (option->value)
2654       {
2655         for (valptr = option->value; *valptr; valptr ++)
2656           if (strchr(" \"\'\\", *valptr))
2657             length += 2;
2658           else
2659             length ++;
2660       }
2661     }
2662
2663     length += 13 + strlen(p->job_sheets[0]) + strlen(p->job_sheets[1]);
2664     length += 32;
2665     if (BrowseLocalOptions)
2666       length += 12 + strlen(BrowseLocalOptions);
2667
2668    /*
2669     * Allocate the new string...
2670     */
2671
2672     if ((p->browse_attrs = calloc(1, length)) == NULL)
2673       cupsdLogMessage(CUPSD_LOG_ERROR,
2674                       "Unable to allocate %d bytes for browse data!",
2675                       length);
2676     else
2677     {
2678      /*
2679       * Got the allocated string, now copy the options and attributes over...
2680       */
2681
2682       sprintf(p->browse_attrs, "job-sheets=%s,%s lease-duration=%d",
2683               p->job_sheets[0], p->job_sheets[1], BrowseTimeout);
2684       attrptr = p->browse_attrs + strlen(p->browse_attrs);
2685
2686       if (BrowseLocalOptions)
2687       {
2688         sprintf(attrptr, " ipp-options=%s", BrowseLocalOptions);
2689         attrptr += strlen(attrptr);
2690       }
2691
2692       for (i = p->num_options, option = p->options;
2693            i > 0;
2694            i --, option ++)
2695       {
2696         *attrptr++ = ' ';
2697         strcpy(attrptr, option->name);
2698         attrptr += strlen(attrptr);
2699
2700         if (option->value)
2701         {
2702           *attrptr++ = '=';
2703
2704           for (valptr = option->value; *valptr; valptr ++)
2705           {
2706             if (strchr(" \"\'\\", *valptr))
2707               *attrptr++ = '\\';
2708
2709             *attrptr++ = *valptr;
2710           }
2711         }
2712       }
2713     }
2714   }
2715
2716  /*
2717   * Populate the document-format-supported attribute...
2718   */
2719
2720   add_printer_formats(p);
2721
2722   DEBUG_printf(("cupsdSetPrinterAttrs: leaving name = %s, type = %x\n", p->name,
2723                 p->type));
2724
2725  /*
2726   * Add name-default attributes...
2727   */
2728
2729   add_printer_defaults(p);
2730
2731 #ifdef __sgi
2732  /*
2733   * Write the IRIX printer config and status files...
2734   */
2735
2736   write_irix_config(p);
2737   write_irix_state(p);
2738 #endif /* __sgi */
2739
2740  /*
2741   * Re-register profiles...
2742   */
2743
2744   cupsdCmsUnregisterPrinter(p);
2745   cupsdCmsRegisterPrinter(p);
2746
2747  /*
2748   * Let the browse protocols reflect the change
2749   */
2750
2751   cupsdRegisterPrinter(p);
2752 }
2753
2754
2755 /*
2756  * 'cupsdSetPrinterReasons()' - Set/update the reasons strings.
2757  */
2758
2759 int                                     /* O - 1 if something changed, 0 otherwise */
2760 cupsdSetPrinterReasons(
2761     cupsd_printer_t *p,                 /* I - Printer */
2762     const char      *s)                 /* I - Reasons strings */
2763 {
2764   int           i,                      /* Looping var */
2765                 changed = 0;            /* Did something change? */
2766   const char    *sptr;                  /* Pointer into reasons */
2767   char          reason[255],            /* Reason string */
2768                 *rptr;                  /* Pointer into reason */
2769
2770
2771   cupsdLogMessage(CUPSD_LOG_DEBUG2,
2772                   "cupsdSetPrinterReasons(p=%p(%s),s=\"%s\"", p, p->name, s);
2773
2774   if (s[0] == '-' || s[0] == '+')
2775   {
2776    /*
2777     * Add/remove reasons...
2778     */
2779
2780     sptr = s + 1;
2781   }
2782   else
2783   {
2784    /*
2785     * Replace reasons...
2786     */
2787
2788     sptr = s;
2789
2790     for (i = 0; i < p->num_reasons; i ++)
2791       _cupsStrFree(p->reasons[i]);
2792
2793     p->num_reasons = 0;
2794     changed        = 1;
2795
2796     dirty_printer(p);
2797   }
2798
2799   if (!strcmp(s, "none"))
2800     return (changed);
2801
2802  /*
2803   * Loop through all of the reasons...
2804   */
2805
2806   while (*sptr)
2807   {
2808    /*
2809     * Skip leading whitespace and commas...
2810     */
2811
2812     while (isspace(*sptr & 255) || *sptr == ',')
2813       sptr ++;
2814
2815     for (rptr = reason; *sptr && !isspace(*sptr & 255) && *sptr != ','; sptr ++)
2816       if (rptr < (reason + sizeof(reason) - 1))
2817         *rptr++ = *sptr;
2818
2819     if (rptr == reason)
2820       break;
2821
2822     *rptr = '\0';
2823
2824     if (s[0] == '-')
2825     {
2826      /*
2827       * Remove reason...
2828       */
2829
2830       for (i = 0; i < p->num_reasons; i ++)
2831         if (!strcmp(reason, p->reasons[i]))
2832         {
2833          /*
2834           * Found a match, so remove it...
2835           */
2836
2837           p->num_reasons --;
2838           changed = 1;
2839           _cupsStrFree(p->reasons[i]);
2840
2841           if (i < p->num_reasons)
2842             memmove(p->reasons + i, p->reasons + i + 1,
2843                     (p->num_reasons - i) * sizeof(char *));
2844
2845           if (!strcmp(reason, "paused") && p->state == IPP_PRINTER_STOPPED)
2846             cupsdSetPrinterState(p, IPP_PRINTER_IDLE, 1);
2847
2848           if (strcmp(reason, "connecting-to-device"))
2849             dirty_printer(p);
2850           break;
2851         }
2852     }
2853     else if (p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
2854     {
2855      /*
2856       * Add reason...
2857       */
2858
2859       for (i = 0; i < p->num_reasons; i ++)
2860         if (!strcmp(reason, p->reasons[i]))
2861           break;
2862
2863       if (i >= p->num_reasons)
2864       {
2865         if (!strncmp(reason, "cups-ipp-missing-", 17) ||
2866             !strncmp(reason, "cups-ipp-wrong-", 15))
2867           log_ipp_conformance(p, reason);
2868
2869         if (i >= (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
2870         {
2871           cupsdLogMessage(CUPSD_LOG_ALERT,
2872                           "Too many printer-state-reasons values for %s (%d)",
2873                           p->name, i + 1);
2874           return (changed);
2875         }
2876
2877         p->reasons[i] = _cupsStrAlloc(reason);
2878         p->num_reasons ++;
2879         changed = 1;
2880
2881         if (!strcmp(reason, "paused") && p->state != IPP_PRINTER_STOPPED)
2882           cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, 1);
2883
2884         if (strcmp(reason, "connecting-to-device"))
2885           dirty_printer(p);
2886       }
2887     }
2888   }
2889
2890   return (changed);
2891 }
2892
2893
2894 /*
2895  * 'cupsdSetPrinterState()' - Update the current state of a printer.
2896  */
2897
2898 void
2899 cupsdSetPrinterState(
2900     cupsd_printer_t *p,                 /* I - Printer to change */
2901     ipp_pstate_t    s,                  /* I - New state */
2902     int             update)             /* I - Update printers.conf? */
2903 {
2904   ipp_pstate_t  old_state;              /* Old printer state */
2905   static const char * const printer_states[] =
2906   {                                     /* State strings */
2907     "idle",
2908     "processing",
2909     "stopped"
2910   };
2911
2912
2913  /*
2914   * Can't set status of remote printers...
2915   */
2916
2917   if (p->type & CUPS_PRINTER_DISCOVERED)
2918     return;
2919
2920  /*
2921   * Set the new state...
2922   */
2923
2924   old_state = p->state;
2925   p->state  = s;
2926
2927   if (old_state != s)
2928   {
2929     cupsdAddEvent(s == IPP_PRINTER_STOPPED ? CUPSD_EVENT_PRINTER_STOPPED :
2930                       CUPSD_EVENT_PRINTER_STATE, p, NULL,
2931                   "%s \"%s\" state changed to %s.",
2932                   (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
2933                   p->name, printer_states[p->state - IPP_PRINTER_IDLE]);
2934
2935    /*
2936     * Let the browse code know this needs to be updated...
2937     */
2938
2939     BrowseNext     = p;
2940     p->state_time  = time(NULL);
2941     p->browse_time = 0;
2942
2943 #ifdef __sgi
2944     write_irix_state(p);
2945 #endif /* __sgi */
2946   }
2947
2948  /*
2949   * Set/clear the paused reason as needed...
2950   */
2951
2952   if (s == IPP_PRINTER_STOPPED)
2953     cupsdSetPrinterReasons(p, "+paused");
2954   else
2955     cupsdSetPrinterReasons(p, "-paused");
2956
2957  /*
2958   * Clear the message for the queue when going to processing...
2959   */
2960
2961   if (s == IPP_PRINTER_PROCESSING)
2962     p->state_message[0] = '\0';
2963
2964  /*
2965   * Let the browse protocols reflect the change...
2966   */
2967
2968   if (update)
2969     cupsdRegisterPrinter(p);
2970
2971  /*
2972   * Save the printer configuration if a printer goes from idle or processing
2973   * to stopped (or visa-versa)...
2974   */
2975
2976   if (update &&
2977       (old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED))
2978     dirty_printer(p);
2979 }
2980
2981
2982 /*
2983  * 'cupsdStopPrinter()' - Stop a printer from printing any jobs...
2984  */
2985
2986 void
2987 cupsdStopPrinter(cupsd_printer_t *p,    /* I - Printer to stop */
2988                  int             update)/* I - Update printers.conf? */
2989 {
2990  /*
2991   * Set the printer state...
2992   */
2993
2994   cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, update);
2995
2996  /*
2997   * See if we have a job printing on this printer...
2998   */
2999
3000   if (p->job && p->job->state_value == IPP_JOB_PROCESSING)
3001     cupsdSetJobState(p->job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
3002                      "Job stopped due to printer being paused.");
3003 }
3004
3005
3006 /*
3007  * 'cupsdUpdatePrinterPPD()' - Update keywords in a printer's PPD file.
3008  */
3009
3010 int                                     /* O - 1 if successful, 0 otherwise */
3011 cupsdUpdatePrinterPPD(
3012     cupsd_printer_t *p,                 /* I - Printer */
3013     int             num_keywords,       /* I - Number of keywords */
3014     cups_option_t   *keywords)          /* I - Keywords */
3015 {
3016   int           i;                      /* Looping var */
3017   cups_file_t   *src,                   /* Original file */
3018                 *dst;                   /* New file */
3019   char          srcfile[1024],          /* Original filename */
3020                 dstfile[1024],          /* New filename */
3021                 line[1024],             /* Line from file */
3022                 keystring[41];          /* Keyword from line */
3023   cups_option_t *keyword;               /* Current keyword */
3024
3025
3026   cupsdLogMessage(CUPSD_LOG_INFO, "Updating keywords in PPD file for %s...",
3027                   p->name);
3028
3029  /*
3030   * Get the old and new PPD filenames...
3031   */
3032
3033   snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd.O", ServerRoot, p->name);
3034   snprintf(dstfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
3035
3036  /*
3037   * Rename the old file and open the old and new...
3038   */
3039
3040   if (rename(dstfile, srcfile))
3041   {
3042     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup PPD file for %s: %s",
3043                     p->name, strerror(errno));
3044     return (0);
3045   }
3046
3047   if ((src = cupsFileOpen(srcfile, "r")) == NULL)
3048   {
3049     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open PPD file \"%s\": %s",
3050                     srcfile, strerror(errno));
3051     rename(srcfile, dstfile);
3052     return (0);
3053   }
3054
3055   if ((dst = cupsFileOpen(dstfile, "w")) == NULL)
3056   {
3057     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create PPD file \"%s\": %s",
3058                     dstfile, strerror(errno));
3059     cupsFileClose(src);
3060     rename(srcfile, dstfile);
3061     return (0);
3062   }
3063
3064  /*
3065   * Copy the first line and then write out all of the keywords...
3066   */
3067
3068   if (!cupsFileGets(src, line, sizeof(line)))
3069   {
3070     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to read PPD file \"%s\": %s",
3071                     srcfile, strerror(errno));
3072     cupsFileClose(src);
3073     cupsFileClose(dst);
3074     rename(srcfile, dstfile);
3075     return (0);
3076   }
3077
3078   cupsFilePrintf(dst, "%s\n", line);
3079
3080   for (i = num_keywords, keyword = keywords; i > 0; i --, keyword ++)
3081   {
3082     cupsdLogMessage(CUPSD_LOG_DEBUG, "*%s: %s", keyword->name, keyword->value);
3083     cupsFilePrintf(dst, "*%s: %s\n", keyword->name, keyword->value);
3084   }
3085
3086  /*
3087   * Then copy the rest of the PPD file, dropping any keywords we changed.
3088   */
3089
3090   while (cupsFileGets(src, line, sizeof(line)))
3091   {
3092    /*
3093     * Skip keywords we've already set...
3094     */
3095
3096     if (sscanf(line, "*%40[^:]:", keystring) == 1 &&
3097         cupsGetOption(keystring, num_keywords, keywords))
3098       continue;
3099
3100    /*
3101     * Otherwise write the line...
3102     */
3103
3104     cupsFilePrintf(dst, "%s\n", line);
3105   }
3106
3107  /*
3108   * Close files and return...
3109   */
3110
3111   cupsFileClose(src);
3112   cupsFileClose(dst);
3113
3114   return (1);
3115 }
3116
3117
3118 /*
3119  * 'cupsdUpdatePrinters()' - Update printers after a partial reload.
3120  */
3121
3122 void
3123 cupsdUpdatePrinters(void)
3124 {
3125   cupsd_printer_t       *p;             /* Current printer */
3126
3127
3128  /*
3129   * Loop through the printers and recreate the printer attributes
3130   * for any local printers since the policy and/or access control
3131   * stuff may have changed.  Also, if browsing is disabled, remove
3132   * any remote printers...
3133   */
3134
3135   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3136        p;
3137        p = (cupsd_printer_t *)cupsArrayNext(Printers))
3138   {
3139    /*
3140     * Remove remote printers if we are no longer browsing...
3141     */
3142
3143     if (!Browsing &&
3144         (p->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_DISCOVERED)))
3145     {
3146       if (p->type & CUPS_PRINTER_IMPLICIT)
3147         cupsArrayRemove(ImplicitPrinters, p);
3148
3149       cupsArraySave(Printers);
3150       cupsdDeletePrinter(p, 0);
3151       cupsArrayRestore(Printers);
3152       continue;
3153     }
3154
3155    /*
3156     * Update the operation policy pointer...
3157     */
3158
3159     if ((p->op_policy_ptr = cupsdFindPolicy(p->op_policy)) == NULL)
3160       p->op_policy_ptr = DefaultPolicyPtr;
3161
3162    /*
3163     * Update printer attributes as needed...
3164     */
3165
3166     if (!(p->type & CUPS_PRINTER_DISCOVERED))
3167       cupsdSetPrinterAttrs(p);
3168   }
3169 }
3170
3171
3172 /*
3173  * 'cupsdValidateDest()' - Validate a printer/class destination.
3174  */
3175
3176 const char *                            /* O - Printer or class name */
3177 cupsdValidateDest(
3178     const char      *uri,               /* I - Printer URI */
3179     cups_ptype_t    *dtype,             /* O - Type (printer or class) */
3180     cupsd_printer_t **printer)          /* O - Printer pointer */
3181 {
3182   cupsd_printer_t       *p;             /* Current printer */
3183   char                  localname[1024],/* Localized hostname */
3184                         *lptr,          /* Pointer into localized hostname */
3185                         *sptr,          /* Pointer into server name */
3186                         *rptr,          /* Pointer into resource */
3187                         scheme[32],     /* Scheme portion of URI */
3188                         username[64],   /* Username portion of URI */
3189                         hostname[HTTP_MAX_HOST],
3190                                         /* Host portion of URI */
3191                         resource[HTTP_MAX_URI];
3192                                         /* Resource portion of URI */
3193   int                   port;           /* Port portion of URI */
3194
3195
3196   DEBUG_printf(("cupsdValidateDest(uri=\"%s\", dtype=%p, printer=%p)\n", uri,
3197                 dtype, printer));
3198
3199  /*
3200   * Initialize return values...
3201   */
3202
3203   if (printer)
3204     *printer = NULL;
3205
3206   if (dtype)
3207     *dtype = (cups_ptype_t)0;
3208
3209  /*
3210   * Pull the hostname and resource from the URI...
3211   */
3212
3213   httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
3214                   username, sizeof(username), hostname, sizeof(hostname),
3215                   &port, resource, sizeof(resource));
3216
3217  /*
3218   * See if the resource is a class or printer...
3219   */
3220
3221   if (!strncmp(resource, "/classes/", 9))
3222   {
3223    /*
3224     * Class...
3225     */
3226
3227     rptr = resource + 9;
3228   }
3229   else if (!strncmp(resource, "/printers/", 10))
3230   {
3231    /*
3232     * Printer...
3233     */
3234
3235     rptr = resource + 10;
3236   }
3237   else
3238   {
3239    /*
3240     * Bad resource name...
3241     */
3242
3243     return (NULL);
3244   }
3245
3246  /*
3247   * See if the printer or class name exists...
3248   */
3249
3250   p = cupsdFindDest(rptr);
3251
3252   if (p == NULL && strchr(rptr, '@') == NULL)
3253     return (NULL);
3254   else if (p != NULL)
3255   {
3256     if (printer)
3257       *printer = p;
3258
3259     if (dtype)
3260       *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
3261                           CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED);
3262
3263     return (p->name);
3264   }
3265
3266  /*
3267   * Change localhost to the server name...
3268   */
3269
3270   if (!_cups_strcasecmp(hostname, "localhost"))
3271     strlcpy(hostname, ServerName, sizeof(hostname));
3272
3273   strlcpy(localname, hostname, sizeof(localname));
3274
3275   if (!_cups_strcasecmp(hostname, ServerName))
3276   {
3277    /*
3278     * Localize the hostname...
3279     */
3280
3281     lptr = strchr(localname, '.');
3282     sptr = strchr(ServerName, '.');
3283
3284     if (sptr != NULL && lptr != NULL)
3285     {
3286      /*
3287       * Strip the common domain name components...
3288       */
3289
3290       while (lptr != NULL)
3291       {
3292         if (!_cups_strcasecmp(lptr, sptr))
3293         {
3294           *lptr = '\0';
3295           break;
3296         }
3297         else
3298           lptr = strchr(lptr + 1, '.');
3299       }
3300     }
3301   }
3302
3303   DEBUG_printf(("localized hostname is \"%s\"...\n", localname));
3304
3305  /*
3306   * Find a matching printer or class...
3307   */
3308
3309   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3310        p;
3311        p = (cupsd_printer_t *)cupsArrayNext(Printers))
3312     if (!_cups_strcasecmp(p->hostname, localname) &&
3313         !_cups_strcasecmp(p->name, rptr))
3314     {
3315       if (printer)
3316         *printer = p;
3317
3318       if (dtype)
3319         *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
3320                             CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED);
3321
3322       return (p->name);
3323     }
3324
3325   return (NULL);
3326 }
3327
3328
3329 /*
3330  * 'cupsdWritePrintcap()' - Write a pseudo-printcap file for older applications
3331  *                          that need it...
3332  */
3333
3334 void
3335 cupsdWritePrintcap(void)
3336 {
3337   int                   i;              /* Looping var */
3338   cups_file_t           *fp;            /* Printcap file */
3339   cupsd_printer_t       *p;             /* Current printer */
3340
3341
3342 #ifdef __sgi
3343  /*
3344   * Update the IRIX printer state for the default printer; if
3345   * no printers remain, then the default printer file will be
3346   * removed...
3347   */
3348
3349   write_irix_state(DefaultPrinter);
3350 #endif /* __sgi */
3351
3352  /*
3353   * See if we have a printcap file; if not, don't bother writing it.
3354   */
3355
3356   if (!Printcap || !*Printcap)
3357     return;
3358
3359   cupsdLogMessage(CUPSD_LOG_INFO, "Generating printcap %s...", Printcap);
3360
3361  /*
3362   * Open the printcap file...
3363   */
3364
3365   if ((fp = cupsFileOpen(Printcap, "w")) == NULL)
3366     return;
3367
3368  /*
3369   * Put a comment header at the top so that users will know where the
3370   * data has come from...
3371   */
3372
3373   if (PrintcapFormat != PRINTCAP_PLIST)
3374     cupsFilePrintf(fp, "# This file was automatically generated by cupsd(8) "
3375                        "from the\n"
3376                        "# %s/printers.conf file.  All changes to this file\n"
3377                        "# will be lost.\n", ServerRoot);
3378
3379  /*
3380   * Write a new printcap with the current list of printers.
3381   */
3382
3383   switch (PrintcapFormat)
3384   {
3385     case PRINTCAP_BSD :
3386        /*
3387         * Each printer is put in the file as:
3388         *
3389         *    Printer1:
3390         *    Printer2:
3391         *    Printer3:
3392         *    ...
3393         *    PrinterN:
3394         */
3395
3396         if (DefaultPrinter)
3397           cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", DefaultPrinter->name,
3398                          DefaultPrinter->info, ServerName,
3399                          DefaultPrinter->name);
3400
3401         for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3402              p;
3403              p = (cupsd_printer_t *)cupsArrayNext(Printers))
3404           if (p != DefaultPrinter)
3405             cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", p->name, p->info,
3406                            ServerName, p->name);
3407         break;
3408
3409     case PRINTCAP_PLIST :
3410        /*
3411         * Each printer is written as a dictionary in a plist file.
3412         * Currently the printer-name, printer-info, printer-is-accepting-jobs,
3413         * printer-location, printer-make-and-model, printer-state,
3414         * printer-state-reasons, printer-type, and (sanitized) device-uri.
3415         */
3416
3417         cupsFilePuts(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3418                          "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD "
3419                          "PLIST 1.0//EN\" \"http://www.apple.com/DTDs/"
3420                          "PropertyList-1.0.dtd\">\n"
3421                          "<plist version=\"1.0\">\n"
3422                          "<array>\n");
3423
3424         for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3425              p;
3426              p = (cupsd_printer_t *)cupsArrayNext(Printers))
3427         {
3428           cupsFilePuts(fp, "\t<dict>\n"
3429                            "\t\t<key>printer-name</key>\n"
3430                            "\t\t<string>");
3431           write_xml_string(fp, p->name);
3432           cupsFilePuts(fp, "</string>\n"
3433                            "\t\t<key>printer-info</key>\n"
3434                            "\t\t<string>");
3435           write_xml_string(fp, p->info);
3436           cupsFilePrintf(fp, "</string>\n"
3437                              "\t\t<key>printer-is-accepting-jobs</key>\n"
3438                              "\t\t<%s/>\n"
3439                              "\t\t<key>printer-location</key>\n"
3440                              "\t\t<string>", p->accepting ? "true" : "false");
3441           write_xml_string(fp, p->location);
3442           cupsFilePuts(fp, "</string>\n"
3443                            "\t\t<key>printer-make-and-model</key>\n"
3444                            "\t\t<string>");
3445           write_xml_string(fp, p->make_model);
3446           cupsFilePrintf(fp, "</string>\n"
3447                              "\t\t<key>printer-state</key>\n"
3448                              "\t\t<integer>%d</integer>\n"
3449                              "\t\t<key>printer-state-reasons</key>\n"
3450                              "\t\t<array>\n", p->state);
3451           for (i = 0; i < p->num_reasons; i ++)
3452           {
3453             cupsFilePuts(fp, "\t\t\t<string>");
3454             write_xml_string(fp, p->reasons[i]);
3455             cupsFilePuts(fp, "</string>\n");
3456           }
3457           cupsFilePrintf(fp, "\t\t</array>\n"
3458                              "\t\t<key>printer-type</key>\n"
3459                              "\t\t<integer>%d</integer>\n"
3460                              "\t\t<key>device-uri</key>\n"
3461                              "\t\t<string>", p->type);
3462           write_xml_string(fp, p->sanitized_device_uri);
3463           cupsFilePuts(fp, "</string>\n"
3464                            "\t</dict>\n");
3465         }
3466         cupsFilePuts(fp, "</array>\n"
3467                          "</plist>\n");
3468         break;
3469
3470     case PRINTCAP_SOLARIS :
3471        /*
3472         * Each printer is put in the file as:
3473         *
3474         *    _all:all=Printer1,Printer2,Printer3,...,PrinterN
3475         *    _default:use=DefaultPrinter
3476         *    Printer1:\
3477         *            :bsdaddr=ServerName,Printer1:\
3478         *            :description=Description:
3479         *    Printer2:
3480         *            :bsdaddr=ServerName,Printer2:\
3481         *            :description=Description:
3482         *    Printer3:
3483         *            :bsdaddr=ServerName,Printer3:\
3484         *            :description=Description:
3485         *    ...
3486         *    PrinterN:
3487         *            :bsdaddr=ServerName,PrinterN:\
3488         *            :description=Description:
3489         */
3490
3491         cupsFilePuts(fp, "_all:all=");
3492         for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3493              p;
3494              p = (cupsd_printer_t *)cupsArrayCurrent(Printers))
3495           cupsFilePrintf(fp, "%s%c", p->name,
3496                          cupsArrayNext(Printers) ? ',' : '\n');
3497
3498         if (DefaultPrinter)
3499           cupsFilePrintf(fp, "_default:use=%s\n", DefaultPrinter->name);
3500
3501         for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3502              p;
3503              p = (cupsd_printer_t *)cupsArrayNext(Printers))
3504           cupsFilePrintf(fp, "%s:\\\n"
3505                              "\t:bsdaddr=%s,%s:\\\n"
3506                              "\t:description=%s:\n",
3507                          p->name, ServerName, p->name,
3508                          p->info ? p->info : "");
3509         break;
3510   }
3511
3512  /*
3513   * Close the file...
3514   */
3515
3516   cupsFileClose(fp);
3517 }
3518
3519
3520 /*
3521  * 'add_printer_defaults()' - Add name-default attributes to the printer attributes.
3522  */
3523
3524 static void
3525 add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
3526 {
3527   int           i;                      /* Looping var */
3528   int           num_options;            /* Number of default options */
3529   cups_option_t *options,               /* Default options */
3530                 *option;                /* Current option */
3531   char          name[256];              /* name-default */
3532
3533
3534  /*
3535   * Maintain a common array of default attribute names...
3536   */
3537
3538   if (!CommonDefaults)
3539   {
3540     CommonDefaults = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3541
3542     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("copies-default"));
3543     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("document-format-default"));
3544     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("finishings-default"));
3545     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-hold-until-default"));
3546     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-priority-default"));
3547     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-sheets-default"));
3548     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("media-col-default"));
3549     cupsArrayAdd(CommonDefaults, _cupsStrAlloc("number-up-default"));
3550     cupsArrayAdd(CommonDefaults,
3551                  _cupsStrAlloc("orientation-requested-default"));
3552   }
3553
3554  /*
3555   * Add all of the default options from the .conf files...
3556   */
3557
3558   for (num_options = 0, options = NULL, i = p->num_options, option = p->options;
3559        i > 0;
3560        i --, option ++)
3561   {
3562     if (strcmp(option->name, "ipp-options") &&
3563         strcmp(option->name, "job-sheets") &&
3564         strcmp(option->name, "lease-duration"))
3565     {
3566       snprintf(name, sizeof(name), "%s-default", option->name);
3567       num_options = cupsAddOption(name, option->value, num_options, &options);
3568
3569       if (!cupsArrayFind(CommonDefaults, name))
3570         cupsArrayAdd(CommonDefaults, _cupsStrAlloc(name));
3571     }
3572   }
3573
3574  /*
3575   * Convert options to IPP attributes...
3576   */
3577
3578   cupsEncodeOptions2(p->attrs, num_options, options, IPP_TAG_PRINTER);
3579   cupsFreeOptions(num_options, options);
3580
3581  /*
3582   * Add standard -default attributes as needed...
3583   */
3584
3585   if (!cupsGetOption("copies", p->num_options, p->options))
3586     ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default",
3587                   1);
3588
3589   if (!cupsGetOption("document-format", p->num_options, p->options))
3590     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
3591                  "document-format-default", NULL, "application/octet-stream");
3592
3593   if (!cupsGetOption("job-hold-until", p->num_options, p->options))
3594     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3595                  "job-hold-until-default", NULL, "no-hold");
3596
3597   if (!cupsGetOption("job-priority", p->num_options, p->options))
3598     ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3599                   "job-priority-default", 50);
3600
3601   if (!cupsGetOption("number-up", p->num_options, p->options))
3602     ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3603                   "number-up-default", 1);
3604
3605   if (!cupsGetOption("notify-lease-duration", p->num_options, p->options))
3606     ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3607                   "notify-lease-duration-default", DefaultLeaseDuration);
3608
3609   if (!cupsGetOption("notify-events", p->num_options, p->options))
3610     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3611                  "notify-events-default", NULL, "job-completed");
3612
3613   if (!cupsGetOption("orientation-requested", p->num_options, p->options))
3614     ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
3615                  "orientation-requested-default", NULL, NULL);
3616
3617   if (!cupsGetOption("print-quality", p->num_options, p->options))
3618     ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3619                   "print-quality-default", IPP_QUALITY_NORMAL);
3620 }
3621
3622
3623 /*
3624  * 'add_printer_filter()' - Add a MIME filter for a printer.
3625  */
3626
3627 static void
3628 add_printer_filter(
3629     cupsd_printer_t  *p,                /* I - Printer to add to */
3630     mime_type_t      *filtertype,       /* I - Filter or prefilter MIME type */
3631     const char       *filter)           /* I - Filter to add */
3632 {
3633   char          super[MIME_MAX_SUPER],  /* Super-type for filter */
3634                 type[MIME_MAX_TYPE],    /* Type for filter */
3635                 dsuper[MIME_MAX_SUPER], /* Destination super-type for filter */
3636                 dtype[MIME_MAX_TYPE],   /* Destination type for filter */
3637                 dest[MIME_MAX_SUPER + MIME_MAX_TYPE + 2],
3638                                         /* Destination super/type */
3639                 program[1024];          /* Program/filter name */
3640   int           cost;                   /* Cost of filter */
3641   size_t        maxsize = 0;            /* Maximum supported file size */
3642   mime_type_t   *temptype,              /* MIME type looping var */
3643                 *desttype;              /* Destination MIME type */
3644   mime_filter_t *filterptr;             /* MIME filter */
3645   char          filename[1024];         /* Full filter filename */
3646
3647
3648   cupsdLogMessage(CUPSD_LOG_DEBUG2,
3649                   "add_printer_filter(p=%p(%s), filtertype=%p(%s/%s), "
3650                   "filter=\"%s\")", p, p->name, filtertype, filtertype->super,
3651                   filtertype->type, filter);
3652
3653  /*
3654   * Parse the filter string; it should be in one of the following formats:
3655   *
3656   *     source/type cost program
3657   *     source/type cost maxsize(nnnn) program
3658   *     source/type dest/type cost program
3659   *     source/type dest/type cost maxsize(nnnn) program
3660   */
3661
3662   if (sscanf(filter, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3663              super, type, dsuper, dtype, &cost, program) == 6)
3664   {
3665     snprintf(dest, sizeof(dest), "%s/%s/%s", p->name, dsuper, dtype);
3666
3667     if ((desttype = mimeType(MimeDatabase, "printer", dest)) == NULL)
3668     {
3669       desttype = mimeAddType(MimeDatabase, "printer", dest);
3670       if (!p->dest_types)
3671         p->dest_types = cupsArrayNew(NULL, NULL);
3672
3673       cupsArrayAdd(p->dest_types, desttype);
3674     }
3675
3676   }
3677   else
3678   {
3679     if (sscanf(filter, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, &cost,
3680                program) == 4)
3681     {
3682       desttype = filtertype;
3683     }
3684     else
3685     {
3686       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!",
3687                       p->name, filter);
3688       return;
3689     }
3690   }
3691
3692   if (!strncmp(program, "maxsize(", 8))
3693   {
3694     char        *ptr;                   /* Pointer into maxsize(nnnn) program */
3695
3696     maxsize = strtoll(program + 8, &ptr, 10);
3697
3698     if (*ptr != ')')
3699     {
3700       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!",
3701                       p->name, filter);
3702       return;
3703     }
3704
3705     ptr ++;
3706     while (_cups_isspace(*ptr))
3707       ptr ++;
3708
3709     _cups_strcpy(program, ptr);
3710   }
3711
3712  /*
3713   * Check permissions on the filter and its containing directory...
3714   */
3715
3716   if (strcmp(program, "-"))
3717   {
3718     if (program[0] == '/')
3719       strlcpy(filename, program, sizeof(filename));
3720     else
3721       snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program);
3722
3723     _cupsFileCheck(filename, _CUPS_FILE_CHECK_PROGRAM, !RunUser,
3724                    cupsdLogFCMessage, p);
3725   }
3726
3727  /*
3728   * Add the filter to the MIME database, supporting wildcards as needed...
3729   */
3730
3731   for (temptype = mimeFirstType(MimeDatabase);
3732        temptype;
3733        temptype = mimeNextType(MimeDatabase))
3734     if (((super[0] == '*' && _cups_strcasecmp(temptype->super, "printer")) ||
3735          !_cups_strcasecmp(temptype->super, super)) &&
3736         (type[0] == '*' || !_cups_strcasecmp(temptype->type, type)))
3737     {
3738       if (desttype != filtertype)
3739       {
3740         cupsdLogMessage(CUPSD_LOG_DEBUG2,
3741                         "add_printer_filter: %s: adding filter %s/%s %s/%s %d "
3742                         "%s", p->name, temptype->super, temptype->type,
3743                         desttype->super, desttype->type,
3744                         cost, program);
3745         filterptr = mimeAddFilter(MimeDatabase, temptype, desttype, cost,
3746                                   program);
3747
3748         if (!mimeFilterLookup(MimeDatabase, desttype, filtertype))
3749         {
3750           cupsdLogMessage(CUPSD_LOG_DEBUG2,
3751                           "add_printer_filter: %s: adding filter %s/%s %s/%s "
3752                           "0 -", p->name, desttype->super, desttype->type,
3753                           filtertype->super, filtertype->type);
3754           mimeAddFilter(MimeDatabase, desttype, filtertype, 0, "-");
3755         }
3756       }
3757       else
3758       {
3759         cupsdLogMessage(CUPSD_LOG_DEBUG2,
3760                         "add_printer_filter: %s: adding filter %s/%s %s/%s %d "
3761                         "%s", p->name, temptype->super, temptype->type,
3762                         filtertype->super, filtertype->type,
3763                         cost, program);
3764         filterptr = mimeAddFilter(MimeDatabase, temptype, filtertype, cost,
3765                                   program);
3766       }
3767
3768       if (filterptr)
3769         filterptr->maxsize = maxsize;
3770     }
3771 }
3772
3773
3774 /*
3775  * 'add_printer_formats()' - Add document-format-supported values for a printer.
3776  */
3777
3778 static void
3779 add_printer_formats(cupsd_printer_t *p) /* I - Printer */
3780 {
3781   int           i;                      /* Looping var */
3782   mime_type_t   *type;                  /* Current MIME type */
3783   cups_array_t  *filters;               /* Filters */
3784   ipp_attribute_t *attr;                /* document-format-supported attribute */
3785   char          mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
3786                                         /* MIME type name */
3787
3788
3789  /*
3790   * Raw (and remote) queues advertise all of the supported MIME
3791   * types...
3792   */
3793
3794   cupsArrayDelete(p->filetypes);
3795   p->filetypes = NULL;
3796
3797   if (p->raw)
3798   {
3799     ippAddStrings(p->attrs, IPP_TAG_PRINTER,
3800                   (ipp_tag_t)(IPP_TAG_MIMETYPE | IPP_TAG_COPY),
3801                   "document-format-supported", NumMimeTypes, NULL, MimeTypes);
3802     return;
3803   }
3804
3805  /*
3806   * Otherwise, loop through the supported MIME types and see if there
3807   * are filters for them...
3808   */
3809
3810   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer_formats: %d types, %d filters",
3811                   mimeNumTypes(MimeDatabase), mimeNumFilters(MimeDatabase));
3812
3813   p->filetypes = cupsArrayNew(NULL, NULL);
3814
3815   for (type = mimeFirstType(MimeDatabase);
3816        type;
3817        type = mimeNextType(MimeDatabase))
3818   {
3819     if (!_cups_strcasecmp(type->super, "printer"))
3820       continue;
3821
3822     snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type);
3823
3824     if ((filters = mimeFilter(MimeDatabase, type, p->filetype, NULL)) != NULL)
3825     {
3826       cupsdLogMessage(CUPSD_LOG_DEBUG2,
3827                       "add_printer_formats: %s: %s needs %d filters",
3828                       p->name, mimetype, cupsArrayCount(filters));
3829
3830       cupsArrayDelete(filters);
3831       cupsArrayAdd(p->filetypes, type);
3832     }
3833     else
3834       cupsdLogMessage(CUPSD_LOG_DEBUG2,
3835                       "add_printer_formats: %s: %s not supported",
3836                       p->name, mimetype);
3837   }
3838
3839  /*
3840   * Add the file formats that can be filtered...
3841   */
3842
3843   if ((type = mimeType(MimeDatabase, "application", "octet-stream")) == NULL ||
3844       !cupsArrayFind(p->filetypes, type))
3845     i = 1;
3846   else
3847     i = 0;
3848
3849   cupsdLogMessage(CUPSD_LOG_DEBUG2,
3850                   "add_printer_formats: %s: %d supported types",
3851                   p->name, cupsArrayCount(p->filetypes) + i);
3852
3853   attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
3854                        "document-format-supported",
3855                        cupsArrayCount(p->filetypes) + i, NULL, NULL);
3856
3857   if (i)
3858     attr->values[0].string.text = _cupsStrAlloc("application/octet-stream");
3859
3860   for (type = (mime_type_t *)cupsArrayFirst(p->filetypes);
3861        type;
3862        i ++, type = (mime_type_t *)cupsArrayNext(p->filetypes))
3863   {
3864     snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type);
3865
3866     attr->values[i].string.text = _cupsStrAlloc(mimetype);
3867   }
3868
3869 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
3870   {
3871     char                pdl[1024];      /* Buffer to build pdl list */
3872     mime_filter_t       *filter;        /* MIME filter looping var */
3873
3874
3875    /*
3876     * We only support raw printing if this is not a Tioga PrintJobMgr based
3877     * queue and if application/octet-stream is a known type...
3878     */
3879
3880     for (filter = (mime_filter_t *)cupsArrayFirst(MimeDatabase->filters);
3881          filter;
3882          filter = (mime_filter_t *)cupsArrayNext(MimeDatabase->filters))
3883     {
3884       if (filter->dst == p->filetype && filter->filter &&
3885           strstr(filter->filter, "PrintJobMgr"))
3886         break;
3887     }
3888
3889     pdl[0] = '\0';
3890
3891     if (!filter && mimeType(MimeDatabase, "application", "octet-stream"))
3892       strlcat(pdl, "application/octet-stream,", sizeof(pdl));
3893
3894    /*
3895     * Then list a bunch of formats that are supported by the printer...
3896     */
3897
3898     for (type = (mime_type_t *)cupsArrayFirst(p->filetypes);
3899          type;
3900          type = (mime_type_t *)cupsArrayNext(p->filetypes))
3901     {
3902       if (!_cups_strcasecmp(type->super, "application"))
3903       {
3904         if (!_cups_strcasecmp(type->type, "pdf"))
3905           strlcat(pdl, "application/pdf,", sizeof(pdl));
3906         else if (!_cups_strcasecmp(type->type, "postscript"))
3907           strlcat(pdl, "application/postscript,", sizeof(pdl));
3908       }
3909       else if (!_cups_strcasecmp(type->super, "image"))
3910       {
3911         if (!_cups_strcasecmp(type->type, "jpeg"))
3912           strlcat(pdl, "image/jpeg,", sizeof(pdl));
3913         else if (!_cups_strcasecmp(type->type, "png"))
3914           strlcat(pdl, "image/png,", sizeof(pdl));
3915         else if (!_cups_strcasecmp(type->type, "pwg-raster"))
3916           strlcat(pdl, "image/pwg-raster,", sizeof(pdl));
3917       }
3918     }
3919
3920     if (pdl[0])
3921       pdl[strlen(pdl) - 1] = '\0';      /* Remove trailing comma */
3922
3923     cupsdSetString(&p->pdl, pdl);
3924   }
3925 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
3926 }
3927
3928
3929 /*
3930  * 'compare_printers()' - Compare two printers.
3931  */
3932
3933 static int                              /* O - Result of comparison */
3934 compare_printers(void *first,           /* I - First printer */
3935                  void *second,          /* I - Second printer */
3936                  void *data)            /* I - App data (not used) */
3937 {
3938   (void)data;
3939
3940   return (_cups_strcasecmp(((cupsd_printer_t *)first)->name,
3941                      ((cupsd_printer_t *)second)->name));
3942 }
3943
3944
3945 /*
3946  * 'delete_printer_filters()' - Delete all MIME filters for a printer.
3947  */
3948
3949 static void
3950 delete_printer_filters(
3951     cupsd_printer_t *p)                 /* I - Printer to remove from */
3952 {
3953   mime_filter_t *filter;                /* MIME filter looping var */
3954   mime_type_t   *type;                  /* Destination types for filters */
3955
3956
3957  /*
3958   * Range check input...
3959   */
3960
3961   if (p == NULL)
3962     return;
3963
3964  /*
3965   * Remove all filters from the MIME database that have a destination
3966   * type == printer...
3967   */
3968
3969   for (filter = mimeFirstFilter(MimeDatabase);
3970        filter;
3971        filter = mimeNextFilter(MimeDatabase))
3972     if (filter->dst == p->filetype || filter->dst == p->prefiltertype ||
3973         cupsArrayFind(p->dest_types, filter->dst))
3974     {
3975      /*
3976       * Delete the current filter...
3977       */
3978
3979       mimeDeleteFilter(MimeDatabase, filter);
3980     }
3981
3982   for (type = (mime_type_t *)cupsArrayFirst(p->dest_types);
3983        type;
3984        type = (mime_type_t *)cupsArrayNext(p->dest_types))
3985     mimeDeleteType(MimeDatabase, type);
3986
3987   cupsArrayDelete(p->dest_types);
3988   p->dest_types = NULL;
3989
3990   cupsdSetPrinterReasons(p, "-cups-insecure-filter-warning"
3991                             ",cups-missing-filter-warning");
3992 }
3993
3994
3995 /*
3996  * 'dirty_printer()' - Mark config and state files dirty for the specified
3997  *                     printer.
3998  */
3999
4000 static void
4001 dirty_printer(cupsd_printer_t *p)       /* I - Printer */
4002 {
4003   if (p->type & CUPS_PRINTER_DISCOVERED)
4004     cupsdMarkDirty(CUPSD_DIRTY_REMOTE);
4005   else if (p->type & CUPS_PRINTER_CLASS)
4006     cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
4007   else
4008     cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4009
4010   if (PrintcapFormat == PRINTCAP_PLIST)
4011     cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
4012 }
4013
4014
4015 /*
4016  * 'load_ppd()' - Load a cached PPD file, updating the cache as needed.
4017  */
4018
4019 static void
4020 load_ppd(cupsd_printer_t *p)            /* I - Printer */
4021 {
4022   int           i, j, k;                /* Looping vars */
4023   char          cache_name[1024];       /* Cache filename */
4024   struct stat   cache_info;             /* Cache file info */
4025   ppd_file_t    *ppd;                   /* PPD file */
4026   char          ppd_name[1024];         /* PPD filename */
4027   struct stat   ppd_info;               /* PPD file info */
4028   int           num_media;              /* Number of media options */
4029   ppd_size_t    *size;                  /* Current PPD size */
4030   ppd_option_t  *duplex,                /* Duplex option */
4031                 *output_bin,            /* OutputBin option */
4032                 *output_mode,           /* OutputMode option */
4033                 *resolution;            /* (Set|JCL|)Resolution option */
4034   ppd_choice_t  *choice,                /* Current PPD choice */
4035                 *input_slot,            /* Current input slot */
4036                 *media_type;            /* Current media type */
4037   ppd_attr_t    *ppd_attr;              /* PPD attribute */
4038   int           xdpi,                   /* Horizontal resolution */
4039                 ydpi;                   /* Vertical resolution */
4040   const char    *resptr;                /* Pointer into resolution keyword */
4041   _pwg_size_t   *pwgsize;               /* Current PWG size */
4042   _pwg_map_t    *pwgsource,             /* Current PWG source */
4043                 *pwgtype;               /* Current PWG type */
4044   ipp_attribute_t *attr;                /* Attribute data */
4045   ipp_value_t   *val;                   /* Attribute value */
4046   int           num_finishings,         /* Number of finishings */
4047                 finishings[5];          /* finishings-supported values */
4048   int           num_qualities,          /* Number of print-quality values */
4049                 qualities[3];           /* print-quality values */
4050   int           num_margins,            /* Number of media-*-margin-supported values */
4051                 margins[16];            /* media-*-margin-supported values */
4052   const char    *filter;                /* Current filter */
4053   static const char * const sides[3] =  /* sides-supported values */
4054                 {
4055                   "one-sided",
4056                   "two-sided-long-edge",
4057                   "two-sided-short-edge"
4058                 };
4059   static const char * const standard_commands[] =
4060                 {                       /* Standard CUPS commands */
4061                   "AutoConfigure",
4062                   "Clean",
4063                   "PrintSelfTestPage"
4064                 };
4065
4066
4067  /*
4068   * Check to see if the cache is up-to-date...
4069   */
4070
4071   snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir, p->name);
4072   if (stat(cache_name, &cache_info))
4073     cache_info.st_mtime = 0;
4074
4075   snprintf(ppd_name, sizeof(ppd_name), "%s/ppd/%s.ppd", ServerRoot, p->name);
4076   if (stat(ppd_name, &ppd_info))
4077     ppd_info.st_mtime = 1;
4078
4079   ippDelete(p->ppd_attrs);
4080   p->ppd_attrs = NULL;
4081
4082   _ppdCacheDestroy(p->pc);
4083   p->pc = NULL;
4084
4085   if (cache_info.st_mtime >= ppd_info.st_mtime)
4086   {
4087     cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", cache_name);
4088
4089     if ((p->pc = _ppdCacheCreateWithFile(cache_name, &p->ppd_attrs)) != NULL &&
4090         p->ppd_attrs)
4091     {
4092      /*
4093       * Loaded successfully!
4094       */
4095
4096       return;
4097     }
4098   }
4099
4100  /*
4101   * Reload PPD attributes from disk...
4102   */
4103
4104   cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4105
4106   cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", ppd_name);
4107
4108   p->type &= ~CUPS_PRINTER_OPTIONS;
4109   p->type |= CUPS_PRINTER_BW;
4110
4111   finishings[0]  = IPP_FINISHINGS_NONE;
4112   num_finishings = 1;
4113
4114   p->ppd_attrs = ippNew();
4115
4116   if ((ppd = ppdOpenFile(ppd_name)) != NULL)
4117   {
4118    /*
4119     * Add make/model and other various attributes...
4120     */
4121
4122     p->pc = _ppdCacheCreateWithPPD(ppd);
4123
4124     ppdMarkDefaults(ppd);
4125
4126     if (ppd->color_device)
4127       p->type |= CUPS_PRINTER_COLOR;
4128     if (ppd->variable_sizes)
4129       p->type |= CUPS_PRINTER_VARIABLE;
4130     if (!ppd->manual_copies)
4131       p->type |= CUPS_PRINTER_COPIES;
4132     if ((ppd_attr = ppdFindAttr(ppd, "cupsFax", NULL)) != NULL)
4133       if (ppd_attr->value && !_cups_strcasecmp(ppd_attr->value, "true"))
4134         p->type |= CUPS_PRINTER_FAX;
4135
4136     ippAddBoolean(p->ppd_attrs, IPP_TAG_PRINTER, "color-supported",
4137                   ppd->color_device);
4138     if (ppd->throughput)
4139     {
4140       ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4141                     "pages-per-minute", ppd->throughput);
4142       if (ppd->color_device)
4143         ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4144                       "pages-per-minute-color", ppd->throughput);
4145     }
4146     else
4147     {
4148      /*
4149       * When there is no speed information, just say "1 page per minute".
4150       */
4151
4152       ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4153                     "pages-per-minute", 1);
4154       if (ppd->color_device)
4155         ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4156                       "pages-per-minute-color", 1);
4157     }
4158
4159     num_qualities = 0;
4160
4161     if ((output_mode = ppdFindOption(ppd, "OutputMode")) != NULL)
4162     {
4163       if (ppdFindChoice(output_mode, "draft") ||
4164           ppdFindChoice(output_mode, "fast"))
4165         qualities[num_qualities ++] = IPP_QUALITY_DRAFT;
4166
4167       qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
4168
4169       if (ppdFindChoice(output_mode, "best") ||
4170           ppdFindChoice(output_mode, "high"))
4171         qualities[num_qualities ++] = IPP_QUALITY_HIGH;
4172     }
4173     else if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
4174     {
4175       do
4176       {
4177         if (strstr(ppd_attr->spec, "draft") ||
4178             strstr(ppd_attr->spec, "Draft"))
4179         {
4180           qualities[num_qualities ++] = IPP_QUALITY_DRAFT;
4181           break;
4182         }
4183       }
4184       while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset",
4185                                          NULL)) != NULL);
4186
4187       qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
4188       qualities[num_qualities ++] = IPP_QUALITY_HIGH;
4189     }
4190     else
4191       qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
4192
4193     ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
4194                    "print-quality-supported", num_qualities, qualities);
4195
4196     if (ppd->nickname)
4197     {
4198      /*
4199       * The NickName can be localized in the character set specified
4200       * by the LanugageEncoding attribute.  However, ppdOpen2() has
4201       * already converted the ppd->nickname member to UTF-8 for us
4202       * (the original attribute value is available separately)
4203       */
4204
4205       cupsdSetString(&p->make_model, ppd->nickname);
4206     }
4207     else if (ppd->modelname)
4208     {
4209      /*
4210       * Model name can only contain specific characters...
4211       */
4212
4213       cupsdSetString(&p->make_model, ppd->modelname);
4214     }
4215     else
4216       cupsdSetString(&p->make_model, "Bad PPD File");
4217
4218     ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4219                  "printer-make-and-model", NULL, p->make_model);
4220
4221    /*
4222     * Add media options from the PPD file...
4223     */
4224
4225     if (ppd->num_sizes == 0 || !p->pc)
4226     {
4227       if (!ppdFindAttr(ppd, "APScannerOnly", NULL))
4228         cupsdLogMessage(CUPSD_LOG_CRIT,
4229                         "The PPD file for printer %s contains no media "
4230                         "options and is therefore invalid!", p->name);
4231
4232       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4233                    "media-default", NULL, "unknown");
4234       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4235                    "media-supported", NULL, "unknown");
4236     }
4237     else
4238     {
4239      /*
4240       * media-default
4241       */
4242
4243       if ((size = ppdPageSize(ppd, NULL)) != NULL)
4244         pwgsize = _ppdCacheGetSize(p->pc, size->name);
4245       else
4246         pwgsize = NULL;
4247
4248       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4249                    "media-default", NULL,
4250                    pwgsize ? pwgsize->map.pwg : "unknown");
4251
4252      /*
4253       * media-col-default
4254       */
4255
4256       if (pwgsize)
4257       {
4258         ipp_t   *col;                   /* Collection value */
4259
4260         input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
4261         media_type = ppdFindMarkedChoice(ppd, "MediaType");
4262         col        = new_media_col(pwgsize,
4263                                    input_slot ?
4264                                        _ppdCacheGetSource(p->pc,
4265                                                           input_slot->choice) :
4266                                        NULL,
4267                                    media_type ?
4268                                        _ppdCacheGetType(p->pc,
4269                                                         media_type->choice) :
4270                                        NULL);
4271
4272         ippAddCollection(p->ppd_attrs, IPP_TAG_PRINTER, "media-col-default",
4273                          col);
4274         ippDelete(col);
4275       }
4276
4277      /*
4278       * media-supported
4279       */
4280
4281       num_media = p->pc->num_sizes;
4282       if (p->pc->custom_min_keyword)
4283         num_media += 2;
4284
4285       if ((attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4286                                 "media-supported", num_media, NULL,
4287                                 NULL)) != NULL)
4288       {
4289         val = attr->values;
4290
4291         for (i = p->pc->num_sizes, pwgsize = p->pc->sizes;
4292              i > 0;
4293              i --, pwgsize ++, val ++)
4294           val->string.text = _cupsStrRetain(pwgsize->map.pwg);
4295
4296         if (p->pc->custom_min_keyword)
4297         {
4298           val->string.text = _cupsStrRetain(p->pc->custom_min_keyword);
4299           val ++;
4300           val->string.text = _cupsStrRetain(p->pc->custom_max_keyword);
4301         }
4302       }
4303
4304      /*
4305       * media-size-supported
4306       */
4307
4308       num_media = p->pc->num_sizes;
4309       if (p->pc->custom_min_keyword)
4310         num_media ++;
4311
4312       if ((attr = ippAddCollections(p->ppd_attrs, IPP_TAG_PRINTER,
4313                                     "media-size-supported", num_media,
4314                                     NULL)) != NULL)
4315       {
4316         val = attr->values;
4317
4318         for (i = p->pc->num_sizes, pwgsize = p->pc->sizes;
4319              i > 0;
4320              i --, pwgsize ++, val ++)
4321         {
4322           val->collection = ippNew();
4323           ippAddInteger(val->collection, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4324                         "x-dimension", pwgsize->width);
4325           ippAddInteger(val->collection, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4326                         "y-dimension", pwgsize->length);
4327         }
4328
4329         if (p->pc->custom_min_keyword)
4330         {
4331           val->collection = ippNew();
4332           ippAddRange(val->collection, IPP_TAG_PRINTER, "x-dimension",
4333                       p->pc->custom_min_width, p->pc->custom_max_width);
4334           ippAddRange(val->collection, IPP_TAG_PRINTER, "y-dimension",
4335                       p->pc->custom_min_length, p->pc->custom_max_length);
4336         }
4337       }
4338
4339      /*
4340       * media-source-supported
4341       */
4342
4343       if (p->pc->num_sources > 0 &&
4344           (attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4345                                 "media-source-supported", p->pc->num_sources,
4346                                 NULL, NULL)) != NULL)
4347       {
4348         for (i = p->pc->num_sources, pwgsource = p->pc->sources,
4349                  val = attr->values;
4350              i > 0;
4351              i --, pwgsource ++, val ++)
4352           val->string.text = _cupsStrRetain(pwgsource->pwg);
4353       }
4354
4355      /*
4356       * media-type-supported
4357       */
4358
4359       if (p->pc->num_types > 0 &&
4360           (attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4361                                 "media-type-supported", p->pc->num_types,
4362                                 NULL, NULL)) != NULL)
4363       {
4364         for (i = p->pc->num_types, pwgtype = p->pc->types,
4365                  val = attr->values;
4366              i > 0;
4367              i --, pwgtype ++, val ++)
4368           val->string.text = _cupsStrRetain(pwgtype->pwg);
4369       }
4370
4371      /*
4372       * media-*-margin-supported
4373       */
4374
4375       for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
4376            i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
4377            i --, pwgsize ++)
4378       {
4379         for (j = 0; j < num_margins; j ++)
4380           if (pwgsize->bottom == margins[j])
4381             break;
4382
4383         if (j >= num_margins)
4384         {
4385           margins[num_margins] = pwgsize->bottom;
4386           num_margins ++;
4387         }
4388       }
4389
4390       if (num_margins > 0)
4391         ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4392                        "media-bottom-margin-supported", num_margins, margins);
4393       else
4394         ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4395                       "media-bottom-margin-supported", 0);
4396
4397       for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
4398            i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
4399            i --, pwgsize ++)
4400       {
4401         for (j = 0; j < num_margins; j ++)
4402           if (pwgsize->left == margins[j])
4403             break;
4404
4405         if (j >= num_margins)
4406         {
4407           margins[num_margins] = pwgsize->left;
4408           num_margins ++;
4409         }
4410       }
4411
4412       if (num_margins > 0)
4413         ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4414                        "media-left-margin-supported", num_margins, margins);
4415       else
4416         ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4417                       "media-left-margin-supported", 0);
4418
4419       for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
4420            i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
4421            i --, pwgsize ++)
4422       {
4423         for (j = 0; j < num_margins; j ++)
4424           if (pwgsize->right == margins[j])
4425             break;
4426
4427         if (j >= num_margins)
4428         {
4429           margins[num_margins] = pwgsize->right;
4430           num_margins ++;
4431         }
4432       }
4433
4434       if (num_margins > 0)
4435         ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4436                        "media-right-margin-supported", num_margins, margins);
4437       else
4438         ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4439                       "media-right-margin-supported", 0);
4440
4441       for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
4442            i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
4443            i --, pwgsize ++)
4444       {
4445         for (j = 0; j < num_margins; j ++)
4446           if (pwgsize->top == margins[j])
4447             break;
4448
4449         if (j >= num_margins)
4450         {
4451           margins[num_margins] = pwgsize->top;
4452           num_margins ++;
4453         }
4454       }
4455
4456       if (num_margins > 0)
4457         ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4458                        "media-top-margin-supported", num_margins, margins);
4459       else
4460         ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4461                       "media-top-margin-supported", 0);
4462
4463      /*
4464       * media-col-database
4465       */
4466
4467       num_media = p->pc->num_sizes;
4468       if (p->pc->num_sources)
4469       {
4470         if (p->pc->num_types > 0)
4471           num_media += p->pc->num_sizes * p->pc->num_sources *
4472                        p->pc->num_types;
4473         else
4474           num_media += p->pc->num_sizes * p->pc->num_sources;
4475       }
4476       else if (p->pc->num_types)
4477         num_media += p->pc->num_sizes * p->pc->num_types;
4478
4479       if ((attr = ippAddCollections(p->ppd_attrs, IPP_TAG_PRINTER,
4480                                     "media-col-database", num_media,
4481                                     NULL)) != NULL)
4482       {
4483         for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, val = attr->values;
4484              i > 0;
4485              i --, pwgsize ++)
4486         {
4487          /*
4488           * Start by adding the page size without source or type...
4489           */
4490
4491           ppdMarkOption(ppd, "PageSize", pwgsize->map.ppd);
4492
4493           val->collection = new_media_col(pwgsize, NULL, NULL);
4494           val ++;
4495
4496          /*
4497           * Then add the specific, supported combinations of size, source, and
4498           * type...
4499           */
4500
4501           if (p->pc->num_sources > 0)
4502           {
4503             for (j = p->pc->num_sources, pwgsource = p->pc->sources;
4504                  j > 0;
4505                  j --, pwgsource ++)
4506             {
4507               ppdMarkOption(ppd, "InputSlot", pwgsource->ppd);
4508
4509               if (p->pc->num_types > 0)
4510               {
4511                 for (k = p->pc->num_types, pwgtype = p->pc->types;
4512                      k > 0;
4513                      k --, pwgtype ++)
4514                 {
4515                   if (!ppdMarkOption(ppd, "MediaType", pwgtype->ppd))
4516                   {
4517                     val->collection = new_media_col(pwgsize, pwgsource->pwg,
4518                                                     pwgtype->pwg);
4519                     val ++;
4520                   }
4521                 }
4522               }
4523               else if (!ppdConflicts(ppd))
4524               {
4525                 val->collection = new_media_col(pwgsize, pwgsource->pwg, NULL);
4526                 val ++;
4527               }
4528             }
4529           }
4530           else if (p->pc->num_types > 0)
4531           {
4532             for (j = p->pc->num_types, pwgtype = p->pc->types;
4533                  j > 0;
4534                  j --, pwgtype ++)
4535             {
4536               if (!ppdMarkOption(ppd, "MediaType", pwgtype->ppd))
4537               {
4538                 val->collection = new_media_col(pwgsize, NULL, pwgtype->pwg);
4539                 val ++;
4540               }
4541             }
4542           }
4543         }
4544
4545        /*
4546         * Update the number of media-col-database values...
4547         */
4548
4549         attr->num_values = val - attr->values;
4550       }
4551     }
4552
4553    /*
4554     * Output bin...
4555     */
4556
4557     if (p->pc && p->pc->num_bins > 0)
4558     {
4559       attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4560                            "output-bin-supported", p->pc->num_bins,
4561                            NULL, NULL);
4562
4563       if (attr != NULL)
4564       {
4565         for (i = 0, val = attr->values;
4566              i < p->pc->num_bins;
4567              i ++, val ++)
4568           val->string.text = _cupsStrAlloc(p->pc->bins[i].pwg);
4569       }
4570
4571       if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
4572       {
4573         for (i = 0; i < p->pc->num_bins; i ++)
4574           if (!strcmp(p->pc->bins[i].ppd, output_bin->defchoice))
4575             break;
4576
4577         if (i >= p->pc->num_bins)
4578           i = 0;
4579
4580         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4581                      "output-bin-default", NULL, p->pc->bins[i].pwg);
4582       }
4583       else
4584         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4585                      "output-bin-default", NULL, p->pc->bins[0].pwg);
4586     }
4587     else if (((ppd_attr = ppdFindAttr(ppd, "DefaultOutputOrder",
4588                                      NULL)) != NULL &&
4589               !_cups_strcasecmp(ppd_attr->value, "Reverse")) ||
4590              (!ppd_attr && ppd->manufacturer && /* "Compatibility heuristic" */
4591               (!_cups_strcasecmp(ppd->manufacturer, "epson") ||
4592                !_cups_strcasecmp(ppd->manufacturer, "lexmark"))))
4593     {
4594      /*
4595       * Report that this printer has a single output bin that leaves pages face
4596       * up.
4597       */
4598
4599       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4600                    "output-bin-supported", NULL, "face-up");
4601       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4602                    "output-bin-default", NULL, "face-up");
4603     }
4604     else
4605     {
4606       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4607                    "output-bin-supported", NULL, "face-down");
4608       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4609                    "output-bin-default", NULL, "face-down");
4610     }
4611
4612    /*
4613     * output-mode and print-color-mode...
4614     */
4615
4616     if (ppd->color_device)
4617     {
4618       static const char * const output_modes[] =
4619       {
4620         "monochrome",
4621         "color"
4622       };
4623
4624       ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4625                     "output-mode-supported", 2, NULL, output_modes);
4626       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4627                    "output-mode-default", NULL, "color");
4628
4629       ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4630                     "print-color-mode-supported", 2, NULL, output_modes);
4631       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4632                    "print-color-mode-default", NULL, "color");
4633     }
4634     else
4635     {
4636       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4637                    "output-mode-supported", NULL, "monochrome");
4638       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4639                    "output-mode-default", NULL, "monochrome");
4640
4641       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4642                    "print-color-mode-supported", NULL, "monochrome");
4643       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4644                    "print-color-mode-default", NULL, "monochrome");
4645     }
4646
4647    /*
4648     * Printer resolutions...
4649     */
4650
4651     if ((resolution = ppdFindOption(ppd, "Resolution")) == NULL)
4652       if ((resolution = ppdFindOption(ppd, "JCLResolution")) == NULL)
4653         if ((resolution = ppdFindOption(ppd, "SetResolution")) == NULL)
4654           resolution = ppdFindOption(ppd, "CNRes_PGP");
4655
4656     if (resolution)
4657     {
4658      /*
4659       * Report all supported resolutions...
4660       */
4661
4662       attr = ippAddResolutions(p->ppd_attrs, IPP_TAG_PRINTER,
4663                                "printer-resolution-supported",
4664                                resolution->num_choices, IPP_RES_PER_INCH,
4665                                NULL, NULL);
4666
4667       for (i = 0, choice = resolution->choices;
4668            i < resolution->num_choices;
4669            i ++, choice ++)
4670       {
4671         xdpi = ydpi = (int)strtol(choice->choice, (char **)&resptr, 10);
4672         if (resptr > choice->choice && xdpi > 0 && *resptr == 'x')
4673           ydpi = (int)strtol(resptr + 1, (char **)&resptr, 10);
4674
4675         if (xdpi <= 0 || ydpi <= 0)
4676         {
4677           cupsdLogMessage(CUPSD_LOG_WARN,
4678                           "Bad resolution \"%s\" for printer %s.",
4679                           choice->choice, p->name);
4680           xdpi = ydpi = 300;
4681         }
4682
4683         attr->values[i].resolution.xres  = xdpi;
4684         attr->values[i].resolution.yres  = ydpi;
4685         attr->values[i].resolution.units = IPP_RES_PER_INCH;
4686
4687         if (choice->marked)
4688           ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4689                            "printer-resolution-default", IPP_RES_PER_INCH,
4690                            xdpi, ydpi);
4691       }
4692     }
4693     else if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL &&
4694              ppd_attr->value)
4695     {
4696      /*
4697       * Just the DefaultResolution to report...
4698       */
4699
4700       xdpi = ydpi = (int)strtol(ppd_attr->value, (char **)&resptr, 10);
4701       if (resptr > ppd_attr->value && xdpi > 0)
4702       {
4703         if (*resptr == 'x')
4704           ydpi = (int)strtol(resptr + 1, (char **)&resptr, 10);
4705         else
4706           ydpi = xdpi;
4707       }
4708
4709       if (xdpi <= 0 || ydpi <= 0)
4710       {
4711         cupsdLogMessage(CUPSD_LOG_WARN,
4712                         "Bad default resolution \"%s\" for printer %s.",
4713                         ppd_attr->value, p->name);
4714         xdpi = ydpi = 300;
4715       }
4716
4717       ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4718                        "printer-resolution-default", IPP_RES_PER_INCH,
4719                        xdpi, ydpi);
4720       ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4721                        "printer-resolution-supported", IPP_RES_PER_INCH,
4722                        xdpi, ydpi);
4723     }
4724     else
4725     {
4726      /*
4727       * No resolutions in PPD - make one up...
4728       */
4729
4730       ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4731                        "printer-resolution-default", IPP_RES_PER_INCH,
4732                        300, 300);
4733       ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4734                        "printer-resolution-supported", IPP_RES_PER_INCH,
4735                        300, 300);
4736     }
4737
4738    /*
4739     * Duplexing, etc...
4740     */
4741
4742     ppdMarkDefaults(ppd);
4743
4744     if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
4745       if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
4746         if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
4747           if ((duplex = ppdFindOption(ppd, "KD03Duplex")) == NULL)
4748             duplex = ppdFindOption(ppd, "JCLDuplex");
4749
4750     if (duplex && duplex->num_choices > 1 &&
4751         !ppdInstallableConflict(ppd, duplex->keyword, "DuplexTumble"))
4752     {
4753       p->type |= CUPS_PRINTER_DUPLEX;
4754
4755       ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4756                     "sides-supported", 3, NULL, sides);
4757
4758       if (!_cups_strcasecmp(duplex->defchoice, "DuplexTumble"))
4759         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4760                      "sides-default", NULL, "two-sided-short-edge");
4761       else if (!_cups_strcasecmp(duplex->defchoice, "DuplexNoTumble"))
4762         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4763                      "sides-default", NULL, "two-sided-long-edge");
4764       else
4765         ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4766                      "sides-default", NULL, "one-sided");
4767     }
4768     else
4769     {
4770       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4771                    "sides-supported", NULL, "one-sided");
4772       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4773                    "sides-default", NULL, "one-sided");
4774     }
4775
4776     if (ppdFindOption(ppd, "Collate") != NULL)
4777       p->type |= CUPS_PRINTER_COLLATE;
4778
4779     if (ppdFindOption(ppd, "StapleLocation") != NULL)
4780     {
4781       p->type |= CUPS_PRINTER_STAPLE;
4782       finishings[num_finishings++] = IPP_FINISHINGS_STAPLE;
4783     }
4784
4785     if (ppdFindOption(ppd, "BindEdge") != NULL)
4786     {
4787       p->type |= CUPS_PRINTER_BIND;
4788       finishings[num_finishings++] = IPP_FINISHINGS_BIND;
4789     }
4790
4791     for (i = 0; i < ppd->num_sizes; i ++)
4792       if (ppd->sizes[i].length > 1728)
4793         p->type |= CUPS_PRINTER_LARGE;
4794       else if (ppd->sizes[i].length > 1008)
4795         p->type |= CUPS_PRINTER_MEDIUM;
4796       else
4797         p->type |= CUPS_PRINTER_SMALL;
4798
4799     if ((ppd_attr = ppdFindAttr(ppd, "APICADriver", NULL)) != NULL &&
4800         ppd_attr->value && !_cups_strcasecmp(ppd_attr->value, "true"))
4801     {
4802       if ((ppd_attr = ppdFindAttr(ppd, "APScannerOnly", NULL)) != NULL &&
4803           ppd_attr->value && !_cups_strcasecmp(ppd_attr->value, "true"))
4804         p->type |= CUPS_PRINTER_SCANNER;
4805       else
4806         p->type |= CUPS_PRINTER_MFP;
4807     }
4808
4809    /*
4810     * Scan the filters in the PPD file...
4811     */
4812
4813     if (p->pc)
4814     {
4815       for (filter = (const char *)cupsArrayFirst(p->pc->filters);
4816            filter;
4817            filter = (const char *)cupsArrayNext(p->pc->filters))
4818       {
4819         if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
4820             _cups_isspace(filter[28]))
4821         {
4822           p->type |= CUPS_PRINTER_COMMANDS;
4823           break;
4824         }
4825       }
4826     }
4827
4828     if (p->type & CUPS_PRINTER_COMMANDS)
4829     {
4830       char      *commands,              /* Copy of commands */
4831                 *start,                 /* Start of name */
4832                 *end;                   /* End of name */
4833       int       count;                  /* Number of commands */
4834
4835       if ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL)
4836       {
4837         for (count = 0, start = ppd_attr->value; *start; count ++)
4838         {
4839           while (_cups_isspace(*start))
4840             start ++;
4841
4842           if (!*start)
4843             break;
4844
4845           while (*start && !isspace(*start & 255))
4846             start ++;
4847         }
4848       }
4849       else
4850         count = 0;
4851
4852       if (count > 0)
4853       {
4854        /*
4855         * Make a copy of the commands string and count how many commands there
4856         * are...
4857         */
4858
4859         attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4860                              "printer-commands", count, NULL, NULL);
4861
4862         commands = strdup(ppd_attr->value);
4863
4864         for (count = 0, start = commands; *start; count ++)
4865         {
4866           while (isspace(*start & 255))
4867             start ++;
4868
4869           if (!*start)
4870             break;
4871
4872           end = start;
4873           while (*end && !isspace(*end & 255))
4874             end ++;
4875
4876           if (*end)
4877             *end++ = '\0';
4878
4879           attr->values[count].string.text = _cupsStrAlloc(start);
4880
4881           start = end;
4882         }
4883
4884         free(commands);
4885       }
4886       else
4887       {
4888        /*
4889         * Add the standard list of commands...
4890         */
4891
4892         ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4893                       "printer-commands",
4894                       (int)(sizeof(standard_commands) /
4895                             sizeof(standard_commands[0])), NULL,
4896                       standard_commands);
4897       }
4898     }
4899     else
4900     {
4901      /*
4902       * No commands supported...
4903       */
4904
4905       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4906                    "printer-commands", NULL, "none");
4907     }
4908
4909    /*
4910     * Show current and available port monitors for this printer...
4911     */
4912
4913     ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "port-monitor",
4914                  NULL, p->port_monitor ? p->port_monitor : "none");
4915
4916     for (i = 1, ppd_attr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
4917          ppd_attr;
4918          i ++, ppd_attr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL));
4919
4920     if (ppd->protocols)
4921     {
4922       if (strstr(ppd->protocols, "TBCP"))
4923         i ++;
4924       else if (strstr(ppd->protocols, "BCP"))
4925         i ++;
4926     }
4927
4928     attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
4929                          "port-monitor-supported", i, NULL, NULL);
4930
4931     attr->values[0].string.text = _cupsStrAlloc("none");
4932
4933     for (i = 1, ppd_attr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
4934          ppd_attr;
4935          i ++, ppd_attr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
4936       attr->values[i].string.text = _cupsStrAlloc(ppd_attr->value);
4937
4938     if (ppd->protocols)
4939     {
4940       if (strstr(ppd->protocols, "TBCP"))
4941         attr->values[i].string.text = _cupsStrAlloc("tbcp");
4942       else if (strstr(ppd->protocols, "BCP"))
4943         attr->values[i].string.text = _cupsStrAlloc("bcp");
4944     }
4945
4946     if (ppdFindAttr(ppd, "APRemoteQueueID", NULL))
4947       p->type |= CUPS_PRINTER_REMOTE;
4948
4949 #ifdef HAVE_APPLICATIONSERVICES_H
4950    /*
4951     * Convert the file referenced in APPrinterIconPath to a 128x128 PNG
4952     * and save it as cacheDir/printername.png
4953     */
4954
4955     if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL &&
4956         ppd_attr->value &&
4957         !_cupsFileCheck(ppd_attr->value, _CUPS_FILE_CHECK_FILE, !RunUser,
4958                         cupsdLogFCMessage, p))
4959     {
4960       CGImageRef        imageRef = NULL;/* Current icon image */
4961       CGImageRef        biggestIconRef = NULL;
4962                                         /* Biggest icon image */
4963       CGImageRef        closestTo128IconRef = NULL;
4964                                         /* Icon image closest to and >= 128 */
4965       CGImageSourceRef  sourceRef;      /* The file's image source */
4966       char              outPath[HTTP_MAX_URI];
4967                                         /* The path to the PNG file */
4968       CFURLRef          outUrl;         /* The URL made from the outPath */
4969       CFURLRef          icnsFileUrl;    /* The URL of the original ICNS icon file */
4970       CGImageDestinationRef destRef;    /* The image destination to write */
4971       size_t            bytesPerRow;    /* The bytes per row used for resizing */
4972       CGContextRef      context;        /* The CG context used for resizing */
4973
4974       snprintf(outPath, sizeof(outPath), "%s/%s.png", CacheDir, p->name);
4975       outUrl      = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
4976                                                             (UInt8 *)outPath,
4977                                                             strlen(outPath),
4978                                                             FALSE);
4979       icnsFileUrl = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
4980                                                             (UInt8 *)ppd_attr->value,
4981                                                             strlen(ppd_attr->value),
4982                                                             FALSE);
4983       if (outUrl && icnsFileUrl)
4984       {
4985         sourceRef = CGImageSourceCreateWithURL(icnsFileUrl, NULL);
4986         if (sourceRef)
4987         {
4988           for (i = 0; i < CGImageSourceGetCount(sourceRef); i ++)
4989           {
4990             imageRef = CGImageSourceCreateImageAtIndex(sourceRef, i, NULL);
4991             if (!imageRef)
4992               continue;
4993
4994             if (CGImageGetWidth(imageRef) == CGImageGetHeight(imageRef))
4995             {
4996              /*
4997               * Loop through remembering the icon closest to 128 but >= 128
4998               * and then remember the largest icon.
4999               */
5000
5001               if (CGImageGetWidth(imageRef) >= 128 &&
5002                   (!closestTo128IconRef ||
5003                    CGImageGetWidth(imageRef) <
5004                        CGImageGetWidth(closestTo128IconRef)))
5005               {
5006                 CGImageRelease(closestTo128IconRef);
5007                 CGImageRetain(imageRef);
5008                 closestTo128IconRef = imageRef;
5009               }
5010
5011               if (!biggestIconRef ||
5012                   CGImageGetWidth(imageRef) > CGImageGetWidth(biggestIconRef))
5013               {
5014                 CGImageRelease(biggestIconRef);
5015                 CGImageRetain(imageRef);
5016                 biggestIconRef = imageRef;
5017               }
5018             }
5019
5020             CGImageRelease(imageRef);
5021           }
5022
5023           if (biggestIconRef)
5024           {
5025            /*
5026             * If biggestIconRef is NULL, we found no icons. Otherwise we first
5027             * want the closest to 128, but if none are larger than 128, we want
5028             * the largest icon available.
5029             */
5030
5031             imageRef = closestTo128IconRef ? closestTo128IconRef :
5032                                              biggestIconRef;
5033             CGImageRetain(imageRef);
5034             CGImageRelease(biggestIconRef);
5035             if (closestTo128IconRef)
5036               CGImageRelease(closestTo128IconRef);
5037             destRef = CGImageDestinationCreateWithURL(outUrl, kUTTypePNG, 1,
5038                                                       NULL);
5039             if (destRef)
5040             {
5041               if (CGImageGetWidth(imageRef) != 128)
5042               {
5043                 bytesPerRow = CGImageGetBytesPerRow(imageRef) /
5044                               CGImageGetWidth(imageRef) * 128;
5045                 context     = CGBitmapContextCreate(NULL, 128, 128,
5046                                                     CGImageGetBitsPerComponent(imageRef),
5047                                                     bytesPerRow,
5048                                                     CGImageGetColorSpace(imageRef),
5049                                                     kCGImageAlphaPremultipliedFirst);
5050                 if (context)
5051                 {
5052                   CGContextDrawImage(context, CGRectMake(0, 0, 128, 128),
5053                                      imageRef);
5054                   CGImageRelease(imageRef);
5055                   imageRef = CGBitmapContextCreateImage(context);
5056                   CGContextRelease(context);
5057                 }
5058               }
5059
5060               CGImageDestinationAddImage(destRef, imageRef, NULL);
5061               CGImageDestinationFinalize(destRef);
5062               CFRelease(destRef);
5063             }
5064
5065             CGImageRelease(imageRef);
5066           }
5067
5068           CFRelease(sourceRef);
5069         }
5070       }
5071
5072       if (outUrl)
5073         CFRelease(outUrl);
5074
5075       if (icnsFileUrl)
5076         CFRelease(icnsFileUrl);
5077     }
5078 #endif /* HAVE_APPLICATIONSERVICES_H */
5079
5080    /*
5081     * Close the PPD and set the type...
5082     */
5083
5084     ppdClose(ppd);
5085   }
5086   else if (!access(ppd_name, 0))
5087   {
5088     int                 pline;          /* PPD line number */
5089     ppd_status_t        pstatus;        /* PPD load status */
5090
5091
5092     pstatus = ppdLastError(&pline);
5093
5094     cupsdLogMessage(CUPSD_LOG_ERROR, "PPD file for %s cannot be loaded!",
5095                     p->name);
5096
5097     if (pstatus <= PPD_ALLOC_ERROR)
5098       cupsdLogMessage(CUPSD_LOG_ERROR, "%s", strerror(errno));
5099     else
5100       cupsdLogMessage(CUPSD_LOG_ERROR, "%s on line %d.",
5101                       ppdErrorString(pstatus), pline);
5102
5103     cupsdLogMessage(CUPSD_LOG_INFO,
5104                     "Hint: Run \"cupstestppd %s\" and fix any errors.",
5105                     ppd_name);
5106   }
5107   else
5108   {
5109    /*
5110     * If we have an interface script, add a filter entry for it...
5111     */
5112
5113     char        interface[1024];        /* Interface script */
5114
5115
5116     snprintf(interface, sizeof(interface), "%s/interfaces/%s", ServerRoot,
5117              p->name);
5118     if (!access(interface, X_OK))
5119     {
5120      /*
5121       * Yes, we have a System V style interface script; use it!
5122       */
5123
5124       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5125                    "printer-make-and-model", NULL,
5126                    "Local System V Printer");
5127     }
5128     else if (!strncmp(p->device_uri, "ipp://", 6) &&
5129              (strstr(p->device_uri, "/printers/") != NULL ||
5130               strstr(p->device_uri, "/classes/") != NULL ||
5131               (strstr(p->device_uri, "._ipp.") != NULL &&
5132                !strcmp(p->device_uri + strlen(p->device_uri) - 5,
5133                        "/cups"))))
5134     {
5135      /*
5136       * Tell the client this is really a hard-wired remote printer.
5137       */
5138
5139       p->type |= CUPS_PRINTER_REMOTE;
5140
5141      /*
5142       * Point the printer-uri-supported attribute to the
5143       * remote printer...
5144       */
5145
5146       if (strchr(p->device_uri, '?'))
5147       {
5148        /*
5149         * Strip trailing "?options" from URI...
5150         */
5151
5152         char    resource[HTTP_MAX_URI], /* New URI */
5153                 *ptr;                   /* Pointer into URI */
5154
5155         strlcpy(resource, p->device_uri, sizeof(resource));
5156         if ((ptr = strchr(resource, '?')) != NULL)
5157           *ptr = '\0';
5158
5159         ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
5160                      "printer-uri-supported", NULL, resource);
5161       }
5162       else
5163         ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
5164                      "printer-uri-supported", NULL, p->device_uri);
5165
5166      /*
5167       * Then set the make-and-model accordingly...
5168       */
5169
5170       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5171                    "printer-make-and-model", NULL, "Remote Printer");
5172
5173      /*
5174       * Print all files directly...
5175       */
5176
5177       p->raw    = 1;
5178       p->remote = 1;
5179     }
5180     else
5181     {
5182      /*
5183       * Otherwise we have neither - treat this as a "dumb" printer
5184       * with no PPD file...
5185       */
5186
5187       ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5188                    "printer-make-and-model", NULL, "Local Raw Printer");
5189
5190       p->raw = 1;
5191     }
5192   }
5193
5194   ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
5195                  "finishings-supported", num_finishings, finishings);
5196   ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
5197                 "finishings-default", IPP_FINISHINGS_NONE);
5198
5199   if (ppd && p->pc)
5200   {
5201    /*
5202     * Save cached PPD attributes to disk...
5203     */
5204
5205     cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Saving %s...", cache_name);
5206
5207     _ppdCacheWriteFile(p->pc, cache_name, p->ppd_attrs);
5208   }
5209   else
5210   {
5211    /*
5212     * Remove cache files...
5213     */
5214
5215     if (cache_info.st_mtime)
5216       unlink(cache_name);
5217   }
5218 }
5219
5220
5221 /*
5222  * 'log_ipp_conformance()' - Log an IPP conformance issue with a printer.
5223  */
5224
5225 static void
5226 log_ipp_conformance(
5227     cupsd_printer_t *p,                 /* I - Printer */
5228     const char      *reason)            /* I - Printer state reason */
5229 {
5230   const char    *message;               /* Message to log */
5231 #ifdef __APPLE__
5232   aslmsg        aslm;                   /* Apple System Log message */
5233 #endif /* __APPLE__ */
5234
5235
5236  /*
5237   * Strip the leading "cups-ipp-" from the reason and create a log message for
5238   * it...
5239   */
5240
5241   reason += 9;
5242   if (!strcmp(reason, "missing-cancel-job"))
5243     message = "Printer does not support REQUIRED Cancel-Job operation.";
5244   else if (!strcmp(reason, "missing-get-job-attributes"))
5245     message = "Printer does not support REQUIRED Get-Job-Attributes operation.";
5246   else if (!strcmp(reason, "missing-print-job"))
5247     message = "Printer does not support REQUIRED Print-Job operation.";
5248   else if (!strcmp(reason, "missing-validate-job"))
5249     message = "Printer does not support REQUIRED Validate-Job operation.";
5250   else if (!strcmp(reason, "missing-get-printer-attributes"))
5251     message = "Printer does not support REQUIRED Get-Printer-Attributes operation.";
5252   else if (!strcmp(reason, "missing-send-document"))
5253     message = "Printer supports Create-Job but not Send-Document operation.";
5254   else if (!strcmp(reason, "missing-job-history"))
5255     message = "Printer does not provide REQUIRED job history.";
5256   else if (!strcmp(reason, "missing-job-id"))
5257     message = "Printer does not provide REQUIRED job-id attribute.";
5258   else if (!strcmp(reason, "missing-job-state"))
5259     message = "Printer does not provide REQUIRED job-state attribute.";
5260   else if (!strcmp(reason, "missing-operations-supported"))
5261     message = "Printer does not provide REQUIRED operations-supported "
5262               "attribute.";
5263   else if (!strcmp(reason, "missing-printer-is-accepting-jobs"))
5264     message = "Printer does not provide REQUIRED printer-is-accepting-jobs "
5265               "attribute.";
5266   else if (!strcmp(reason, "missing-printer-state-reasons"))
5267     message = "Printer does not provide REQUIRED printer-state-reasons "
5268               "attribute.";
5269   else if (!strcmp(reason, "wrong-http-version"))
5270     message = "Printer does not use REQUIRED HTTP/1.1 transport.";
5271   else
5272     message = "Unknown IPP conformance failure.";
5273
5274   cupsdLogMessage(CUPSD_LOG_WARN, "%s: %s", p->name, message);
5275
5276 #ifdef __APPLE__
5277  /*
5278   * Report the failure information to Apple if the user opts into providing
5279   * feedback to Apple...
5280   */
5281
5282   aslm = asl_new(ASL_TYPE_MSG);
5283   if (aslm)
5284   {
5285     asl_set(aslm, "com.apple.message.domain", "com.apple.printing.ipp.conformance");
5286     asl_set(aslm, "com.apple.message.domain_scope", "com.apple.printing.ipp.conformance");
5287     asl_set(aslm, "com.apple.message.signature", reason);
5288     asl_set(aslm, "com.apple.message.signature2",
5289             p->make_model ? p->make_model : "Unknown");
5290     asl_log(NULL, aslm, ASL_LEVEL_NOTICE, "%s: %s",
5291             p->make_model ? p->make_model : "Unknown", message);
5292     asl_free(aslm);
5293   }
5294 #endif /* __APPLE__ */
5295 }
5296
5297
5298 /*
5299  * 'new_media_col()' - Create a media-col collection value.
5300  */
5301
5302 static ipp_t *                          /* O - Collection value */
5303 new_media_col(_pwg_size_t *size,        /* I - media-size/margin values */
5304               const char  *source,      /* I - media-source value */
5305               const char  *type)        /* I - media-type value */
5306 {
5307   ipp_t *media_col,                     /* Collection value */
5308         *media_size;                    /* media-size value */
5309
5310
5311   media_col = ippNew();
5312
5313   media_size = ippNew();
5314   ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5315                 "x-dimension", size->width);
5316   ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5317                 "y-dimension", size->length);
5318   ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
5319   ippDelete(media_size);
5320
5321   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5322                 "media-bottom-margin", size->bottom);
5323   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5324                 "media-left-margin", size->left);
5325   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5326                 "media-right-margin", size->right);
5327   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5328                 "media-top-margin", size->top);
5329
5330   if (source)
5331     ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source",
5332                  NULL, source);
5333
5334   if (type)
5335     ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type",
5336                  NULL, type);
5337
5338   return (media_col);
5339 }
5340
5341
5342 #ifdef __sgi
5343 /*
5344  * 'write_irix_config()' - Update the config files used by the IRIX
5345  *                         desktop tools.
5346  */
5347
5348 static void
5349 write_irix_config(cupsd_printer_t *p)   /* I - Printer to update */
5350 {
5351   char          filename[1024];         /* Interface script filename */
5352   cups_file_t   *fp;                    /* Interface script file */
5353   ipp_attribute_t *attr;                /* Attribute data */
5354
5355
5356  /*
5357   * Add dummy interface and GUI scripts to fool SGI's "challenged" printing
5358   * tools.  First the interface script that tells the tools what kind of
5359   * printer we have...
5360   */
5361
5362   snprintf(filename, sizeof(filename), "/var/spool/lp/interface/%s", p->name);
5363
5364   if (p->type & CUPS_PRINTER_CLASS)
5365     unlink(filename);
5366   else if ((fp = cupsFileOpen(filename, "w")) != NULL)
5367   {
5368     cupsFilePuts(fp, "#!/bin/sh\n");
5369
5370     if ((attr = ippFindAttribute(p->attrs, "printer-make-and-model",
5371                                  IPP_TAG_TEXT)) != NULL)
5372       cupsFilePrintf(fp, "NAME=\"%s\"\n", attr->values[0].string.text);
5373     else if (p->type & CUPS_PRINTER_CLASS)
5374       cupsFilePuts(fp, "NAME=\"Printer Class\"\n");
5375     else
5376       cupsFilePuts(fp, "NAME=\"Remote Destination\"\n");
5377
5378     if (p->type & CUPS_PRINTER_COLOR)
5379       cupsFilePuts(fp, "TYPE=ColorPostScript\n");
5380     else
5381       cupsFilePuts(fp, "TYPE=MonoPostScript\n");
5382
5383     cupsFilePrintf(fp, "HOSTNAME=%s\n", ServerName);
5384     cupsFilePrintf(fp, "HOSTPRINTER=%s\n", p->name);
5385
5386     cupsFileClose(fp);
5387
5388     chmod(filename, 0755);
5389     chown(filename, User, Group);
5390   }
5391
5392  /*
5393   * Then the member file that tells which device file the queue is connected
5394   * to...  Networked printers use "/dev/null" in this file, so that's what
5395   * we use (the actual device URI can confuse some apps...)
5396   */
5397
5398   snprintf(filename, sizeof(filename), "/var/spool/lp/member/%s", p->name);
5399
5400   if (p->type & CUPS_PRINTER_CLASS)
5401     unlink(filename);
5402   else if ((fp = cupsFileOpen(filename, "w")) != NULL)
5403   {
5404     cupsFilePuts(fp, "/dev/null\n");
5405
5406     cupsFileClose(fp);
5407
5408     chmod(filename, 0644);
5409     chown(filename, User, Group);
5410   }
5411
5412  /*
5413   * The gui_interface file is a script or program that launches a GUI
5414   * option panel for the printer, using options specified on the
5415   * command-line in the third argument.  The option panel must send
5416   * any printing options to stdout on a single line when the user
5417   * accepts them, or nothing if the user cancels the dialog.
5418   *
5419   * The default options panel program is /usr/bin/glpoptions, from
5420   * the ESP Print Pro software.  You can select another using the
5421   * PrintcapGUI option.
5422   */
5423
5424   snprintf(filename, sizeof(filename), "/var/spool/lp/gui_interface/ELF/%s.gui", p->name);
5425
5426   if (p->type & CUPS_PRINTER_CLASS)
5427     unlink(filename);
5428   else if ((fp = cupsFileOpen(filename, "w")) != NULL)
5429   {
5430     cupsFilePuts(fp, "#!/bin/sh\n");
5431     cupsFilePrintf(fp, "%s -d %s -o \"$3\"\n", PrintcapGUI, p->name);
5432
5433     cupsFileClose(fp);
5434
5435     chmod(filename, 0755);
5436     chown(filename, User, Group);
5437   }
5438
5439  /*
5440   * The POD config file is needed by the printstatus command to show
5441   * the printer location and device.
5442   */
5443
5444   snprintf(filename, sizeof(filename), "/var/spool/lp/pod/%s.config", p->name);
5445
5446   if (p->type & CUPS_PRINTER_CLASS)
5447     unlink(filename);
5448   else if ((fp = cupsFileOpen(filename, "w")) != NULL)
5449   {
5450     cupsFilePrintf(fp, "Printer Class      | %s\n",
5451             (p->type & CUPS_PRINTER_COLOR) ? "ColorPostScript" : "MonoPostScript");
5452     cupsFilePrintf(fp, "Printer Model      | %s\n", p->make_model ? p->make_model : "");
5453     cupsFilePrintf(fp, "Location Code      | %s\n", p->location ? p->location : "");
5454     cupsFilePrintf(fp, "Physical Location  | %s\n", p->info ? p->info : "");
5455     cupsFilePrintf(fp, "Port Path          | %s\n", p->device_uri);
5456     cupsFilePrintf(fp, "Config Path        | /var/spool/lp/pod/%s.config\n", p->name);
5457     cupsFilePrintf(fp, "Active Status Path | /var/spool/lp/pod/%s.status\n", p->name);
5458     cupsFilePuts(fp, "Status Update Wait | 10 seconds\n");
5459
5460     cupsFileClose(fp);
5461
5462     chmod(filename, 0664);
5463     chown(filename, User, Group);
5464   }
5465 }
5466
5467
5468 /*
5469  * 'write_irix_state()' - Update the status files used by IRIX printing
5470  *                        desktop tools.
5471  */
5472
5473 static void
5474 write_irix_state(cupsd_printer_t *p)    /* I - Printer to update */
5475 {
5476   char          filename[1024];         /* Interface script filename */
5477   cups_file_t   *fp;                    /* Interface script file */
5478   int           tag;                    /* Status tag value */
5479
5480
5481   if (p)
5482   {
5483    /*
5484     * The POD status file is needed for the printstatus window to
5485     * provide the current status of the printer.
5486     */
5487
5488     snprintf(filename, sizeof(filename), "/var/spool/lp/pod/%s.status", p->name);
5489
5490     if (p->type & CUPS_PRINTER_CLASS)
5491       unlink(filename);
5492     else if ((fp = cupsFileOpen(filename, "w")) != NULL)
5493     {
5494       cupsFilePrintf(fp, "Operational Status | %s\n",
5495               (p->state == IPP_PRINTER_IDLE)       ? "Idle" :
5496               (p->state == IPP_PRINTER_PROCESSING) ? "Busy" :
5497                                                      "Faulted");
5498       cupsFilePrintf(fp, "Information        | 01 00 00 | %s\n", CUPS_SVERSION);
5499       cupsFilePrintf(fp, "Information        | 02 00 00 | Device URI: %s\n",
5500               p->device_uri);
5501       cupsFilePrintf(fp, "Information        | 03 00 00 | %s jobs\n",
5502               p->accepting ? "Accepting" : "Not accepting");
5503       cupsFilePrintf(fp, "Information        | 04 00 00 | %s\n", p->state_message);
5504
5505       cupsFileClose(fp);
5506
5507       chmod(filename, 0664);
5508       chown(filename, User, Group);
5509     }
5510
5511    /*
5512     * The activeicons file is needed to provide desktop icons for printers:
5513     *
5514     * [ quoted from /usr/lib/print/tagit ]
5515     *
5516     * --- Type of printer tags (base values)
5517     *
5518     * Dumb=66048                        # 0x10200
5519     * DumbColor=66080           # 0x10220
5520     * Raster=66112              # 0x10240
5521     * ColorRaster=66144         # 0x10260
5522     * Plotter=66176             # 0x10280
5523     * PostScript=66208          # 0x102A0
5524     * ColorPostScript=66240     # 0x102C0
5525     * MonoPostScript=66272      # 0x102E0
5526     *
5527     * --- Printer state modifiers for local printers
5528     *
5529     * Idle=0                    # 0x0
5530     * Busy=1                    # 0x1
5531     * Faulted=2                 # 0x2
5532     * Unknown=3                 # 0x3 (Faulted due to unknown reason)
5533     *
5534     * --- Printer state modifiers for network printers
5535     *
5536     * NetIdle=8                 # 0x8
5537     * NetBusy=9                 # 0x9
5538     * NetFaulted=10             # 0xA
5539     * NetUnknown=11             # 0xB (Faulted due to unknown reason)
5540     */
5541
5542     snprintf(filename, sizeof(filename), "/var/spool/lp/activeicons/%s", p->name);
5543
5544     if (p->type & CUPS_PRINTER_CLASS)
5545       unlink(filename);
5546     else if ((fp = cupsFileOpen(filename, "w")) != NULL)
5547     {
5548       if (p->type & CUPS_PRINTER_COLOR)
5549         tag = 66240;
5550       else
5551         tag = 66272;
5552
5553       if (p->type & CUPS_PRINTER_REMOTE)
5554         tag |= 8;
5555
5556       if (p->state == IPP_PRINTER_PROCESSING)
5557         tag |= 1;
5558
5559       else if (p->state == IPP_PRINTER_STOPPED)
5560         tag |= 2;
5561
5562       cupsFilePuts(fp, "#!/bin/sh\n");
5563       cupsFilePrintf(fp, "#Tag %d\n", tag);
5564
5565       cupsFileClose(fp);
5566
5567       chmod(filename, 0755);
5568       chown(filename, User, Group);
5569     }
5570   }
5571
5572  /*
5573   * The default file is needed by the printers window to show
5574   * the default printer.
5575   */
5576
5577   snprintf(filename, sizeof(filename), "/var/spool/lp/default");
5578
5579   if (DefaultPrinter != NULL)
5580   {
5581     if ((fp = cupsFileOpen(filename, "w")) != NULL)
5582     {
5583       cupsFilePrintf(fp, "%s\n", DefaultPrinter->name);
5584
5585       cupsFileClose(fp);
5586
5587       chmod(filename, 0644);
5588       chown(filename, User, Group);
5589     }
5590   }
5591   else
5592     unlink(filename);
5593 }
5594 #endif /* __sgi */
5595
5596
5597 /*
5598  * 'write_xml_string()' - Write a string with XML escaping.
5599  */
5600
5601 static void
5602 write_xml_string(cups_file_t *fp,       /* I - File to write to */
5603                  const char  *s)        /* I - String to write */
5604 {
5605   const char    *start;                 /* Start of current sequence */
5606
5607
5608   if (!s)
5609     return;
5610
5611   for (start = s; *s; s ++)
5612   {
5613     if (*s == '&')
5614     {
5615       if (s > start)
5616         cupsFileWrite(fp, start, s - start);
5617
5618       cupsFilePuts(fp, "&amp;");
5619       start = s + 1;
5620     }
5621     else if (*s == '<')
5622     {
5623       if (s > start)
5624         cupsFileWrite(fp, start, s - start);
5625
5626       cupsFilePuts(fp, "&lt;");
5627       start = s + 1;
5628     }
5629   }
5630
5631   if (s > start)
5632     cupsFilePuts(fp, start);
5633 }
5634
5635
5636 /*
5637  * End of "$Id: printers.c 10295 2012-02-15 23:21:06Z mike $".
5638  */