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