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