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