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