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