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