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