X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fprinters.c;h=b0298c0ec382991293a4204f32b58202bd9a5bad;hb=4220952d4d4e3b02701326ea2189a71ab3529a5f;hp=1934383ec2ac16cbc9402d77db0224f8ec4099df;hpb=323c5de1e804061e344172cc9a9551bb0ee71005;p=thirdparty%2Fcups.git diff --git a/scheduler/printers.c b/scheduler/printers.c index 1934383ec..b0298c0ec 100644 --- a/scheduler/printers.c +++ b/scheduler/printers.c @@ -1,60 +1,62 @@ /* - * "$Id: printers.c 6436 2007-04-02 23:24:02Z mike $" + * "$Id: printers.c 7968 2008-09-19 23:03:01Z mike $" * - * Printer routines for the Common UNIX Printing System (CUPS). + * Printer routines for the CUPS scheduler. * + * Copyright 2007-2010 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the - * property of Easy Software Products and are protected by Federal - * copyright law. Distribution and use rights are outlined in the file - * "LICENSE.txt" which should have been included with this file. If this - * file is missing or damaged please contact Easy Software Products - * at: - * - * Attn: CUPS Licensing Information - * Easy Software Products - * 44141 Airport View Drive, Suite 204 - * Hollywood, Maryland 20636 USA - * - * Voice: (301) 373-9600 - * EMail: cups-info@cups.org - * WWW: http://www.cups.org + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". * * Contents: * - * cupsdAddPrinter() - Add a printer to the system. - * cupsdAddPrinterHistory() - Add the current printer state to the history. - * cupsdAddPrinterUser() - Add a user to the ACL. - * cupsdDeleteAllPrinters() - Delete all printers from the system. - * cupsdDeletePrinter() - Delete a printer from the system. - * cupsdFindPrinter() - Find a printer in the list. - * cupsdFreePrinterUsers() - Free allow/deny users. - * cupsdLoadAllPrinters() - Load printers from the printers.conf file. - * cupsdRenamePrinter() - Rename a printer. - * cupsdSaveAllPrinters() - Save all printer definitions to the - * printers.conf file. - * cupsdSetPrinterAttrs() - Set printer attributes based upon the PPD - * file. - * cupsdSetPrinterReasons() - Set/update the reasons strings. - * cupsdSetPrinterState() - Update the current state of a printer. - * cupsdStopPrinter() - Stop a printer from printing any jobs... - * cupsdUpdatePrinters() - Update printers after a partial reload. - * cupsdValidateDest() - Validate a printer/class destination. - * cupsdWritePrintcap() - Write a pseudo-printcap file for older - * applications that need it... - * cupsdSanitizeURI() - Sanitize a device URI... - * add_printer_defaults() - Add name-default attributes to the printer - * attributes. - * add_printer_filter() - Add a MIME filter for a printer. - * add_printer_formats() - Add document-format-supported values for - * a printer. - * compare_printers() - Compare two printers. - * delete_printer_filters() - Delete all MIME filters for a printer. - * write_irix_config() - Update the config files used by the IRIX - * desktop tools. - * write_irix_state() - Update the status files used by IRIX - * printing desktop tools. + * cupsdAddPrinter() - Add a printer to the system. + * cupsdAddPrinterHistory() - Add the current printer state to the history. + * cupsdAddPrinterUser() - Add a user to the ACL. + * cupsdCreateCommonData() - Create the common printer data. + * cupsdDeleteAllPrinters() - Delete all printers from the system. + * cupsdDeletePrinter() - Delete a printer from the system. + * cupsdFindDest() - Find a destination in the list. + * cupsdFindPrinter() - Find a printer in the list. + * cupsdFreePrinterUsers() - Free allow/deny users. + * cupsdLoadAllPrinters() - Load printers from the printers.conf file. + * cupsdRenamePrinter() - Rename a printer. + * cupsdSaveAllPrinters() - Save all printer definitions to the + * printers.conf file. + * cupsdSetAuthInfoRequired() - Set the required authentication info. + * cupsdSetDeviceURI() - Set the device URI for a printer. + * cupsdSetPrinterAttr() - Set a printer attribute. + * cupsdSetPrinterAttrs() - Set printer attributes based upon the PPD + * file. + * cupsdSetPrinterReasons() - Set/update the reasons strings. + * cupsdSetPrinterState() - Update the current state of a printer. + * cupsdStopPrinter() - Stop a printer from printing any jobs... + * cupsdUpdatePrinterPPD() - Update keywords in a printer's PPD file. + * cupsdUpdatePrinters() - Update printers after a partial reload. + * cupsdValidateDest() - Validate a printer/class destination. + * cupsdWritePrintcap() - Write a pseudo-printcap file for older + * applications that need it... + * add_printer_defaults() - Add name-default attributes to the printer + * attributes. + * add_printer_filter() - Add a MIME filter for a printer. + * add_printer_formats() - Add document-format-supported values for a + * printer. + * add_string_array() - Add a string to an array of CUPS strings. + * compare_printers() - Compare two printers. + * delete_printer_filters() - Delete all MIME filters for a printer. + * delete_string_array() - Delete an array of CUPS strings. + * load_ppd() - Load a cached PPD file, updating the cache as + * needed. + * new_media_col() - Create a media-col collection value. + * write_irix_config() - Update the config files used by the IRIX + * desktop tools. + * write_irix_state() - Update the status files used by IRIX printing + * desktop tools. + * write_xml_string() - Write a string with XML escaping. */ /* @@ -73,12 +75,18 @@ static void add_printer_defaults(cupsd_printer_t *p); static void add_printer_filter(cupsd_printer_t *p, mime_type_t *type, const char *filter); static void add_printer_formats(cupsd_printer_t *p); +static void add_string_array(cups_array_t **a, const char *s); static int compare_printers(void *first, void *second, void *data); static void delete_printer_filters(cupsd_printer_t *p); +static void delete_string_array(cups_array_t **a); +static void load_ppd(cupsd_printer_t *p); +static ipp_t *new_media_col(_pwg_size_t *size, const char *source, + const char *type); #ifdef __sgi static void write_irix_config(cupsd_printer_t *p); static void write_irix_state(cupsd_printer_t *p); #endif /* __sgi */ +static void write_xml_string(cups_file_t *fp, const char *s); /* @@ -112,8 +120,9 @@ cupsdAddPrinter(const char *name) /* I - Name of printer */ cupsdSetString(&p->info, name); cupsdSetString(&p->hostname, ServerName); - cupsdSetStringf(&p->uri, "ipp://%s:%d/printers/%s", ServerName, LocalPort, name); - cupsdSetStringf(&p->device_uri, "file:/dev/null"); + cupsdSetStringf(&p->uri, "ipp://%s:%d/printers/%s", ServerName, RemotePort, + name); + cupsdSetDeviceURI(p, "file:///dev/null"); p->state = IPP_PRINTER_STOPPED; p->state_time = time(NULL); @@ -139,6 +148,8 @@ cupsdAddPrinter(const char *name) /* I - Name of printer */ if (!Printers) Printers = cupsArrayNew(compare_printers, NULL); + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdAddPrinter: Adding %s to Printers", p->name); cupsArrayAdd(Printers, p); if (!ImplicitPrinters) @@ -196,15 +207,9 @@ cupsdAddPrinterHistory( ippAddBoolean(history, IPP_TAG_PRINTER, "printer-is-shared", p->shared); ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-state-message", NULL, p->state_message); -#ifdef __APPLE__ - if (p->recoverable) - ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "com.apple.print.recoverable-message", NULL, p->recoverable); -#endif /* __APPLE__ */ if (p->num_reasons == 0) ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "printer-state-reasons", NULL, - p->state == IPP_PRINTER_STOPPED ? "paused" : "none"); + "printer-state-reasons", NULL, "none"); else ippAddStrings(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", p->num_reasons, NULL, @@ -264,9 +269,10 @@ cupsdCreateCommonData(void) cups_array_t *notifiers; /* Notifier array */ char filename[1024], /* Filename */ *notifier; /* Current notifier */ + cupsd_policy_t *p; /* Current policy */ static const int nups[] = /* number-up-supported values */ { 1, 2, 4, 6, 9, 16 }; - static const ipp_orient_t orients[4] =/* orientation-requested-supported values */ + static const int orients[4] =/* orientation-requested-supported values */ { IPP_PORTRAIT, IPP_LANDSCAPE, @@ -287,9 +293,11 @@ cupsdCreateCommonData(void) static const char * const versions[] =/* ipp-versions-supported values */ { "1.0", - "1.1" + "1.1", + "2.0", + "2.1" }; - static const ipp_op_t ops[] = /* operations-supported values */ + static const int ops[] = /* operations-supported values */ { IPP_PRINT_JOB, IPP_VALIDATE_JOB, @@ -301,10 +309,13 @@ cupsdCreateCommonData(void) IPP_GET_PRINTER_ATTRIBUTES, IPP_HOLD_JOB, IPP_RELEASE_JOB, + IPP_RESTART_JOB, IPP_PAUSE_PRINTER, IPP_RESUME_PRINTER, IPP_PURGE_JOBS, + IPP_SET_PRINTER_ATTRIBUTES, IPP_SET_JOB_ATTRIBUTES, + IPP_GET_PRINTER_SUPPORTED_VALUES, IPP_CREATE_PRINTER_SUBSCRIPTION, IPP_CREATE_JOB_SUBSCRIPTION, IPP_GET_SUBSCRIPTION_ATTRIBUTES, @@ -314,6 +325,11 @@ cupsdCreateCommonData(void) IPP_GET_NOTIFICATIONS, IPP_ENABLE_PRINTER, IPP_DISABLE_PRINTER, + IPP_HOLD_NEW_JOBS, + IPP_RELEASE_HELD_NEW_JOBS, + IPP_CANCEL_JOBS, + IPP_CANCEL_MY_JOBS, + IPP_CLOSE_JOB, CUPS_GET_DEFAULT, CUPS_GET_PRINTERS, CUPS_ADD_PRINTER, @@ -328,6 +344,8 @@ cupsdCreateCommonData(void) CUPS_GET_PPDS, CUPS_MOVE_JOB, CUPS_AUTHENTICATE_JOB, + CUPS_GET_PPD, + CUPS_GET_DOCUMENT, IPP_RESTART_JOB }; static const char * const charsets[] =/* charset-supported values */ @@ -342,17 +360,21 @@ cupsdCreateCommonData(void) ,"gzip" #endif /* HAVE_LIBZ */ }; + static const char * const media_col_supported[] = + { /* media-col-supported values */ + "media-bottom-margin", + "media-left-margin", + "media-right-margin", + "media-size", + "media-source", + "media-top-margin", + "media-type" + }; static const char * const multiple_document_handling[] = { /* multiple-document-handling-supported values */ "separate-documents-uncollated-copies", "separate-documents-collated-copies" }; - static const char * const errors[] = /* printer-error-policy-supported values */ - { - "abort-job", - "retry-job", - "stop-printer" - }; static const char * const notify_attrs[] = { /* notify-attributes-supported values */ "printer-state-change-time", @@ -383,6 +405,61 @@ cupsdCreateCommonData(void) "server-started", "server-stopped" }; + static const char * const job_creation[] = + { /* job-creation-attributes-supported */ + "copies", + "finishings", + "ipp-attribute-fidelity", + "job-hold-until", + "job-name", + "job-priority", + "job-sheets", + "media", + "media-col", + "multiple-document-handling", + "number-up", + "output-bin", + "orientation-requested", + "page-ranges", + "print-quality", + "printer-resolution", + "sides" + }; + static const char * const job_settable[] = + { /* job-settable-attributes-supported */ + "copies", + "finishings", + "job-hold-until", + "job-name", + "job-priority", + "media", + "media-col", + "multiple-document-handling", + "number-up", + "output-bin", + "orientation-requested", + "page-ranges", + "print-quality", + "printer-resolution", + "sides" + }; + static const char * const printer_settable[] = + { /* printer-settable-attributes-supported */ + "printer-info", + "printer-location" + }; + static const char * const which_jobs[] = + { /* which-jobs-supported values */ + "completed", + "not-completed", + "aborted", + "all", + "canceled", + "pending", + "pending-held", + "processing", + "processing-stopped" + }; if (CommonData) @@ -396,16 +473,16 @@ cupsdCreateCommonData(void) */ /* charset-configured */ - ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET, - "charset-configured", NULL, DefaultCharset); + ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY, + "charset-configured", NULL, "utf-8"); /* charset-supported */ - ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET, + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY, "charset-supported", sizeof(charsets) / sizeof(charsets[0]), NULL, charsets); /* compression-supported */ - ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "compression-supported", sizeof(compressions) / sizeof(compressions[0]), NULL, compressions); @@ -413,24 +490,47 @@ cupsdCreateCommonData(void) /* copies-supported */ ippAddRange(CommonData, IPP_TAG_PRINTER, "copies-supported", 1, MaxCopies); - /* generated-natural-language-supported */ + /* cups-version */ + ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_TEXT | IPP_TAG_COPY, + "cups-version", NULL, CUPS_SVERSION + 6); + + /* generated-natural-language-supported (no IPP_TAG_COPY) */ ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE, "generated-natural-language-supported", NULL, DefaultLanguage); /* ipp-versions-supported */ - ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]), NULL, versions); + /* ippget-event-life */ + ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "ippget-event-life", 15); + + /* job-creation-attributes-supported */ + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, + "job-creation-attributes-supported", + sizeof(job_creation) / sizeof(job_creation[0]), + NULL, job_creation); + /* job-hold-until-supported */ - ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-hold-until-supported", sizeof(holds) / sizeof(holds[0]), NULL, holds); + /* job-ids-supported */ + ippAddBoolean(CommonData, IPP_TAG_PRINTER, "job-ids-supported", 1); + /* job-priority-supported */ ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 100); + /* job-settable-attributes-supported */ + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, + "job-settable-attributes-supported", + sizeof(job_settable) / sizeof(job_settable[0]), + NULL, job_settable); + /* job-sheets-supported */ if (cupsArrayCount(Banners) > 0) { @@ -439,10 +539,12 @@ cupsdCreateCommonData(void) */ if (Classification && !ClassifyOverride) - attr = ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME, + attr = ippAddString(CommonData, IPP_TAG_PRINTER, + IPP_TAG_NAME | IPP_TAG_COPY, "job-sheets-supported", NULL, Classification); else - attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME, + attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, + IPP_TAG_NAME | IPP_TAG_COPY, "job-sheets-supported", cupsArrayCount(Banners) + 1, NULL, NULL); @@ -460,15 +562,22 @@ cupsdCreateCommonData(void) for (i = 1, banner = (cupsd_banner_t *)cupsArrayFirst(Banners); banner; i ++, banner = (cupsd_banner_t *)cupsArrayNext(Banners)) - attr->values[i].string.text = _cupsStrAlloc(banner->name); + attr->values[i].string.text = banner->name; } } else - ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME, + ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY, "job-sheets-supported", NULL, "none"); + /* media-col-supported */ + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, + "media-col-supported", + sizeof(media_col_supported) / + sizeof(media_col_supported[0]), NULL, + media_col_supported); + /* multiple-document-handling-supported */ - ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "multiple-document-handling-supported", sizeof(multiple_document_handling) / sizeof(multiple_document_handling[0]), NULL, @@ -480,14 +589,14 @@ cupsdCreateCommonData(void) /* multiple-operation-time-out */ ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "multiple-operation-time-out", 60); + "multiple-operation-time-out", MultipleOperationTimeout); - /* natural-language-configured */ + /* natural-language-configured (no IPP_TAG_COPY) */ ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE, "natural-language-configured", NULL, DefaultLanguage); /* notify-attributes-supported */ - ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "notify-attributes-supported", (int)(sizeof(notify_attrs) / sizeof(notify_attrs[0])), NULL, notify_attrs); @@ -502,13 +611,13 @@ cupsdCreateCommonData(void) "notify-max-events-supported", MaxEvents); /* notify-events-supported */ - ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "notify-events-supported", (int)(sizeof(notify_events) / sizeof(notify_events[0])), NULL, notify_events); /* notify-pull-method-supported */ - ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "notify-pull-method-supported", NULL, "ippget"); /* notify-schemes-supported */ @@ -545,29 +654,42 @@ cupsdCreateCommonData(void) /* operations-supported */ ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", - sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, (int *)ops); + sizeof(ops) / sizeof(ops[0]) + JobFiles - 1, ops); /* orientation-requested-supported */ ippAddIntegers(CommonData, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "orientation-requested-supported", 4, (int *)orients); + "orientation-requested-supported", 4, orients); /* page-ranges-supported */ ippAddBoolean(CommonData, IPP_TAG_PRINTER, "page-ranges-supported", 1); /* pdf-override-supported */ - ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "pdl-override-supported", NULL, "not-attempted"); - /* printer-error-policy-supported */ - ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME, - "printer-error-policy-supported", - sizeof(errors) / sizeof(errors[0]), NULL, errors); - /* printer-op-policy-supported */ - attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME, - "printer-op-policy-supported", NumPolicies, NULL, NULL); - for (i = 0; i < NumPolicies; i ++) - attr->values[i].string.text = _cupsStrAlloc(Policies[i]->name); + attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY, + "printer-op-policy-supported", cupsArrayCount(Policies), + NULL, NULL); + for (i = 0, p = (cupsd_policy_t *)cupsArrayFirst(Policies); + p; + i ++, p = (cupsd_policy_t *)cupsArrayNext(Policies)) + attr->values[i].string.text = p->name; + + /* printer-settable-attributes-supported */ + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, + "printer-settable-attributes-supported", + sizeof(printer_settable) / sizeof(printer_settable[0]), + NULL, printer_settable); + + /* server-is-sharing-printers */ + ippAddBoolean(CommonData, IPP_TAG_PRINTER, "server-is-sharing-printers", + BrowseLocalProtocols != 0 && Browsing); + + /* which-jobs-supported */ + ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, + "which-jobs-supported", + sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs); } @@ -584,8 +706,10 @@ cupsdDeleteAllPrinters(void) for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers)) - if (!(p->type & CUPS_PRINTER_CLASS)) - cupsdDeletePrinter(p, 0); + { + p->op_policy_ptr = DefaultPolicyPtr; + cupsdDeletePrinter(p, 0); + } } @@ -593,14 +717,15 @@ cupsdDeleteAllPrinters(void) * 'cupsdDeletePrinter()' - Delete a printer from the system. */ -void +int /* O - 1 if classes affected, 0 otherwise */ cupsdDeletePrinter( cupsd_printer_t *p, /* I - Printer to delete */ int update) /* I - Update printers.conf? */ { - int i; /* Looping var */ + int i, /* Looping var */ + changed = 0; /* Class changed? */ #ifdef __sgi - char filename[1024]; /* Interface script filename */ + char filename[1024]; /* Interface script filename */ #endif /* __sgi */ @@ -617,7 +742,12 @@ cupsdDeletePrinter( * Stop printing on this printer... */ - cupsdStopPrinter(p, update); + cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, update); + + if (p->job) + cupsdSetJobState(p->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE, + update ? "Job stopped due to printer being deleted." : + "Job stopped."); /* * If this printer is the next for browsing, point to the next one... @@ -633,10 +763,17 @@ cupsdDeletePrinter( * Remove the printer from the list... */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdDeletePrinter: Removing %s from Printers", p->name); cupsArrayRemove(Printers, p); if (p->type & CUPS_PRINTER_IMPLICIT) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdDeletePrinter: Removing %s from ImplicitPrinters", + p->name); cupsArrayRemove(ImplicitPrinters, p); + } /* * Remove the dummy interface/icon/option files under IRIX... @@ -697,7 +834,7 @@ cupsdDeletePrinter( if (!(p->type & CUPS_PRINTER_IMPLICIT)) { - cupsdDeletePrinterFromClasses(p); + changed = cupsdDeletePrinterFromClasses(p); /* * Deregister from any browse protocols... @@ -721,16 +858,20 @@ cupsdDeletePrinter( free(p->history); } + delete_printer_filters(p); + for (i = 0; i < p->num_reasons; i ++) - free(p->reasons[i]); + _cupsStrFree(p->reasons[i]); ippDelete(p->attrs); - - delete_printer_filters(p); + ippDelete(p->ppd_attrs); mimeDeleteType(MimeDatabase, p->filetype); mimeDeleteType(MimeDatabase, p->prefiltertype); + delete_string_array(&(p->filters)); + delete_string_array(&(p->pre_filters)); + cupsdFreePrinterUsers(p); cupsdFreeQuotas(p); @@ -743,6 +884,7 @@ cupsdDeletePrinter( cupsdClearString(&p->job_sheets[0]); cupsdClearString(&p->job_sheets[1]); cupsdClearString(&p->device_uri); + cupsdClearString(&p->sanitized_device_uri); cupsdClearString(&p->port_monitor); cupsdClearString(&p->op_policy); cupsdClearString(&p->error_policy); @@ -760,10 +902,6 @@ cupsdDeletePrinter( if (p->browse_attrs) free(p->browse_attrs); -#ifdef __APPLE__ - cupsdClearString(&p->recoverable); -#endif /* __APPLE__ */ - cupsFreeOptions(p->num_options, p->options); free(p); @@ -773,6 +911,8 @@ cupsdDeletePrinter( */ cupsArrayRestore(Printers); + + return (changed); } @@ -839,9 +979,10 @@ cupsdFreePrinterUsers( void cupsdLoadAllPrinters(void) { + int i; /* Looping var */ cups_file_t *fp; /* printers.conf file */ int linenum; /* Current line number */ - char line[1024], /* Line from file */ + char line[4096], /* Line from file */ *value, /* Pointer to value */ *valueptr; /* Pointer into value */ cupsd_printer_t *p; /* Current printer */ @@ -900,11 +1041,8 @@ cupsdLoadAllPrinters(void) DefaultPrinter = p; } else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "")) { @@ -917,7 +1055,7 @@ cupsdLoadAllPrinters(void) cupsdSetPrinterAttrs(p); cupsdAddPrinterHistory(p); - if (p->device_uri && strncmp(p->device_uri, "file:", 5) && + if (strncmp(p->device_uri, "file:", 5) && p->state != IPP_PRINTER_STOPPED) { /* @@ -945,17 +1083,13 @@ cupsdLoadAllPrinters(void) p = NULL; } else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!p) { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; } else if (!strcasecmp(line, "AuthInfoRequired")) { @@ -969,6 +1103,11 @@ cupsdLoadAllPrinters(void) if (value) cupsdSetString(&p->info, value); } + else if (!strcasecmp(line, "MakeModel")) + { + if (value) + cupsdSetString(&p->make_model, value); + } else if (!strcasecmp(line, "Location")) { if (value) @@ -977,13 +1116,10 @@ cupsdLoadAllPrinters(void) else if (!strcasecmp(line, "DeviceURI")) { if (value) - cupsdSetString(&p->device_uri, value); + cupsdSetDeviceURI(p, value); else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "Option") && value) { @@ -1011,11 +1147,30 @@ cupsdLoadAllPrinters(void) else if (value) cupsdClearString(&p->port_monitor); else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; + } + else if (!strcasecmp(line, "Reason")) + { + if (value && + strcmp(value, "connecting-to-device") && + strcmp(value, "cups-insecure-filter-warning") && + strcmp(value, "cups-missing-filter-warning")) + { + for (i = 0 ; i < p->num_reasons; i ++) + if (!strcmp(value, p->reasons[i])) + break; + + if (i >= p->num_reasons && + p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0]))) + { + p->reasons[p->num_reasons] = _cupsStrAlloc(value); + p->num_reasons ++; + } } + else + cupsdLogMessage(CUPSD_LOG_ERROR, + "Syntax error on line %d of printers.conf.", linenum); } else if (!strcasecmp(line, "State")) { @@ -1026,13 +1181,23 @@ cupsdLoadAllPrinters(void) if (value && !strcasecmp(value, "idle")) p->state = IPP_PRINTER_IDLE; else if (value && !strcasecmp(value, "stopped")) + { p->state = IPP_PRINTER_STOPPED; + + for (i = 0 ; i < p->num_reasons; i ++) + if (!strcmp("paused", p->reasons[i])) + break; + + if (i >= p->num_reasons && + p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0]))) + { + p->reasons[p->num_reasons] = _cupsStrAlloc("paused"); + p->num_reasons ++; + } + } else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "StateMessage")) { @@ -1069,11 +1234,54 @@ cupsdLoadAllPrinters(void) !strcasecmp(value, "false"))) p->accepting = 0; else + cupsdLogMessage(CUPSD_LOG_ERROR, + "Syntax error on line %d of printers.conf.", linenum); + } + else if (!strcasecmp(line, "Type")) + { + if (value) + p->type = atoi(value); + else + cupsdLogMessage(CUPSD_LOG_ERROR, + "Syntax error on line %d of printers.conf.", linenum); + } + else if (!strcasecmp(line, "Product")) + { + if (value) + { +#ifdef HAVE_DNSSD + p->product = _cupsStrAlloc(value); +#endif /* HAVE_DNSSD */ + } + else + cupsdLogMessage(CUPSD_LOG_ERROR, + "Syntax error on line %d of printers.conf.", linenum); + } + else if (!strcasecmp(line, "Filter")) + { + if (value) { + if (!p->filters) + p->filters = cupsArrayNew(NULL, NULL); + + cupsArrayAdd(p->filters, _cupsStrAlloc(value)); + } + else cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; + } + else if (!strcasecmp(line, "PreFilter")) + { + if (value) + { + if (!p->pre_filters) + p->pre_filters = cupsArrayNew(NULL, NULL); + + cupsArrayAdd(p->pre_filters, _cupsStrAlloc(value)); } + else + cupsdLogMessage(CUPSD_LOG_ERROR, + "Syntax error on line %d of printers.conf.", linenum); } else if (!strcasecmp(line, "Shared")) { @@ -1092,11 +1300,8 @@ cupsdLoadAllPrinters(void) !strcasecmp(value, "false"))) p->shared = 0; else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "JobSheets")) { @@ -1121,17 +1326,14 @@ cupsdLoadAllPrinters(void) for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++); if (*valueptr) - *valueptr++ = '\0'; + *valueptr = '\0'; cupsdSetString(&p->job_sheets[1], value); } } else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "AllowUser")) { @@ -1141,11 +1343,8 @@ cupsdLoadAllPrinters(void) cupsdAddPrinterUser(p, value); } else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "DenyUser")) { @@ -1155,44 +1354,32 @@ cupsdLoadAllPrinters(void) cupsdAddPrinterUser(p, value); } else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "QuotaPeriod")) { if (value) p->quota_period = atoi(value); else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "PageLimit")) { if (value) p->page_limit = atoi(value); else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "KLimit")) { if (value) p->k_limit = atoi(value); else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "OpPolicy")) { @@ -1212,21 +1399,35 @@ cupsdLoadAllPrinters(void) value, linenum); } else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; - } } else if (!strcasecmp(line, "ErrorPolicy")) { if (value) cupsdSetString(&p->error_policy, value); else - { cupsdLogMessage(CUPSD_LOG_ERROR, "Syntax error on line %d of printers.conf.", linenum); - return; + } + else if (!strcasecmp(line, "Attribute") && value) + { + for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++); + + if (!*valueptr) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Syntax error on line %d of printers.conf.", linenum); + else + { + for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0'); + + if (!p->attrs) + cupsdSetPrinterAttrs(p); + + if (!strcmp(value, "marker-change-time")) + p->marker_time = atoi(valueptr); + else + cupsdSetPrinterAttr(p, value, valueptr); } } else @@ -1258,10 +1459,17 @@ cupsdRenamePrinter( * Remove the printer from the array(s) first... */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdRenamePrinter: Removing %s from Printers", p->name); cupsArrayRemove(Printers, p); if (p->type & CUPS_PRINTER_IMPLICIT) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdRenamePrinter: Removing %s from ImplicitPrinters", + p->name); cupsArrayRemove(ImplicitPrinters, p); + } /* * Rename the printer type... @@ -1289,10 +1497,17 @@ cupsdRenamePrinter( * Add the printer back to the printer array(s)... */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdRenamePrinter: Adding %s to Printers", p->name); cupsArrayAdd(Printers, p); if (p->type & CUPS_PRINTER_IMPLICIT) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdRenamePrinter: Adding %s to ImplicitPrinters", + p->name); cupsArrayAdd(ImplicitPrinters, p); + } } @@ -1306,13 +1521,15 @@ cupsdSaveAllPrinters(void) { int i; /* Looping var */ cups_file_t *fp; /* printers.conf file */ - char temp[1024]; /* Temporary string */ - char backup[1024]; /* printers.conf.O file */ + char temp[1024], /* Temporary string */ + backup[1024], /* printers.conf.O file */ + value[2048], /* Value string */ + *ptr; /* Pointer into value */ cupsd_printer_t *printer; /* Current printer class */ time_t curtime; /* Current time */ struct tm *curdate; /* Current date */ cups_option_t *option; /* Current option */ - const char *ptr; /* Pointer into info/location */ + ipp_attribute_t *marker; /* Current marker attribute */ /* @@ -1359,6 +1576,7 @@ cupsdSaveAllPrinters(void) cupsFilePuts(fp, "# Printer configuration file for " CUPS_SVERSION "\n"); cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp); + cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n"); /* * Write each local printer known to the system... @@ -1372,7 +1590,7 @@ cupsdSaveAllPrinters(void) * Skip remote destinations and printer classes... */ - if ((printer->type & CUPS_PRINTER_REMOTE) || + if ((printer->type & CUPS_PRINTER_DISCOVERED) || (printer->type & CUPS_PRINTER_CLASS) || (printer->type & CUPS_PRINTER_IMPLICIT)) continue; @@ -1388,63 +1606,79 @@ cupsdSaveAllPrinters(void) if (printer->num_auth_info_required > 0) { - cupsFilePrintf(fp, "AuthInfoRequired %s", printer->auth_info_required[0]); - for (i = 1; i < printer->num_auth_info_required; i ++) - cupsFilePrintf(fp, ",%s", printer->auth_info_required[i]); - cupsFilePutChar(fp, '\n'); - } - - if (printer->info) - { - if ((ptr = strchr(printer->info, '#')) != NULL) + switch (printer->num_auth_info_required) { - /* - * Need to quote the first # in the info string... - */ - - cupsFilePuts(fp, "Info "); - cupsFileWrite(fp, printer->info, ptr - printer->info); - cupsFilePutChar(fp, '\\'); - cupsFilePuts(fp, ptr); - cupsFilePutChar(fp, '\n'); + case 1 : + strlcpy(value, printer->auth_info_required[0], sizeof(value)); + break; + + case 2 : + snprintf(value, sizeof(value), "%s,%s", + printer->auth_info_required[0], + printer->auth_info_required[1]); + break; + + case 3 : + default : + snprintf(value, sizeof(value), "%s,%s,%s", + printer->auth_info_required[0], + printer->auth_info_required[1], + printer->auth_info_required[2]); + break; } - else - cupsFilePrintf(fp, "Info %s\n", printer->info); + + cupsFilePutConf(fp, "AuthInfoRequired", value); } + if (printer->info) + cupsFilePutConf(fp, "Info", printer->info); + if (printer->location) - { - if ((ptr = strchr(printer->info, '#')) != NULL) - { - /* - * Need to quote the first # in the location string... - */ + cupsFilePutConf(fp, "Location", printer->location); - cupsFilePuts(fp, "Location "); - cupsFileWrite(fp, printer->location, ptr - printer->location); - cupsFilePutChar(fp, '\\'); - cupsFilePuts(fp, ptr); - cupsFilePutChar(fp, '\n'); - } - else - cupsFilePrintf(fp, "Location %s\n", printer->location); - } - if (printer->device_uri) - cupsFilePrintf(fp, "DeviceURI %s\n", printer->device_uri); + if (printer->make_model) + cupsFilePutConf(fp, "MakeModel", printer->make_model); + + cupsFilePutConf(fp, "DeviceURI", printer->device_uri); if (printer->port_monitor) - cupsFilePrintf(fp, "PortMonitor %s\n", printer->port_monitor); + cupsFilePutConf(fp, "PortMonitor", printer->port_monitor); if (printer->state == IPP_PRINTER_STOPPED) { cupsFilePuts(fp, "State Stopped\n"); - cupsFilePrintf(fp, "StateMessage %s\n", printer->state_message); + + if (printer->state_message) + cupsFilePutConf(fp, "StateMessage", printer->state_message); } else cupsFilePuts(fp, "State Idle\n"); cupsFilePrintf(fp, "StateTime %d\n", (int)printer->state_time); + for (i = 0; i < printer->num_reasons; i ++) + if (strcmp(printer->reasons[i], "connecting-to-device") && + strcmp(printer->reasons[i], "cups-insecure-filter-warning") && + strcmp(printer->reasons[i], "cups-missing-filter-warning")) + cupsFilePutConf(fp, "Reason", printer->reasons[i]); + + cupsFilePrintf(fp, "Type %d\n", printer->type); + +#ifdef HAVE_DNSSD + if (printer->product) + cupsFilePutConf(fp, "Product", printer->product); +#endif /* HAVE_DNSSD */ + + for (ptr = (char *)cupsArrayFirst(printer->filters); + ptr; + ptr = (char *)cupsArrayNext(printer->filters)) + cupsFilePutConf(fp, "Filter", ptr); + + for (ptr = (char *)cupsArrayFirst(printer->pre_filters); + ptr; + ptr = (char *)cupsArrayNext(printer->pre_filters)) + cupsFilePutConf(fp, "PreFilter", ptr); + if (printer->accepting) cupsFilePuts(fp, "Accepting Yes\n"); else @@ -1455,26 +1689,136 @@ cupsdSaveAllPrinters(void) else cupsFilePuts(fp, "Shared No\n"); - cupsFilePrintf(fp, "JobSheets %s %s\n", printer->job_sheets[0], - printer->job_sheets[1]); + snprintf(value, sizeof(value), "%s %s", printer->job_sheets[0], + printer->job_sheets[1]); + cupsFilePutConf(fp, "JobSheets", value); cupsFilePrintf(fp, "QuotaPeriod %d\n", printer->quota_period); cupsFilePrintf(fp, "PageLimit %d\n", printer->page_limit); cupsFilePrintf(fp, "KLimit %d\n", printer->k_limit); for (i = 0; i < printer->num_users; i ++) - cupsFilePrintf(fp, "%sUser %s\n", printer->deny_users ? "Deny" : "Allow", - printer->users[i]); + cupsFilePutConf(fp, printer->deny_users ? "DenyUser" : "AllowUser", + printer->users[i]); if (printer->op_policy) - cupsFilePrintf(fp, "OpPolicy %s\n", printer->op_policy); + cupsFilePutConf(fp, "OpPolicy", printer->op_policy); if (printer->error_policy) - cupsFilePrintf(fp, "ErrorPolicy %s\n", printer->error_policy); + cupsFilePutConf(fp, "ErrorPolicy", printer->error_policy); for (i = printer->num_options, option = printer->options; i > 0; i --, option ++) - cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value); + { + snprintf(value, sizeof(value), "%s %s", option->name, option->value); + cupsFilePutConf(fp, "Option", value); + } + + if ((marker = ippFindAttribute(printer->attrs, "marker-colors", + IPP_TAG_NAME)) != NULL) + { + snprintf(value, sizeof(value), "%s ", marker->name); + + for (i = 0, ptr = value + strlen(value); + i < marker->num_values && ptr < (value + sizeof(value) - 1); + i ++) + { + if (i) + *ptr++ = ','; + + strlcpy(ptr, marker->values[i].string.text, + value + sizeof(value) - ptr); + ptr += strlen(ptr); + } + + *ptr = '\0'; + cupsFilePutConf(fp, "Attribute", value); + } + + if ((marker = ippFindAttribute(printer->attrs, "marker-levels", + IPP_TAG_INTEGER)) != NULL) + { + cupsFilePrintf(fp, "Attribute %s %d", marker->name, + marker->values[0].integer); + for (i = 1; i < marker->num_values; i ++) + cupsFilePrintf(fp, ",%d", marker->values[i].integer); + cupsFilePuts(fp, "\n"); + } + + if ((marker = ippFindAttribute(printer->attrs, "marker-low-levels", + IPP_TAG_INTEGER)) != NULL) + { + cupsFilePrintf(fp, "Attribute %s %d", marker->name, + marker->values[0].integer); + for (i = 1; i < marker->num_values; i ++) + cupsFilePrintf(fp, ",%d", marker->values[i].integer); + cupsFilePuts(fp, "\n"); + } + + if ((marker = ippFindAttribute(printer->attrs, "marker-high-levels", + IPP_TAG_INTEGER)) != NULL) + { + cupsFilePrintf(fp, "Attribute %s %d", marker->name, + marker->values[0].integer); + for (i = 1; i < marker->num_values; i ++) + cupsFilePrintf(fp, ",%d", marker->values[i].integer); + cupsFilePuts(fp, "\n"); + } + + if ((marker = ippFindAttribute(printer->attrs, "marker-message", + IPP_TAG_TEXT)) != NULL) + { + snprintf(value, sizeof(value), "%s %s", marker->name, + marker->values[0].string.text); + + cupsFilePutConf(fp, "Attribute", value); + } + + if ((marker = ippFindAttribute(printer->attrs, "marker-names", + IPP_TAG_NAME)) != NULL) + { + snprintf(value, sizeof(value), "%s ", marker->name); + + for (i = 0, ptr = value + strlen(value); + i < marker->num_values && ptr < (value + sizeof(value) - 1); + i ++) + { + if (i) + *ptr++ = ','; + + strlcpy(ptr, marker->values[i].string.text, + value + sizeof(value) - ptr); + ptr += strlen(ptr); + } + + *ptr = '\0'; + cupsFilePutConf(fp, "Attribute", value); + } + + if ((marker = ippFindAttribute(printer->attrs, "marker-types", + IPP_TAG_KEYWORD)) != NULL) + { + snprintf(value, sizeof(value), "%s ", marker->name); + + for (i = 0, ptr = value + strlen(value); + i < marker->num_values && ptr < (value + sizeof(value) - 1); + i ++) + { + if (i) + *ptr++ = ','; + + strlcpy(ptr, marker->values[i].string.text, + value + sizeof(value) - ptr); + ptr += strlen(ptr); + } + + *ptr = '\0'; + cupsFilePutConf(fp, "Attribute", value); + } + + if (printer->marker_time) + cupsFilePrintf(fp, "Attribute marker-change-time %ld\n", + (long)printer->marker_time); cupsFilePuts(fp, "\n"); @@ -1541,8 +1885,6 @@ cupsdSetAuthInfoRequired( p->auth_info_required[p->num_auth_info_required] = "negotiate"; p->num_auth_info_required ++; - - return (1); } else if ((end - values) == 6 && !strncmp(values, "domain", 6)) { @@ -1561,6 +1903,8 @@ cupsdSetAuthInfoRequired( } else return (0); + + values = (*end) ? end + 1 : end; } if (p->num_auth_info_required == 0) @@ -1569,6 +1913,16 @@ cupsdSetAuthInfoRequired( p->num_auth_info_required = 1; } + /* + * Update the printer-type value as needed... + */ + + if (p->num_auth_info_required > 1 || + strcmp(p->auth_info_required[0], "none")) + p->type |= CUPS_PRINTER_AUTHENTICATED; + else + p->type &= ~CUPS_PRINTER_AUTHENTICATED; + return (1); } @@ -1579,6 +1933,16 @@ cupsdSetAuthInfoRequired( if (!attr || attr->num_values > 4) return (0); + /* + * Update the printer-type value as needed... + */ + + if (attr->num_values > 1 || + strcmp(attr->values[0].string.text, "none")) + p->type |= CUPS_PRINTER_AUTHENTICATED; + else + p->type &= ~CUPS_PRINTER_AUTHENTICATED; + for (i = 0; i < attr->num_values; i ++) { if (!strcmp(attr->values[i].string.text, "none")) @@ -1625,110 +1989,311 @@ cupsdSetAuthInfoRequired( /* - * 'cupsdSetPrinterAttrs()' - Set printer attributes based upon the PPD file. + * 'cupsdSetDeviceURI()' - Set the device URI for a printer. */ void -cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ +cupsdSetDeviceURI(cupsd_printer_t *p, /* I - Printer */ + const char *uri) /* I - Device URI */ { - int i, /* Looping var */ - length; /* Length of browse attributes */ - char uri[HTTP_MAX_URI]; /* URI for printer */ - char resource[HTTP_MAX_URI]; /* Resource portion of URI */ - char filename[1024]; /* Name of PPD file */ - int num_media; /* Number of media options */ - cupsd_location_t *auth; /* Pointer to authentication element */ - const char *auth_supported; /* Authentication supported */ - cups_ptype_t printer_type; /* Printer type data */ - ppd_file_t *ppd; /* PPD file data */ - ppd_option_t *input_slot, /* InputSlot options */ - *media_type, /* MediaType options */ - *page_size, /* PageSize options */ - *output_bin, /* OutputBin options */ - *media_quality, /* EFMediaQualityMode options */ - *duplex; /* Duplex options */ - ppd_attr_t *ppdattr; /* PPD attribute */ - ipp_attribute_t *attr; /* Attribute data */ - ipp_value_t *val; /* Attribute value */ - int num_finishings; /* Number of finishings */ - ipp_finish_t finishings[5]; /* finishings-supported values */ - cups_option_t *option; /* Current printer option */ - static const char * const sides[3] = /* sides-supported values */ - { - "one-sided", - "two-sided-long-edge", - "two-sided-short-edge" - }; - + char buffer[1024], /* URI buffer */ + *start, /* Start of data after scheme */ + *slash, /* First slash after scheme:// */ + *ptr; /* Pointer into user@host:port part */ - DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name, - p->type)); /* - * Make sure that we have the common attributes defined... + * Set the full device URI.. */ - if (!CommonData) - cupsdCreateCommonData(); + cupsdSetString(&(p->device_uri), uri); /* - * Clear out old filters, if any... + * Copy the device URI to a temporary buffer so we can sanitize any auth + * info in it... */ - delete_printer_filters(p); + strlcpy(buffer, uri, sizeof(buffer)); /* - * Figure out the authentication that is required for the printer. + * Find the end of the scheme:// part... */ - auth_supported = "requesting-user-name"; - if (!(p->type & CUPS_PRINTER_REMOTE)) + if ((ptr = strchr(buffer, ':')) != NULL) { - if (p->type & CUPS_PRINTER_CLASS) - snprintf(resource, sizeof(resource), "/classes/%s", p->name); - else - snprintf(resource, sizeof(resource), "/printers/%s", p->name); + for (start = ptr + 1; *start; start ++) + if (*start != '/') + break; + + /* + * Find the next slash (/) in the URI... + */ - if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL || - auth->type == AUTH_NONE) - auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB); + if ((slash = strchr(start, '/')) == NULL) + slash = start + strlen(start); /* No slash, point to the end */ + + /* + * Check for an @ sign before the slash... + */ - if (auth) + if ((ptr = strchr(start, '@')) != NULL && ptr < slash) { - if (auth->type == AUTH_BASIC || auth->type == AUTH_BASICDIGEST) - { - auth_supported = "basic"; - cupsdSetAuthInfoRequired(p, "username,password", NULL); - } - else if (auth->type == AUTH_DIGEST) - { - auth_supported = "digest"; - cupsdSetAuthInfoRequired(p, "username,password", NULL); - } + /* + * Found an @ sign and it is before the resource part, so we have + * an authentication string. Copy the remaining URI over the + * authentication string... + */ + + _cups_strcpy(start, ptr + 1); + } + } + + /* + * Save the sanitized URI... + */ + + cupsdSetString(&(p->sanitized_device_uri), buffer); +} + + +/* + * 'cupsdSetPrinterAttr()' - Set a printer attribute. + */ + +void +cupsdSetPrinterAttr( + cupsd_printer_t *p, /* I - Printer */ + const char *name, /* I - Attribute name */ + char *value) /* I - Attribute value string */ +{ + ipp_attribute_t *attr; /* Attribute */ + int i, /* Looping var */ + count; /* Number of values */ + char *ptr; /* Pointer into value */ + ipp_tag_t value_tag; /* Value tag for this attribute */ + + + /* + * Don't allow empty values... + */ + + if (!*value && strcmp(name, "marker-message")) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Ignoring empty \"%s\" attribute", name); + return; + } + + /* + * Count the number of values... + */ + + for (count = 1, ptr = value; + (ptr = strchr(ptr, ',')) != NULL; + ptr ++, count ++); + + /* + * Then add or update the attribute as needed... + */ + + if (!strcmp(name, "marker-levels") || !strcmp(name, "marker-low-levels") || + !strcmp(name, "marker-high-levels")) + { + /* + * Integer values... + */ + + if ((attr = ippFindAttribute(p->attrs, name, IPP_TAG_INTEGER)) != NULL && + attr->num_values < count) + { + ippDeleteAttribute(p->attrs, attr); + attr = NULL; + } + + if (attr) + attr->num_values = count; + else + attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, name, + count, NULL); + + if (!attr) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for printer attribute " + "(%d values)", count); + return; + } + + for (i = 0; i < count; i ++) + { + if ((ptr = strchr(value, ',')) != NULL) + *ptr++ = '\0'; + + attr->values[i].integer = strtol(value, NULL, 10); + + if (ptr) + value = ptr; + } + } + else + { + /* + * Name or keyword values... + */ + + if (!strcmp(name, "marker-types")) + value_tag = IPP_TAG_KEYWORD; + else if (!strcmp(name, "marker-message")) + value_tag = IPP_TAG_TEXT; + else + value_tag = IPP_TAG_NAME; + + if ((attr = ippFindAttribute(p->attrs, name, value_tag)) != NULL && + attr->num_values < count) + { + ippDeleteAttribute(p->attrs, attr); + attr = NULL; + } + + if (attr) + { + for (i = 0; i < attr->num_values; i ++) + _cupsStrFree(attr->values[i].string.text); + + attr->num_values = count; + } + else + attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, value_tag, name, + count, NULL, NULL); + + if (!attr) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for printer attribute " + "(%d values)", count); + return; + } + + for (i = 0; i < count; i ++) + { + if ((ptr = strchr(value, ',')) != NULL) + *ptr++ = '\0'; + + attr->values[i].string.text = _cupsStrAlloc(value); + + if (ptr) + value = ptr; + } + } +} + + +/* + * 'cupsdSetPrinterAttrs()' - Set printer attributes based upon the PPD file. + */ + +void +cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ +{ + int i, /* Looping var */ + length; /* Length of browse attributes */ + char resource[HTTP_MAX_URI]; /* Resource portion of URI */ + int num_air; /* Number of auth-info-required values */ + const char * const *air; /* auth-info-required values */ + cupsd_location_t *auth; /* Pointer to authentication element */ + const char *auth_supported; /* Authentication supported */ + ipp_t *oldattrs; /* Old printer attributes */ + ipp_attribute_t *attr; /* Attribute data */ + cups_option_t *option; /* Current printer option */ + char *filter; /* Current filter */ + static const char * const air_none[] = + { /* No authentication */ + "none" + }; + static const char * const air_userpass[] = + { /* Basic/Digest authentication */ + "username", + "password" + }; + + + DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name, + p->type)); + + /* + * Make sure that we have the common attributes defined... + */ + + if (!CommonData) + cupsdCreateCommonData(); + + /* + * Clear out old filters, if any... + */ + + delete_printer_filters(p); + + /* + * Figure out the authentication that is required for the printer. + */ + + auth_supported = "requesting-user-name"; + num_air = 1; + air = air_none; + + if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none")) + { + num_air = p->num_auth_info_required; + air = p->auth_info_required; + } + else if ((p->type & CUPS_PRINTER_AUTHENTICATED) && + (p->type & CUPS_PRINTER_DISCOVERED)) + { + num_air = 2; + air = air_userpass; + } + + if (p->type & CUPS_PRINTER_CLASS) + snprintf(resource, sizeof(resource), "/classes/%s", p->name); + else + snprintf(resource, sizeof(resource), "/printers/%s", p->name); + + if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL || + auth->type == CUPSD_AUTH_NONE) + auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB); + + if (auth) + { + int auth_type; /* Authentication type */ + + + if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT) + auth_type = DefaultAuthType; + + if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST) + auth_supported = "basic"; + else if (auth_type == CUPSD_AUTH_DIGEST) + auth_supported = "digest"; #ifdef HAVE_GSSAPI - else if (auth->type == AUTH_NEGOTIATE) - { - auth_supported = "negotiate"; - cupsdSetAuthInfoRequired(p, "negotiate", NULL); - } + else if (auth_type == CUPSD_AUTH_NEGOTIATE) + auth_supported = "negotiate"; #endif /* HAVE_GSSAPI */ - if (auth->type != AUTH_NONE) - p->type |= CUPS_PRINTER_AUTHENTICATED; + if (!(p->type & CUPS_PRINTER_DISCOVERED)) + { + if (auth_type != CUPSD_AUTH_NONE) + p->type |= CUPS_PRINTER_AUTHENTICATED; else - p->type &= ~CUPS_PRINTER_AUTHENTICATED; + p->type &= ~CUPS_PRINTER_AUTHENTICATED; } - else - p->type &= ~CUPS_PRINTER_AUTHENTICATED; } + else if (!(p->type & CUPS_PRINTER_DISCOVERED)) + p->type &= ~CUPS_PRINTER_AUTHENTICATED; /* * Create the required IPP attributes for a printer... */ - if (p->attrs) - ippDelete(p->attrs); - + oldattrs = p->attrs; p->attrs = ippNew(); ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, @@ -1741,8 +2306,6 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ NULL, p->location ? p->location : ""); ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, p->info ? p->info : ""); - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", - NULL, p->uri); if (p->num_users) { @@ -1762,15 +2325,10 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ "job-k-limit", p->k_limit); ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-page-limit", p->page_limit); - if (p->num_auth_info_required) - ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "auth-info-required", p->num_auth_info_required, - NULL, p->auth_info_required); - else - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "auth-info-required", NULL, "none"); + ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "auth-info-required", num_air, NULL, air); - if (cupsArrayCount(Banners) > 0 && !(p->type & CUPS_PRINTER_REMOTE)) + if (cupsArrayCount(Banners) > 0 && !(p->type & CUPS_PRINTER_DISCOVERED)) { /* * Setup the job-sheets-default attribute... @@ -1788,12 +2346,10 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ } } - printer_type = p->type; - p->raw = 0; p->remote = 0; - if (p->type & CUPS_PRINTER_REMOTE) + if (p->type & CUPS_PRINTER_DISCOVERED) { /* * Tell the client this is a remote printer of some type... @@ -1802,6 +2358,9 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, p->uri); + ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", + NULL, p->uri); + if (p->make_model) ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, p->make_model); @@ -1819,11 +2378,10 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ * or class... */ - p->type &= ~CUPS_PRINTER_OPTIONS; - if (p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) { p->raw = 1; + p->type &= ~CUPS_PRINTER_OPTIONS; /* * Add class-specific attributes... @@ -1843,478 +2401,180 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ if (p->num_printers > 0) { /* - * Add a list of member URIs and names... + * Add a list of member names; URIs are added in copy_printer_attrs... */ - attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, - "member-uris", p->num_printers, NULL, NULL); + attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, + "member-names", p->num_printers, NULL, NULL); p->type |= CUPS_PRINTER_OPTIONS; for (i = 0; i < p->num_printers; i ++) { if (attr != NULL) - attr->values[i].string.text = _cupsStrAlloc(p->printers[i]->uri); + attr->values[i].string.text = _cupsStrRetain(p->printers[i]->name); p->type &= ~CUPS_PRINTER_OPTIONS | p->printers[i]->type; } - - attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, - "member-names", p->num_printers, NULL, NULL); - - if (attr != NULL) - { - for (i = 0; i < p->num_printers; i ++) - attr->values[i].string.text = _cupsStrAlloc(p->printers[i]->name); - } } } else { /* - * Add printer-specific attributes... Start by sanitizing the device - * URI so it doesn't have a username or password in it... + * Add printer-specific attributes... */ - if (!p->device_uri) - strcpy(uri, "file:/dev/null"); - else if (strstr(p->device_uri, "://") != NULL) - { - /* - * http://..., ipp://..., etc. - */ - - cupsdSanitizeURI(p->device_uri, uri, sizeof(uri)); - } - else - { - /* - * file:..., serial:..., etc. - */ - - strlcpy(uri, p->device_uri, sizeof(uri)); - } - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL, - uri); + p->sanitized_device_uri); /* * Assign additional attributes from the PPD file (if any)... */ - p->type |= CUPS_PRINTER_BW; - finishings[0] = IPP_FINISHINGS_NONE; - num_finishings = 1; - - snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, - p->name); - - if ((ppd = ppdOpenFile(filename)) != NULL) - { - /* - * Add make/model and other various attributes... - */ + load_ppd(p); - if (ppd->color_device) - p->type |= CUPS_PRINTER_COLOR; - if (ppd->variable_sizes) - p->type |= CUPS_PRINTER_VARIABLE; - if (!ppd->manual_copies) - p->type |= CUPS_PRINTER_COPIES; - if ((ppdattr = ppdFindAttr(ppd, "cupsFax", NULL)) != NULL) - if (ppdattr->value && !strcasecmp(ppdattr->value, "true")) - p->type |= CUPS_PRINTER_FAX; - - ippAddBoolean(p->attrs, IPP_TAG_PRINTER, "color-supported", - ppd->color_device); - if (ppd->throughput) - ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "pages-per-minute", ppd->throughput); - - if (ppd->nickname) - { - /* - * The NickName can be localized in the character set specified - * by the LanugageEncoding attribute. However, ppdOpen2() has - * already converted the ppd->nickname member to UTF-8 for us - * (the original attribute value is available separately) - */ + /* + * Add filters for printer... + */ - cupsdSetString(&p->make_model, ppd->nickname); - } - else if (ppd->modelname) - { - /* - * Model name can only contain specific characters... - */ + cupsdSetPrinterReasons(p, "-cups-missing-filter-warning," + "cups-insecure-filter-warning"); - cupsdSetString(&p->make_model, ppd->modelname); - } - else - cupsdSetString(&p->make_model, "Bad PPD File"); + for (filter = (char *)cupsArrayFirst(p->filters); + filter; + filter = (char *)cupsArrayNext(p->filters)) + add_printer_filter(p, p->filetype, filter); - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "printer-make-and-model", NULL, p->make_model); + if (p->pre_filters) + { + p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", p->name); - /* - * Add media options from the PPD file... - */ + for (filter = (char *)cupsArrayFirst(p->pre_filters); + filter; + filter = (char *)cupsArrayNext(p->pre_filters)) + add_printer_filter(p, p->prefiltertype, filter); + } + } + } - if ((input_slot = ppdFindOption(ppd, "InputSlot")) != NULL) - num_media = input_slot->num_choices; - else - num_media = 0; + /* + * Copy marker attributes as needed... + */ - if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL) - num_media += media_type->num_choices; + if (oldattrs) + { + ipp_attribute_t *oldattr; /* Old attribute */ - if ((page_size = ppdFindOption(ppd, "PageSize")) != NULL) - num_media += page_size->num_choices; - if ((media_quality = ppdFindOption(ppd, "EFMediaQualityMode")) != NULL) - num_media += media_quality->num_choices; + if ((oldattr = ippFindAttribute(oldattrs, "marker-colors", + IPP_TAG_NAME)) != NULL) + { + if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, + "marker-colors", oldattr->num_values, NULL, + NULL)) != NULL) + { + for (i = 0; i < oldattr->num_values; i ++) + attr->values[i].string.text = + _cupsStrRetain(oldattr->values[i].string.text); + } + } - if (num_media == 0) - { - cupsdLogMessage(CUPSD_LOG_CRIT, - "The PPD file for printer %s contains no media " - "options and is therefore invalid!", p->name); - } - else - { - attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-supported", num_media, NULL, NULL); - if (attr != NULL) - { - val = attr->values; + if ((oldattr = ippFindAttribute(oldattrs, "marker-levels", + IPP_TAG_INTEGER)) != NULL) + { + if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "marker-levels", oldattr->num_values, + NULL)) != NULL) + { + for (i = 0; i < oldattr->num_values; i ++) + attr->values[i].integer = oldattr->values[i].integer; + } + } - if (input_slot != NULL) - for (i = 0; i < input_slot->num_choices; i ++, val ++) - val->string.text = _cupsStrAlloc(input_slot->choices[i].choice); + if ((oldattr = ippFindAttribute(oldattrs, "marker-message", + IPP_TAG_TEXT)) != NULL) + ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "marker-message", + NULL, oldattr->values[0].string.text); - if (media_type != NULL) - for (i = 0; i < media_type->num_choices; i ++, val ++) - val->string.text = _cupsStrAlloc(media_type->choices[i].choice); + if ((oldattr = ippFindAttribute(oldattrs, "marker-low-levels", + IPP_TAG_INTEGER)) != NULL) + { + if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "marker-low-levels", oldattr->num_values, + NULL)) != NULL) + { + for (i = 0; i < oldattr->num_values; i ++) + attr->values[i].integer = oldattr->values[i].integer; + } + } - if (media_quality != NULL) - for (i = 0; i < media_quality->num_choices; i ++, val ++) - val->string.text = _cupsStrAlloc(media_quality->choices[i].choice); + if ((oldattr = ippFindAttribute(oldattrs, "marker-high-levels", + IPP_TAG_INTEGER)) != NULL) + { + if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "marker-high-levels", oldattr->num_values, + NULL)) != NULL) + { + for (i = 0; i < oldattr->num_values; i ++) + attr->values[i].integer = oldattr->values[i].integer; + } + } - if (page_size != NULL) - { - for (i = 0; i < page_size->num_choices; i ++, val ++) - val->string.text = _cupsStrAlloc(page_size->choices[i].choice); - - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-default", NULL, page_size->defchoice); - } - else if (input_slot != NULL) - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-default", NULL, input_slot->defchoice); - else if (media_type != NULL) - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-default", NULL, media_type->defchoice); - else if (media_quality != NULL) - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-default", NULL, media_quality->defchoice); - else - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "media-default", NULL, "none"); - } - } + if ((oldattr = ippFindAttribute(oldattrs, "marker-names", + IPP_TAG_NAME)) != NULL) + { + if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, + "marker-names", oldattr->num_values, NULL, + NULL)) != NULL) + { + for (i = 0; i < oldattr->num_values; i ++) + attr->values[i].string.text = + _cupsStrRetain(oldattr->values[i].string.text); + } + } - /* - * Output bin... - */ + if ((oldattr = ippFindAttribute(oldattrs, "marker-types", + IPP_TAG_KEYWORD)) != NULL) + { + if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "marker-types", oldattr->num_values, NULL, + NULL)) != NULL) + { + for (i = 0; i < oldattr->num_values; i ++) + attr->values[i].string.text = + _cupsStrRetain(oldattr->values[i].string.text); + } + } - if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL) - { - attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "output-bin-supported", output_bin->num_choices, - NULL, NULL); + ippDelete(oldattrs); + } - if (attr != NULL) - { - for (i = 0, val = attr->values; - i < output_bin->num_choices; - i ++, val ++) - val->string.text = _cupsStrAlloc(output_bin->choices[i].choice); - } - } + /* + * Force sharing off for remote queues... + */ - /* - * Duplexing, etc... - */ + if (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) + p->shared = 0; + else + { + /* + * Copy the printer options into a browse attributes string we can re-use. + */ - if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL) - if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL) - if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL) - if ((duplex = ppdFindOption(ppd, "KD03Duplex")) == NULL) - duplex = ppdFindOption(ppd, "JCLDuplex"); + const char *valptr; /* Pointer into value */ + char *attrptr; /* Pointer into attribute string */ - if (duplex && duplex->num_choices > 1) - { - p->type |= CUPS_PRINTER_DUPLEX; - ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "sides-supported", 3, NULL, sides); + /* + * Free the old browse attributes as needed... + */ - if (!strcasecmp(duplex->defchoice, "DuplexTumble")) - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "sides-default", NULL, "two-sided-short-edge"); - else if (!strcasecmp(duplex->defchoice, "DuplexNoTumble")) - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "sides-default", NULL, "two-sided-long-edge"); - else - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "sides-default", NULL, "one-sided"); - } + if (p->browse_attrs) + free(p->browse_attrs); - if (ppdFindOption(ppd, "Collate") != NULL) - p->type |= CUPS_PRINTER_COLLATE; - - if (ppdFindOption(ppd, "StapleLocation") != NULL) - { - p->type |= CUPS_PRINTER_STAPLE; - finishings[num_finishings++] = IPP_FINISHINGS_STAPLE; - } - - if (ppdFindOption(ppd, "BindEdge") != NULL) - { - p->type |= CUPS_PRINTER_BIND; - finishings[num_finishings++] = IPP_FINISHINGS_BIND; - } - - for (i = 0; i < ppd->num_sizes; i ++) - if (ppd->sizes[i].length > 1728) - p->type |= CUPS_PRINTER_LARGE; - else if (ppd->sizes[i].length > 1008) - p->type |= CUPS_PRINTER_MEDIUM; - else - p->type |= CUPS_PRINTER_SMALL; - - /* - * Add a filter from application/vnd.cups-raw to printer/name to - * handle "raw" printing by users. - */ - - add_printer_filter(p, p->filetype, "application/vnd.cups-raw 0 -"); - - /* - * Add any pre-filters in the PPD file... - */ - - if ((ppdattr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL) - { - p->prefiltertype = mimeAddType(MimeDatabase, "prefilter", p->name); - - for (; ppdattr; ppdattr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) - if (ppdattr->value) - add_printer_filter(p, p->prefiltertype, ppdattr->value); - } - - /* - * Add any filters in the PPD file... - */ - - DEBUG_printf(("ppd->num_filters = %d\n", ppd->num_filters)); - for (i = 0; i < ppd->num_filters; i ++) - { - DEBUG_printf(("ppd->filters[%d] = \"%s\"\n", i, ppd->filters[i])); - add_printer_filter(p, p->filetype, ppd->filters[i]); - } - - if (ppd->num_filters == 0) - { - /* - * If there are no filters, add a PostScript printing filter. - */ - - add_printer_filter(p, p->filetype, - "application/vnd.cups-postscript 0 -"); - } - - /* - * Show current and available port monitors for this printer... - */ - - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "port-monitor", - NULL, p->port_monitor ? p->port_monitor : "none"); - - - for (i = 1, ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL); - ppdattr; - i ++, ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL)); - - if (ppd->protocols) - { - if (strstr(ppd->protocols, "TBCP")) - i ++; - else if (strstr(ppd->protocols, "BCP")) - i ++; - } - - attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "port-monitor-supported", i, NULL, NULL); - - attr->values[0].string.text = _cupsStrAlloc("none"); - - for (i = 1, ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL); - ppdattr; - i ++, ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL)) - attr->values[i].string.text = _cupsStrAlloc(ppdattr->value); - - if (ppd->protocols) - { - if (strstr(ppd->protocols, "TBCP")) - attr->values[i].string.text = _cupsStrAlloc("tbcp"); - else if (strstr(ppd->protocols, "BCP")) - attr->values[i].string.text = _cupsStrAlloc("bcp"); - } - -#ifdef HAVE_DNSSD - cupsdSetString(&p->product, ppd->product); -#endif /* HAVE_DNSSD */ - - /* - * Close the PPD and set the type... - */ - - ppdClose(ppd); - - printer_type = p->type; - } - else if (!access(filename, 0)) - { - int pline; /* PPD line number */ - ppd_status_t pstatus; /* PPD load status */ - - - pstatus = ppdLastError(&pline); - - cupsdLogMessage(CUPSD_LOG_ERROR, "PPD file for %s cannot be loaded!", - p->name); - - if (pstatus <= PPD_ALLOC_ERROR) - cupsdLogMessage(CUPSD_LOG_ERROR, "%s", strerror(errno)); - else - cupsdLogMessage(CUPSD_LOG_ERROR, "%s on line %d.", - ppdErrorString(pstatus), pline); - - cupsdLogMessage(CUPSD_LOG_INFO, - "Hint: Run \"cupstestppd %s\" and fix any errors.", - filename); - - /* - * Add a filter from application/vnd.cups-raw to printer/name to - * handle "raw" printing by users. - */ - - add_printer_filter(p, p->filetype, "application/vnd.cups-raw 0 -"); - - /* - * Add a PostScript filter, since this is still possibly PS printer. - */ - - add_printer_filter(p, p->filetype, - "application/vnd.cups-postscript 0 -"); - } - else - { - /* - * If we have an interface script, add a filter entry for it... - */ - - snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, - p->name); - if (!access(filename, X_OK)) - { - /* - * Yes, we have a System V style interface script; use it! - */ - - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "printer-make-and-model", NULL, - "Local System V Printer"); - - snprintf(filename, sizeof(filename), "*/* 0 %s/interfaces/%s", - ServerRoot, p->name); - add_printer_filter(p, p->filetype, filename); - } - else if (p->device_uri && - !strncmp(p->device_uri, "ipp://", 6) && - (strstr(p->device_uri, "/printers/") != NULL || - strstr(p->device_uri, "/classes/") != NULL)) - { - /* - * Tell the client this is really a hard-wired remote printer. - */ - - printer_type |= CUPS_PRINTER_REMOTE; - - /* - * Point the printer-uri-supported attribute to the - * remote printer... - */ - - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, - "printer-uri-supported", NULL, p->device_uri); - - /* - * Then set the make-and-model accordingly... - */ - - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "printer-make-and-model", NULL, "Remote Printer"); - - /* - * Print all files directly... - */ - - p->raw = 1; - p->remote = 1; - } - else - { - /* - * Otherwise we have neither - treat this as a "dumb" printer - * with no PPD file... - */ - - ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "printer-make-and-model", NULL, "Local Raw Printer"); - - p->raw = 1; - } - } - - ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "finishings-supported", num_finishings, (int *)finishings); - ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "finishings-default", IPP_FINISHINGS_NONE); - } - } - - /* - * Copy the printer options into a browse attributes string we can re-use. - */ - - if (!(p->type & CUPS_PRINTER_REMOTE)) - { - const char *valptr; /* Pointer into value */ - char *attrptr; /* Pointer into attribute string */ - - - /* - * Free the old browse attributes as needed... - */ - - if (p->browse_attrs) - free(p->browse_attrs); - - /* - * Compute the length of all attributes + job-sheets, lease-duration, - * and BrowseLocalOptions. - */ + /* + * Compute the length of all attributes + job-sheets, lease-duration, + * and BrowseLocalOptions. + */ for (length = 1, i = p->num_options, option = p->options; i > 0; @@ -2337,14 +2597,6 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ if (BrowseLocalOptions) length += 12 + strlen(BrowseLocalOptions); - if (p->num_auth_info_required > 0) - { - length += 18; /* auth-info-required */ - - for (i = 0; i < p->num_auth_info_required; i ++) - length += strlen(p->auth_info_required[i]) + 1; - } - /* * Allocate the new string... */ @@ -2390,21 +2642,6 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ } } } - - if (p->num_auth_info_required > 0) - { - strcpy(attrptr, "auth-info-required"); - attrptr += 18; - - for (i = 0; i < p->num_auth_info_required; i ++) - { - *attrptr++ = i ? ',' : '='; - strcpy(attrptr, p->auth_info_required[i]); - attrptr += strlen(attrptr); - } - } - else - *attrptr = '\0'; } } @@ -2444,17 +2681,21 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */ * 'cupsdSetPrinterReasons()' - Set/update the reasons strings. */ -void +int /* O - 1 if something changed, 0 otherwise */ cupsdSetPrinterReasons( - cupsd_printer_t *p, /* I - Printer */ - const char *s) /* I - Reasons strings */ + cupsd_printer_t *p, /* I - Printer */ + const char *s) /* I - Reasons strings */ { - int i; /* Looping var */ + int i, /* Looping var */ + changed = 0; /* Did something change? */ const char *sptr; /* Pointer into reasons */ char reason[255], /* Reason string */ *rptr; /* Pointer into reason */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdSetPrinterReasons(p=%p(%s),s=\"%s\"", p, p->name, s); + if (s[0] == '-' || s[0] == '+') { /* @@ -2472,11 +2713,20 @@ cupsdSetPrinterReasons( sptr = s; for (i = 0; i < p->num_reasons; i ++) - free(p->reasons[i]); + _cupsStrFree(p->reasons[i]); p->num_reasons = 0; + changed = 1; + + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); + + if (PrintcapFormat == PRINTCAP_PLIST) + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); } + if (!strcmp(s, "none")) + return (changed); + /* * Loop through all of the reasons... */ @@ -2506,23 +2756,31 @@ cupsdSetPrinterReasons( */ for (i = 0; i < p->num_reasons; i ++) - if (!strcasecmp(reason, p->reasons[i])) + if (!strcmp(reason, p->reasons[i])) { /* * Found a match, so remove it... */ p->num_reasons --; - free(p->reasons[i]); + changed = 1; + _cupsStrFree(p->reasons[i]); if (i < p->num_reasons) memmove(p->reasons + i, p->reasons + i + 1, (p->num_reasons - i) * sizeof(char *)); - i --; - if (!strcmp(reason, "paused") && p->state == IPP_PRINTER_STOPPED) cupsdSetPrinterState(p, IPP_PRINTER_IDLE, 1); + + if (strcmp(reason, "connecting-to-device")) + { + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); + + if (PrintcapFormat == PRINTCAP_PLIST) + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); + } + break; } } else if (p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0]))) @@ -2532,19 +2790,38 @@ cupsdSetPrinterReasons( */ for (i = 0; i < p->num_reasons; i ++) - if (!strcasecmp(reason, p->reasons[i])) + if (!strcmp(reason, p->reasons[i])) break; if (i >= p->num_reasons) { - p->reasons[i] = strdup(reason); + if (i >= (int)(sizeof(p->reasons) / sizeof(p->reasons[0]))) + { + cupsdLogMessage(CUPSD_LOG_ALERT, + "Too many printer-state-reasons values for %s (%d)", + p->name, i + 1); + return (changed); + } + + p->reasons[i] = _cupsStrAlloc(reason); p->num_reasons ++; + changed = 1; if (!strcmp(reason, "paused") && p->state != IPP_PRINTER_STOPPED) cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, 1); + + if (strcmp(reason, "connecting-to-device")) + { + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); + + if (PrintcapFormat == PRINTCAP_PLIST) + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); + } } } } + + return (changed); } @@ -2559,28 +2836,38 @@ cupsdSetPrinterState( int update) /* I - Update printers.conf? */ { ipp_pstate_t old_state; /* Old printer state */ + static const char * const printer_states[] = + { /* State strings */ + "idle", + "processing", + "stopped" + }; /* * Can't set status of remote printers... */ - if (p->type & CUPS_PRINTER_REMOTE) + if (p->type & CUPS_PRINTER_DISCOVERED) return; /* * Set the new state... */ + if (PrintcapFormat == PRINTCAP_PLIST) + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); + old_state = p->state; p->state = s; if (old_state != s) { - cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE_CHANGED, p, NULL, - "%s \"%s\" state changed.", + cupsdAddEvent(s == IPP_PRINTER_STOPPED ? CUPSD_EVENT_PRINTER_STOPPED : + CUPSD_EVENT_PRINTER_STATE, p, NULL, + "%s \"%s\" state changed to %s.", (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer", - p->name); + p->name, printer_states[p->state]); /* * Let the browse code know this needs to be updated... @@ -2595,26 +2882,47 @@ cupsdSetPrinterState( #endif /* __sgi */ } + /* + * Set/clear the paused reason as needed... + */ + + if (s == IPP_PRINTER_STOPPED) + cupsdSetPrinterReasons(p, "+paused"); + else + cupsdSetPrinterReasons(p, "-paused"); + + /* + * Clear the message for the queue when going to processing... + */ + + if (s == IPP_PRINTER_PROCESSING) + p->state_message[0] = '\0'; + + /* + * Update the printer history... + */ + cupsdAddPrinterHistory(p); /* * Let the browse protocols reflect the change... */ - cupsdRegisterPrinter(p); + if (update) + cupsdRegisterPrinter(p); /* * Save the printer configuration if a printer goes from idle or processing * to stopped (or visa-versa)... */ - if ((old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED) && - update) + if (update && + (old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED)) { if (p->type & CUPS_PRINTER_CLASS) - cupsdSaveAllClasses(); + cupsdMarkDirty(CUPSD_DIRTY_CLASSES); else - cupsdSaveAllPrinters(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); } } @@ -2627,9 +2935,6 @@ void cupsdStopPrinter(cupsd_printer_t *p, /* I - Printer to stop */ int update)/* I - Update printers.conf? */ { - cupsd_job_t *job; /* Active print job */ - - /* * Set the printer state... */ @@ -2640,32 +2945,121 @@ cupsdStopPrinter(cupsd_printer_t *p, /* I - Printer to stop */ * See if we have a job printing on this printer... */ - if (p->job) + if (p->job && p->job->state_value == IPP_JOB_PROCESSING) + cupsdSetJobState(p->job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT, + "Job stopped due to printer being paused."); +} + + +/* + * 'cupsdUpdatePrinterPPD()' - Update keywords in a printer's PPD file. + */ + +int /* O - 1 if successful, 0 otherwise */ +cupsdUpdatePrinterPPD( + cupsd_printer_t *p, /* I - Printer */ + int num_keywords, /* I - Number of keywords */ + cups_option_t *keywords) /* I - Keywords */ +{ + int i; /* Looping var */ + cups_file_t *src, /* Original file */ + *dst; /* New file */ + char srcfile[1024], /* Original filename */ + dstfile[1024], /* New filename */ + line[1024], /* Line from file */ + keystring[41]; /* Keyword from line */ + cups_option_t *keyword; /* Current keyword */ + + + cupsdLogMessage(CUPSD_LOG_INFO, "Updating keywords in PPD file for %s...", + p->name); + + /* + * Get the old and new PPD filenames... + */ + + snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd.O", ServerRoot, p->name); + snprintf(dstfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot, p->name); + + /* + * Rename the old file and open the old and new... + */ + + if (rename(dstfile, srcfile)) { - /* - * Get pointer to job... - */ + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup PPD file for %s: %s", + p->name, strerror(errno)); + return (0); + } + + if ((src = cupsFileOpen(srcfile, "r")) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open PPD file \"%s\": %s", + srcfile, strerror(errno)); + rename(srcfile, dstfile); + return (0); + } + + if ((dst = cupsFileOpen(dstfile, "w")) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create PPD file \"%s\": %s", + dstfile, strerror(errno)); + cupsFileClose(src); + rename(srcfile, dstfile); + return (0); + } + + /* + * Copy the first line and then write out all of the keywords... + */ + + if (!cupsFileGets(src, line, sizeof(line))) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to read PPD file \"%s\": %s", + srcfile, strerror(errno)); + cupsFileClose(src); + cupsFileClose(dst); + rename(srcfile, dstfile); + return (0); + } - job = (cupsd_job_t *)p->job; + cupsFilePrintf(dst, "%s\n", line); + for (i = num_keywords, keyword = keywords; i > 0; i --, keyword ++) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "*%s: %s", keyword->name, keyword->value); + cupsFilePrintf(dst, "*%s: %s\n", keyword->name, keyword->value); + } + + /* + * Then copy the rest of the PPD file, dropping any keywords we changed. + */ + + while (cupsFileGets(src, line, sizeof(line))) + { /* - * Stop it... + * Skip keywords we've already set... */ - cupsdStopJob(job, 0); + if (sscanf(line, "*%40[^:]:", keystring) == 1 && + cupsGetOption(keystring, num_keywords, keywords)) + continue; /* - * Reset the state to pending... + * Otherwise write the line... */ - job->state->values[0].integer = IPP_JOB_PENDING; - job->state_value = IPP_JOB_PENDING; + cupsFilePrintf(dst, "%s\n", line); + } - cupsdSaveJob(job); + /* + * Close files and return... + */ - cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job, - "Job stopped due to printer being paused"); - } + cupsFileClose(src); + cupsFileClose(dst); + + return (1); } @@ -2694,7 +3088,8 @@ cupsdUpdatePrinters(void) * Remove remote printers if we are no longer browsing... */ - if (!Browsing && (p->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_REMOTE))) + if (!Browsing && + (p->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_DISCOVERED))) { if (p->type & CUPS_PRINTER_IMPLICIT) cupsArrayRemove(ImplicitPrinters, p); @@ -2716,7 +3111,7 @@ cupsdUpdatePrinters(void) * Update printer attributes as needed... */ - if (!(p->type & CUPS_PRINTER_REMOTE)) + if (!(p->type & CUPS_PRINTER_DISCOVERED)) cupsdSetPrinterAttrs(p); } } @@ -2811,7 +3206,7 @@ cupsdValidateDest( if (dtype) *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT | - CUPS_PRINTER_REMOTE); + CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED); return (p->name); } @@ -2870,7 +3265,7 @@ cupsdValidateDest( if (dtype) *dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT | - CUPS_PRINTER_REMOTE); + CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED); return (p->name); } @@ -2887,7 +3282,8 @@ cupsdValidateDest( void cupsdWritePrintcap(void) { - cups_file_t *fp; /* printcap file */ + int i; /* Looping var */ + cups_file_t *fp; /* Printcap file */ cupsd_printer_t *p; /* Current printer */ @@ -2908,6 +3304,8 @@ cupsdWritePrintcap(void) if (!Printcap || !*Printcap) return; + cupsdLogMessage(CUPSD_LOG_INFO, "Generating printcap %s...", Printcap); + /* * Open the printcap file... */ @@ -2920,83 +3318,144 @@ cupsdWritePrintcap(void) * data has come from... */ - cupsFilePuts(fp, "# This file was automatically generated by cupsd(8) from the\n"); - cupsFilePrintf(fp, "# %s/printers.conf file. All changes to this file\n", - ServerRoot); - cupsFilePuts(fp, "# will be lost.\n"); + if (PrintcapFormat != PRINTCAP_PLIST) + cupsFilePrintf(fp, "# This file was automatically generated by cupsd(8) " + "from the\n" + "# %s/printers.conf file. All changes to this file\n" + "# will be lost.\n", ServerRoot); - if (Printers) + /* + * Write a new printcap with the current list of printers. + */ + + switch (PrintcapFormat) { - /* - * Write a new printcap with the current list of printers. - */ + case PRINTCAP_BSD : + /* + * Each printer is put in the file as: + * + * Printer1: + * Printer2: + * Printer3: + * ... + * PrinterN: + */ - switch (PrintcapFormat) - { - case PRINTCAP_BSD: - /* - * Each printer is put in the file as: - * - * Printer1: - * Printer2: - * Printer3: - * ... - * PrinterN: - */ + if (DefaultPrinter) + cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", DefaultPrinter->name, + DefaultPrinter->info, ServerName, + DefaultPrinter->name); + + for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); + p; + p = (cupsd_printer_t *)cupsArrayNext(Printers)) + if (p != DefaultPrinter) + cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", p->name, p->info, + ServerName, p->name); + break; - if (DefaultPrinter) - cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", DefaultPrinter->name, - DefaultPrinter->info, ServerName, DefaultPrinter->name); + case PRINTCAP_PLIST : + /* + * Each printer is written as a dictionary in a plist file. + * Currently the printer-name, printer-info, printer-is-accepting-jobs, + * printer-location, printer-make-and-model, printer-state, + * printer-state-reasons, printer-type, and (sanitized) device-uri. + */ - for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); - p; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) - if (p != DefaultPrinter) - cupsFilePrintf(fp, "%s|%s:rm=%s:rp=%s:\n", p->name, p->info, - ServerName, p->name); - break; + cupsFilePuts(fp, "\n" + "\n" + "\n" + "\n"); - case PRINTCAP_SOLARIS: - /* - * Each printer is put in the file as: - * - * _all:all=Printer1,Printer2,Printer3,...,PrinterN - * _default:use=DefaultPrinter - * Printer1:\ - * :bsdaddr=ServerName,Printer1:\ - * :description=Description: - * Printer2: - * :bsdaddr=ServerName,Printer2:\ - * :description=Description: - * Printer3: - * :bsdaddr=ServerName,Printer3:\ - * :description=Description: - * ... - * PrinterN: - * :bsdaddr=ServerName,PrinterN:\ - * :description=Description: - */ + for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); + p; + p = (cupsd_printer_t *)cupsArrayNext(Printers)) + { + cupsFilePuts(fp, "\t\n" + "\t\tprinter-name\n" + "\t\t"); + write_xml_string(fp, p->name); + cupsFilePuts(fp, "\n" + "\t\tprinter-info\n" + "\t\t"); + write_xml_string(fp, p->info); + cupsFilePrintf(fp, "\n" + "\t\tprinter-is-accepting-jobs\n" + "\t\t<%s/>\n" + "\t\tprinter-location\n" + "\t\t", p->accepting ? "true" : "false"); + write_xml_string(fp, p->location); + cupsFilePuts(fp, "\n" + "\t\tprinter-make-and-model\n" + "\t\t"); + write_xml_string(fp, p->make_model); + cupsFilePrintf(fp, "\n" + "\t\tprinter-state\n" + "\t\t%d\n" + "\t\tprinter-state-reasons\n" + "\t\t\n", p->state); + for (i = 0; i < p->num_reasons; i ++) + { + cupsFilePuts(fp, "\t\t\t"); + write_xml_string(fp, p->reasons[i]); + cupsFilePuts(fp, "\n"); + } + cupsFilePrintf(fp, "\t\t\n" + "\t\tprinter-type\n" + "\t\t%d\n" + "\t\tdevice-uri\n" + "\t\t", p->type); + write_xml_string(fp, p->sanitized_device_uri); + cupsFilePuts(fp, "\n" + "\t\n"); + } + cupsFilePuts(fp, "\n" + "\n"); + break; - cupsFilePuts(fp, "_all:all="); - for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); - p; - p = (cupsd_printer_t *)cupsArrayCurrent(Printers)) - cupsFilePrintf(fp, "%s%c", p->name, - cupsArrayNext(Printers) ? ',' : '\n'); + case PRINTCAP_SOLARIS : + /* + * Each printer is put in the file as: + * + * _all:all=Printer1,Printer2,Printer3,...,PrinterN + * _default:use=DefaultPrinter + * Printer1:\ + * :bsdaddr=ServerName,Printer1:\ + * :description=Description: + * Printer2: + * :bsdaddr=ServerName,Printer2:\ + * :description=Description: + * Printer3: + * :bsdaddr=ServerName,Printer3:\ + * :description=Description: + * ... + * PrinterN: + * :bsdaddr=ServerName,PrinterN:\ + * :description=Description: + */ - if (DefaultPrinter) - cupsFilePrintf(fp, "_default:use=%s\n", DefaultPrinter->name); - - for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); - p; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) - cupsFilePrintf(fp, "%s:\\\n" - "\t:bsdaddr=%s,%s:\\\n" - "\t:description=%s:\n", - p->name, ServerName, p->name, p->info ? p->info : ""); - break; - } - } + cupsFilePuts(fp, "_all:all="); + for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); + p; + p = (cupsd_printer_t *)cupsArrayCurrent(Printers)) + cupsFilePrintf(fp, "%s%c", p->name, + cupsArrayNext(Printers) ? ',' : '\n'); + + if (DefaultPrinter) + cupsFilePrintf(fp, "_default:use=%s\n", DefaultPrinter->name); + + for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); + p; + p = (cupsd_printer_t *)cupsArrayNext(Printers)) + cupsFilePrintf(fp, "%s:\\\n" + "\t:bsdaddr=%s,%s:\\\n" + "\t:description=%s:\n", + p->name, ServerName, p->name, + p->info ? p->info : ""); + break; + } /* * Close the file... @@ -3006,74 +3465,6 @@ cupsdWritePrintcap(void) } -/* - * 'cupsdSanitizeURI()' - Sanitize a device URI... - */ - -char * /* O - New device URI */ -cupsdSanitizeURI(const char *uri, /* I - Original device URI */ - char *buffer, /* O - New device URI */ - int buflen) /* I - Size of new device URI buffer */ -{ - char *start, /* Start of data after scheme */ - *slash, /* First slash after scheme:// */ - *ptr; /* Pointer into user@host:port part */ - - - /* - * Range check input... - */ - - if (!uri || !buffer || buflen < 2) - return (NULL); - - /* - * Copy the device URI to the new buffer... - */ - - strlcpy(buffer, uri, buflen); - - /* - * Find the end of the scheme:// part... - */ - - if ((ptr = strchr(buffer, ':')) == NULL) - return (buffer); /* No scheme: part... */ - - for (start = ptr + 1; *start; start ++) - if (*start != '/') - break; - - /* - * Find the next slash (/) in the URI... - */ - - if ((slash = strchr(start, '/')) == NULL) - slash = start + strlen(start); /* No slash, point to the end */ - - /* - * Check for an @ sign before the slash... - */ - - if ((ptr = strchr(start, '@')) != NULL && ptr < slash) - { - /* - * Found an @ sign and it is before the resource part, so we have - * an authentication string. Copy the remaining URI over the - * authentication string... - */ - - _cups_strcpy(start, ptr + 1); - } - - /* - * Return the new device URI... - */ - - return (buffer); -} - - /* * 'add_printer_defaults()' - Add name-default attributes to the printer attributes. */ @@ -3102,18 +3493,17 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */ cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-hold-until-default")); cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-priority-default")); cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-sheets-default")); - cupsArrayAdd(CommonDefaults, _cupsStrAlloc("media-default")); + cupsArrayAdd(CommonDefaults, _cupsStrAlloc("media-col-default")); cupsArrayAdd(CommonDefaults, _cupsStrAlloc("number-up-default")); cupsArrayAdd(CommonDefaults, _cupsStrAlloc("orientation-requested-default")); - cupsArrayAdd(CommonDefaults, _cupsStrAlloc("sides-default")); } /* * Add all of the default options from the .conf files... */ - for (num_options = 0, i = p->num_options, option = p->options; + for (num_options = 0, options = NULL, i = p->num_options, option = p->options; i > 0; i --, option ++) { @@ -3145,7 +3535,7 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */ 1); if (!cupsGetOption("document-format", p->num_options, p->options)) - ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, + ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, "document-format-default", NULL, "application/octet-stream"); if (!cupsGetOption("job-hold-until", p->num_options, p->options)) @@ -3160,10 +3550,6 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "number-up-default", 1); - if (!cupsGetOption("orientation-requested", p->num_options, p->options)) - ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, - "orientation-requested-default", IPP_PORTRAIT); - if (!cupsGetOption("notify-lease-duration", p->num_options, p->options)) ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "notify-lease-duration-default", DefaultLeaseDuration); @@ -3171,6 +3557,14 @@ add_printer_defaults(cupsd_printer_t *p)/* I - Printer */ if (!cupsGetOption("notify-events", p->num_options, p->options)) ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "notify-events-default", NULL, "job-completed"); + + if (!cupsGetOption("orientation-requested", p->num_options, p->options)) + ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, + "orientation-requested-default", NULL, NULL); + + if (!cupsGetOption("print-quality", p->num_options, p->options)) + ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, + "print-quality-default", IPP_QUALITY_NORMAL); } @@ -3189,266 +3583,1488 @@ add_printer_filter( program[1024]; /* Program/filter name */ int cost; /* Cost of filter */ mime_type_t *temptype; /* MIME type looping var */ - char filename[1024]; /* Full filter filename */ + char filename[1024], /* Full filter filename */ + *dirsep; /* Pointer to directory separator */ + struct stat fileinfo; /* File information */ + + + /* + * Parse the filter string; it should be in the following format: + * + * super/type cost program + */ + + if (sscanf(filter, "%15[^/]/%31s%d%*[ \t]%1023[^\n]", super, type, &cost, + program) != 4) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!", + p->name, filter); + return; + } + + /* + * See if the filter program exists; if not, stop the printer and flag + * the error! + */ + + if (strcmp(program, "-")) + { + if (program[0] == '/') + strlcpy(filename, program, sizeof(filename)); + else + snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program); + + if (stat(filename, &fileinfo)) + { + memset(&fileinfo, 0, sizeof(fileinfo)); + + snprintf(p->state_message, sizeof(p->state_message), + "Filter \"%s\" for printer \"%s\" not available: %s", + filename, p->name, strerror(errno)); + cupsdSetPrinterReasons(p, "+cups-missing-filter-warning"); + + cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message); + } + + /* + * When running as root, do additional security checks... + */ + + if (!RunUser) + { + /* + * Only use filters that are owned by root and do not have group or world + * write permissions. + */ + + if (fileinfo.st_uid || + (fileinfo.st_mode & (S_ISUID | S_IWGRP | S_IWOTH)) != 0) + { + if (fileinfo.st_uid) + snprintf(p->state_message, sizeof(p->state_message), + "Filter \"%s\" for printer \"%s\" not owned by root", + filename, p->name); + else + snprintf(p->state_message, sizeof(p->state_message), + "Filter \"%s\" for printer \"%s\" has insecure permissions " + "(0%o)", filename, p->name, fileinfo.st_mode); + + cupsdSetPrinterReasons(p, "+cups-insecure-filter-warning"); + + cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message); + } + else if (fileinfo.st_mode) + { + /* + * Similarly, check that the parent directory is also owned by root and + * does not have world write permissions. + */ + + if ((dirsep = strrchr(filename, '/')) != NULL) + *dirsep = '\0'; + + if (!stat(filename, &fileinfo) && + (fileinfo.st_uid || + (fileinfo.st_mode & (S_ISUID | S_IWGRP | S_IWOTH)) != 0)) + { + if (fileinfo.st_uid) + snprintf(p->state_message, sizeof(p->state_message), + "Filter directory \"%s\" for printer \"%s\" not owned by " + "root", filename, p->name); + else + snprintf(p->state_message, sizeof(p->state_message), + "Filter directory \"%s\" for printer \"%s\" has insecure " + "permissions (0%o)", filename, p->name, fileinfo.st_mode); + + cupsdSetPrinterReasons(p, "+cups-insecure-filter-warning"); + + cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message); + } + } + } + } + + /* + * Add the filter to the MIME database, supporting wildcards as needed... + */ + + for (temptype = mimeFirstType(MimeDatabase); + temptype; + temptype = mimeNextType(MimeDatabase)) + if (((super[0] == '*' && strcasecmp(temptype->super, "printer")) || + !strcasecmp(temptype->super, super)) && + (type[0] == '*' || !strcasecmp(temptype->type, type))) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "add_printer_filter: %s: adding filter %s/%s %s/%s %d %s", + p->name, temptype->super, temptype->type, + filtertype->super, filtertype->type, + cost, program); + mimeAddFilter(MimeDatabase, temptype, filtertype, cost, program); + } +} + + +/* + * 'add_printer_formats()' - Add document-format-supported values for a printer. + */ + +static void +add_printer_formats(cupsd_printer_t *p) /* I - Printer */ +{ + int i; /* Looping var */ + mime_type_t *type; /* Current MIME type */ + cups_array_t *filters; /* Filters */ + ipp_attribute_t *attr; /* document-format-supported attribute */ + char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2]; + /* MIME type name */ + + + /* + * Raw (and remote) queues advertise all of the supported MIME + * types... + */ + + cupsArrayDelete(p->filetypes); + p->filetypes = NULL; + + if (p->raw) + { + ippAddStrings(p->attrs, IPP_TAG_PRINTER, + (ipp_tag_t)(IPP_TAG_MIMETYPE | IPP_TAG_COPY), + "document-format-supported", NumMimeTypes, NULL, MimeTypes); + return; + } + + /* + * Otherwise, loop through the supported MIME types and see if there + * are filters for them... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer_formats: %d types, %d filters", + mimeNumTypes(MimeDatabase), mimeNumFilters(MimeDatabase)); + + p->filetypes = cupsArrayNew(NULL, NULL); + + for (type = mimeFirstType(MimeDatabase); + type; + type = mimeNextType(MimeDatabase)) + { + snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type); + + if ((filters = mimeFilter(MimeDatabase, type, p->filetype, NULL)) != NULL) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "add_printer_formats: %s: %s needs %d filters", + p->name, mimetype, cupsArrayCount(filters)); + + cupsArrayDelete(filters); + cupsArrayAdd(p->filetypes, type); + } + else + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "add_printer_formats: %s: %s not supported", + p->name, mimetype); + } + + /* + * Add the file formats that can be filtered... + */ + + if ((type = mimeType(MimeDatabase, "application", "octet-stream")) == NULL || + !cupsArrayFind(p->filetypes, type)) + i = 1; + else + i = 0; + + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "add_printer_formats: %s: %d supported types", + p->name, cupsArrayCount(p->filetypes) + i); + + attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, + "document-format-supported", + cupsArrayCount(p->filetypes) + i, NULL, NULL); + + if (i) + attr->values[0].string.text = _cupsStrAlloc("application/octet-stream"); + + for (type = (mime_type_t *)cupsArrayFirst(p->filetypes); + type; + i ++, type = (mime_type_t *)cupsArrayNext(p->filetypes)) + { + snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type); + + attr->values[i].string.text = _cupsStrAlloc(mimetype); + } + +#ifdef HAVE_DNSSD + { + char pdl[1024]; /* Buffer to build pdl list */ + mime_filter_t *filter; /* MIME filter looping var */ + + + /* + * We only support raw printing if this is not a Tioga PrintJobMgr based + * queue and if application/octet-stream is a known type... + */ + + for (filter = (mime_filter_t *)cupsArrayFirst(MimeDatabase->filters); + filter; + filter = (mime_filter_t *)cupsArrayNext(MimeDatabase->filters)) + { + if (filter->dst == p->filetype && filter->filter && + strstr(filter->filter, "PrintJobMgr")) + break; + } + + pdl[0] = '\0'; + + if (!filter && mimeType(MimeDatabase, "application", "octet-stream")) + strlcat(pdl, "application/octet-stream,", sizeof(pdl)); + + /* + * Then list a bunch of formats that are supported by the printer... + */ + + for (type = (mime_type_t *)cupsArrayFirst(p->filetypes); + type; + type = (mime_type_t *)cupsArrayNext(p->filetypes)) + { + if (!strcasecmp(type->super, "application")) + { + if (!strcasecmp(type->type, "pdf")) + strlcat(pdl, "application/pdf,", sizeof(pdl)); + else if (!strcasecmp(type->type, "postscript")) + strlcat(pdl, "application/postscript,", sizeof(pdl)); + } + else if (!strcasecmp(type->super, "image")) + { + if (!strcasecmp(type->type, "jpeg")) + strlcat(pdl, "image/jpeg,", sizeof(pdl)); + else if (!strcasecmp(type->type, "png")) + strlcat(pdl, "image/png,", sizeof(pdl)); + } + } + + if (pdl[0]) + pdl[strlen(pdl) - 1] = '\0'; /* Remove trailing comma */ + + cupsdSetString(&p->pdl, pdl); + } +#endif /* HAVE_DNSSD */ +} + + +/* + * 'add_string_array()' - Add a string to an array of CUPS strings. + */ + +static void +add_string_array(cups_array_t **a, /* I - Array */ + const char *s) /* I - String */ +{ + if (!*a) + *a = cupsArrayNew(NULL, NULL); + + cupsArrayAdd(*a, _cupsStrAlloc(s)); +} + + +/* + * 'compare_printers()' - Compare two printers. + */ + +static int /* O - Result of comparison */ +compare_printers(void *first, /* I - First printer */ + void *second, /* I - Second printer */ + void *data) /* I - App data (not used) */ +{ + return (strcasecmp(((cupsd_printer_t *)first)->name, + ((cupsd_printer_t *)second)->name)); +} + + +/* + * 'delete_printer_filters()' - Delete all MIME filters for a printer. + */ + +static void +delete_printer_filters( + cupsd_printer_t *p) /* I - Printer to remove from */ +{ + mime_filter_t *filter; /* MIME filter looping var */ + + + /* + * Range check input... + */ + + if (p == NULL) + return; + + /* + * Remove all filters from the MIME database that have a destination + * type == printer... + */ + + for (filter = mimeFirstFilter(MimeDatabase); + filter; + filter = mimeNextFilter(MimeDatabase)) + if (filter->dst == p->filetype || filter->dst == p->prefiltertype) + { + /* + * Delete the current filter... + */ + + mimeDeleteFilter(MimeDatabase, filter); + } + + cupsdSetPrinterReasons(p, "-cups-insecure-filter-warning" + ",cups-missing-filter-warning"); +} + + +/* + * 'delete_string_array()' - Delete an array of CUPS strings. + */ + +static void +delete_string_array(cups_array_t **a) /* I - Array */ +{ + char *ptr; /* Current string */ + + + for (ptr = (char *)cupsArrayFirst(*a); + ptr; + ptr = (char *)cupsArrayNext(*a)) + _cupsStrFree(ptr); + + cupsArrayDelete(*a); + *a = NULL; +} + + +/* + * 'load_ppd()' - Load a cached PPD file, updating the cache as needed. + */ + +static void +load_ppd(cupsd_printer_t *p) /* I - Printer */ +{ + int i, j, k; /* Looping vars */ + cups_file_t *cache; /* IPP cache file */ + char cache_name[1024]; /* IPP cache filename */ + struct stat cache_info; /* IPP cache file info */ + char pwg_name[1024]; /* PWG cache filename */ + struct stat pwg_info; /* PWG cache file info */ + ppd_file_t *ppd; /* PPD file */ + char ppd_name[1024]; /* PPD filename */ + struct stat ppd_info; /* PPD file info */ + int num_media; /* Number of media options */ + ppd_size_t *size; /* Current PPD size */ + ppd_option_t *duplex, /* Duplex option */ + *output_bin, /* OutputBin option */ + *output_mode, /* OutputMode option */ + *resolution; /* (Set|JCL|)Resolution option */ + ppd_choice_t *choice, /* Current PPD choice */ + *input_slot, /* Current input slot */ + *media_type; /* Current media type */ + ppd_attr_t *ppd_attr; /* PPD attribute */ + int xdpi, /* Horizontal resolution */ + ydpi; /* Vertical resolution */ + const char *resptr; /* Pointer into resolution keyword */ + _pwg_size_t *pwgsize; /* Current PWG size */ + _pwg_map_t *pwgsource, /* Current PWG source */ + *pwgtype; /* Current PWG type */ + ipp_attribute_t *attr; /* Attribute data */ + ipp_value_t *val; /* Attribute value */ + int num_finishings, /* Number of finishings */ + finishings[5]; /* finishings-supported values */ + int num_qualities, /* Number of print-quality values */ + qualities[3]; /* print-quality values */ + int num_margins, /* Number of media-*-margin-supported values */ + margins[16]; /* media-*-margin-supported values */ + static const char * const sides[3] = /* sides-supported values */ + { + "one-sided", + "two-sided-long-edge", + "two-sided-short-edge" + }; + static const char * const standard_commands[] = + { /* Standard CUPS commands */ + "AutoConfigure", + "Clean", + "PrintSelfTestPage" + }; + + + /* + * Check to see if the cache is up-to-date... + */ + + snprintf(cache_name, sizeof(cache_name), "%s/%s.ipp3", CacheDir, p->name); + if (stat(cache_name, &cache_info)) + cache_info.st_mtime = 0; + + snprintf(pwg_name, sizeof(pwg_name), "%s/%s.pwg2", CacheDir, p->name); + if (stat(pwg_name, &pwg_info)) + pwg_info.st_mtime = 0; + + snprintf(ppd_name, sizeof(ppd_name), "%s/ppd/%s.ppd", ServerRoot, p->name); + if (stat(ppd_name, &ppd_info)) + ppd_info.st_mtime = 1; + + ippDelete(p->ppd_attrs); + p->ppd_attrs = ippNew(); + + _pwgDestroy(p->pwg); + p->pwg = NULL; + + if (pwg_info.st_mtime >= ppd_info.st_mtime) + p->pwg = _pwgCreateWithFile(pwg_name); + + if (cache_info.st_mtime >= ppd_info.st_mtime && p->pwg && + (cache = cupsFileOpen(cache_name, "r")) != NULL) + { + /* + * Load cached information and return... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", cache_name); + + if (ippReadIO(cache, (ipp_iocb_t)cupsFileRead, 1, NULL, + p->ppd_attrs) == IPP_DATA) + { + cupsFileClose(cache); + return; + } + + cupsFileClose(cache); + } + + /* + * Reload PPD attributes from disk... + */ + + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); + + _pwgDestroy(p->pwg); + p->pwg = NULL; + + cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Loading %s...", ppd_name); + + delete_string_array(&(p->filters)); + delete_string_array(&(p->pre_filters)); + + p->type &= ~CUPS_PRINTER_OPTIONS; + p->type |= CUPS_PRINTER_BW; + + finishings[0] = IPP_FINISHINGS_NONE; + num_finishings = 1; + + if ((ppd = ppdOpenFile(ppd_name)) != NULL) + { + /* + * Add make/model and other various attributes... + */ + + p->pwg = _pwgCreateWithPPD(ppd); + + ppdMarkDefaults(ppd); + + if (ppd->color_device) + p->type |= CUPS_PRINTER_COLOR; + if (ppd->variable_sizes) + p->type |= CUPS_PRINTER_VARIABLE; + if (!ppd->manual_copies) + p->type |= CUPS_PRINTER_COPIES; + if ((ppd_attr = ppdFindAttr(ppd, "cupsFax", NULL)) != NULL) + if (ppd_attr->value && !strcasecmp(ppd_attr->value, "true")) + p->type |= CUPS_PRINTER_FAX; + + ippAddBoolean(p->ppd_attrs, IPP_TAG_PRINTER, "color-supported", + ppd->color_device); + if (ppd->throughput) + { + ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "pages-per-minute", ppd->throughput); + if (ppd->color_device) + ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "pages-per-minute-color", ppd->throughput); + } + + num_qualities = 0; + + if ((output_mode = ppdFindOption(ppd, "OutputMode")) != NULL) + { + if (ppdFindChoice(output_mode, "draft") || + ppdFindChoice(output_mode, "fast")) + qualities[num_qualities ++] = IPP_QUALITY_DRAFT; + if (ppdFindChoice(output_mode, "normal") || + ppdFindChoice(output_mode, "good")) + qualities[num_qualities ++] = IPP_QUALITY_NORMAL; + if (ppdFindChoice(output_mode, "best") || + ppdFindChoice(output_mode, "high")) + qualities[num_qualities ++] = IPP_QUALITY_HIGH; + } + else if ((ppd_attr = ppdFindAttr(ppd, "APPrinterPreset", NULL)) != NULL) + { + do + { + if (strstr(ppd_attr->spec, "draft") || + strstr(ppd_attr->spec, "Draft")) + { + qualities[num_qualities ++] = IPP_QUALITY_DRAFT; + break; + } + } + while ((ppd_attr = ppdFindNextAttr(ppd, "APPrinterPreset", + NULL)) != NULL); + + qualities[num_qualities ++] = IPP_QUALITY_NORMAL; + qualities[num_qualities ++] = IPP_QUALITY_HIGH; + } + + if (num_qualities == 0) + qualities[num_qualities ++] = IPP_QUALITY_NORMAL; + + ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, + "print-quality-supported", num_qualities, qualities); + + if (ppd->nickname) + { + /* + * The NickName can be localized in the character set specified + * by the LanugageEncoding attribute. However, ppdOpen2() has + * already converted the ppd->nickname member to UTF-8 for us + * (the original attribute value is available separately) + */ + + cupsdSetString(&p->make_model, ppd->nickname); + } + else if (ppd->modelname) + { + /* + * Model name can only contain specific characters... + */ + + cupsdSetString(&p->make_model, ppd->modelname); + } + else + cupsdSetString(&p->make_model, "Bad PPD File"); + + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, + "printer-make-and-model", NULL, p->make_model); + + /* + * Add media options from the PPD file... + */ + + if (ppd->num_sizes == 0 || !p->pwg) + { + if (!ppdFindAttr(ppd, "APScannerOnly", NULL)) + cupsdLogMessage(CUPSD_LOG_CRIT, + "The PPD file for printer %s contains no media " + "options and is therefore invalid!", p->name); + + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "media-default", NULL, "unknown"); + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "media-supported", NULL, "unknown"); + } + else + { + /* + * media-default + */ + + if ((size = ppdPageSize(ppd, NULL)) != NULL) + pwgsize = _pwgGetSize(p->pwg, size->name); + else + pwgsize = NULL; + + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "media-default", NULL, + pwgsize ? pwgsize->map.pwg : "unknown"); + + /* + * media-col-default + */ + + if (pwgsize) + { + ipp_t *col; /* Collection value */ + + input_slot = ppdFindMarkedChoice(ppd, "InputSlot"); + media_type = ppdFindMarkedChoice(ppd, "MediaType"); + col = new_media_col(pwgsize, + input_slot ? + _pwgGetSource(p->pwg, + input_slot->choice) : + NULL, + media_type ? + _pwgGetType(p->pwg, + media_type->choice) : + NULL); + + ippAddCollection(p->ppd_attrs, IPP_TAG_PRINTER, "media-col-default", + col); + ippDelete(col); + } + + /* + * media-supported + */ + + num_media = p->pwg->num_sizes; + if (p->pwg->custom_min_keyword) + num_media += 2; + + if ((attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "media-supported", num_media, NULL, + NULL)) != NULL) + { + val = attr->values; + + for (i = p->pwg->num_sizes, pwgsize = p->pwg->sizes; + i > 0; + i --, pwgsize ++, val ++) + val->string.text = _cupsStrRetain(pwgsize->map.pwg); + + if (p->pwg->custom_min_keyword) + { + val->string.text = _cupsStrRetain(p->pwg->custom_min_keyword); + val ++; + val->string.text = _cupsStrRetain(p->pwg->custom_max_keyword); + } + } + + /* + * media-source-supported + */ + + if (p->pwg->num_sources > 0 && + (attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "media-source-supported", p->pwg->num_sources, + NULL, NULL)) != NULL) + { + for (i = p->pwg->num_sources, pwgsource = p->pwg->sources, + val = attr->values; + i > 0; + i --, pwgsource ++, val ++) + val->string.text = _cupsStrRetain(pwgsource->pwg); + } + + /* + * media-type-supported + */ + + if (p->pwg->num_types > 0 && + (attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "media-type-supported", p->pwg->num_types, + NULL, NULL)) != NULL) + { + for (i = p->pwg->num_types, pwgtype = p->pwg->types, + val = attr->values; + i > 0; + i --, pwgtype ++, val ++) + val->string.text = _cupsStrRetain(pwgtype->pwg); + } + + /* + * media-*-margin-supported + */ + + for (i = p->pwg->num_sizes, pwgsize = p->pwg->sizes, num_margins = 0; + i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); + i --, pwgsize ++) + { + for (j = 0; j < num_margins; j ++) + if (pwgsize->bottom == margins[j]) + break; + + if (j >= num_margins) + { + margins[num_margins] = pwgsize->bottom; + num_margins ++; + } + } + + if (num_margins > 0) + ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-bottom-margin-supported", num_margins, margins); + else + ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-bottom-margin-supported", 0); + + for (i = p->pwg->num_sizes, pwgsize = p->pwg->sizes, num_margins = 0; + i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); + i --, pwgsize ++) + { + for (j = 0; j < num_margins; j ++) + if (pwgsize->left == margins[j]) + break; + + if (j >= num_margins) + { + margins[num_margins] = pwgsize->left; + num_margins ++; + } + } + + if (num_margins > 0) + ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-left-margin-supported", num_margins, margins); + else + ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-left-margin-supported", 0); + + for (i = p->pwg->num_sizes, pwgsize = p->pwg->sizes, num_margins = 0; + i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); + i --, pwgsize ++) + { + for (j = 0; j < num_margins; j ++) + if (pwgsize->right == margins[j]) + break; + + if (j >= num_margins) + { + margins[num_margins] = pwgsize->right; + num_margins ++; + } + } + + if (num_margins > 0) + ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-right-margin-supported", num_margins, margins); + else + ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-right-margin-supported", 0); + + for (i = p->pwg->num_sizes, pwgsize = p->pwg->sizes, num_margins = 0; + i > 0 && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); + i --, pwgsize ++) + { + for (j = 0; j < num_margins; j ++) + if (pwgsize->top == margins[j]) + break; + + if (j >= num_margins) + { + margins[num_margins] = pwgsize->top; + num_margins ++; + } + } + + if (num_margins > 0) + ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-top-margin-supported", num_margins, margins); + else + ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-top-margin-supported", 0); + + /* + * media-col-database + */ + + num_media = p->pwg->num_sizes; + if (p->pwg->num_sources) + { + if (p->pwg->num_types > 0) + num_media += p->pwg->num_sizes * p->pwg->num_sources * + p->pwg->num_types; + else + num_media += p->pwg->num_sizes * p->pwg->num_sources; + } + else if (p->pwg->num_types) + num_media += p->pwg->num_sizes * p->pwg->num_types; + + if ((attr = ippAddCollections(p->ppd_attrs, IPP_TAG_PRINTER, + "media-col-database", num_media, + NULL)) != NULL) + { + for (i = p->pwg->num_sizes, pwgsize = p->pwg->sizes, val = attr->values; + i > 0; + i --, pwgsize ++) + { + /* + * Start by adding the page size without source or type... + */ + + ppdMarkOption(ppd, "PageSize", pwgsize->map.ppd); + + val->collection = new_media_col(pwgsize, NULL, NULL); + val ++; + + /* + * Then add the specific, supported combinations of size, source, and + * type... + */ + + if (p->pwg->num_sources > 0) + { + for (j = p->pwg->num_sources, pwgsource = p->pwg->sources; + j > 0; + j --, pwgsource ++) + { + ppdMarkOption(ppd, "InputSlot", pwgsource->ppd); + + if (p->pwg->num_types > 0) + { + for (k = p->pwg->num_types, pwgtype = p->pwg->types; + k > 0; + k --, pwgtype ++) + { + if (!ppdMarkOption(ppd, "MediaType", pwgtype->ppd)) + { + val->collection = new_media_col(pwgsize, pwgsource->pwg, + pwgtype->pwg); + val ++; + } + } + } + else if (!ppdConflicts(ppd)) + { + val->collection = new_media_col(pwgsize, pwgsource->pwg, NULL); + val ++; + } + } + } + else if (p->pwg->num_types > 0) + { + for (j = p->pwg->num_types, pwgtype = p->pwg->types; + j > 0; + j --, pwgtype ++) + { + if (!ppdMarkOption(ppd, "MediaType", pwgtype->ppd)) + { + val->collection = new_media_col(pwgsize, NULL, pwgtype->pwg); + val ++; + } + } + } + } + + /* + * Update the number of media-col-database values... + */ + + attr->num_values = val - attr->values; + } + } + + /* + * Output bin... + */ + + if (p->pwg && p->pwg->num_bins > 0) + { + attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "output-bin-supported", p->pwg->num_bins, + NULL, NULL); + + if (attr != NULL) + { + for (i = 0, val = attr->values; + i < p->pwg->num_bins; + i ++, val ++) + val->string.text = _cupsStrAlloc(p->pwg->bins[i].pwg); + } + + if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL) + { + for (i = 0; i < p->pwg->num_bins; i ++) + if (!strcmp(p->pwg->bins[i].ppd, output_bin->defchoice)) + break; + + if (i >= p->pwg->num_bins) + i = 0; + + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "output-bin-default", NULL, p->pwg->bins[i].pwg); + } + else + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "output-bin-default", NULL, p->pwg->bins[0].pwg); + } + else if (((ppd_attr = ppdFindAttr(ppd, "DefaultOutputOrder", + NULL)) != NULL && + !strcasecmp(ppd_attr->value, "Reverse")) || + (!ppd_attr && ppd->manufacturer && /* EPSON "compatibility heuristic" */ + !strcasecmp(ppd->manufacturer, "epson"))) + { + /* + * Report that this printer has a single output bin that leaves pages face + * up. + */ + + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "output-bin-supported", NULL, "face-up"); + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "output-bin-default", NULL, "face-up"); + } + else + { + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "output-bin-supported", NULL, "face-down"); + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "output-bin-default", NULL, "face-down"); + } + + /* + * Printer resolutions... + */ + + if ((resolution = ppdFindOption(ppd, "Resolution")) == NULL) + if ((resolution = ppdFindOption(ppd, "JCLResolution")) == NULL) + if ((resolution = ppdFindOption(ppd, "SetResolution")) == NULL) + resolution = ppdFindOption(ppd, "CNRes_PGP"); + + if (resolution) + { + /* + * Report all supported resolutions... + */ + + attr = ippAddResolutions(p->ppd_attrs, IPP_TAG_PRINTER, + "printer-resolution-supported", + resolution->num_choices, IPP_RES_PER_INCH, + NULL, NULL); + + for (i = 0, choice = resolution->choices; + i < resolution->num_choices; + i ++, choice ++) + { + xdpi = ydpi = (int)strtol(choice->choice, (char **)&resptr, 10); + if (resptr > choice->choice && xdpi > 0 && *resptr == 'x') + ydpi = (int)strtol(resptr + 1, (char **)&resptr, 10); + + if (xdpi <= 0 || ydpi <= 0) + { + cupsdLogMessage(CUPSD_LOG_WARN, + "Bad resolution \"%s\" for printer %s.", + choice->choice, p->name); + xdpi = ydpi = 72; + } + + attr->values[i].resolution.xres = xdpi; + attr->values[i].resolution.yres = ydpi; + attr->values[i].resolution.units = IPP_RES_PER_INCH; + + if (choice->marked) + ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER, + "printer-resolution-default", IPP_RES_PER_INCH, + xdpi, ydpi); + } + } + else if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL && + ppd_attr->value) + { + /* + * Just the DefaultResolution to report... + */ + + xdpi = ydpi = (int)strtol(ppd_attr->value, (char **)&resptr, 10); + if (resptr > ppd_attr->value && xdpi > 0) + { + if (*resptr == 'x') + ydpi = (int)strtol(resptr + 1, (char **)&resptr, 10); + else + ydpi = xdpi; + } + + if (xdpi <= 0 || ydpi <= 0) + { + cupsdLogMessage(CUPSD_LOG_WARN, + "Bad default resolution \"%s\" for printer %s.", + ppd_attr->value, p->name); + xdpi = ydpi = 72; + } + + ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER, + "printer-resolution-default", IPP_RES_PER_INCH, + xdpi, ydpi); + ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER, + "printer-resolution-supported", IPP_RES_PER_INCH, + xdpi, ydpi); + } + else + { + /* + * No resolutions in PPD - make one up... + */ + + ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER, + "printer-resolution-default", IPP_RES_PER_INCH, + 72, 72); + ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER, + "printer-resolution-supported", IPP_RES_PER_INCH, + 72, 72); + } + + /* + * Duplexing, etc... + */ + + if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL) + if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL) + if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL) + if ((duplex = ppdFindOption(ppd, "KD03Duplex")) == NULL) + duplex = ppdFindOption(ppd, "JCLDuplex"); + + if (duplex && duplex->num_choices > 1 && + !ppdInstallableConflict(ppd, duplex->keyword, "DuplexTumble")) + { + p->type |= CUPS_PRINTER_DUPLEX; + + ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "sides-supported", 3, NULL, sides); + + if (!strcasecmp(duplex->defchoice, "DuplexTumble")) + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "sides-default", NULL, "two-sided-short-edge"); + else if (!strcasecmp(duplex->defchoice, "DuplexNoTumble")) + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "sides-default", NULL, "two-sided-long-edge"); + else + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "sides-default", NULL, "one-sided"); + } + else + { + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "sides-supported", NULL, "one-sided"); + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "sides-default", NULL, "one-sided"); + } + + if (ppdFindOption(ppd, "Collate") != NULL) + p->type |= CUPS_PRINTER_COLLATE; + + if (ppdFindOption(ppd, "StapleLocation") != NULL) + { + p->type |= CUPS_PRINTER_STAPLE; + finishings[num_finishings++] = IPP_FINISHINGS_STAPLE; + } + + if (ppdFindOption(ppd, "BindEdge") != NULL) + { + p->type |= CUPS_PRINTER_BIND; + finishings[num_finishings++] = IPP_FINISHINGS_BIND; + } + + for (i = 0; i < ppd->num_sizes; i ++) + if (ppd->sizes[i].length > 1728) + p->type |= CUPS_PRINTER_LARGE; + else if (ppd->sizes[i].length > 1008) + p->type |= CUPS_PRINTER_MEDIUM; + else + p->type |= CUPS_PRINTER_SMALL; + + if ((ppd_attr = ppdFindAttr(ppd, "APICADriver", NULL)) != NULL && + ppd_attr->value && !strcasecmp(ppd_attr->value, "true")) + { + if ((ppd_attr = ppdFindAttr(ppd, "APScannerOnly", NULL)) != NULL && + ppd_attr->value && !strcasecmp(ppd_attr->value, "true")) + p->type |= CUPS_PRINTER_SCANNER; + else + p->type |= CUPS_PRINTER_MFP; + } + + /* + * Add a filter from application/vnd.cups-raw to printer/name to + * handle "raw" printing by users. + */ + + add_string_array(&(p->filters), "application/vnd.cups-raw 0 -"); + + /* + * Add any pre-filters in the PPD file... + */ + + if ((ppd_attr = ppdFindAttr(ppd, "cupsPreFilter", NULL)) != NULL) + { + for (; ppd_attr; ppd_attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) + if (ppd_attr->value) + add_string_array(&(p->pre_filters), ppd_attr->value); + } + + /* + * Add any filters in the PPD file... + */ + + DEBUG_printf(("ppd->num_filters = %d\n", ppd->num_filters)); + for (i = 0; i < ppd->num_filters; i ++) + { + DEBUG_printf(("ppd->filters[%d] = \"%s\"\n", i, ppd->filters[i])); + add_string_array(&(p->filters), ppd->filters[i]); + + if (!strncasecmp(ppd->filters[i], "application/vnd.cups-command", 28) && + isspace(ppd->filters[i][28] & 255)) + p->type |= CUPS_PRINTER_COMMANDS; + } + + if (ppd->num_filters == 0) + { + /* + * If there are no filters, add PostScript printing filters. + */ + + add_string_array(&(p->filters), + "application/vnd.cups-command 0 commandtops"); + add_string_array(&(p->filters), + "application/vnd.cups-postscript 0 -"); + + p->type |= CUPS_PRINTER_COMMANDS; + } + else if (!(p->type & CUPS_PRINTER_COMMANDS)) + { + /* + * See if this is a PostScript device without a command filter... + */ + + for (i = 0; i < ppd->num_filters; i ++) + if (!strncasecmp(ppd->filters[i], + "application/vnd.cups-postscript", 31) && + isspace(ppd->filters[i][31] & 255)) + break; + + if (i < ppd->num_filters) + { + /* + * Add the generic PostScript command filter... + */ + + add_string_array(&(p->filters), + "application/vnd.cups-command 0 commandtops"); + p->type |= CUPS_PRINTER_COMMANDS; + } + } + + if (p->type & CUPS_PRINTER_COMMANDS) + { + char *commands, /* Copy of commands */ + *start, /* Start of name */ + *end; /* End of name */ + int count; /* Number of commands */ + + + if ((ppd_attr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL && + ppd_attr->value && ppd_attr->value[0]) + { + for (count = 0, start = ppd_attr->value; *start; count ++) + { + while (isspace(*start & 255)) + start ++; + + if (!*start) + break; + + while (*start && !isspace(*start & 255)) + start ++; + } + } + else + count = 0; + + if (count > 0) + { + /* + * Make a copy of the commands string and count how many ... + */ + + attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "printer-commands", count, NULL, NULL); + + commands = strdup(ppd_attr->value); + + for (count = 0, start = commands; *start; count ++) + { + while (isspace(*start & 255)) + start ++; + if (!*start) + break; - /* - * Parse the filter string; it should be in the following format: - * - * super/type cost program - */ + end = start; + while (*end && !isspace(*end & 255)) + end ++; - if (sscanf(filter, "%15[^/]/%31s%d%1023s", super, type, &cost, program) != 4) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "%s: invalid filter string \"%s\"!", - p->name, filter); - return; - } + if (*end) + *end++ = '\0'; - /* - * See if the filter program exists; if not, stop the printer and flag - * the error! - */ + attr->values[count].string.text = _cupsStrAlloc(start); - if (strcmp(program, "-")) - { - if (program[0] == '/') - strlcpy(filename, program, sizeof(filename)); - else - snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program); + start = end; + } + + free(commands); + } + else + { + /* + * Add the standard list of commands... + */ - if (access(filename, X_OK)) + ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "printer-commands", + (int)(sizeof(standard_commands) / + sizeof(standard_commands[0])), NULL, + standard_commands); + } + } + else { - snprintf(p->state_message, sizeof(p->state_message), - "Filter \"%s\" for printer \"%s\" not available: %s", - program, p->name, strerror(errno)); - cupsdSetPrinterReasons(p, "+cups-missing-filter-error"); - cupsdSetPrinterState(p, IPP_PRINTER_STOPPED, 0); + /* + * No commands supported... + */ - cupsdLogMessage(CUPSD_LOG_ERROR, "%s", p->state_message); + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, + "printer-commands", NULL, "none"); } - } - /* - * Mark the CUPS_PRINTER_COMMANDS bit if we have a filter for - * application/vnd.cups-command... - */ + /* + * Show current and available port monitors for this printer... + */ - if (!strcasecmp(super, "application") && - !strcasecmp(type, "vnd.cups-command")) - p->type |= CUPS_PRINTER_COMMANDS; + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "port-monitor", + NULL, p->port_monitor ? p->port_monitor : "none"); - /* - * Add the filter to the MIME database, supporting wildcards as needed... - */ + for (i = 1, ppd_attr = ppdFindAttr(ppd, "cupsPortMonitor", NULL); + ppd_attr; + i ++, ppd_attr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL)); - for (temptype = mimeFirstType(MimeDatabase); - temptype; - temptype = mimeNextType(MimeDatabase)) - if (((super[0] == '*' && strcasecmp(temptype->super, "printer")) || - !strcasecmp(temptype->super, super)) && - (type[0] == '*' || !strcasecmp(temptype->type, type))) + if (ppd->protocols) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "add_printer_filter: %s: adding filter %s/%s %s/%s %d %s", - p->name, temptype->super, temptype->type, - filtertype->super, filtertype->type, - cost, program); - mimeAddFilter(MimeDatabase, temptype, filtertype, cost, program); + if (strstr(ppd->protocols, "TBCP")) + i ++; + else if (strstr(ppd->protocols, "BCP")) + i ++; } -} + attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, + "port-monitor-supported", i, NULL, NULL); -/* - * 'add_printer_formats()' - Add document-format-supported values for a printer. - */ + attr->values[0].string.text = _cupsStrAlloc("none"); -static void -add_printer_formats(cupsd_printer_t *p) /* I - Printer */ -{ - int i; /* Looping var */ - mime_type_t *type; /* Current MIME type */ - cups_array_t *filters; /* Filters */ - ipp_attribute_t *attr; /* document-format-supported attribute */ - char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2]; - /* MIME type name */ + for (i = 1, ppd_attr = ppdFindAttr(ppd, "cupsPortMonitor", NULL); + ppd_attr; + i ++, ppd_attr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL)) + attr->values[i].string.text = _cupsStrAlloc(ppd_attr->value); + if (ppd->protocols) + { + if (strstr(ppd->protocols, "TBCP")) + attr->values[i].string.text = _cupsStrAlloc("tbcp"); + else if (strstr(ppd->protocols, "BCP")) + attr->values[i].string.text = _cupsStrAlloc("bcp"); + } - /* - * Raw (and remote) queues advertise all of the supported MIME - * types... - */ +#ifdef HAVE_DNSSD + cupsdSetString(&p->product, ppd->product); +#endif /* HAVE_DNSSD */ - cupsArrayDelete(p->filetypes); - p->filetypes = NULL; + if (ppdFindAttr(ppd, "APRemoteQueueID", NULL)) + p->type |= CUPS_PRINTER_REMOTE; - if (p->raw) - { - ippAddStrings(p->attrs, IPP_TAG_PRINTER, - (ipp_tag_t)(IPP_TAG_MIMETYPE | IPP_TAG_COPY), - "document-format-supported", NumMimeTypes, NULL, MimeTypes); - return; + /* + * Close the PPD and set the type... + */ + + ppdClose(ppd); } + else if (!access(ppd_name, 0)) + { + int pline; /* PPD line number */ + ppd_status_t pstatus; /* PPD load status */ - /* - * Otherwise, loop through the supported MIME types and see if there - * are filters for them... - */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer_formats: %d types, %d filters", - mimeNumTypes(MimeDatabase), mimeNumFilters(MimeDatabase)); + pstatus = ppdLastError(&pline); - p->filetypes = cupsArrayNew(NULL, NULL); + cupsdLogMessage(CUPSD_LOG_ERROR, "PPD file for %s cannot be loaded!", + p->name); - for (type = mimeFirstType(MimeDatabase); - type; - type = mimeNextType(MimeDatabase)) - { - snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type); + if (pstatus <= PPD_ALLOC_ERROR) + cupsdLogMessage(CUPSD_LOG_ERROR, "%s", strerror(errno)); + else + cupsdLogMessage(CUPSD_LOG_ERROR, "%s on line %d.", + ppdErrorString(pstatus), pline); - if ((filters = mimeFilter(MimeDatabase, type, p->filetype, NULL)) != NULL) - { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "add_printer_formats: %s: %s needs %d filters", - p->name, mimetype, cupsArrayCount(filters)); + cupsdLogMessage(CUPSD_LOG_INFO, + "Hint: Run \"cupstestppd %s\" and fix any errors.", + ppd_name); - cupsArrayDelete(filters); - cupsArrayAdd(p->filetypes, type); - } - else - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "add_printer_formats: %s: %s not supported", - p->name, mimetype); - } + /* + * Add a filter from application/vnd.cups-raw to printer/name to + * handle "raw" printing by users. + */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "add_printer_formats: %s: %d supported types", - p->name, cupsArrayCount(p->filetypes) + 1); + add_string_array(&(p->filters), "application/vnd.cups-raw 0 -"); - /* - * Add the file formats that can be filtered... - */ + /* + * Add a PostScript filter, since this is still possibly PS printer. + */ - if ((type = mimeType(MimeDatabase, "application", "octet-stream")) == NULL || - !cupsArrayFind(p->filetypes, type)) - i = 1; + add_string_array(&(p->filters), "application/vnd.cups-postscript 0 -"); + } else - i = 0; + { + /* + * If we have an interface script, add a filter entry for it... + */ - attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, - "document-format-supported", - cupsArrayCount(p->filetypes) + 1, NULL, NULL); + char interface[1024]; /* Interface script */ - if (i) - attr->values[0].string.text = _cupsStrAlloc("application/octet-stream"); - for (type = (mime_type_t *)cupsArrayFirst(p->filetypes); - type; - i ++, type = (mime_type_t *)cupsArrayNext(p->filetypes)) - { - snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type); + snprintf(interface, sizeof(interface), "%s/interfaces/%s", ServerRoot, + p->name); + if (!access(interface, X_OK)) + { + /* + * Yes, we have a System V style interface script; use it! + */ - attr->values[i].string.text = _cupsStrAlloc(mimetype); - } + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, + "printer-make-and-model", NULL, + "Local System V Printer"); -#ifdef HAVE_DNSSD - { - char pdl[1024]; /* Buffer to build pdl list */ - mime_filter_t *filter; /* MIME filter looping var */ + snprintf(interface, sizeof(interface), "*/* 0 %s/interfaces/%s", + ServerRoot, p->name); + add_string_array(&(p->filters), interface); + } + else if (!strncmp(p->device_uri, "ipp://", 6) && + (strstr(p->device_uri, "/printers/") != NULL || + strstr(p->device_uri, "/classes/") != NULL || + (strstr(p->device_uri, "._ipp.") != NULL && + !strcmp(p->device_uri + strlen(p->device_uri) - 5, + "/cups")))) + { + /* + * Tell the client this is really a hard-wired remote printer. + */ + p->type |= CUPS_PRINTER_REMOTE; - pdl[0] = '\0'; + /* + * Point the printer-uri-supported attribute to the + * remote printer... + */ - if (mimeType(MimeDatabase, "application", "pdf")) - strlcat(pdl, "application/pdf,", sizeof(pdl)); + ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, + "printer-uri-supported", NULL, p->device_uri); - if (mimeType(MimeDatabase, "application", "postscript")) - strlcat(pdl, "application/postscript,", sizeof(pdl)); + /* + * Then set the make-and-model accordingly... + */ - if (mimeType(MimeDatabase, "application", "vnd.cups-raster")) - strlcat(pdl, "application/vnd.cups-raster,", sizeof(pdl)); + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, + "printer-make-and-model", NULL, "Remote Printer"); - /* - * Determine if this is a Tioga PrintJobMgr based queue... - */ + /* + * Print all files directly... + */ - for (filter = (mime_filter_t *)cupsArrayFirst(MimeDatabase->filters); - filter; - filter = (mime_filter_t *)cupsArrayNext(MimeDatabase->filters)) + p->raw = 1; + p->remote = 1; + } + else { - if (filter->dst == p->filetype && filter->filter && - strstr(filter->filter, "PrintJobMgr")) - break; + /* + * Otherwise we have neither - treat this as a "dumb" printer + * with no PPD file... + */ + + ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, + "printer-make-and-model", NULL, "Local Raw Printer"); + + p->raw = 1; } + } + + ippAddIntegers(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, + "finishings-supported", num_finishings, finishings); + ippAddInteger(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, + "finishings-default", IPP_FINISHINGS_NONE); + if (ppd && (cache = cupsFileOpen(cache_name, "w9")) != NULL) + { /* - * We only support raw printing if this is not a Tioga PrintJobMgr based - * queue and if application/octet-stream is a known conversion... + * Save cached PPD attributes to disk... */ - if (!filter && mimeType(MimeDatabase, "application", "octet-stream")) - strlcat(pdl, "application/octet-stream,", sizeof(pdl)); + cupsdLogMessage(CUPSD_LOG_DEBUG, "load_ppd: Saving %s...", cache_name); - if (mimeType(MimeDatabase, "image", "png")) - strlcat(pdl, "image/png,", sizeof(pdl)); + p->ppd_attrs->state = IPP_IDLE; - if (pdl[0]) - pdl[strlen(pdl) - 1] = '\0'; /* Remove trailing comma */ + if (ippWriteIO(cache, (ipp_iocb_t)cupsFileWrite, 1, NULL, + p->ppd_attrs) != IPP_DATA) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to save PPD cache file \"%s\" - %s", cache_name, + strerror(errno)); + unlink(cache_name); + } - cupsdSetString(&p->pdl, pdl); - } -#endif /* HAVE_DNSSD */ -} + cupsFileClose(cache); + _pwgWriteFile(p->pwg, pwg_name); + } + else + { + /* + * Remove cache files... + */ -/* - * 'compare_printers()' - Compare two printers. - */ + if (cache_info.st_mtime) + unlink(cache_name); -static int /* O - Result of comparison */ -compare_printers(void *first, /* I - First printer */ - void *second, /* I - Second printer */ - void *data) /* I - App data (not used) */ -{ - return (strcasecmp(((cupsd_printer_t *)first)->name, - ((cupsd_printer_t *)second)->name)); + if (pwg_info.st_mtime) + unlink(pwg_name); + } } /* - * 'delete_printer_filters()' - Delete all MIME filters for a printer. + * 'new_media_col()' - Create a media-col collection value. */ -static void -delete_printer_filters( - cupsd_printer_t *p) /* I - Printer to remove from */ +static ipp_t * /* O - Collection value */ +new_media_col(_pwg_size_t *size, /* I - media-size/margin values */ + const char *source, /* I - media-source value */ + const char *type) /* I - media-type value */ { - mime_filter_t *filter; /* MIME filter looping var */ - - - /* - * Range check input... - */ - - if (p == NULL) - return; - - /* - * Remove all filters from the MIME database that have a destination - * type == printer... - */ - - for (filter = mimeFirstFilter(MimeDatabase); - filter; - filter = mimeNextFilter(MimeDatabase)) - if (filter->dst == p->filetype) - { - /* - * Delete the current filter... - */ - - mimeDeleteFilter(MimeDatabase, filter); - } + ipp_t *media_col, /* Collection value */ + *media_size; /* media-size value */ + + + media_col = ippNew(); + + media_size = ippNew(); + ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "x-dimension", size->width); + ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "y-dimension", size->length); + ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size); + ippDelete(media_size); + + ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-bottom-margin", size->bottom); + ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-left-margin", size->left); + ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-right-margin", size->right); + ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "media-top-margin", size->top); + + if (source) + ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source", + NULL, source); + + if (type) + ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type", + NULL, type); + + return (media_col); } @@ -3565,7 +5181,7 @@ write_irix_config(cupsd_printer_t *p) /* I - Printer to update */ cupsFilePrintf(fp, "Printer Model | %s\n", p->make_model ? p->make_model : ""); cupsFilePrintf(fp, "Location Code | %s\n", p->location ? p->location : ""); cupsFilePrintf(fp, "Physical Location | %s\n", p->info ? p->info : ""); - cupsFilePrintf(fp, "Port Path | %s\n", p->device_uri ? p->device_uri : ""); + cupsFilePrintf(fp, "Port Path | %s\n", p->device_uri); cupsFilePrintf(fp, "Config Path | /var/spool/lp/pod/%s.config\n", p->name); cupsFilePrintf(fp, "Active Status Path | /var/spool/lp/pod/%s.status\n", p->name); cupsFilePuts(fp, "Status Update Wait | 10 seconds\n"); @@ -3610,7 +5226,7 @@ write_irix_state(cupsd_printer_t *p) /* I - Printer to update */ "Faulted"); cupsFilePrintf(fp, "Information | 01 00 00 | %s\n", CUPS_SVERSION); cupsFilePrintf(fp, "Information | 02 00 00 | Device URI: %s\n", - p->device_uri ? p->device_uri : ""); + p->device_uri); cupsFilePrintf(fp, "Information | 03 00 00 | %s jobs\n", p->accepting ? "Accepting" : "Not accepting"); cupsFilePrintf(fp, "Information | 04 00 00 | %s\n", p->state_message); @@ -3708,5 +5324,44 @@ write_irix_state(cupsd_printer_t *p) /* I - Printer to update */ /* - * End of "$Id: printers.c 6436 2007-04-02 23:24:02Z mike $". + * 'write_xml_string()' - Write a string with XML escaping. + */ + +static void +write_xml_string(cups_file_t *fp, /* I - File to write to */ + const char *s) /* I - String to write */ +{ + const char *start; /* Start of current sequence */ + + + if (!s) + return; + + for (start = s; *s; s ++) + { + if (*s == '&') + { + if (s > start) + cupsFileWrite(fp, start, s - start); + + cupsFilePuts(fp, "&"); + start = s + 1; + } + else if (*s == '<') + { + if (s > start) + cupsFileWrite(fp, start, s - start); + + cupsFilePuts(fp, "<"); + start = s + 1; + } + } + + if (s > start) + cupsFilePuts(fp, start); +} + + +/* + * End of "$Id: printers.c 7968 2008-09-19 23:03:01Z mike $". */