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