2
* "$Id: printers.c 10996 2013-05-29 11:51:34Z msweet $"
4
* Printer routines for the CUPS scheduler.
6
* Copyright 2007-2012 by Apple Inc.
7
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
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
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
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
42
* add_printer_filter() - Add a MIME filter for a printer.
43
* add_printer_formats() - Add document-format-supported values for a
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
49
* load_ppd() - Load a cached PPD file, updating the cache as
51
* new_media_col() - Create a media-col collection value.
52
* write_xml_string() - Write a string with XML escaping.
56
* Include necessary headers...
61
#ifdef HAVE_APPLICATIONSERVICES_H
62
# include <ApplicationServices/ApplicationServices.h>
63
#endif /* HAVE_APPLICATIONSERVICES_H */
64
#ifdef HAVE_SYS_MOUNT_H
65
# include <sys/mount.h>
66
#endif /* HAVE_SYS_MOUNT_H */
67
#ifdef HAVE_SYS_STATVFS_H
68
# include <sys/statvfs.h>
69
#elif defined(HAVE_SYS_STATFS_H)
70
# include <sys/statfs.h>
71
#endif /* HAVE_SYS_STATVFS_H */
74
#endif /* HAVE_SYS_VFS_H */
77
#endif /* __APPLE__ */
84
static void add_printer_defaults(cupsd_printer_t *p);
85
static void add_printer_filter(cupsd_printer_t *p, mime_type_t *type,
87
static void add_printer_formats(cupsd_printer_t *p);
88
static int compare_printers(void *first, void *second, void *data);
89
static void delete_printer_filters(cupsd_printer_t *p);
90
static void dirty_printer(cupsd_printer_t *p);
91
static void load_ppd(cupsd_printer_t *p);
92
static void log_ipp_conformance(cupsd_printer_t *p, const char *reason);
93
static ipp_t *new_media_col(_pwg_size_t *size, const char *source,
95
static void write_xml_string(cups_file_t *fp, const char *s);
99
* 'cupsdAddPrinter()' - Add a printer to the system.
102
cupsd_printer_t * /* O - New printer */
103
cupsdAddPrinter(const char *name) /* I - Name of printer */
105
cupsd_printer_t *p; /* New printer */
106
char uri[1024], /* Printer URI */
107
uuid[64]; /* Printer UUID */
111
* Range check input...
114
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddPrinter(\"%s\")", name);
117
* Create a new printer entity...
120
if ((p = calloc(1, sizeof(cupsd_printer_t))) == NULL)
122
cupsdLogMessage(CUPSD_LOG_CRIT, "Unable to allocate memory for printer - %s",
127
cupsdSetString(&p->name, name);
128
cupsdSetString(&p->info, name);
129
cupsdSetString(&p->hostname, ServerName);
131
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
132
ServerName, RemotePort, "/printers/%s", name);
133
cupsdSetString(&p->uri, uri);
134
cupsdSetString(&p->uuid, _httpAssembleUUID(ServerName, RemotePort, name, 0,
135
uuid, sizeof(uuid)));
136
cupsdSetDeviceURI(p, "file:///dev/null");
137
cupsdSetString(&p->ppd_timestamp, "*");
138
p->state = IPP_PRINTER_STOPPED;
139
p->state_time = time(NULL);
141
p->color_managed = 1;
142
p->shared = DefaultShared;
143
p->filetype = mimeAddType(MimeDatabase, "printer", name);
145
cupsdSetString(&p->job_sheets[0], "none");
146
cupsdSetString(&p->job_sheets[1], "none");
148
cupsdSetString(&p->error_policy, ErrorPolicy);
149
cupsdSetString(&p->op_policy, DefaultPolicy);
151
p->op_policy_ptr = DefaultPolicyPtr;
154
* Insert the printer in the printer list alphabetically...
158
Printers = cupsArrayNew(compare_printers, NULL);
160
cupsdLogMessage(CUPSD_LOG_DEBUG2,
161
"cupsdAddPrinter: Adding %s to Printers", p->name);
162
cupsArrayAdd(Printers, p);
165
* Return the new printer...
173
* 'cupsdCreateCommonData()' - Create the common printer data.
177
cupsdCreateCommonData(void)
179
int i; /* Looping var */
180
ipp_attribute_t *attr; /* Attribute data */
181
cups_dir_t *dir; /* Notifier directory */
182
cups_dentry_t *dent; /* Notifier directory entry */
183
cups_array_t *notifiers; /* Notifier array */
184
char filename[1024], /* Filename */
185
*notifier; /* Current notifier */
186
cupsd_policy_t *p; /* Current policy */
187
int k_supported; /* Maximum file size supported */
189
struct statvfs spoolinfo; /* FS info for spool directory */
190
double spoolsize; /* FS size */
191
#elif defined(HAVE_STATFS)
192
struct statfs spoolinfo; /* FS info for spool directory */
193
double spoolsize; /* FS size */
194
#endif /* HAVE_STATVFS */
195
static const int nups[] = /* number-up-supported values */
196
{ 1, 2, 4, 6, 9, 16 };
197
static const int orients[4] =/* orientation-requested-supported values */
201
IPP_REVERSE_LANDSCAPE,
204
static const char * const holds[] = /* job-hold-until-supported values */
215
static const char * const versions[] =/* ipp-versions-supported values */
222
static const int ops[] = /* operations-supported values */
229
IPP_GET_JOB_ATTRIBUTES,
231
IPP_GET_PRINTER_ATTRIBUTES,
238
IPP_SET_PRINTER_ATTRIBUTES,
239
IPP_SET_JOB_ATTRIBUTES,
240
IPP_GET_PRINTER_SUPPORTED_VALUES,
241
IPP_CREATE_PRINTER_SUBSCRIPTION,
242
IPP_CREATE_JOB_SUBSCRIPTION,
243
IPP_GET_SUBSCRIPTION_ATTRIBUTES,
244
IPP_GET_SUBSCRIPTIONS,
245
IPP_RENEW_SUBSCRIPTION,
246
IPP_CANCEL_SUBSCRIPTION,
247
IPP_GET_NOTIFICATIONS,
251
IPP_RELEASE_HELD_NEW_JOBS,
268
CUPS_AUTHENTICATE_JOB,
273
static const char * const charsets[] =/* charset-supported values */
278
static const char * const compressions[] =
279
{ /* document-compression-supported values */
283
#endif /* HAVE_LIBZ */
285
static const char * const media_col_supported[] =
286
{ /* media-col-supported values */
287
"media-bottom-margin",
289
"media-right-margin",
295
static const char * const multiple_document_handling[] =
296
{ /* multiple-document-handling-supported values */
297
"separate-documents-uncollated-copies",
298
"separate-documents-collated-copies"
300
static const char * const notify_attrs[] =
301
{ /* notify-attributes-supported values */
302
"printer-state-change-time",
303
"notify-lease-expiration-time",
304
"notify-subscriber-user-name"
306
static const char * const notify_events[] =
307
{ /* notify-events-supported values */
309
"job-config-changed",
316
"printer-config-changed",
318
"printer-finishings-changed",
319
"printer-media-changed",
323
"printer-state-changed",
330
static const char * const job_creation[] =
331
{ /* job-creation-attributes-supported */
334
"ipp-attribute-fidelity",
341
"multiple-document-handling",
344
"orientation-requested",
348
"printer-resolution",
351
static const char * const job_settable[] =
352
{ /* job-settable-attributes-supported */
360
"multiple-document-handling",
363
"orientation-requested",
367
"printer-resolution",
370
static const char * const pdf_versions[] =
371
{ /* pdf-versions-supported */
382
static const char * const printer_settable[] =
383
{ /* printer-settable-attributes-supported */
387
static const char * const which_jobs[] =
388
{ /* which-jobs-supported values */
402
ippDelete(CommonData);
404
CommonData = ippNew();
407
* Get the maximum spool size based on the size of the filesystem used for
408
* the RequestRoot directory. If the host OS doesn't support the statfs call
409
* or the filesystem is larger than 2TiB, always report INT_MAX.
413
if (statvfs(RequestRoot, &spoolinfo))
414
k_supported = INT_MAX;
415
else if ((spoolsize = (double)spoolinfo.f_frsize * spoolinfo.f_blocks / 1024) >
417
k_supported = INT_MAX;
419
k_supported = (int)spoolsize;
421
#elif defined(HAVE_STATFS)
422
if (statfs(RequestRoot, &spoolinfo))
423
k_supported = INT_MAX;
424
else if ((spoolsize = (double)spoolinfo.f_bsize * spoolinfo.f_blocks / 1024) >
426
k_supported = INT_MAX;
428
k_supported = (int)spoolsize;
431
k_supported = INT_MAX;
432
#endif /* HAVE_STATVFS */
435
* This list of attributes is sorted to improve performance when the
436
* client provides a requested-attributes attribute...
439
/* charset-configured */
440
ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
441
"charset-configured", NULL, "utf-8");
443
/* charset-supported */
444
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
445
"charset-supported", sizeof(charsets) / sizeof(charsets[0]),
448
/* compression-supported */
449
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
450
"compression-supported",
451
sizeof(compressions) / sizeof(compressions[0]),
454
/* copies-supported */
455
ippAddRange(CommonData, IPP_TAG_PRINTER, "copies-supported", 1, MaxCopies);
458
ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_TEXT | IPP_TAG_COPY,
459
"cups-version", NULL, CUPS_SVERSION + 6);
461
/* generated-natural-language-supported (no IPP_TAG_COPY) */
462
ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
463
"generated-natural-language-supported", NULL, DefaultLanguage);
465
/* ipp-versions-supported */
466
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
467
"ipp-versions-supported", sizeof(versions) / sizeof(versions[0]),
470
/* ippget-event-life */
471
ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
472
"ippget-event-life", 15);
474
/* job-creation-attributes-supported */
475
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
476
"job-creation-attributes-supported",
477
sizeof(job_creation) / sizeof(job_creation[0]),
480
/* job-hold-until-supported */
481
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
482
"job-hold-until-supported", sizeof(holds) / sizeof(holds[0]),
485
/* job-ids-supported */
486
ippAddBoolean(CommonData, IPP_TAG_PRINTER, "job-ids-supported", 1);
488
/* job-k-octets-supported */
489
ippAddRange(CommonData, IPP_TAG_PRINTER, "job-k-octets-supported", 0,
492
/* job-priority-supported */
493
ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
494
"job-priority-supported", 100);
496
/* job-settable-attributes-supported */
497
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
498
"job-settable-attributes-supported",
499
sizeof(job_settable) / sizeof(job_settable[0]),
502
/* job-sheets-supported */
503
if (cupsArrayCount(Banners) > 0)
506
* Setup the job-sheets-supported attribute...
509
if (Classification && !ClassifyOverride)
510
attr = ippAddString(CommonData, IPP_TAG_PRINTER,
511
IPP_TAG_NAME | IPP_TAG_COPY,
512
"job-sheets-supported", NULL, Classification);
514
attr = ippAddStrings(CommonData, IPP_TAG_PRINTER,
515
IPP_TAG_NAME | IPP_TAG_COPY,
516
"job-sheets-supported", cupsArrayCount(Banners) + 1,
520
cupsdLogMessage(CUPSD_LOG_EMERG,
521
"Unable to allocate memory for "
522
"job-sheets-supported attribute: %s!", strerror(errno));
523
else if (!Classification || ClassifyOverride)
525
cupsd_banner_t *banner; /* Current banner */
528
attr->values[0].string.text = _cupsStrAlloc("none");
530
for (i = 1, banner = (cupsd_banner_t *)cupsArrayFirst(Banners);
532
i ++, banner = (cupsd_banner_t *)cupsArrayNext(Banners))
533
attr->values[i].string.text = banner->name;
537
ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
538
"job-sheets-supported", NULL, "none");
540
/* jpeg-k-octets-supported */
541
ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-k-octets-supported", 0,
544
/* jpeg-x-dimension-supported */
545
ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-x-dimension-supported", 0,
548
/* jpeg-y-dimension-supported */
549
ippAddRange(CommonData, IPP_TAG_PRINTER, "jpeg-y-dimension-supported", 1,
552
/* media-col-supported */
553
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
554
"media-col-supported",
555
sizeof(media_col_supported) /
556
sizeof(media_col_supported[0]), NULL,
557
media_col_supported);
559
/* multiple-document-handling-supported */
560
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
561
"multiple-document-handling-supported",
562
sizeof(multiple_document_handling) /
563
sizeof(multiple_document_handling[0]), NULL,
564
multiple_document_handling);
566
/* multiple-document-jobs-supported */
567
ippAddBoolean(CommonData, IPP_TAG_PRINTER,
568
"multiple-document-jobs-supported", 1);
570
/* multiple-operation-time-out */
571
ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
572
"multiple-operation-time-out", MultipleOperationTimeout);
574
/* natural-language-configured (no IPP_TAG_COPY) */
575
ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
576
"natural-language-configured", NULL, DefaultLanguage);
578
/* notify-attributes-supported */
579
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
580
"notify-attributes-supported",
581
(int)(sizeof(notify_attrs) / sizeof(notify_attrs[0])),
584
/* notify-lease-duration-supported */
585
ippAddRange(CommonData, IPP_TAG_PRINTER,
586
"notify-lease-duration-supported", 0,
587
MaxLeaseDuration ? MaxLeaseDuration : 2147483647);
589
/* notify-max-events-supported */
590
ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
591
"notify-max-events-supported", MaxEvents);
593
/* notify-events-supported */
594
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
595
"notify-events-supported",
596
(int)(sizeof(notify_events) / sizeof(notify_events[0])),
597
NULL, notify_events);
599
/* notify-pull-method-supported */
600
ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
601
"notify-pull-method-supported", NULL, "ippget");
603
/* notify-schemes-supported */
604
snprintf(filename, sizeof(filename), "%s/notifier", ServerBin);
605
if ((dir = cupsDirOpen(filename)) != NULL)
607
notifiers = cupsArrayNew((cups_array_func_t)strcmp, NULL);
609
while ((dent = cupsDirRead(dir)) != NULL)
610
if (S_ISREG(dent->fileinfo.st_mode) &&
611
(dent->fileinfo.st_mode & S_IXOTH) != 0)
612
cupsArrayAdd(notifiers, _cupsStrAlloc(dent->filename));
614
if (cupsArrayCount(notifiers) > 0)
616
attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
617
"notify-schemes-supported",
618
cupsArrayCount(notifiers), NULL, NULL);
620
for (i = 0, notifier = (char *)cupsArrayFirst(notifiers);
622
i ++, notifier = (char *)cupsArrayNext(notifiers))
623
attr->values[i].string.text = notifier;
626
cupsArrayDelete(notifiers);
630
/* number-up-supported */
631
ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
632
"number-up-supported", sizeof(nups) / sizeof(nups[0]), nups);
634
/* operations-supported */
635
ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
636
"operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
638
/* orientation-requested-supported */
639
ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM,
640
"orientation-requested-supported", 4, orients);
642
/* page-ranges-supported */
643
ippAddBoolean(CommonData, IPP_TAG_PRINTER, "page-ranges-supported", 1);
645
/* pdf-k-octets-supported */
646
ippAddRange(CommonData, IPP_TAG_PRINTER, "pdf-k-octets-supported", 0,
649
/* pdf-versions-supported */
650
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
651
"pdf-versions-supported",
652
sizeof(pdf_versions) / sizeof(pdf_versions[0]), NULL,
655
/* pdl-override-supported */
656
ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
657
"pdl-override-supported", NULL, "attempted");
659
/* printer-op-policy-supported */
660
attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
661
"printer-op-policy-supported", cupsArrayCount(Policies),
663
for (i = 0, p = (cupsd_policy_t *)cupsArrayFirst(Policies);
665
i ++, p = (cupsd_policy_t *)cupsArrayNext(Policies))
666
attr->values[i].string.text = p->name;
668
/* printer-settable-attributes-supported */
669
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
670
"printer-settable-attributes-supported",
671
sizeof(printer_settable) / sizeof(printer_settable[0]),
672
NULL, printer_settable);
674
/* server-is-sharing-printers */
675
ippAddBoolean(CommonData, IPP_TAG_PRINTER, "server-is-sharing-printers",
676
BrowseLocalProtocols != 0 && Browsing);
678
/* which-jobs-supported */
679
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
680
"which-jobs-supported",
681
sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
686
* 'cupsdDeleteAllPrinters()' - Delete all printers from the system.
690
cupsdDeleteAllPrinters(void)
692
cupsd_printer_t *p; /* Pointer to current printer/class */
695
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
697
p = (cupsd_printer_t *)cupsArrayNext(Printers))
699
p->op_policy_ptr = DefaultPolicyPtr;
700
cupsdDeletePrinter(p, 0);
706
* 'cupsdDeletePrinter()' - Delete a printer from the system.
709
int /* O - 1 if classes affected, 0 otherwise */
711
cupsd_printer_t *p, /* I - Printer to delete */
712
int update) /* I - Update printers.conf? */
714
int i, /* Looping var */
715
changed = 0; /* Class changed? */
718
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeletePrinter(p=%p(%s), update=%d)",
722
* Save the current position in the Printers array...
725
cupsArraySave(Printers);
728
* Stop printing on this printer...
731
cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, update);
733
p->state = IPP_PRINTER_STOPPED; /* Force for browsed printers */
736
cupsdSetJobState(p->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
737
update ? "Job stopped due to printer being deleted." :
741
* Remove the printer from the list...
744
cupsdLogMessage(CUPSD_LOG_DEBUG2,
745
"cupsdDeletePrinter: Removing %s from Printers", p->name);
746
cupsArrayRemove(Printers, p);
749
* If p is the default printer, assign a different one...
752
if (p == DefaultPrinter)
753
DefaultPrinter = NULL;
756
* Remove this printer from any classes...
759
changed = cupsdDeletePrinterFromClasses(p);
762
* Deregister from any browse protocols...
765
cupsdDeregisterPrinter(p, 1);
768
* Free all memory used by the printer...
771
if (p->printers != NULL)
774
delete_printer_filters(p);
776
for (i = 0; i < p->num_reasons; i ++)
777
_cupsStrFree(p->reasons[i]);
780
ippDelete(p->ppd_attrs);
782
mimeDeleteType(MimeDatabase, p->filetype);
783
mimeDeleteType(MimeDatabase, p->prefiltertype);
785
cupsdFreeStrings(&(p->users));
788
cupsdClearString(&p->uri);
789
cupsdClearString(&p->hostname);
790
cupsdClearString(&p->name);
791
cupsdClearString(&p->location);
792
cupsdClearString(&p->make_model);
793
cupsdClearString(&p->info);
794
cupsdClearString(&p->job_sheets[0]);
795
cupsdClearString(&p->job_sheets[1]);
796
cupsdClearString(&p->device_uri);
797
cupsdClearString(&p->ppd_timestamp);
798
cupsdClearString(&p->sanitized_device_uri);
799
cupsdClearString(&p->port_monitor);
800
cupsdClearString(&p->op_policy);
801
cupsdClearString(&p->error_policy);
803
cupsdClearString(&p->alert);
804
cupsdClearString(&p->alert_description);
806
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
807
cupsdClearString(&p->pdl);
808
cupsdClearString(&p->reg_name);
809
#endif /* HAVE_DNSSD || HAVE_AVAHI */
811
cupsArrayDelete(p->filetypes);
813
cupsFreeOptions(p->num_options, p->options);
818
* Restore the previous position in the Printers array...
821
cupsArrayRestore(Printers);
828
* 'cupsdFindDest()' - Find a destination in the list.
831
cupsd_printer_t * /* O - Destination in list */
832
cupsdFindDest(const char *name) /* I - Name of printer or class to find */
834
cupsd_printer_t key; /* Search key */
837
key.name = (char *)name;
838
return ((cupsd_printer_t *)cupsArrayFind(Printers, &key));
843
* 'cupsdFindPrinter()' - Find a printer in the list.
846
cupsd_printer_t * /* O - Printer in list */
847
cupsdFindPrinter(const char *name) /* I - Name of printer to find */
849
cupsd_printer_t *p; /* Printer in list */
852
if ((p = cupsdFindDest(name)) != NULL && (p->type & CUPS_PRINTER_CLASS))
860
* 'cupsdLoadAllPrinters()' - Load printers from the printers.conf file.
864
cupsdLoadAllPrinters(void)
866
int i; /* Looping var */
867
cups_file_t *fp; /* printers.conf file */
868
int linenum; /* Current line number */
869
char line[4096], /* Line from file */
870
*value, /* Pointer to value */
871
*valueptr; /* Pointer into value */
872
cupsd_printer_t *p; /* Current printer */
876
* Open the printers.conf file...
879
snprintf(line, sizeof(line), "%s/printers.conf", ServerRoot);
880
if ((fp = cupsdOpenConfFile(line)) == NULL)
884
* Read printer configurations until we hit EOF...
890
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
893
* Decode the directive...
896
if (!_cups_strcasecmp(line, "<Printer") ||
897
!_cups_strcasecmp(line, "<DefaultPrinter"))
900
* <Printer name> or <DefaultPrinter name>
903
if (p == NULL && value)
906
* Add the printer and a base file type...
909
cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading printer %s...", value);
911
p = cupsdAddPrinter(value);
913
p->state = IPP_PRINTER_IDLE;
916
* Set the default printer as needed...
919
if (!_cups_strcasecmp(line, "<DefaultPrinter"))
923
cupsdLogMessage(CUPSD_LOG_ERROR,
924
"Syntax error on line %d of printers.conf.", linenum);
926
else if (!_cups_strcasecmp(line, "</Printer>"))
931
* Close out the current printer...
934
cupsdSetPrinterAttrs(p);
936
if (strncmp(p->device_uri, "file:", 5) &&
937
p->state != IPP_PRINTER_STOPPED)
940
* See if the backend exists...
943
snprintf(line, sizeof(line), "%s/backend/%s", ServerBin,
946
if ((valueptr = strchr(line + strlen(ServerBin), ':')) != NULL)
947
*valueptr = '\0'; /* Chop everything but URI scheme */
952
* Backend does not exist, stop printer...
955
p->state = IPP_PRINTER_STOPPED;
956
snprintf(p->state_message, sizeof(p->state_message),
957
"Backend %s does not exist!", line);
964
cupsdLogMessage(CUPSD_LOG_ERROR,
965
"Syntax error on line %d of printers.conf.", linenum);
969
cupsdLogMessage(CUPSD_LOG_ERROR,
970
"Syntax error on line %d of printers.conf.", linenum);
972
else if (!_cups_strcasecmp(line, "UUID"))
974
if (value && !strncmp(value, "urn:uuid:", 9))
975
cupsdSetString(&(p->uuid), value);
977
cupsdLogMessage(CUPSD_LOG_ERROR,
978
"Bad UUID on line %d of printers.conf.", linenum);
980
else if (!_cups_strcasecmp(line, "AuthInfoRequired"))
982
if (!cupsdSetAuthInfoRequired(p, value, NULL))
983
cupsdLogMessage(CUPSD_LOG_ERROR,
984
"Bad AuthInfoRequired on line %d of printers.conf.",
987
else if (!_cups_strcasecmp(line, "Info"))
990
cupsdSetString(&p->info, value);
992
else if (!_cups_strcasecmp(line, "MakeModel"))
995
cupsdSetString(&p->make_model, value);
997
else if (!_cups_strcasecmp(line, "PPDTimeStamp"))
1000
cupsdSetString(&p->ppd_timestamp, value);
1002
else if (!_cups_strcasecmp(line, "Location"))
1005
cupsdSetString(&p->location, value);
1007
else if (!_cups_strcasecmp(line, "DeviceURI"))
1010
cupsdSetDeviceURI(p, value);
1012
cupsdLogMessage(CUPSD_LOG_ERROR,
1013
"Syntax error on line %d of printers.conf.", linenum);
1015
else if (!_cups_strcasecmp(line, "Option") && value)
1021
for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
1024
cupsdLogMessage(CUPSD_LOG_ERROR,
1025
"Syntax error on line %d of printers.conf.", linenum);
1028
for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
1030
p->num_options = cupsAddOption(value, valueptr, p->num_options,
1034
else if (!_cups_strcasecmp(line, "PortMonitor"))
1036
if (value && strcmp(value, "none"))
1037
cupsdSetString(&p->port_monitor, value);
1039
cupsdClearString(&p->port_monitor);
1041
cupsdLogMessage(CUPSD_LOG_ERROR,
1042
"Syntax error on line %d of printers.conf.", linenum);
1044
else if (!_cups_strcasecmp(line, "Reason"))
1047
strcmp(value, "connecting-to-device") &&
1048
strcmp(value, "cups-insecure-filter-warning") &&
1049
strcmp(value, "cups-missing-filter-warning"))
1051
for (i = 0 ; i < p->num_reasons; i ++)
1052
if (!strcmp(value, p->reasons[i]))
1055
if (i >= p->num_reasons &&
1056
p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
1058
p->reasons[p->num_reasons] = _cupsStrAlloc(value);
1063
cupsdLogMessage(CUPSD_LOG_ERROR,
1064
"Syntax error on line %d of printers.conf.", linenum);
1066
else if (!_cups_strcasecmp(line, "State"))
1069
* Set the initial queue state...
1072
if (value && !_cups_strcasecmp(value, "idle"))
1073
p->state = IPP_PRINTER_IDLE;
1074
else if (value && !_cups_strcasecmp(value, "stopped"))
1076
p->state = IPP_PRINTER_STOPPED;
1078
for (i = 0 ; i < p->num_reasons; i ++)
1079
if (!strcmp("paused", p->reasons[i]))
1082
if (i >= p->num_reasons &&
1083
p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
1085
p->reasons[p->num_reasons] = _cupsStrAlloc("paused");
1090
cupsdLogMessage(CUPSD_LOG_ERROR,
1091
"Syntax error on line %d of printers.conf.", linenum);
1093
else if (!_cups_strcasecmp(line, "StateMessage"))
1096
* Set the initial queue state message...
1100
strlcpy(p->state_message, value, sizeof(p->state_message));
1102
else if (!_cups_strcasecmp(line, "StateTime"))
1105
* Set the state time...
1109
p->state_time = atoi(value);
1111
else if (!_cups_strcasecmp(line, "Accepting"))
1114
* Set the initial accepting state...
1118
(!_cups_strcasecmp(value, "yes") ||
1119
!_cups_strcasecmp(value, "on") ||
1120
!_cups_strcasecmp(value, "true")))
1123
(!_cups_strcasecmp(value, "no") ||
1124
!_cups_strcasecmp(value, "off") ||
1125
!_cups_strcasecmp(value, "false")))
1128
cupsdLogMessage(CUPSD_LOG_ERROR,
1129
"Syntax error on line %d of printers.conf.", linenum);
1131
else if (!_cups_strcasecmp(line, "Type"))
1134
p->type = atoi(value);
1136
cupsdLogMessage(CUPSD_LOG_ERROR,
1137
"Syntax error on line %d of printers.conf.", linenum);
1139
else if (!_cups_strcasecmp(line, "Shared"))
1142
* Set the initial shared state...
1146
(!_cups_strcasecmp(value, "yes") ||
1147
!_cups_strcasecmp(value, "on") ||
1148
!_cups_strcasecmp(value, "true")))
1151
(!_cups_strcasecmp(value, "no") ||
1152
!_cups_strcasecmp(value, "off") ||
1153
!_cups_strcasecmp(value, "false")))
1156
cupsdLogMessage(CUPSD_LOG_ERROR,
1157
"Syntax error on line %d of printers.conf.", linenum);
1159
else if (!_cups_strcasecmp(line, "ColorManaged"))
1162
* Set the initial color-managed state...
1166
(!_cups_strcasecmp(value, "yes") ||
1167
!_cups_strcasecmp(value, "on") ||
1168
!_cups_strcasecmp(value, "true")))
1169
p->color_managed = 1;
1171
(!_cups_strcasecmp(value, "no") ||
1172
!_cups_strcasecmp(value, "off") ||
1173
!_cups_strcasecmp(value, "false")))
1174
p->color_managed = 0;
1176
cupsdLogMessage(CUPSD_LOG_ERROR,
1177
"Syntax error on line %d of printers.conf.", linenum);
1179
else if (!_cups_strcasecmp(line, "JobSheets"))
1182
* Set the initial job sheets...
1187
for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
1192
cupsdSetString(&p->job_sheets[0], value);
1194
while (isspace(*valueptr & 255))
1199
for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++);
1204
cupsdSetString(&p->job_sheets[1], value);
1208
cupsdLogMessage(CUPSD_LOG_ERROR,
1209
"Syntax error on line %d of printers.conf.", linenum);
1211
else if (!_cups_strcasecmp(line, "AllowUser"))
1216
cupsdAddString(&(p->users), value);
1219
cupsdLogMessage(CUPSD_LOG_ERROR,
1220
"Syntax error on line %d of printers.conf.", linenum);
1222
else if (!_cups_strcasecmp(line, "DenyUser"))
1227
cupsdAddString(&(p->users), value);
1230
cupsdLogMessage(CUPSD_LOG_ERROR,
1231
"Syntax error on line %d of printers.conf.", linenum);
1233
else if (!_cups_strcasecmp(line, "QuotaPeriod"))
1236
p->quota_period = atoi(value);
1238
cupsdLogMessage(CUPSD_LOG_ERROR,
1239
"Syntax error on line %d of printers.conf.", linenum);
1241
else if (!_cups_strcasecmp(line, "PageLimit"))
1244
p->page_limit = atoi(value);
1246
cupsdLogMessage(CUPSD_LOG_ERROR,
1247
"Syntax error on line %d of printers.conf.", linenum);
1249
else if (!_cups_strcasecmp(line, "KLimit"))
1252
p->k_limit = atoi(value);
1254
cupsdLogMessage(CUPSD_LOG_ERROR,
1255
"Syntax error on line %d of printers.conf.", linenum);
1257
else if (!_cups_strcasecmp(line, "OpPolicy"))
1261
cupsd_policy_t *pol; /* Policy */
1264
if ((pol = cupsdFindPolicy(value)) != NULL)
1266
cupsdSetString(&p->op_policy, value);
1267
p->op_policy_ptr = pol;
1270
cupsdLogMessage(CUPSD_LOG_ERROR,
1271
"Bad policy \"%s\" on line %d of printers.conf",
1275
cupsdLogMessage(CUPSD_LOG_ERROR,
1276
"Syntax error on line %d of printers.conf.", linenum);
1278
else if (!_cups_strcasecmp(line, "ErrorPolicy"))
1281
cupsdSetString(&p->error_policy, value);
1283
cupsdLogMessage(CUPSD_LOG_ERROR,
1284
"Syntax error on line %d of printers.conf.", linenum);
1286
else if (!_cups_strcasecmp(line, "Attribute") && value)
1288
for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
1291
cupsdLogMessage(CUPSD_LOG_ERROR,
1292
"Syntax error on line %d of printers.conf.", linenum);
1295
for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
1298
cupsdSetPrinterAttrs(p);
1300
if (!strcmp(value, "marker-change-time"))
1301
p->marker_time = atoi(valueptr);
1303
cupsdSetPrinterAttr(p, value, valueptr);
1306
else if (_cups_strcasecmp(line, "Filter") &&
1307
_cups_strcasecmp(line, "Prefilter") &&
1308
_cups_strcasecmp(line, "Product"))
1311
* Something else we don't understand (and that wasn't used in a prior
1312
* release of CUPS...
1315
cupsdLogMessage(CUPSD_LOG_ERROR,
1316
"Unknown configuration directive %s on line %d of "
1317
"printers.conf.", line, linenum);
1326
* 'cupsdRenamePrinter()' - Rename a printer.
1331
cupsd_printer_t *p, /* I - Printer */
1332
const char *name) /* I - New name */
1335
* Remove the printer from the array(s) first...
1338
cupsdLogMessage(CUPSD_LOG_DEBUG2,
1339
"cupsdRenamePrinter: Removing %s from Printers", p->name);
1340
cupsArrayRemove(Printers, p);
1343
* Rename the printer type...
1346
mimeDeleteType(MimeDatabase, p->filetype);
1347
p->filetype = mimeAddType(MimeDatabase, "printer", name);
1349
if (p->prefiltertype)
1351
mimeDeleteType(MimeDatabase, p->prefiltertype);
1352
p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", name);
1356
* Rename the printer...
1359
cupsdSetString(&p->name, name);
1362
* Reset printer attributes...
1365
cupsdSetPrinterAttrs(p);
1368
* Add the printer back to the printer array(s)...
1371
cupsdLogMessage(CUPSD_LOG_DEBUG2,
1372
"cupsdRenamePrinter: Adding %s to Printers", p->name);
1373
cupsArrayAdd(Printers, p);
1378
* 'cupsdSaveAllPrinters()' - Save all printer definitions to the printers.conf
1383
cupsdSaveAllPrinters(void)
1385
int i; /* Looping var */
1386
cups_file_t *fp; /* printers.conf file */
1387
char filename[1024], /* printers.conf filename */
1388
temp[1024], /* Temporary string */
1389
value[2048], /* Value string */
1390
*ptr, /* Pointer into value */
1391
*name; /* Current user/group name */
1392
cupsd_printer_t *printer; /* Current printer class */
1393
time_t curtime; /* Current time */
1394
struct tm *curdate; /* Current date */
1395
cups_option_t *option; /* Current option */
1396
ipp_attribute_t *marker; /* Current marker attribute */
1400
* Create the printers.conf file...
1403
snprintf(filename, sizeof(filename), "%s/printers.conf", ServerRoot);
1405
if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm & 0600)) == NULL)
1408
cupsdLogMessage(CUPSD_LOG_INFO, "Saving printers.conf...");
1411
* Write a small header to the file...
1414
curtime = time(NULL);
1415
curdate = localtime(&curtime);
1416
strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1418
cupsFilePuts(fp, "# Printer configuration file for " CUPS_SVERSION "\n");
1419
cupsFilePrintf(fp, "# Written by cupsd\n");
1420
cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
1423
* Write each local printer known to the system...
1426
for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
1428
printer = (cupsd_printer_t *)cupsArrayNext(Printers))
1431
* Skip printer classes...
1434
if (printer->type & CUPS_PRINTER_CLASS)
1438
* Write printers as needed...
1441
if (printer == DefaultPrinter)
1442
cupsFilePrintf(fp, "<DefaultPrinter %s>\n", printer->name);
1444
cupsFilePrintf(fp, "<Printer %s>\n", printer->name);
1446
cupsFilePrintf(fp, "UUID %s\n", printer->uuid);
1448
if (printer->num_auth_info_required > 0)
1450
switch (printer->num_auth_info_required)
1453
strlcpy(value, printer->auth_info_required[0], sizeof(value));
1457
snprintf(value, sizeof(value), "%s,%s",
1458
printer->auth_info_required[0],
1459
printer->auth_info_required[1]);
1464
snprintf(value, sizeof(value), "%s,%s,%s",
1465
printer->auth_info_required[0],
1466
printer->auth_info_required[1],
1467
printer->auth_info_required[2]);
1471
cupsFilePutConf(fp, "AuthInfoRequired", value);
1475
cupsFilePutConf(fp, "Info", printer->info);
1477
if (printer->location)
1478
cupsFilePutConf(fp, "Location", printer->location);
1480
if (printer->make_model)
1481
cupsFilePutConf(fp, "MakeModel", printer->make_model);
1483
cupsFilePutConf(fp, "DeviceURI", printer->device_uri);
1485
if (printer->ppd_timestamp)
1486
cupsFilePutConf(fp, "PPDTimeStamp", printer->ppd_timestamp);
1488
if (printer->port_monitor)
1489
cupsFilePutConf(fp, "PortMonitor", printer->port_monitor);
1491
if (printer->state == IPP_PRINTER_STOPPED)
1493
cupsFilePuts(fp, "State Stopped\n");
1495
if (printer->state_message)
1496
cupsFilePutConf(fp, "StateMessage", printer->state_message);
1499
cupsFilePuts(fp, "State Idle\n");
1501
cupsFilePrintf(fp, "StateTime %d\n", (int)printer->state_time);
1503
for (i = 0; i < printer->num_reasons; i ++)
1504
if (strcmp(printer->reasons[i], "connecting-to-device") &&
1505
strcmp(printer->reasons[i], "cups-insecure-filter-warning") &&
1506
strcmp(printer->reasons[i], "cups-missing-filter-warning"))
1507
cupsFilePutConf(fp, "Reason", printer->reasons[i]);
1509
cupsFilePrintf(fp, "Type %d\n", printer->type);
1511
if (printer->accepting)
1512
cupsFilePuts(fp, "Accepting Yes\n");
1514
cupsFilePuts(fp, "Accepting No\n");
1516
if (printer->shared)
1517
cupsFilePuts(fp, "Shared Yes\n");
1519
cupsFilePuts(fp, "Shared No\n");
1521
if (printer->color_managed)
1522
cupsFilePuts(fp, "ColorManaged Yes\n");
1524
cupsFilePuts(fp, "ColorManaged No\n");
1526
snprintf(value, sizeof(value), "%s %s", printer->job_sheets[0],
1527
printer->job_sheets[1]);
1528
cupsFilePutConf(fp, "JobSheets", value);
1530
cupsFilePrintf(fp, "QuotaPeriod %d\n", printer->quota_period);
1531
cupsFilePrintf(fp, "PageLimit %d\n", printer->page_limit);
1532
cupsFilePrintf(fp, "KLimit %d\n", printer->k_limit);
1534
for (name = (char *)cupsArrayFirst(printer->users);
1536
name = (char *)cupsArrayNext(printer->users))
1537
cupsFilePutConf(fp, printer->deny_users ? "DenyUser" : "AllowUser", name);
1539
if (printer->op_policy)
1540
cupsFilePutConf(fp, "OpPolicy", printer->op_policy);
1541
if (printer->error_policy)
1542
cupsFilePutConf(fp, "ErrorPolicy", printer->error_policy);
1544
for (i = printer->num_options, option = printer->options;
1548
snprintf(value, sizeof(value), "%s %s", option->name, option->value);
1549
cupsFilePutConf(fp, "Option", value);
1552
if ((marker = ippFindAttribute(printer->attrs, "marker-colors",
1553
IPP_TAG_NAME)) != NULL)
1555
snprintf(value, sizeof(value), "%s ", marker->name);
1557
for (i = 0, ptr = value + strlen(value);
1558
i < marker->num_values && ptr < (value + sizeof(value) - 1);
1564
strlcpy(ptr, marker->values[i].string.text,
1565
value + sizeof(value) - ptr);
1570
cupsFilePutConf(fp, "Attribute", value);
1573
if ((marker = ippFindAttribute(printer->attrs, "marker-levels",
1574
IPP_TAG_INTEGER)) != NULL)
1576
cupsFilePrintf(fp, "Attribute %s %d", marker->name,
1577
marker->values[0].integer);
1578
for (i = 1; i < marker->num_values; i ++)
1579
cupsFilePrintf(fp, ",%d", marker->values[i].integer);
1580
cupsFilePuts(fp, "\n");
1583
if ((marker = ippFindAttribute(printer->attrs, "marker-low-levels",
1584
IPP_TAG_INTEGER)) != NULL)
1586
cupsFilePrintf(fp, "Attribute %s %d", marker->name,
1587
marker->values[0].integer);
1588
for (i = 1; i < marker->num_values; i ++)
1589
cupsFilePrintf(fp, ",%d", marker->values[i].integer);
1590
cupsFilePuts(fp, "\n");
1593
if ((marker = ippFindAttribute(printer->attrs, "marker-high-levels",
1594
IPP_TAG_INTEGER)) != NULL)
1596
cupsFilePrintf(fp, "Attribute %s %d", marker->name,
1597
marker->values[0].integer);
1598
for (i = 1; i < marker->num_values; i ++)
1599
cupsFilePrintf(fp, ",%d", marker->values[i].integer);
1600
cupsFilePuts(fp, "\n");
1603
if ((marker = ippFindAttribute(printer->attrs, "marker-message",
1604
IPP_TAG_TEXT)) != NULL)
1606
snprintf(value, sizeof(value), "%s %s", marker->name,
1607
marker->values[0].string.text);
1609
cupsFilePutConf(fp, "Attribute", value);
1612
if ((marker = ippFindAttribute(printer->attrs, "marker-names",
1613
IPP_TAG_NAME)) != NULL)
1615
snprintf(value, sizeof(value), "%s ", marker->name);
1617
for (i = 0, ptr = value + strlen(value);
1618
i < marker->num_values && ptr < (value + sizeof(value) - 1);
1624
strlcpy(ptr, marker->values[i].string.text,
1625
value + sizeof(value) - ptr);
1630
cupsFilePutConf(fp, "Attribute", value);
1633
if ((marker = ippFindAttribute(printer->attrs, "marker-types",
1634
IPP_TAG_KEYWORD)) != NULL)
1636
snprintf(value, sizeof(value), "%s ", marker->name);
1638
for (i = 0, ptr = value + strlen(value);
1639
i < marker->num_values && ptr < (value + sizeof(value) - 1);
1645
strlcpy(ptr, marker->values[i].string.text,
1646
value + sizeof(value) - ptr);
1651
cupsFilePutConf(fp, "Attribute", value);
1654
if (printer->marker_time)
1655
cupsFilePrintf(fp, "Attribute marker-change-time %ld\n",
1656
(long)printer->marker_time);
1658
cupsFilePuts(fp, "</Printer>\n");
1661
cupsdCloseCreatedConfFile(fp, filename);
1666
* 'cupsdSetAuthInfoRequired()' - Set the required authentication info.
1669
int /* O - 1 if value OK, 0 otherwise */
1670
cupsdSetAuthInfoRequired(
1671
cupsd_printer_t *p, /* I - Printer */
1672
const char *values, /* I - Plain text value (or NULL) */
1673
ipp_attribute_t *attr) /* I - IPP attribute value (or NULL) */
1675
int i; /* Looping var */
1678
p->num_auth_info_required = 0;
1681
* Do we have a plain text value?
1687
* Yes, grab the keywords...
1690
const char *end; /* End of current value */
1693
while (*values && p->num_auth_info_required < 4)
1695
if ((end = strchr(values, ',')) == NULL)
1696
end = values + strlen(values);
1698
if ((end - values) == 4 && !strncmp(values, "none", 4))
1700
if (p->num_auth_info_required != 0 || *end)
1703
p->auth_info_required[p->num_auth_info_required] = "none";
1704
p->num_auth_info_required ++;
1708
else if ((end - values) == 9 && !strncmp(values, "negotiate", 9))
1710
if (p->num_auth_info_required != 0 || *end)
1713
p->auth_info_required[p->num_auth_info_required] = "negotiate";
1714
p->num_auth_info_required ++;
1717
* Don't allow sharing of queues that require Kerberos authentication.
1722
cupsdDeregisterPrinter(p, 1);
1726
else if ((end - values) == 6 && !strncmp(values, "domain", 6))
1728
p->auth_info_required[p->num_auth_info_required] = "domain";
1729
p->num_auth_info_required ++;
1731
else if ((end - values) == 8 && !strncmp(values, "password", 8))
1733
p->auth_info_required[p->num_auth_info_required] = "password";
1734
p->num_auth_info_required ++;
1736
else if ((end - values) == 8 && !strncmp(values, "username", 8))
1738
p->auth_info_required[p->num_auth_info_required] = "username";
1739
p->num_auth_info_required ++;
1744
values = (*end) ? end + 1 : end;
1747
if (p->num_auth_info_required == 0)
1749
p->auth_info_required[0] = "none";
1750
p->num_auth_info_required = 1;
1754
* Update the printer-type value as needed...
1757
if (p->num_auth_info_required > 1 ||
1758
strcmp(p->auth_info_required[0], "none"))
1759
p->type |= CUPS_PRINTER_AUTHENTICATED;
1761
p->type &= ~CUPS_PRINTER_AUTHENTICATED;
1767
* Grab values from an attribute instead...
1770
if (!attr || attr->num_values > 4)
1773
for (i = 0; i < attr->num_values; i ++)
1775
if (!strcmp(attr->values[i].string.text, "none"))
1777
if (p->num_auth_info_required != 0 || attr->num_values != 1)
1780
p->auth_info_required[p->num_auth_info_required] = "none";
1781
p->num_auth_info_required ++;
1785
else if (!strcmp(attr->values[i].string.text, "negotiate"))
1787
if (p->num_auth_info_required != 0 || attr->num_values != 1)
1790
p->auth_info_required[p->num_auth_info_required] = "negotiate";
1791
p->num_auth_info_required ++;
1794
* Don't allow sharing of queues that require Kerberos authentication.
1799
cupsdDeregisterPrinter(p, 1);
1805
else if (!strcmp(attr->values[i].string.text, "domain"))
1807
p->auth_info_required[p->num_auth_info_required] = "domain";
1808
p->num_auth_info_required ++;
1810
else if (!strcmp(attr->values[i].string.text, "password"))
1812
p->auth_info_required[p->num_auth_info_required] = "password";
1813
p->num_auth_info_required ++;
1815
else if (!strcmp(attr->values[i].string.text, "username"))
1817
p->auth_info_required[p->num_auth_info_required] = "username";
1818
p->num_auth_info_required ++;
1829
* 'cupsdSetDeviceURI()' - Set the device URI for a printer.
1833
cupsdSetDeviceURI(cupsd_printer_t *p, /* I - Printer */
1834
const char *uri) /* I - Device URI */
1836
char buffer[1024], /* URI buffer */
1837
*start, /* Start of data after scheme */
1838
*slash, /* First slash after scheme:// */
1839
*ptr; /* Pointer into user@host:port part */
1843
* Set the full device URI..
1846
cupsdSetString(&(p->device_uri), uri);
1849
* Copy the device URI to a temporary buffer so we can sanitize any auth
1853
strlcpy(buffer, uri, sizeof(buffer));
1856
* Find the end of the scheme:// part...
1859
if ((ptr = strchr(buffer, ':')) != NULL)
1861
for (start = ptr + 1; *start; start ++)
1866
* Find the next slash (/) in the URI...
1869
if ((slash = strchr(start, '/')) == NULL)
1870
slash = start + strlen(start); /* No slash, point to the end */
1873
* Check for an @ sign before the slash...
1876
if ((ptr = strchr(start, '@')) != NULL && ptr < slash)
1879
* Found an @ sign and it is before the resource part, so we have
1880
* an authentication string. Copy the remaining URI over the
1881
* authentication string...
1884
_cups_strcpy(start, ptr + 1);
1889
* Save the sanitized URI...
1892
cupsdSetString(&(p->sanitized_device_uri), buffer);
1897
* 'cupsdSetPrinterAttr()' - Set a printer attribute.
1901
cupsdSetPrinterAttr(
1902
cupsd_printer_t *p, /* I - Printer */
1903
const char *name, /* I - Attribute name */
1904
char *value) /* I - Attribute value string */
1906
ipp_attribute_t *attr; /* Attribute */
1907
int i, /* Looping var */
1908
count; /* Number of values */
1909
char *ptr, /* Pointer into value */
1910
*start, /* Start of value */
1911
quote; /* Quote character */
1912
ipp_tag_t value_tag; /* Value tag for this attribute */
1916
* Don't allow empty values...
1919
if (!*value && strcmp(name, "marker-message"))
1921
cupsdLogMessage(CUPSD_LOG_ERROR, "Ignoring empty \"%s\" attribute", name);
1926
* Count the number of values...
1929
for (count = 1, quote = '\0', ptr = value;
1937
else if (*ptr == '\\' && ptr[1])
1939
else if (*ptr == '\'' || *ptr == '\"')
1941
else if (*ptr == ',')
1946
* Then add or update the attribute as needed...
1949
if (!strcmp(name, "marker-levels") || !strcmp(name, "marker-low-levels") ||
1950
!strcmp(name, "marker-high-levels"))
1956
if ((attr = ippFindAttribute(p->attrs, name, IPP_TAG_INTEGER)) != NULL &&
1957
attr->num_values < count)
1959
ippDeleteAttribute(p->attrs, attr);
1964
attr->num_values = count;
1966
attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, name,
1971
cupsdLogMessage(CUPSD_LOG_ERROR,
1972
"Unable to allocate memory for printer attribute "
1973
"(%d values)", count);
1977
for (i = 0; i < count; i ++)
1979
if ((ptr = strchr(value, ',')) != NULL)
1982
attr->values[i].integer = strtol(value, NULL, 10);
1991
* Name or keyword values...
1994
if (!strcmp(name, "marker-types"))
1995
value_tag = IPP_TAG_KEYWORD;
1996
else if (!strcmp(name, "marker-message"))
1997
value_tag = IPP_TAG_TEXT;
1999
value_tag = IPP_TAG_NAME;
2001
if ((attr = ippFindAttribute(p->attrs, name, value_tag)) != NULL &&
2002
attr->num_values < count)
2004
ippDeleteAttribute(p->attrs, attr);
2010
for (i = 0; i < attr->num_values; i ++)
2011
_cupsStrFree(attr->values[i].string.text);
2013
attr->num_values = count;
2016
attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, value_tag, name,
2021
cupsdLogMessage(CUPSD_LOG_ERROR,
2022
"Unable to allocate memory for printer attribute "
2023
"(%d values)", count);
2027
for (i = 0, quote = '\0', ptr = value; i < count; i ++)
2029
for (start = ptr; *ptr; ptr ++)
2032
*ptr = quote = '\0';
2035
else if (*ptr == '\\' && ptr[1])
2036
_cups_strcpy(ptr, ptr + 1);
2037
else if (*ptr == '\'' || *ptr == '\"')
2044
_cups_strcpy(ptr, ptr + 1);
2046
else if (*ptr == ',')
2053
attr->values[i].string.text = _cupsStrAlloc(start);
2060
* 'cupsdSetPrinterAttrs()' - Set printer attributes based upon the PPD file.
2064
cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
2066
int i; /* Looping var */
2067
char resource[HTTP_MAX_URI]; /* Resource portion of URI */
2068
cupsd_location_t *auth; /* Pointer to authentication element */
2069
const char *auth_supported; /* Authentication supported */
2070
ipp_t *oldattrs; /* Old printer attributes */
2071
ipp_attribute_t *attr; /* Attribute data */
2072
char *name, /* Current user/group name */
2073
*filter; /* Current filter */
2076
DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name,
2080
* Make sure that we have the common attributes defined...
2084
cupsdCreateCommonData();
2087
* Clear out old filters, if any...
2090
delete_printer_filters(p);
2093
* Figure out the authentication that is required for the printer.
2096
auth_supported = "requesting-user-name";
2098
if (p->type & CUPS_PRINTER_CLASS)
2099
snprintf(resource, sizeof(resource), "/classes/%s", p->name);
2101
snprintf(resource, sizeof(resource), "/printers/%s", p->name);
2103
if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
2104
auth->type == CUPSD_AUTH_NONE)
2105
auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
2109
int auth_type; /* Authentication type */
2112
if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT)
2113
auth_type = cupsdDefaultAuthType();
2115
if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
2116
auth_supported = "basic";
2117
else if (auth_type == CUPSD_AUTH_DIGEST)
2118
auth_supported = "digest";
2120
else if (auth_type == CUPSD_AUTH_NEGOTIATE)
2121
auth_supported = "negotiate";
2122
#endif /* HAVE_GSSAPI */
2124
if (auth_type != CUPSD_AUTH_NONE)
2125
p->type |= CUPS_PRINTER_AUTHENTICATED;
2127
p->type &= ~CUPS_PRINTER_AUTHENTICATED;
2130
p->type &= ~CUPS_PRINTER_AUTHENTICATED;
2133
* Create the required IPP attributes for a printer...
2136
oldattrs = p->attrs;
2137
p->attrs = ippNew();
2139
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2140
"uri-authentication-supported", NULL, auth_supported);
2141
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2142
"uri-security-supported", NULL, "none");
2143
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL,
2145
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
2146
NULL, p->location ? p->location : "");
2147
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
2148
NULL, p->info ? p->info : "");
2149
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL,
2151
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2152
"ppd-timestamp", NULL, p->ppd_timestamp ? p->ppd_timestamp : "*");
2154
if (cupsArrayCount(p->users) > 0)
2157
attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2158
"requesting-user-name-denied",
2159
cupsArrayCount(p->users), NULL, NULL);
2161
attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2162
"requesting-user-name-allowed",
2163
cupsArrayCount(p->users), NULL, NULL);
2165
for (i = 0, name = (char *)cupsArrayFirst(p->users);
2167
i ++, name = (char *)cupsArrayNext(p->users))
2168
attr->values[i].string.text = _cupsStrAlloc(name);
2171
ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2172
"job-quota-period", p->quota_period);
2173
ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2174
"job-k-limit", p->k_limit);
2175
ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2176
"job-page-limit", p->page_limit);
2177
if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none"))
2178
ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2179
"auth-info-required", p->num_auth_info_required, NULL,
2180
p->auth_info_required);
2182
if (cupsArrayCount(Banners) > 0)
2185
* Setup the job-sheets-default attribute...
2188
attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2189
"job-sheets-default", 2, NULL, NULL);
2193
attr->values[0].string.text = _cupsStrAlloc(Classification ?
2194
Classification : p->job_sheets[0]);
2195
attr->values[1].string.text = _cupsStrAlloc(Classification ?
2196
Classification : p->job_sheets[1]);
2204
* Assign additional attributes depending on whether this is a printer
2208
if (p->type & CUPS_PRINTER_CLASS)
2211
p->type &= ~CUPS_PRINTER_OPTIONS;
2214
* Add class-specific attributes...
2217
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2218
"printer-make-and-model", NULL, "Local Printer Class");
2219
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
2220
"file:///dev/null");
2222
if (p->num_printers > 0)
2225
* Add a list of member names; URIs are added in copy_printer_attrs...
2228
attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2229
"member-names", p->num_printers, NULL, NULL);
2230
p->type |= CUPS_PRINTER_OPTIONS;
2232
for (i = 0; i < p->num_printers; i ++)
2235
attr->values[i].string.text = _cupsStrAlloc(p->printers[i]->name);
2237
p->type &= ~CUPS_PRINTER_OPTIONS | p->printers[i]->type;
2244
* Add printer-specific attributes...
2247
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
2248
p->sanitized_device_uri);
2251
* Assign additional attributes from the PPD file (if any)...
2257
* Add filters for printer...
2260
cupsdSetPrinterReasons(p, "-cups-missing-filter-warning,"
2261
"cups-insecure-filter-warning");
2263
if (p->pc && p->pc->filters)
2265
for (filter = (char *)cupsArrayFirst(p->pc->filters);
2267
filter = (char *)cupsArrayNext(p->pc->filters))
2268
add_printer_filter(p, p->filetype, filter);
2270
else if (!(p->type & CUPS_PRINTER_REMOTE))
2272
char interface[1024]; /* Interface script */
2275
snprintf(interface, sizeof(interface), "%s/interfaces/%s", ServerRoot,
2277
if (!access(interface, X_OK))
2280
* Yes, we have a System V style interface script; use it!
2283
snprintf(interface, sizeof(interface), "*/* 0 %s/interfaces/%s",
2284
ServerRoot, p->name);
2285
add_printer_filter(p, p->filetype, interface);
2290
* Add a filter from application/vnd.cups-raw to printer/name to
2291
* handle "raw" printing by users.
2294
add_printer_filter(p, p->filetype, "application/vnd.cups-raw 0 -");
2297
* Add a PostScript filter, since this is still possibly PS printer.
2300
add_printer_filter(p, p->filetype,
2301
"application/vnd.cups-postscript 0 -");
2305
if (p->pc && p->pc->prefilters)
2307
if (!p->prefiltertype)
2308
p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", p->name);
2310
for (filter = (char *)cupsArrayFirst(p->pc->prefilters);
2312
filter = (char *)cupsArrayNext(p->pc->prefilters))
2313
add_printer_filter(p, p->prefiltertype, filter);
2318
* Copy marker attributes as needed...
2323
ipp_attribute_t *oldattr; /* Old attribute */
2326
if ((oldattr = ippFindAttribute(oldattrs, "marker-colors",
2327
IPP_TAG_NAME)) != NULL)
2329
if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2330
"marker-colors", oldattr->num_values, NULL,
2333
for (i = 0; i < oldattr->num_values; i ++)
2334
attr->values[i].string.text =
2335
_cupsStrAlloc(oldattr->values[i].string.text);
2339
if ((oldattr = ippFindAttribute(oldattrs, "marker-levels",
2340
IPP_TAG_INTEGER)) != NULL)
2342
if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2343
"marker-levels", oldattr->num_values,
2346
for (i = 0; i < oldattr->num_values; i ++)
2347
attr->values[i].integer = oldattr->values[i].integer;
2351
if ((oldattr = ippFindAttribute(oldattrs, "marker-message",
2352
IPP_TAG_TEXT)) != NULL)
2353
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "marker-message",
2354
NULL, oldattr->values[0].string.text);
2356
if ((oldattr = ippFindAttribute(oldattrs, "marker-low-levels",
2357
IPP_TAG_INTEGER)) != NULL)
2359
if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2360
"marker-low-levels", oldattr->num_values,
2363
for (i = 0; i < oldattr->num_values; i ++)
2364
attr->values[i].integer = oldattr->values[i].integer;
2368
if ((oldattr = ippFindAttribute(oldattrs, "marker-high-levels",
2369
IPP_TAG_INTEGER)) != NULL)
2371
if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2372
"marker-high-levels", oldattr->num_values,
2375
for (i = 0; i < oldattr->num_values; i ++)
2376
attr->values[i].integer = oldattr->values[i].integer;
2380
if ((oldattr = ippFindAttribute(oldattrs, "marker-names",
2381
IPP_TAG_NAME)) != NULL)
2383
if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
2384
"marker-names", oldattr->num_values, NULL,
2387
for (i = 0; i < oldattr->num_values; i ++)
2388
attr->values[i].string.text =
2389
_cupsStrAlloc(oldattr->values[i].string.text);
2393
if ((oldattr = ippFindAttribute(oldattrs, "marker-types",
2394
IPP_TAG_KEYWORD)) != NULL)
2396
if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2397
"marker-types", oldattr->num_values, NULL,
2400
for (i = 0; i < oldattr->num_values; i ++)
2401
attr->values[i].string.text =
2402
_cupsStrAlloc(oldattr->values[i].string.text);
2406
ippDelete(oldattrs);
2410
* Force sharing off for remote queues...
2413
if (p->type & CUPS_PRINTER_REMOTE)
2417
* Populate the document-format-supported attribute...
2420
add_printer_formats(p);
2422
DEBUG_printf(("cupsdSetPrinterAttrs: leaving name = %s, type = %x\n", p->name,
2426
* Add name-default attributes...
2429
add_printer_defaults(p);
2432
* Let the browse protocols reflect the change
2435
cupsdRegisterPrinter(p);
2440
* 'cupsdSetPrinterReasons()' - Set/update the reasons strings.
2443
int /* O - 1 if something changed, 0 otherwise */
2444
cupsdSetPrinterReasons(
2445
cupsd_printer_t *p, /* I - Printer */
2446
const char *s) /* I - Reasons strings */
2448
int i, /* Looping var */
2449
changed = 0; /* Did something change? */
2450
const char *sptr; /* Pointer into reasons */
2451
char reason[255], /* Reason string */
2452
*rptr; /* Pointer into reason */
2455
cupsdLogMessage(CUPSD_LOG_DEBUG2,
2456
"cupsdSetPrinterReasons(p=%p(%s),s=\"%s\"", p, p->name, s);
2458
if (s[0] == '-' || s[0] == '+')
2461
* Add/remove reasons...
2469
* Replace reasons...
2474
for (i = 0; i < p->num_reasons; i ++)
2475
_cupsStrFree(p->reasons[i]);
2483
if (!strcmp(s, "none"))
2487
* Loop through all of the reasons...
2493
* Skip leading whitespace and commas...
2496
while (isspace(*sptr & 255) || *sptr == ',')
2499
for (rptr = reason; *sptr && !isspace(*sptr & 255) && *sptr != ','; sptr ++)
2500
if (rptr < (reason + sizeof(reason) - 1))
2514
for (i = 0; i < p->num_reasons; i ++)
2515
if (!strcmp(reason, p->reasons[i]))
2518
* Found a match, so remove it...
2523
_cupsStrFree(p->reasons[i]);
2525
if (i < p->num_reasons)
2526
memmove(p->reasons + i, p->reasons + i + 1,
2527
(p->num_reasons - i) * sizeof(char *));
2529
if (!strcmp(reason, "paused") && p->state == IPP_PRINTER_STOPPED)
2530
cupsdSetPrinterState(p, IPP_PRINTER_IDLE, 1);
2532
if (strcmp(reason, "connecting-to-device"))
2537
else if (p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
2543
for (i = 0; i < p->num_reasons; i ++)
2544
if (!strcmp(reason, p->reasons[i]))
2547
if (i >= p->num_reasons)
2549
if (!strncmp(reason, "cups-ipp-missing-", 17) ||
2550
!strncmp(reason, "cups-ipp-wrong-", 15))
2551
log_ipp_conformance(p, reason);
2553
if (i >= (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
2555
cupsdLogMessage(CUPSD_LOG_ALERT,
2556
"Too many printer-state-reasons values for %s (%d)",
2561
p->reasons[i] = _cupsStrAlloc(reason);
2565
if (!strcmp(reason, "paused") && p->state != IPP_PRINTER_STOPPED)
2566
cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, 1);
2568
if (strcmp(reason, "connecting-to-device"))
2579
* 'cupsdSetPrinterState()' - Update the current state of a printer.
2583
cupsdSetPrinterState(
2584
cupsd_printer_t *p, /* I - Printer to change */
2585
ipp_pstate_t s, /* I - New state */
2586
int update) /* I - Update printers.conf? */
2588
cupsd_job_t *job; /* Current job */
2589
ipp_pstate_t old_state; /* Old printer state */
2590
static const char * const printer_states[] =
2591
{ /* State strings */
2599
* Set the new state...
2602
old_state = p->state;
2607
cupsdAddEvent(s == IPP_PRINTER_STOPPED ? CUPSD_EVENT_PRINTER_STOPPED :
2608
CUPSD_EVENT_PRINTER_STATE, p, NULL,
2609
"%s \"%s\" state changed to %s.",
2610
(p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
2611
p->name, printer_states[p->state - IPP_PRINTER_IDLE]);
2614
* Let the browse code know this needs to be updated...
2617
p->state_time = time(NULL);
2621
* Set/clear the paused reason as needed...
2624
if (s == IPP_PRINTER_STOPPED)
2625
cupsdSetPrinterReasons(p, "+paused");
2627
cupsdSetPrinterReasons(p, "-paused");
2631
for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
2633
job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
2634
if (job->reasons && job->state_value == IPP_JOB_PENDING &&
2635
!_cups_strcasecmp(job->dest, p->name))
2636
ippSetString(job->attrs, &job->reasons, 0,
2637
s == IPP_PRINTER_STOPPED ? "printer-stopped" : "none");
2641
* Clear the message for the queue when going to processing...
2644
if (s == IPP_PRINTER_PROCESSING)
2645
p->state_message[0] = '\0';
2648
* Let the browse protocols reflect the change...
2652
cupsdRegisterPrinter(p);
2655
* Save the printer configuration if a printer goes from idle or processing
2656
* to stopped (or visa-versa)...
2660
(old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED))
2666
* 'cupsdStopPrinter()' - Stop a printer from printing any jobs...
2670
cupsdStopPrinter(cupsd_printer_t *p, /* I - Printer to stop */
2671
int update)/* I - Update printers.conf? */
2674
* Set the printer state...
2677
cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, update);
2680
* See if we have a job printing on this printer...
2683
if (p->job && p->job->state_value == IPP_JOB_PROCESSING)
2684
cupsdSetJobState(p->job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
2685
"Job stopped due to printer being paused.");
2690
* 'cupsdUpdatePrinterPPD()' - Update keywords in a printer's PPD file.
2693
int /* O - 1 if successful, 0 otherwise */
2694
cupsdUpdatePrinterPPD(
2695
cupsd_printer_t *p, /* I - Printer */
2696
int num_keywords, /* I - Number of keywords */
2697
cups_option_t *keywords) /* I - Keywords */
2699
int i; /* Looping var */
2700
cups_file_t *src, /* Original file */
2701
*dst; /* New file */
2702
char srcfile[1024], /* Original filename */
2703
dstfile[1024], /* New filename */
2704
line[1024], /* Line from file */
2705
keystring[41]; /* Keyword from line */
2706
cups_option_t *keyword; /* Current keyword */
2709
cupsdLogMessage(CUPSD_LOG_INFO, "Updating keywords in PPD file for %s...",
2713
* Get the old and new PPD filenames...
2716
snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd.O", ServerRoot, p->name);
2717
snprintf(dstfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
2720
* Rename the old file and open the old and new...
2723
if (rename(dstfile, srcfile))
2725
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup PPD file for %s: %s",
2726
p->name, strerror(errno));
2730
if ((src = cupsFileOpen(srcfile, "r")) == NULL)
2732
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open PPD file \"%s\": %s",
2733
srcfile, strerror(errno));
2734
rename(srcfile, dstfile);
2738
if ((dst = cupsFileOpen(dstfile, "w")) == NULL)
2740
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create PPD file \"%s\": %s",
2741
dstfile, strerror(errno));
2743
rename(srcfile, dstfile);
2748
* Copy the first line and then write out all of the keywords...
2751
if (!cupsFileGets(src, line, sizeof(line)))
2753
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to read PPD file \"%s\": %s",
2754
srcfile, strerror(errno));
2757
rename(srcfile, dstfile);
2761
cupsFilePrintf(dst, "%s\n", line);
2763
for (i = num_keywords, keyword = keywords; i > 0; i --, keyword ++)
2765
cupsdLogMessage(CUPSD_LOG_DEBUG, "*%s: %s", keyword->name, keyword->value);
2766
cupsFilePrintf(dst, "*%s: %s\n", keyword->name, keyword->value);
2770
* Then copy the rest of the PPD file, dropping any keywords we changed.
2773
while (cupsFileGets(src, line, sizeof(line)))
2776
* Skip keywords we've already set...
2779
if (sscanf(line, "*%40[^:]:", keystring) == 1 &&
2780
cupsGetOption(keystring, num_keywords, keywords))
2784
* Otherwise write the line...
2787
cupsFilePrintf(dst, "%s\n", line);
2791
* Close files and return...
2802
* 'cupsdUpdatePrinters()' - Update printers after a partial reload.
2806
cupsdUpdatePrinters(void)
2808
cupsd_printer_t *p; /* Current printer */
2812
* Loop through the printers and recreate the printer attributes
2813
* for any local printers since the policy and/or access control
2814
* stuff may have changed. Also, if browsing is disabled, remove
2815
* any remote printers...
2818
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
2820
p = (cupsd_printer_t *)cupsArrayNext(Printers))
2823
* Update the operation policy pointer...
2826
if ((p->op_policy_ptr = cupsdFindPolicy(p->op_policy)) == NULL)
2827
p->op_policy_ptr = DefaultPolicyPtr;
2830
* Update printer attributes...
2833
cupsdSetPrinterAttrs(p);
2839
* 'cupsdValidateDest()' - Validate a printer/class destination.
2842
const char * /* O - Printer or class name */
2844
const char *uri, /* I - Printer URI */
2845
cups_ptype_t *dtype, /* O - Type (printer or class) */
2846
cupsd_printer_t **printer) /* O - Printer pointer */
2848
cupsd_printer_t *p; /* Current printer */
2849
char localname[1024],/* Localized hostname */
2850
*lptr, /* Pointer into localized hostname */
2851
*sptr, /* Pointer into server name */
2852
*rptr, /* Pointer into resource */
2853
scheme[32], /* Scheme portion of URI */
2854
username[64], /* Username portion of URI */
2855
hostname[HTTP_MAX_HOST],
2856
/* Host portion of URI */
2857
resource[HTTP_MAX_URI];
2858
/* Resource portion of URI */
2859
int port; /* Port portion of URI */
2862
DEBUG_printf(("cupsdValidateDest(uri=\"%s\", dtype=%p, printer=%p)\n", uri,
2866
* Initialize return values...
2873
*dtype = (cups_ptype_t)0;
2876
* Pull the hostname and resource from the URI...
2879
httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
2880
username, sizeof(username), hostname, sizeof(hostname),
2881
&port, resource, sizeof(resource));
2884
* See if the resource is a class or printer...
2887
if (!strncmp(resource, "/classes/", 9))
2893
rptr = resource + 9;
2895
else if (!strncmp(resource, "/printers/", 10))
2901
rptr = resource + 10;
2906
* Bad resource name...
2913
* See if the printer or class name exists...
2916
p = cupsdFindDest(rptr);
2918
if (p == NULL && strchr(rptr, '@') == NULL)
2926
*dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
2932
* Change localhost to the server name...
2935
if (!_cups_strcasecmp(hostname, "localhost"))
2936
strlcpy(hostname, ServerName, sizeof(hostname));
2938
strlcpy(localname, hostname, sizeof(localname));
2940
if (!_cups_strcasecmp(hostname, ServerName))
2943
* Localize the hostname...
2946
lptr = strchr(localname, '.');
2947
sptr = strchr(ServerName, '.');
2949
if (sptr != NULL && lptr != NULL)
2952
* Strip the common domain name components...
2955
while (lptr != NULL)
2957
if (!_cups_strcasecmp(lptr, sptr))
2963
lptr = strchr(lptr + 1, '.');
2968
DEBUG_printf(("localized hostname is \"%s\"...\n", localname));
2971
* Find a matching printer or class...
2974
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
2976
p = (cupsd_printer_t *)cupsArrayNext(Printers))
2977
if (!_cups_strcasecmp(p->hostname, localname) &&
2978
!_cups_strcasecmp(p->name, rptr))
2984
*dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
2994
* 'cupsdWritePrintcap()' - Write a pseudo-printcap file for older applications
2999
cupsdWritePrintcap(void)
3001
int i; /* Looping var */
3002
cups_file_t *fp; /* Printcap file */
3003
cupsd_printer_t *p; /* Current printer */
3007
* See if we have a printcap file; if not, don't bother writing it.
3010
if (!Printcap || !*Printcap)
3013
cupsdLogMessage(CUPSD_LOG_INFO, "Generating printcap %s...", Printcap);
3016
* Open the printcap file...
3019
if ((fp = cupsFileOpen(Printcap, "w")) == NULL)
3023
* Put a comment header at the top so that users will know where the
3024
* data has come from...
3027
if (PrintcapFormat != PRINTCAP_PLIST)
3028
cupsFilePrintf(fp, "# This file was automatically generated by cupsd(8) "
3030
"# %s/printers.conf file. All changes to this file\n"
3031
"# will be lost.\n", ServerRoot);
3034
* Write a new printcap with the current list of printers.
3037
switch (PrintcapFormat)
3041
* Each printer is put in the file as:
3051
cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", DefaultPrinter->name,
3052
DefaultPrinter->info, ServerName,
3053
DefaultPrinter->name);
3055
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3057
p = (cupsd_printer_t *)cupsArrayNext(Printers))
3058
if (p != DefaultPrinter)
3059
cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", p->name, p->info,
3060
ServerName, p->name);
3063
case PRINTCAP_PLIST :
3065
* Each printer is written as a dictionary in a plist file.
3066
* Currently the printer-name, printer-info, printer-is-accepting-jobs,
3067
* printer-location, printer-make-and-model, printer-state,
3068
* printer-state-reasons, printer-type, and (sanitized) device-uri.
3071
cupsFilePuts(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3072
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD "
3073
"PLIST 1.0//EN\" \"http://www.apple.com/DTDs/"
3074
"PropertyList-1.0.dtd\">\n"
3075
"<plist version=\"1.0\">\n"
3078
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3080
p = (cupsd_printer_t *)cupsArrayNext(Printers))
3082
cupsFilePuts(fp, "\t<dict>\n"
3083
"\t\t<key>printer-name</key>\n"
3085
write_xml_string(fp, p->name);
3086
cupsFilePuts(fp, "</string>\n"
3087
"\t\t<key>printer-info</key>\n"
3089
write_xml_string(fp, p->info);
3090
cupsFilePrintf(fp, "</string>\n"
3091
"\t\t<key>printer-is-accepting-jobs</key>\n"
3093
"\t\t<key>printer-location</key>\n"
3094
"\t\t<string>", p->accepting ? "true" : "false");
3096
write_xml_string(fp, p->location);
3097
cupsFilePuts(fp, "</string>\n"
3098
"\t\t<key>printer-make-and-model</key>\n"
3100
write_xml_string(fp, p->make_model);
3101
cupsFilePrintf(fp, "</string>\n"
3102
"\t\t<key>printer-state</key>\n"
3103
"\t\t<integer>%d</integer>\n"
3104
"\t\t<key>printer-state-reasons</key>\n"
3105
"\t\t<array>\n", p->state);
3106
for (i = 0; i < p->num_reasons; i ++)
3108
cupsFilePuts(fp, "\t\t\t<string>");
3109
write_xml_string(fp, p->reasons[i]);
3110
cupsFilePuts(fp, "</string>\n");
3112
cupsFilePrintf(fp, "\t\t</array>\n"
3113
"\t\t<key>printer-type</key>\n"
3114
"\t\t<integer>%d</integer>\n"
3115
"\t\t<key>device-uri</key>\n"
3116
"\t\t<string>", p->type);
3117
write_xml_string(fp, p->sanitized_device_uri);
3118
cupsFilePuts(fp, "</string>\n"
3121
cupsFilePuts(fp, "</array>\n"
3125
case PRINTCAP_SOLARIS :
3127
* Each printer is put in the file as:
3129
* _all:all=Printer1,Printer2,Printer3,...,PrinterN
3130
* _default:use=DefaultPrinter
3132
* :bsdaddr=ServerName,Printer1:\
3133
* :description=Description:
3135
* :bsdaddr=ServerName,Printer2:\
3136
* :description=Description:
3138
* :bsdaddr=ServerName,Printer3:\
3139
* :description=Description:
3142
* :bsdaddr=ServerName,PrinterN:\
3143
* :description=Description:
3146
cupsFilePuts(fp, "_all:all=");
3147
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3149
p = (cupsd_printer_t *)cupsArrayCurrent(Printers))
3150
cupsFilePrintf(fp, "%s%c", p->name,
3151
cupsArrayNext(Printers) ? ',' : '\n');
3154
cupsFilePrintf(fp, "_default:use=%s\n", DefaultPrinter->name);
3156
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3158
p = (cupsd_printer_t *)cupsArrayNext(Printers))
3159
cupsFilePrintf(fp, "%s:\\\n"
3160
"\t:bsdaddr=%s,%s:\\\n"
3161
"\t:description=%s:\n",
3162
p->name, ServerName, p->name,
3163
p->info ? p->info : "");
3176
* 'add_printer_defaults()' - Add name-default attributes to the printer attributes.
3180
add_printer_defaults(cupsd_printer_t *p)/* I - Printer */
3182
int i; /* Looping var */
3183
int num_options; /* Number of default options */
3184
cups_option_t *options, /* Default options */
3185
*option; /* Current option */
3186
char name[256]; /* name-default */
3190
* Maintain a common array of default attribute names...
3193
if (!CommonDefaults)
3195
CommonDefaults = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3197
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("copies-default"));
3198
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("document-format-default"));
3199
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("finishings-default"));
3200
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-account-id-default"));
3201
cupsArrayAdd(CommonDefaults,
3202
_cupsStrAlloc("job-accounting-user-id-default"));
3203
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-hold-until-default"));
3204
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-priority-default"));
3205
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-sheets-default"));
3206
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("media-col-default"));
3207
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("number-up-default"));
3208
cupsArrayAdd(CommonDefaults,
3209
_cupsStrAlloc("orientation-requested-default"));
3213
* Add all of the default options from the .conf files...
3216
for (num_options = 0, options = NULL, i = p->num_options, option = p->options;
3220
if (strcmp(option->name, "ipp-options") &&
3221
strcmp(option->name, "job-sheets") &&
3222
strcmp(option->name, "lease-duration"))
3224
snprintf(name, sizeof(name), "%s-default", option->name);
3225
num_options = cupsAddOption(name, option->value, num_options, &options);
3227
if (!cupsArrayFind(CommonDefaults, name))
3228
cupsArrayAdd(CommonDefaults, _cupsStrAlloc(name));
3233
* Convert options to IPP attributes...
3236
cupsEncodeOptions2(p->attrs, num_options, options, IPP_TAG_PRINTER);
3237
cupsFreeOptions(num_options, options);
3240
* Add standard -default attributes as needed...
3243
if (!cupsGetOption("copies", p->num_options, p->options))
3244
ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default",
3247
if (!cupsGetOption("document-format", p->num_options, p->options))
3248
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
3249
"document-format-default", NULL, "application/octet-stream");
3251
if (!cupsGetOption("job-hold-until", p->num_options, p->options))
3252
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3253
"job-hold-until-default", NULL, "no-hold");
3255
if (!cupsGetOption("job-priority", p->num_options, p->options))
3256
ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3257
"job-priority-default", 50);
3259
if (!cupsGetOption("number-up", p->num_options, p->options))
3260
ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3261
"number-up-default", 1);
3263
if (!cupsGetOption("notify-lease-duration", p->num_options, p->options))
3264
ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3265
"notify-lease-duration-default", DefaultLeaseDuration);
3267
if (!cupsGetOption("notify-events", p->num_options, p->options))
3268
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3269
"notify-events-default", NULL, "job-completed");
3271
if (!cupsGetOption("orientation-requested", p->num_options, p->options))
3272
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
3273
"orientation-requested-default", NULL, NULL);
3275
if (!cupsGetOption("print-quality", p->num_options, p->options))
3276
ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3277
"print-quality-default", IPP_QUALITY_NORMAL);
3282
* 'add_printer_filter()' - Add a MIME filter for a printer.
3287
cupsd_printer_t *p, /* I - Printer to add to */
3288
mime_type_t *filtertype, /* I - Filter or prefilter MIME type */
3289
const char *filter) /* I - Filter to add */
3291
char super[MIME_MAX_SUPER], /* Super-type for filter */
3292
type[MIME_MAX_TYPE], /* Type for filter */
3293
dsuper[MIME_MAX_SUPER], /* Destination super-type for filter */
3294
dtype[MIME_MAX_TYPE], /* Destination type for filter */
3295
dest[MIME_MAX_SUPER + MIME_MAX_TYPE + 2],
3296
/* Destination super/type */
3297
program[1024]; /* Program/filter name */
3298
int cost; /* Cost of filter */
3299
size_t maxsize = 0; /* Maximum supported file size */
3300
mime_type_t *temptype, /* MIME type looping var */
3301
*desttype; /* Destination MIME type */
3302
mime_filter_t *filterptr; /* MIME filter */
3303
char filename[1024]; /* Full filter filename */
3306
cupsdLogMessage(CUPSD_LOG_DEBUG2,
3307
"add_printer_filter(p=%p(%s), filtertype=%p(%s/%s), "
3308
"filter=\"%s\")", p, p->name, filtertype, filtertype->super,
3309
filtertype->type, filter);
3312
* Parse the filter string; it should be in one of the following formats:
3314
* source/type cost program
3315
* source/type cost maxsize(nnnn) program
3316
* source/type dest/type cost program
3317
* source/type dest/type cost maxsize(nnnn) program
3320
if (sscanf(filter, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3321
super, type, dsuper, dtype, &cost, program) == 6)
3323
snprintf(dest, sizeof(dest), "%s/%s/%s", p->name, dsuper, dtype);
3325
if ((desttype = mimeType(MimeDatabase, "printer", dest)) == NULL)
3327
desttype = mimeAddType(MimeDatabase, "printer", dest);
3329
p->dest_types = cupsArrayNew(NULL, NULL);
3331
cupsArrayAdd(p->dest_types, desttype);
3337
if (sscanf(filter, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, &cost,
3340
desttype = filtertype;
3344
cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!",
3350
if (!strncmp(program, "maxsize(", 8))
3352
char *ptr; /* Pointer into maxsize(nnnn) program */
3354
maxsize = strtoll(program + 8, &ptr, 10);
3358
cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!",
3364
while (_cups_isspace(*ptr))
3367
_cups_strcpy(program, ptr);
3371
* Check permissions on the filter and its containing directory...
3374
if (strcmp(program, "-"))
3376
if (program[0] == '/')
3377
strlcpy(filename, program, sizeof(filename));
3379
snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program);
3381
_cupsFileCheck(filename, _CUPS_FILE_CHECK_PROGRAM, !RunUser,
3382
cupsdLogFCMessage, p);
3386
* Add the filter to the MIME database, supporting wildcards as needed...
3389
for (temptype = mimeFirstType(MimeDatabase);
3391
temptype = mimeNextType(MimeDatabase))
3392
if (((super[0] == '*' && _cups_strcasecmp(temptype->super, "printer")) ||
3393
!_cups_strcasecmp(temptype->super, super)) &&
3394
(type[0] == '*' || !_cups_strcasecmp(temptype->type, type)))
3396
if (desttype != filtertype)
3398
cupsdLogMessage(CUPSD_LOG_DEBUG2,
3399
"add_printer_filter: %s: adding filter %s/%s %s/%s %d "
3400
"%s", p->name, temptype->super, temptype->type,
3401
desttype->super, desttype->type,
3403
filterptr = mimeAddFilter(MimeDatabase, temptype, desttype, cost,
3406
if (!mimeFilterLookup(MimeDatabase, desttype, filtertype))
3408
cupsdLogMessage(CUPSD_LOG_DEBUG2,
3409
"add_printer_filter: %s: adding filter %s/%s %s/%s "
3410
"0 -", p->name, desttype->super, desttype->type,
3411
filtertype->super, filtertype->type);
3412
mimeAddFilter(MimeDatabase, desttype, filtertype, 0, "-");
3417
cupsdLogMessage(CUPSD_LOG_DEBUG2,
3418
"add_printer_filter: %s: adding filter %s/%s %s/%s %d "
3419
"%s", p->name, temptype->super, temptype->type,
3420
filtertype->super, filtertype->type,
3422
filterptr = mimeAddFilter(MimeDatabase, temptype, filtertype, cost,
3427
filterptr->maxsize = maxsize;
3433
* 'add_printer_formats()' - Add document-format-supported values for a printer.
3437
add_printer_formats(cupsd_printer_t *p) /* I - Printer */
3439
int i; /* Looping var */
3440
mime_type_t *type; /* Current MIME type */
3441
cups_array_t *filters; /* Filters */
3442
ipp_attribute_t *attr; /* document-format-supported attribute */
3443
char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
3444
/* MIME type name */
3448
* Raw (and remote) queues advertise all of the supported MIME
3452
cupsArrayDelete(p->filetypes);
3453
p->filetypes = NULL;
3457
ippAddStrings(p->attrs, IPP_TAG_PRINTER,
3458
(ipp_tag_t)(IPP_TAG_MIMETYPE | IPP_TAG_COPY),
3459
"document-format-supported", NumMimeTypes, NULL, MimeTypes);
3464
* Otherwise, loop through the supported MIME types and see if there
3465
* are filters for them...
3468
cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer_formats: %d types, %d filters",
3469
mimeNumTypes(MimeDatabase), mimeNumFilters(MimeDatabase));
3471
p->filetypes = cupsArrayNew(NULL, NULL);
3473
for (type = mimeFirstType(MimeDatabase);
3475
type = mimeNextType(MimeDatabase))
3477
if (!_cups_strcasecmp(type->super, "printer"))
3480
snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type);
3482
if ((filters = mimeFilter(MimeDatabase, type, p->filetype, NULL)) != NULL)
3484
cupsdLogMessage(CUPSD_LOG_DEBUG2,
3485
"add_printer_formats: %s: %s needs %d filters",
3486
p->name, mimetype, cupsArrayCount(filters));
3488
cupsArrayDelete(filters);
3489
cupsArrayAdd(p->filetypes, type);
3492
cupsdLogMessage(CUPSD_LOG_DEBUG2,
3493
"add_printer_formats: %s: %s not supported",
3498
* Add the file formats that can be filtered...
3501
if ((type = mimeType(MimeDatabase, "application", "octet-stream")) == NULL ||
3502
!cupsArrayFind(p->filetypes, type))
3507
cupsdLogMessage(CUPSD_LOG_DEBUG2,
3508
"add_printer_formats: %s: %d supported types",
3509
p->name, cupsArrayCount(p->filetypes) + i);
3511
attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
3512
"document-format-supported",
3513
cupsArrayCount(p->filetypes) + i, NULL, NULL);
3516
attr->values[0].string.text = _cupsStrAlloc("application/octet-stream");
3518
for (type = (mime_type_t *)cupsArrayFirst(p->filetypes);
3520
i ++, type = (mime_type_t *)cupsArrayNext(p->filetypes))
3522
snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type);
3524
attr->values[i].string.text = _cupsStrAlloc(mimetype);
3527
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
3529
char pdl[1024]; /* Buffer to build pdl list */
3530
mime_filter_t *filter; /* MIME filter looping var */
3534
* We only support raw printing if this is not a Tioga PrintJobMgr based
3535
* queue and if application/octet-stream is a known type...
3538
for (filter = (mime_filter_t *)cupsArrayFirst(MimeDatabase->filters);
3540
filter = (mime_filter_t *)cupsArrayNext(MimeDatabase->filters))
3542
if (filter->dst == p->filetype && filter->filter &&
3543
strstr(filter->filter, "PrintJobMgr"))
3549
if (!filter && mimeType(MimeDatabase, "application", "octet-stream"))
3550
strlcat(pdl, "application/octet-stream,", sizeof(pdl));
3553
* Then list a bunch of formats that are supported by the printer...
3556
for (type = (mime_type_t *)cupsArrayFirst(p->filetypes);
3558
type = (mime_type_t *)cupsArrayNext(p->filetypes))
3560
if (!_cups_strcasecmp(type->super, "application"))
3562
if (!_cups_strcasecmp(type->type, "pdf"))
3563
strlcat(pdl, "application/pdf,", sizeof(pdl));
3564
else if (!_cups_strcasecmp(type->type, "postscript"))
3565
strlcat(pdl, "application/postscript,", sizeof(pdl));
3567
else if (!_cups_strcasecmp(type->super, "image"))
3569
if (!_cups_strcasecmp(type->type, "urf"))
3570
strlcat(pdl, "image/urf,", sizeof(pdl));
3571
else if (!_cups_strcasecmp(type->type, "jpeg"))
3572
strlcat(pdl, "image/jpeg,", sizeof(pdl));
3573
else if (!_cups_strcasecmp(type->type, "png"))
3574
strlcat(pdl, "image/png,", sizeof(pdl));
3575
else if (!_cups_strcasecmp(type->type, "pwg-raster"))
3576
strlcat(pdl, "image/pwg-raster,", sizeof(pdl));
3581
pdl[strlen(pdl) - 1] = '\0'; /* Remove trailing comma */
3583
cupsdSetString(&p->pdl, pdl);
3585
#endif /* HAVE_DNSSD || HAVE_AVAHI */
3590
* 'compare_printers()' - Compare two printers.
3593
static int /* O - Result of comparison */
3594
compare_printers(void *first, /* I - First printer */
3595
void *second, /* I - Second printer */
3596
void *data) /* I - App data (not used) */
3600
return (_cups_strcasecmp(((cupsd_printer_t *)first)->name,
3601
((cupsd_printer_t *)second)->name));
3606
* 'delete_printer_filters()' - Delete all MIME filters for a printer.
3610
delete_printer_filters(
3611
cupsd_printer_t *p) /* I - Printer to remove from */
3613
mime_filter_t *filter; /* MIME filter looping var */
3614
mime_type_t *type; /* Destination types for filters */
3618
* Range check input...
3625
* Remove all filters from the MIME database that have a destination
3626
* type == printer...
3629
for (filter = mimeFirstFilter(MimeDatabase);
3631
filter = mimeNextFilter(MimeDatabase))
3632
if (filter->dst == p->filetype || filter->dst == p->prefiltertype ||
3633
cupsArrayFind(p->dest_types, filter->dst))
3636
* Delete the current filter...
3639
mimeDeleteFilter(MimeDatabase, filter);
3642
for (type = (mime_type_t *)cupsArrayFirst(p->dest_types);
3644
type = (mime_type_t *)cupsArrayNext(p->dest_types))
3645
mimeDeleteType(MimeDatabase, type);
3647
cupsArrayDelete(p->dest_types);
3648
p->dest_types = NULL;
3650
cupsdSetPrinterReasons(p, "-cups-insecure-filter-warning"
3651
",cups-missing-filter-warning");
3656
* 'dirty_printer()' - Mark config and state files dirty for the specified
3661
dirty_printer(cupsd_printer_t *p) /* I - Printer */
3663
if (p->type & CUPS_PRINTER_CLASS)
3664
cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
3666
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
3668
if (PrintcapFormat == PRINTCAP_PLIST)
3669
cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
3674
* 'load_ppd()' - Load a cached PPD file, updating the cache as needed.
3678
load_ppd(cupsd_printer_t *p) /* I - Printer */
3680
int i, j, k; /* Looping vars */
3681
char cache_name[1024]; /* Cache filename */
3682
struct stat cache_info; /* Cache file info */
3683
ppd_file_t *ppd; /* PPD file */
3684
char ppd_name[1024]; /* PPD filename */
3685
struct stat ppd_info; /* PPD file info */
3686
int num_media; /* Number of media options */
3687
ppd_size_t *size; /* Current PPD size */
3688
ppd_option_t *duplex, /* Duplex option */
3689
*output_bin, /* OutputBin option */
3690
*output_mode, /* OutputMode option */
3691
*resolution; /* (Set|JCL|)Resolution option */
3692
ppd_choice_t *choice, /* Current PPD choice */
3693
*input_slot, /* Current input slot */
3694
*media_type; /* Current media type */
3695
ppd_attr_t *ppd_attr; /* PPD attribute */
3696
int xdpi, /* Horizontal resolution */
3697
ydpi; /* Vertical resolution */
3698
const char *resptr; /* Pointer into resolution keyword */
3699
_pwg_size_t *pwgsize; /* Current PWG size */
3700
_pwg_map_t *pwgsource, /* Current PWG source */
3701
*pwgtype; /* Current PWG type */
3702
ipp_attribute_t *attr; /* Attribute data */
3703
_ipp_value_t *val; /* Attribute value */
3704
int num_finishings, /* Number of finishings */
3705
finishings[5]; /* finishings-supported values */
3706
int num_qualities, /* Number of print-quality values */
3707
qualities[3]; /* print-quality values */
3708
int num_margins, /* Number of media-*-margin-supported values */
3709
margins[16]; /* media-*-margin-supported values */
3710
const char *filter, /* Current filter */
3711
*mandatory; /* Current mandatory attribute */
3712
static const char * const sides[3] = /* sides-supported values */
3715
"two-sided-long-edge",
3716
"two-sided-short-edge"
3718
static const char * const standard_commands[] =
3719
{ /* Standard CUPS commands */
3727
* Check to see if the cache is up-to-date...
3730
snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir, p->name);
3731
if (stat(cache_name, &cache_info))
3732
cache_info.st_mtime = 0;
3734
snprintf(ppd_name, sizeof(ppd_name), "%s/ppd/%s.ppd", ServerRoot, p->name);
3735
if (stat(ppd_name, &ppd_info))
3736
ppd_info.st_mtime = 1;
3738
ippDelete(p->ppd_attrs);
3739
p->ppd_attrs = NULL;
3741
_ppdCacheDestroy(p->pc);
3744
if (cache_info.st_mtime >= ppd_info.st_mtime)
3746
cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", cache_name);
3748
if ((p->pc = _ppdCacheCreateWithFile(cache_name, &p->ppd_attrs)) != NULL &&
3752
* Loaded successfully!
3760
* Reload PPD attributes from disk...
3763
cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
3765
cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", ppd_name);
3767
p->type &= ~CUPS_PRINTER_OPTIONS;
3768
p->type |= CUPS_PRINTER_BW;
3770
finishings[0] = IPP_FINISHINGS_NONE;
3773
p->ppd_attrs = ippNew();
3775
if ((ppd = _ppdOpenFile(ppd_name, _PPD_LOCALIZATION_NONE)) != NULL)
3778
* Add make/model and other various attributes...
3781
p->pc = _ppdCacheCreateWithPPD(ppd);
3784
cupsdLogMessage(CUPSD_LOG_WARN, "Unable to create cache of \"%s\": %s",
3785
ppd_name, cupsLastErrorString());
3787
ppdMarkDefaults(ppd);
3789
if (ppd->color_device)
3790
p->type |= CUPS_PRINTER_COLOR;
3791
if (ppd->variable_sizes)
3792
p->type |= CUPS_PRINTER_VARIABLE;
3793
if (!ppd->manual_copies)
3794
p->type |= CUPS_PRINTER_COPIES;
3795
if ((ppd_attr = ppdFindAttr(ppd, "cupsFax", NULL)) != NULL)
3796
if (ppd_attr->value && !_cups_strcasecmp(ppd_attr->value, "true"))
3797
p->type |= CUPS_PRINTER_FAX;
3799
ippAddBoolean(p->ppd_attrs, IPP_TAG_PRINTER, "color-supported",
3802
if (p->pc && p->pc->charge_info_uri)
3803
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
3804
"printer-charge-info-uri", NULL, p->pc->charge_info_uri);
3806
if (p->pc && p->pc->account_id)
3807
ippAddBoolean(p->ppd_attrs, IPP_TAG_PRINTER, "job-account-id-supported",
3810
if (p->pc && p->pc->accounting_user_id)
3811
ippAddBoolean(p->ppd_attrs, IPP_TAG_PRINTER,
3812
"job-accounting-user-id-supported", 1);
3814
if (p->pc && p->pc->password)
3816
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3817
"job-password-encryption-supported", NULL, "none");
3818
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3819
"job-password-supported", strlen(p->pc->password));
3822
if (ppd->throughput)
3824
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3825
"pages-per-minute", ppd->throughput);
3826
if (ppd->color_device)
3827
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3828
"pages-per-minute-color", ppd->throughput);
3833
* When there is no speed information, just say "1 page per minute".
3836
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3837
"pages-per-minute", 1);
3838
if (ppd->color_device)
3839
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3840
"pages-per-minute-color", 1);
3845
if ((output_mode = ppdFindOption(ppd, "OutputMode")) != NULL)
3847
if (ppdFindChoice(output_mode, "draft") ||
3848
ppdFindChoice(output_mode, "fast"))
3849
qualities[num_qualities ++] = IPP_QUALITY_DRAFT;
3851
qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
3853
if (ppdFindChoice(output_mode, "best") ||
3854
ppdFindChoice(output_mode, "high"))
3855
qualities[num_qualities ++] = IPP_QUALITY_HIGH;
3857
else if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL)
3861
if (strstr(ppd_attr->spec, "draft") ||
3862
strstr(ppd_attr->spec, "Draft"))
3864
qualities[num_qualities ++] = IPP_QUALITY_DRAFT;
3868
while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset",
3871
qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
3872
qualities[num_qualities ++] = IPP_QUALITY_HIGH;
3875
qualities[num_qualities ++] = IPP_QUALITY_NORMAL;
3877
ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3878
"print-quality-supported", num_qualities, qualities);
3883
* The NickName can be localized in the character set specified
3884
* by the LanugageEncoding attribute. However, ppdOpen2() has
3885
* already converted the ppd->nickname member to UTF-8 for us
3886
* (the original attribute value is available separately)
3889
cupsdSetString(&p->make_model, ppd->nickname);
3891
else if (ppd->modelname)
3894
* Model name can only contain specific characters...
3897
cupsdSetString(&p->make_model, ppd->modelname);
3900
cupsdSetString(&p->make_model, "Bad PPD File");
3902
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
3903
"printer-make-and-model", NULL, p->make_model);
3906
* Add media options from the PPD file...
3909
if (ppd->num_sizes == 0 || !p->pc)
3911
if (!ppdFindAttr(ppd, "APScannerOnly", NULL))
3912
cupsdLogMessage(CUPSD_LOG_CRIT,
3913
"The PPD file for printer %s contains no media "
3914
"options and is therefore invalid!", p->name);
3916
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3917
"media-default", NULL, "unknown");
3918
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3919
"media-supported", NULL, "unknown");
3927
if ((size = ppdPageSize(ppd, NULL)) != NULL)
3928
pwgsize = _ppdCacheGetSize(p->pc, size->name);
3932
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3933
"media-default", NULL,
3934
pwgsize ? pwgsize->map.pwg : "unknown");
3942
ipp_t *col; /* Collection value */
3944
input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
3945
media_type = ppdFindMarkedChoice(ppd, "MediaType");
3946
col = new_media_col(pwgsize,
3948
_ppdCacheGetSource(p->pc,
3949
input_slot->choice) :
3952
_ppdCacheGetType(p->pc,
3953
media_type->choice) :
3956
ippAddCollection(p->ppd_attrs, IPP_TAG_PRINTER, "media-col-default",
3965
num_media = p->pc->num_sizes;
3966
if (p->pc->custom_min_keyword)
3969
if ((attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3970
"media-supported", num_media, NULL,
3975
for (i = p->pc->num_sizes, pwgsize = p->pc->sizes;
3977
i --, pwgsize ++, val ++)
3978
val->string.text = _cupsStrAlloc(pwgsize->map.pwg);
3980
if (p->pc->custom_min_keyword)
3982
val->string.text = _cupsStrAlloc(p->pc->custom_min_keyword);
3984
val->string.text = _cupsStrAlloc(p->pc->custom_max_keyword);
3989
* media-size-supported
3992
num_media = p->pc->num_sizes;
3993
if (p->pc->custom_min_keyword)
3996
if ((attr = ippAddCollections(p->ppd_attrs, IPP_TAG_PRINTER,
3997
"media-size-supported", num_media,
4002
for (i = p->pc->num_sizes, pwgsize = p->pc->sizes;
4004
i --, pwgsize ++, val ++)
4006
val->collection = ippNew();
4007
ippAddInteger(val->collection, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4008
"x-dimension", pwgsize->width);
4009
ippAddInteger(val->collection, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4010
"y-dimension", pwgsize->length);
4013
if (p->pc->custom_min_keyword)
4015
val->collection = ippNew();
4016
ippAddRange(val->collection, IPP_TAG_PRINTER, "x-dimension",
4017
p->pc->custom_min_width, p->pc->custom_max_width);
4018
ippAddRange(val->collection, IPP_TAG_PRINTER, "y-dimension",
4019
p->pc->custom_min_length, p->pc->custom_max_length);
4024
* media-source-supported
4027
if (p->pc->num_sources > 0 &&
4028
(attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4029
"media-source-supported", p->pc->num_sources,
4030
NULL, NULL)) != NULL)
4032
for (i = p->pc->num_sources, pwgsource = p->pc->sources,
4035
i --, pwgsource ++, val ++)
4036
val->string.text = _cupsStrAlloc(pwgsource->pwg);
4040
* media-type-supported
4043
if (p->pc->num_types > 0 &&
4044
(attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4045
"media-type-supported", p->pc->num_types,
4046
NULL, NULL)) != NULL)
4048
for (i = p->pc->num_types, pwgtype = p->pc->types,
4051
i --, pwgtype ++, val ++)
4052
val->string.text = _cupsStrAlloc(pwgtype->pwg);
4056
* media-*-margin-supported
4059
for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
4060
i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
4063
for (j = 0; j < num_margins; j ++)
4064
if (pwgsize->bottom == margins[j])
4067
if (j >= num_margins)
4069
margins[num_margins] = pwgsize->bottom;
4074
if (num_margins > 0)
4075
ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4076
"media-bottom-margin-supported", num_margins, margins);
4078
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4079
"media-bottom-margin-supported", 0);
4081
for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
4082
i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
4085
for (j = 0; j < num_margins; j ++)
4086
if (pwgsize->left == margins[j])
4089
if (j >= num_margins)
4091
margins[num_margins] = pwgsize->left;
4096
if (num_margins > 0)
4097
ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4098
"media-left-margin-supported", num_margins, margins);
4100
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4101
"media-left-margin-supported", 0);
4103
for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
4104
i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
4107
for (j = 0; j < num_margins; j ++)
4108
if (pwgsize->right == margins[j])
4111
if (j >= num_margins)
4113
margins[num_margins] = pwgsize->right;
4118
if (num_margins > 0)
4119
ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4120
"media-right-margin-supported", num_margins, margins);
4122
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4123
"media-right-margin-supported", 0);
4125
for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, num_margins = 0;
4126
i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0]));
4129
for (j = 0; j < num_margins; j ++)
4130
if (pwgsize->top == margins[j])
4133
if (j >= num_margins)
4135
margins[num_margins] = pwgsize->top;
4140
if (num_margins > 0)
4141
ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4142
"media-top-margin-supported", num_margins, margins);
4144
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4145
"media-top-margin-supported", 0);
4148
* media-col-database
4151
num_media = p->pc->num_sizes;
4152
if (p->pc->num_sources)
4154
if (p->pc->num_types > 0)
4155
num_media += p->pc->num_sizes * p->pc->num_sources *
4158
num_media += p->pc->num_sizes * p->pc->num_sources;
4160
else if (p->pc->num_types)
4161
num_media += p->pc->num_sizes * p->pc->num_types;
4163
if ((attr = ippAddCollections(p->ppd_attrs, IPP_TAG_PRINTER,
4164
"media-col-database", num_media,
4167
for (i = p->pc->num_sizes, pwgsize = p->pc->sizes, val = attr->values;
4172
* Start by adding the page size without source or type...
4175
ppdMarkOption(ppd, "PageSize", pwgsize->map.ppd);
4177
val->collection = new_media_col(pwgsize, NULL, NULL);
4181
* Then add the specific, supported combinations of size, source, and
4185
if (p->pc->num_sources > 0)
4187
for (j = p->pc->num_sources, pwgsource = p->pc->sources;
4191
ppdMarkOption(ppd, "InputSlot", pwgsource->ppd);
4193
if (p->pc->num_types > 0)
4195
for (k = p->pc->num_types, pwgtype = p->pc->types;
4199
if (!ppdMarkOption(ppd, "MediaType", pwgtype->ppd))
4201
val->collection = new_media_col(pwgsize, pwgsource->pwg,
4207
else if (!ppdConflicts(ppd))
4209
val->collection = new_media_col(pwgsize, pwgsource->pwg, NULL);
4214
else if (p->pc->num_types > 0)
4216
for (j = p->pc->num_types, pwgtype = p->pc->types;
4220
if (!ppdMarkOption(ppd, "MediaType", pwgtype->ppd))
4222
val->collection = new_media_col(pwgsize, NULL, pwgtype->pwg);
4230
* Update the number of media-col-database values...
4233
attr->num_values = val - attr->values;
4241
if (p->pc && p->pc->num_bins > 0)
4243
attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4244
"output-bin-supported", p->pc->num_bins,
4249
for (i = 0, val = attr->values;
4250
i < p->pc->num_bins;
4252
val->string.text = _cupsStrAlloc(p->pc->bins[i].pwg);
4255
if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
4257
for (i = 0; i < p->pc->num_bins; i ++)
4258
if (!strcmp(p->pc->bins[i].ppd, output_bin->defchoice))
4261
if (i >= p->pc->num_bins)
4264
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4265
"output-bin-default", NULL, p->pc->bins[i].pwg);
4268
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4269
"output-bin-default", NULL, p->pc->bins[0].pwg);
4271
else if (((ppd_attr = ppdFindAttr(ppd, "DefaultOutputOrder",
4273
!_cups_strcasecmp(ppd_attr->value, "Reverse")) ||
4274
(!ppd_attr && ppd->manufacturer && /* "Compatibility heuristic" */
4275
(!_cups_strcasecmp(ppd->manufacturer, "epson") ||
4276
!_cups_strcasecmp(ppd->manufacturer, "lexmark"))))
4279
* Report that this printer has a single output bin that leaves pages face
4283
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4284
"output-bin-supported", NULL, "face-up");
4285
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4286
"output-bin-default", NULL, "face-up");
4290
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4291
"output-bin-supported", NULL, "face-down");
4292
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4293
"output-bin-default", NULL, "face-down");
4297
* print-color-mode...
4300
if (ppd->color_device)
4302
static const char * const color_modes[] =
4308
ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4309
"print-color-mode-supported", 2, NULL, color_modes);
4310
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4311
"print-color-mode-default", NULL, "color");
4315
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4316
"print-color-mode-supported", NULL, "monochrome");
4317
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4318
"print-color-mode-default", NULL, "monochrome");
4322
* Mandatory job attributes, if any...
4325
if (p->pc && cupsArrayCount(p->pc->mandatory) > 0)
4327
int count = cupsArrayCount(p->pc->mandatory);
4328
/* Number of mandatory attributes */
4330
attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4331
"printer-mandatory-job-attributes", count, NULL,
4334
for (val = attr->values,
4335
mandatory = (char *)cupsArrayFirst(p->pc->mandatory);
4337
val ++, mandatory = (char *)cupsArrayNext(p->pc->mandatory))
4338
val->string.text = _cupsStrAlloc(mandatory);
4342
* Printer resolutions...
4345
if ((resolution = ppdFindOption(ppd, "Resolution")) == NULL)
4346
if ((resolution = ppdFindOption(ppd, "JCLResolution")) == NULL)
4347
if ((resolution = ppdFindOption(ppd, "SetResolution")) == NULL)
4348
resolution = ppdFindOption(ppd, "CNRes_PGP");
4353
* Report all supported resolutions...
4356
attr = ippAddResolutions(p->ppd_attrs, IPP_TAG_PRINTER,
4357
"printer-resolution-supported",
4358
resolution->num_choices, IPP_RES_PER_INCH,
4361
for (i = 0, choice = resolution->choices;
4362
i < resolution->num_choices;
4365
xdpi = ydpi = (int)strtol(choice->choice, (char **)&resptr, 10);
4366
if (resptr > choice->choice && xdpi > 0 && *resptr == 'x')
4367
ydpi = (int)strtol(resptr + 1, (char **)&resptr, 10);
4369
if (xdpi <= 0 || ydpi <= 0)
4371
cupsdLogMessage(CUPSD_LOG_WARN,
4372
"Bad resolution \"%s\" for printer %s.",
4373
choice->choice, p->name);
4377
attr->values[i].resolution.xres = xdpi;
4378
attr->values[i].resolution.yres = ydpi;
4379
attr->values[i].resolution.units = IPP_RES_PER_INCH;
4382
ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4383
"printer-resolution-default", IPP_RES_PER_INCH,
4387
else if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL &&
4391
* Just the DefaultResolution to report...
4394
xdpi = ydpi = (int)strtol(ppd_attr->value, (char **)&resptr, 10);
4395
if (resptr > ppd_attr->value && xdpi > 0)
4398
ydpi = (int)strtol(resptr + 1, (char **)&resptr, 10);
4403
if (xdpi <= 0 || ydpi <= 0)
4405
cupsdLogMessage(CUPSD_LOG_WARN,
4406
"Bad default resolution \"%s\" for printer %s.",
4407
ppd_attr->value, p->name);
4411
ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4412
"printer-resolution-default", IPP_RES_PER_INCH,
4414
ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4415
"printer-resolution-supported", IPP_RES_PER_INCH,
4421
* No resolutions in PPD - make one up...
4424
ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4425
"printer-resolution-default", IPP_RES_PER_INCH,
4427
ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
4428
"printer-resolution-supported", IPP_RES_PER_INCH,
4436
ppdMarkDefaults(ppd);
4438
if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
4439
if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
4440
if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
4441
if ((duplex = ppdFindOption(ppd, "KD03Duplex")) == NULL)
4442
duplex = ppdFindOption(ppd, "JCLDuplex");
4444
if (duplex && duplex->num_choices > 1 &&
4445
!ppdInstallableConflict(ppd, duplex->keyword, "DuplexTumble"))
4447
p->type |= CUPS_PRINTER_DUPLEX;
4449
ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4450
"sides-supported", 3, NULL, sides);
4452
if (!_cups_strcasecmp(duplex->defchoice, "DuplexTumble"))
4453
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4454
"sides-default", NULL, "two-sided-short-edge");
4455
else if (!_cups_strcasecmp(duplex->defchoice, "DuplexNoTumble"))
4456
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4457
"sides-default", NULL, "two-sided-long-edge");
4459
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4460
"sides-default", NULL, "one-sided");
4464
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4465
"sides-supported", NULL, "one-sided");
4466
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4467
"sides-default", NULL, "one-sided");
4470
if (ppdFindOption(ppd, "Collate") != NULL)
4471
p->type |= CUPS_PRINTER_COLLATE;
4473
if (ppdFindOption(ppd, "StapleLocation") != NULL)
4475
p->type |= CUPS_PRINTER_STAPLE;
4476
finishings[num_finishings++] = IPP_FINISHINGS_STAPLE;
4479
if (ppdFindOption(ppd, "BindEdge") != NULL)
4481
p->type |= CUPS_PRINTER_BIND;
4482
finishings[num_finishings++] = IPP_FINISHINGS_BIND;
4485
for (i = 0; i < ppd->num_sizes; i ++)
4486
if (ppd->sizes[i].length > 1728)
4487
p->type |= CUPS_PRINTER_LARGE;
4488
else if (ppd->sizes[i].length > 1008)
4489
p->type |= CUPS_PRINTER_MEDIUM;
4491
p->type |= CUPS_PRINTER_SMALL;
4493
if ((ppd_attr = ppdFindAttr(ppd, "APICADriver", NULL)) != NULL &&
4494
ppd_attr->value && !_cups_strcasecmp(ppd_attr->value, "true"))
4496
if ((ppd_attr = ppdFindAttr(ppd, "APScannerOnly", NULL)) != NULL &&
4497
ppd_attr->value && !_cups_strcasecmp(ppd_attr->value, "true"))
4498
p->type |= CUPS_PRINTER_SCANNER;
4500
p->type |= CUPS_PRINTER_MFP;
4504
* Scan the filters in the PPD file...
4509
for (filter = (const char *)cupsArrayFirst(p->pc->filters);
4511
filter = (const char *)cupsArrayNext(p->pc->filters))
4513
if (!_cups_strncasecmp(filter, "application/vnd.cups-command", 28) &&
4514
_cups_isspace(filter[28]))
4516
p->type |= CUPS_PRINTER_COMMANDS;
4522
if (p->type & CUPS_PRINTER_COMMANDS)
4524
char *commands, /* Copy of commands */
4525
*start, /* Start of name */
4526
*end; /* End of name */
4527
int count; /* Number of commands */
4529
if ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL)
4531
for (count = 0, start = ppd_attr->value; *start; count ++)
4533
while (_cups_isspace(*start))
4539
while (*start && !isspace(*start & 255))
4549
* Make a copy of the commands string and count how many commands there
4553
attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4554
"printer-commands", count, NULL, NULL);
4556
commands = strdup(ppd_attr->value);
4558
for (count = 0, start = commands; *start; count ++)
4560
while (isspace(*start & 255))
4567
while (*end && !isspace(*end & 255))
4573
attr->values[count].string.text = _cupsStrAlloc(start);
4583
* Add the standard list of commands...
4586
ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4588
(int)(sizeof(standard_commands) /
4589
sizeof(standard_commands[0])), NULL,
4596
* No commands supported...
4599
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
4600
"printer-commands", NULL, "none");
4604
* Show current and available port monitors for this printer...
4607
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "port-monitor",
4608
NULL, p->port_monitor ? p->port_monitor : "none");
4610
for (i = 1, ppd_attr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
4612
i ++, ppd_attr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL));
4616
if (strstr(ppd->protocols, "TBCP"))
4618
else if (strstr(ppd->protocols, "BCP"))
4622
attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
4623
"port-monitor-supported", i, NULL, NULL);
4625
attr->values[0].string.text = _cupsStrAlloc("none");
4627
for (i = 1, ppd_attr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
4629
i ++, ppd_attr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
4630
attr->values[i].string.text = _cupsStrAlloc(ppd_attr->value);
4634
if (strstr(ppd->protocols, "TBCP"))
4635
attr->values[i].string.text = _cupsStrAlloc("tbcp");
4636
else if (strstr(ppd->protocols, "BCP"))
4637
attr->values[i].string.text = _cupsStrAlloc("bcp");
4640
if (ppdFindAttr(ppd, "APRemoteQueueID", NULL))
4641
p->type |= CUPS_PRINTER_REMOTE;
4643
#ifdef HAVE_APPLICATIONSERVICES_H
4645
* Convert the file referenced in APPrinterIconPath to a 128x128 PNG
4646
* and save it as cacheDir/printername.png
4649
if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL &&
4651
!_cupsFileCheck(ppd_attr->value, _CUPS_FILE_CHECK_FILE, !RunUser,
4652
cupsdLogFCMessage, p))
4654
CGImageRef imageRef = NULL;/* Current icon image */
4655
CGImageRef biggestIconRef = NULL;
4656
/* Biggest icon image */
4657
CGImageRef closestTo128IconRef = NULL;
4658
/* Icon image closest to and >= 128 */
4659
CGImageSourceRef sourceRef; /* The file's image source */
4660
char outPath[HTTP_MAX_URI];
4661
/* The path to the PNG file */
4662
CFURLRef outUrl; /* The URL made from the outPath */
4663
CFURLRef icnsFileUrl; /* The URL of the original ICNS icon file */
4664
CGImageDestinationRef destRef; /* The image destination to write */
4665
size_t bytesPerRow; /* The bytes per row used for resizing */
4666
CGContextRef context; /* The CG context used for resizing */
4668
snprintf(outPath, sizeof(outPath), "%s/%s.png", CacheDir, p->name);
4669
outUrl = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
4673
icnsFileUrl = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
4674
(UInt8 *)ppd_attr->value,
4675
strlen(ppd_attr->value),
4677
if (outUrl && icnsFileUrl)
4679
sourceRef = CGImageSourceCreateWithURL(icnsFileUrl, NULL);
4682
for (i = 0; i < CGImageSourceGetCount(sourceRef); i ++)
4684
imageRef = CGImageSourceCreateImageAtIndex(sourceRef, i, NULL);
4688
if (CGImageGetWidth(imageRef) == CGImageGetHeight(imageRef))
4691
* Loop through remembering the icon closest to 128 but >= 128
4692
* and then remember the largest icon.
4695
if (CGImageGetWidth(imageRef) >= 128 &&
4696
(!closestTo128IconRef ||
4697
CGImageGetWidth(imageRef) <
4698
CGImageGetWidth(closestTo128IconRef)))
4700
CGImageRelease(closestTo128IconRef);
4701
CGImageRetain(imageRef);
4702
closestTo128IconRef = imageRef;
4705
if (!biggestIconRef ||
4706
CGImageGetWidth(imageRef) > CGImageGetWidth(biggestIconRef))
4708
CGImageRelease(biggestIconRef);
4709
CGImageRetain(imageRef);
4710
biggestIconRef = imageRef;
4714
CGImageRelease(imageRef);
4720
* If biggestIconRef is NULL, we found no icons. Otherwise we first
4721
* want the closest to 128, but if none are larger than 128, we want
4722
* the largest icon available.
4725
imageRef = closestTo128IconRef ? closestTo128IconRef :
4727
CGImageRetain(imageRef);
4728
CGImageRelease(biggestIconRef);
4729
if (closestTo128IconRef)
4730
CGImageRelease(closestTo128IconRef);
4731
destRef = CGImageDestinationCreateWithURL(outUrl, kUTTypePNG, 1,
4735
if (CGImageGetWidth(imageRef) != 128)
4737
bytesPerRow = CGImageGetBytesPerRow(imageRef) /
4738
CGImageGetWidth(imageRef) * 128;
4739
context = CGBitmapContextCreate(NULL, 128, 128,
4740
CGImageGetBitsPerComponent(imageRef),
4742
CGImageGetColorSpace(imageRef),
4743
kCGImageAlphaPremultipliedFirst);
4746
CGContextDrawImage(context, CGRectMake(0, 0, 128, 128),
4748
CGImageRelease(imageRef);
4749
imageRef = CGBitmapContextCreateImage(context);
4750
CGContextRelease(context);
4754
CGImageDestinationAddImage(destRef, imageRef, NULL);
4755
CGImageDestinationFinalize(destRef);
4759
CGImageRelease(imageRef);
4762
CFRelease(sourceRef);
4770
CFRelease(icnsFileUrl);
4772
#endif /* HAVE_APPLICATIONSERVICES_H */
4775
* Close the PPD and set the type...
4780
else if (!access(ppd_name, 0))
4782
int pline; /* PPD line number */
4783
ppd_status_t pstatus; /* PPD load status */
4786
pstatus = ppdLastError(&pline);
4788
cupsdLogMessage(CUPSD_LOG_ERROR, "PPD file for %s cannot be loaded!",
4791
if (pstatus <= PPD_ALLOC_ERROR)
4792
cupsdLogMessage(CUPSD_LOG_ERROR, "%s", strerror(errno));
4794
cupsdLogMessage(CUPSD_LOG_ERROR, "%s on line %d.",
4795
ppdErrorString(pstatus), pline);
4797
cupsdLogMessage(CUPSD_LOG_INFO,
4798
"Hint: Run \"cupstestppd %s\" and fix any errors.",
4804
* If we have an interface script, add a filter entry for it...
4807
char interface[1024]; /* Interface script */
4810
snprintf(interface, sizeof(interface), "%s/interfaces/%s", ServerRoot,
4812
if (!access(interface, X_OK))
4815
* Yes, we have a System V style interface script; use it!
4818
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4819
"printer-make-and-model", NULL,
4820
"Local System V Printer");
4822
else if (((!strncmp(p->device_uri, "ipp://", 6) ||
4823
!strncmp(p->device_uri, "ipps://", 7)) &&
4824
(strstr(p->device_uri, "/printers/") != NULL ||
4825
strstr(p->device_uri, "/classes/") != NULL)) ||
4826
((strstr(p->device_uri, "._ipp.") != NULL ||
4827
strstr(p->device_uri, "._ipps.") != NULL) &&
4828
!strcmp(p->device_uri + strlen(p->device_uri) - 5, "/cups")))
4831
* Tell the client this is really a hard-wired remote printer.
4834
p->type |= CUPS_PRINTER_REMOTE;
4837
* Point the printer-uri-supported attribute to the
4841
if (strchr(p->device_uri, '?'))
4844
* Strip trailing "?options" from URI...
4847
char resource[HTTP_MAX_URI], /* New URI */
4848
*ptr; /* Pointer into URI */
4850
strlcpy(resource, p->device_uri, sizeof(resource));
4851
if ((ptr = strchr(resource, '?')) != NULL)
4854
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
4855
"printer-uri-supported", NULL, resource);
4858
ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
4859
"printer-uri-supported", NULL, p->device_uri);
4862
* Then set the make-and-model accordingly...
4865
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4866
"printer-make-and-model", NULL, "Remote Printer");
4869
* Print all files directly...
4878
* Otherwise we have neither - treat this as a "dumb" printer
4879
* with no PPD file...
4882
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4883
"printer-make-and-model", NULL, "Local Raw Printer");
4889
ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
4890
"finishings-supported", num_finishings, finishings);
4891
ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
4892
"finishings-default", IPP_FINISHINGS_NONE);
4897
* Save cached PPD attributes to disk...
4900
cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Saving %s...", cache_name);
4902
_ppdCacheWriteFile(p->pc, cache_name, p->ppd_attrs);
4907
* Remove cache files...
4910
if (cache_info.st_mtime)
4917
* 'log_ipp_conformance()' - Log an IPP conformance issue with a printer.
4921
log_ipp_conformance(
4922
cupsd_printer_t *p, /* I - Printer */
4923
const char *reason) /* I - Printer state reason */
4925
const char *message; /* Message to log */
4927
aslmsg aslm; /* Apple System Log message */
4928
#endif /* __APPLE__ */
4932
* Strip the leading "cups-ipp-" from the reason and create a log message for
4937
if (!strcmp(reason, "missing-cancel-job"))
4938
message = "Printer does not support REQUIRED Cancel-Job operation.";
4939
else if (!strcmp(reason, "missing-get-job-attributes"))
4940
message = "Printer does not support REQUIRED Get-Job-Attributes operation.";
4941
else if (!strcmp(reason, "missing-print-job"))
4942
message = "Printer does not support REQUIRED Print-Job operation.";
4943
else if (!strcmp(reason, "missing-validate-job"))
4944
message = "Printer does not support REQUIRED Validate-Job operation.";
4945
else if (!strcmp(reason, "missing-get-printer-attributes"))
4946
message = "Printer does not support REQUIRED Get-Printer-Attributes operation.";
4947
else if (!strcmp(reason, "missing-send-document"))
4948
message = "Printer supports Create-Job but not Send-Document operation.";
4949
else if (!strcmp(reason, "missing-job-history"))
4950
message = "Printer does not provide REQUIRED job history.";
4951
else if (!strcmp(reason, "missing-job-id"))
4952
message = "Printer does not provide REQUIRED job-id attribute.";
4953
else if (!strcmp(reason, "missing-job-state"))
4954
message = "Printer does not provide REQUIRED job-state attribute.";
4955
else if (!strcmp(reason, "missing-operations-supported"))
4956
message = "Printer does not provide REQUIRED operations-supported "
4958
else if (!strcmp(reason, "missing-printer-is-accepting-jobs"))
4959
message = "Printer does not provide REQUIRED printer-is-accepting-jobs "
4961
else if (!strcmp(reason, "missing-printer-state-reasons"))
4962
message = "Printer does not provide REQUIRED printer-state-reasons "
4964
else if (!strcmp(reason, "wrong-http-version"))
4965
message = "Printer does not use REQUIRED HTTP/1.1 transport.";
4967
message = "Unknown IPP conformance failure.";
4969
cupsdLogMessage(CUPSD_LOG_WARN, "%s: %s", p->name, message);
4973
* Report the failure information to Apple if the user opts into providing
4974
* feedback to Apple...
4977
aslm = asl_new(ASL_TYPE_MSG);
4980
asl_set(aslm, "com.apple.message.domain", "com.apple.printing.ipp.conformance");
4981
asl_set(aslm, "com.apple.message.domain_scope", "com.apple.printing.ipp.conformance");
4982
asl_set(aslm, "com.apple.message.signature", reason);
4983
asl_set(aslm, "com.apple.message.signature2",
4984
p->make_model ? p->make_model : "Unknown");
4985
asl_log(NULL, aslm, ASL_LEVEL_NOTICE, "%s: %s",
4986
p->make_model ? p->make_model : "Unknown", message);
4989
#endif /* __APPLE__ */
4994
* 'new_media_col()' - Create a media-col collection value.
4997
static ipp_t * /* O - Collection value */
4998
new_media_col(_pwg_size_t *size, /* I - media-size/margin values */
4999
const char *source, /* I - media-source value */
5000
const char *type) /* I - media-type value */
5002
ipp_t *media_col, /* Collection value */
5003
*media_size; /* media-size value */
5006
media_col = ippNew();
5008
media_size = ippNew();
5009
ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5010
"x-dimension", size->width);
5011
ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5012
"y-dimension", size->length);
5013
ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
5014
ippDelete(media_size);
5016
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5017
"media-bottom-margin", size->bottom);
5018
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5019
"media-left-margin", size->left);
5020
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5021
"media-right-margin", size->right);
5022
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5023
"media-top-margin", size->top);
5026
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source",
5030
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type",
5038
* 'write_xml_string()' - Write a string with XML escaping.
5042
write_xml_string(cups_file_t *fp, /* I - File to write to */
5043
const char *s) /* I - String to write */
5045
const char *start; /* Start of current sequence */
5051
for (start = s; *s; s ++)
5056
cupsFileWrite(fp, start, s - start);
5058
cupsFilePuts(fp, "&");
5064
cupsFileWrite(fp, start, s - start);
5066
cupsFilePuts(fp, "<");
5072
cupsFilePuts(fp, start);
5077
* End of "$Id: printers.c 10996 2013-05-29 11:51:34Z msweet $".