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