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