X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fcups.git;a=blobdiff_plain;f=scheduler%2Fipp.c;h=016a1b1af619972480604078d57598929ab90b34;hp=49a1b55e1eb342d5f8d51a7a5a03079dfc3f5593;hb=1f6f3dbcf92ae4c7e22a6b143ef27dd330e8ddbb;hpb=c24d21342fd8b3bb674364f52d01c6c4181d8ee2 diff --git a/scheduler/ipp.c b/scheduler/ipp.c index 49a1b55e1..016a1b1af 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -1,9 +1,9 @@ /* - * "$Id: ipp.c 6755 2007-08-01 19:02:47Z mike $" + * "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $" * * IPP routines for the Common UNIX Printing System (CUPS) scheduler. * - * Copyright 2007 by Apple Inc. + * Copyright 2007-2008 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * This file contains Kerberos support code, copyright 2006 by @@ -25,18 +25,25 @@ * add_job() - Add a job to a print queue. * add_job_state_reasons() - Add the "job-state-reasons" attribute based * upon the job and printer state... - * add_job_subscriptions() - Add any subcriptions for a job. + * add_job_subscriptions() - Add any subscriptions for a job. * add_job_uuid() - Add job-uuid attribute to a job. * add_printer() - Add a printer to the system. * add_printer_state_reasons() - Add the "printer-state-reasons" attribute * based upon the printer state... - * add_queued_job_count() - Add the "queued-job-count" attribute for + * add_queued_job_count() - Add the "queued-job-count" attribute for the + * specified printer or class. + * apple_init_profile() - Initialize a color profile. + * apple_register_profiles() - Register color profiles for a printer. + * apple_unregister_profiles() - Remove color profiles for the specified + * printer. * apply_printer_defaults() - Apply printer default options to a job. * authenticate_job() - Set job authentication info. * cancel_all_jobs() - Cancel all print jobs. * cancel_job() - Cancel a print job. * cancel_subscription() - Cancel a subscription. * check_quotas() - Check quotas for a printer and user. + * check_rss_recipient() - Check that we do not have a duplicate RSS + * feed URI. * copy_attribute() - Copy a single attribute. * copy_attrs() - Copy attributes from one request to another. * copy_banner() - Copy a banner file to the requests directory @@ -54,6 +61,7 @@ * get_default() - Get the default destination. * get_devices() - Get the list of available devices on the * local system. + * get_document() - Get a copy of a job file. * get_job_attrs() - Get job attributes. * get_jobs() - Get a list of jobs for the specified printer. * get_notifications() - Get events for a subscription. @@ -61,7 +69,7 @@ * get_ppds() - Get the list of PPD files on the local * system. * get_printer_attrs() - Get printer attributes. - * get_printers() - Get a list of printers. + * get_printers() - Get a list of printers or classes. * get_subscription_attrs() - Get subscription attributes. * get_subscriptions() - Get subscriptions. * get_username() - Get the username associated with a request. @@ -69,13 +77,13 @@ * move_job() - Move a job to a new destination. * ppd_parse_line() - Parse a PPD default line. * print_job() - Print a file to a printer or class. - * read_ps_line() - Read a line from a PS file... - * read_ps_job_ticket() - Reads a job ticket embedded in a PS file. + * read_job_ticket() - Read a job ticket embedded in a print file. * reject_jobs() - Reject print jobs to a printer. * release_job() - Release a held print job. + * renew_subscription() - Renew an existing subscription... * restart_job() - Restart an old print job. * save_auth_info() - Save authentication information for a job. - * save_krb5_creds() - Save Kerberos credentials for a job. + * save_krb5_creds() - Save Kerberos credentials for the job. * send_document() - Send a file to a printer or class. * send_http_error() - Send a HTTP error back to the IPP client. * send_ipp_status() - Send a status back to the IPP client. @@ -98,16 +106,15 @@ */ #include "cupsd.h" - -#ifdef HAVE_KRB5_H -# include -#endif /* HAVE_KRB5_H */ +#include #ifdef HAVE_LIBPAPER # include #endif /* HAVE_LIBPAPER */ #ifdef __APPLE__ +# include +# include # ifdef HAVE_MEMBERSHIP_H # include # endif /* HAVE_MEMBERSHIP_H */ @@ -138,12 +145,21 @@ static void add_printer(cupsd_client_t *con, ipp_attribute_t *uri); static void add_printer_state_reasons(cupsd_client_t *con, cupsd_printer_t *p); static void add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p); +#ifdef __APPLE__ +static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages, + CMDeviceProfileInfo *profile, unsigned id, + const char *name, const char *text, + const char *iccfile); +static void apple_register_profiles(cupsd_printer_t *p); +static void apple_unregister_profiles(cupsd_printer_t *p); +#endif /* __APPLE__ */ static void apply_printer_defaults(cupsd_printer_t *printer, cupsd_job_t *job); static void authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri); static void cancel_all_jobs(cupsd_client_t *con, ipp_attribute_t *uri); static void cancel_job(cupsd_client_t *con, ipp_attribute_t *uri); static void cancel_subscription(cupsd_client_t *con, int id); +static int check_rss_recipient(const char *recipient); static int check_quotas(cupsd_client_t *con, cupsd_printer_t *p); static ipp_attribute_t *copy_attribute(ipp_t *to, ipp_attribute_t *attr, int quickcopy); @@ -169,6 +185,7 @@ static void create_subscription(cupsd_client_t *con, ipp_attribute_t *uri); static void delete_printer(cupsd_client_t *con, ipp_attribute_t *uri); static void get_default(cupsd_client_t *con); static void get_devices(cupsd_client_t *con); +static void get_document(cupsd_client_t *con, ipp_attribute_t *uri); static void get_jobs(cupsd_client_t *con, ipp_attribute_t *uri); static void get_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri); static void get_notifications(cupsd_client_t *con); @@ -184,7 +201,7 @@ static void move_job(cupsd_client_t *con, ipp_attribute_t *uri); static int ppd_parse_line(const char *line, char *option, int olen, char *choice, int clen); static void print_job(cupsd_client_t *con, ipp_attribute_t *uri); -static void read_ps_job_ticket(cupsd_client_t *con); +static void read_job_ticket(cupsd_client_t *con); static void reject_jobs(cupsd_client_t *con, ipp_attribute_t *uri); static void release_job(cupsd_client_t *con, ipp_attribute_t *uri); static void renew_subscription(cupsd_client_t *con, int sub_id); @@ -232,7 +249,7 @@ cupsdProcessIPPRequest( ipp_attribute_t *attr; /* Current attribute */ ipp_attribute_t *charset; /* Character set attribute */ ipp_attribute_t *language; /* Language attribute */ - ipp_attribute_t *uri; /* Printer URI attribute */ + ipp_attribute_t *uri = NULL; /* Printer or job URI attribute */ ipp_attribute_t *username; /* requesting-user-name attr */ int sub_id; /* Subscription ID */ @@ -364,13 +381,31 @@ cupsdProcessIPPRequest( ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, DefaultLanguage); - if (!charset || !language || - (!uri && - con->request->request.op.operation_id != CUPS_GET_DEFAULT && - con->request->request.op.operation_id != CUPS_GET_PRINTERS && - con->request->request.op.operation_id != CUPS_GET_CLASSES && - con->request->request.op.operation_id != CUPS_GET_DEVICES && - con->request->request.op.operation_id != CUPS_GET_PPDS)) + if (charset && + strcasecmp(charset->values[0].string.text, "us-ascii") && + strcasecmp(charset->values[0].string.text, "utf-8")) + { + /* + * Bad character set... + */ + + cupsdLogMessage(CUPSD_LOG_ERROR, "Unsupported character set \"%s\"!", + charset->values[0].string.text); + cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, + "%04X %s Unsupported attributes-charset value \"%s\"", + IPP_CHARSET, con->http.hostname, + charset->values[0].string.text); + send_ipp_status(con, IPP_BAD_REQUEST, + _("Unsupported character set \"%s\"!"), + charset->values[0].string.text); + } + else if (!charset || !language || + (!uri && + con->request->request.op.operation_id != CUPS_GET_DEFAULT && + con->request->request.op.operation_id != CUPS_GET_PRINTERS && + con->request->request.op.operation_id != CUPS_GET_CLASSES && + con->request->request.op.operation_id != CUPS_GET_DEVICES && + con->request->request.op.operation_id != CUPS_GET_PPDS)) { /* * Return an error, since attributes-charset, @@ -575,6 +610,10 @@ cupsdProcessIPPRequest( get_devices(con); break; + case CUPS_GET_DOCUMENT : + get_document(con, uri); + break; + case CUPS_GET_PPD : get_ppd(con, uri); break; @@ -639,12 +678,17 @@ cupsdProcessIPPRequest( * Sending data from the scheduler... */ - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdProcessIPPRequest: %d status_code=%x (%s)", - con->http.fd, con->response->request.status.status_code, - ippErrorString(con->response->request.status.status_code)); + cupsdLogMessage(con->response->request.status.status_code + >= IPP_BAD_REQUEST && + con->response->request.status.status_code + != IPP_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG, + "Returning IPP %s for %s (%s) from %s", + ippErrorString(con->response->request.status.status_code), + ippOpString(con->request->request.op.operation_id), + uri ? uri->values[0].string.text : "no URI", + con->http.hostname); - if (cupsdSendHeader(con, HTTP_OK, "application/ipp", AUTH_NONE)) + if (cupsdSendHeader(con, HTTP_OK, "application/ipp", CUPSD_AUTH_NONE)) { #ifdef CUPSD_USE_CHUNKING /* @@ -732,7 +776,7 @@ cupsdProcessIPPRequest( * 'cupsdTimeoutJob()' - Timeout a job waiting on job files. */ -void +int /* O - 0 on success, -1 on error */ cupsdTimeoutJob(cupsd_job_t *job) /* I - Job to timeout */ { cupsd_printer_t *printer; /* Destination printer or class */ @@ -757,13 +801,16 @@ cupsdTimeoutJob(cupsd_job_t *job) /* I - Job to timeout */ * Yes... */ - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Adding end banner page \"%s\".", - job->id, attr->values[1].string.text); + cupsdLogJob(job, CUPSD_LOG_INFO, "Adding end banner page \"%s\".", + attr->values[1].string.text); - kbytes = copy_banner(NULL, job, attr->values[1].string.text); + if ((kbytes = copy_banner(NULL, job, attr->values[1].string.text)) < 0) + return (-1); cupsdUpdateQuota(printer, job->username, 0, kbytes); } + + return (0); } @@ -819,14 +866,14 @@ accept_jobs(cupsd_client_t *con, /* I - Client connection */ if (dtype & CUPS_PRINTER_CLASS) { - cupsdSaveAllClasses(); + cupsdMarkDirty(CUPSD_DIRTY_CLASSES); cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" now accepting jobs (\"%s\").", printer->name, get_username(con)); } else { - cupsdSaveAllPrinters(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" now accepting jobs (\"%s\").", @@ -905,16 +952,6 @@ add_class(cupsd_client_t *con, /* I - Client connection */ return; } - /* - * Check policy... - */ - - if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) - { - send_http_error(con, status, NULL); - return; - } - /* * See if the class already exists; if not, create a new class... */ @@ -939,18 +976,31 @@ add_class(cupsd_client_t *con, /* I - Client connection */ } /* - * No, add the pclass... + * No, check the default policy and then add the class... */ + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + pclass = cupsdAddClass(resource + 9); modify = 0; } else if (pclass->type & CUPS_PRINTER_IMPLICIT) { /* - * Rename the implicit class to "AnyClass" or remove it... + * Check the default policy, then rename the implicit class to "AnyClass" + * or remove it... */ + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + if (ImplicitAnyClasses) { snprintf(newname, sizeof(newname), "Any%s", resource + 9); @@ -969,9 +1019,15 @@ add_class(cupsd_client_t *con, /* I - Client connection */ else if (pclass->type & CUPS_PRINTER_DISCOVERED) { /* - * Rename the remote class to "Class"... + * Check the default policy, then rename the remote class to "Class"... */ + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + snprintf(newname, sizeof(newname), "%s@%s", resource + 9, pclass->hostname); cupsdRenamePrinter(pclass, newname); @@ -982,6 +1038,12 @@ add_class(cupsd_client_t *con, /* I - Client connection */ pclass = cupsdAddClass(resource + 9); modify = 0; } + else if ((status = cupsdCheckPolicy(pclass->op_policy_ptr, con, + NULL)) != HTTP_OK) + { + send_http_error(con, status, pclass); + return; + } else modify = 1; @@ -1108,7 +1170,7 @@ add_class(cupsd_client_t *con, /* I - Client connection */ */ cupsdSetPrinterAttrs(pclass); - cupsdSaveAllClasses(); + cupsdMarkDirty(CUPSD_DIRTY_CLASSES); if (need_restart_job && pclass->job) { @@ -1129,7 +1191,7 @@ add_class(cupsd_client_t *con, /* I - Client connection */ if (need_restart_job) cupsdCheckJobs(); - cupsdWritePrintcap(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); if (modify) { @@ -1211,6 +1273,9 @@ add_file(cupsd_client_t *con, /* I - Connection to client */ job->num_files ++; + job->dirty = 1; + cupsdMarkDirty(CUPSD_DIRTY_JOBS); + return (0); } @@ -1239,7 +1304,8 @@ add_job(cupsd_client_t *con, /* I - Client connection */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))", con, con->http.fd, printer, printer->name, - filetype, filetype->super, filetype->type); + filetype, filetype ? filetype->super : "none", + filetype ? filetype->type : "none"); /* * Check remote printing to non-shared printer... @@ -1265,7 +1331,8 @@ add_job(cupsd_client_t *con, /* I - Client connection */ send_http_error(con, status, printer); return (NULL); } - else if ((printer->type & CUPS_PRINTER_AUTHENTICATED) && + else if (printer->num_auth_info_required > 0 && + strcmp(printer->auth_info_required[0], "none") && !con->username[0] && !auth_info) { send_http_error(con, HTTP_UNAUTHORIZED, printer); @@ -1333,6 +1400,34 @@ add_job(cupsd_client_t *con, /* I - Client connection */ } } + if ((attr = ippFindAttribute(con->request, "job-sheets", + IPP_TAG_ZERO)) != NULL) + { + if (attr->value_tag != IPP_TAG_KEYWORD && + attr->value_tag != IPP_TAG_NAME) + { + send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value type!")); + return (NULL); + } + + if (attr->num_values > 2) + { + send_ipp_status(con, IPP_BAD_REQUEST, + _("Too many job-sheets values (%d > 2)!"), + attr->num_values); + return (NULL); + } + + for (i = 0; i < attr->num_values; i ++) + if (strcmp(attr->values[i].string.text, "none") && + !cupsdFindBanner(attr->values[i].string.text)) + { + send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value \"%s\"!"), + attr->values[i].string.text); + return (NULL); + } + } + if ((attr = ippFindAttribute(con->request, "number-up", IPP_TAG_INTEGER)) != NULL) { @@ -1432,8 +1527,11 @@ add_job(cupsd_client_t *con, /* I - Client connection */ job->dtype = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_REMOTE); job->attrs = con->request; + job->dirty = 1; con->request = ippNewRequest(job->attrs->request.op.operation_id); + cupsdMarkDirty(CUPSD_DIRTY_JOBS); + add_job_uuid(con, job); apply_printer_defaults(printer, job); @@ -1476,17 +1574,7 @@ add_job(cupsd_client_t *con, /* I - Client connection */ */ if (auth_info) - { - if (job->attrs->prev) - job->attrs->prev->next = auth_info->next; - else - job->attrs->attrs = auth_info->next; - - if (job->attrs->last == auth_info) - job->attrs->last = job->attrs->prev; - - _ippFreeAttr(auth_info); - } + ippDeleteAttribute(job->attrs, auth_info); } if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name", @@ -1587,8 +1675,7 @@ add_job(cupsd_client_t *con, /* I - Client connection */ IPP_TAG_INTEGER)) != NULL) attr->values[0].integer = 0; else - attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, - "job-k-octets", 0); + ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", 0); if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL) @@ -1671,10 +1758,10 @@ add_job(cupsd_client_t *con, /* I - Client connection */ cupsdSetString(&attr->values[0].string.text, Classification); - cupsdLogMessage(CUPSD_LOG_NOTICE, "[Job %d] CLASSIFICATION FORCED " - "job-sheets=\"%s,none\", " - "job-originating-user-name=\"%s\"", - job->id, Classification, job->username); + cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED " + "job-sheets=\"%s,none\", " + "job-originating-user-name=\"%s\"", + Classification, job->username); } else if (attr->num_values == 2 && strcmp(attr->values[0].string.text, @@ -1688,11 +1775,11 @@ add_job(cupsd_client_t *con, /* I - Client connection */ cupsdSetString(&attr->values[1].string.text, attr->values[0].string.text); - cupsdLogMessage(CUPSD_LOG_NOTICE, "[Job %d] CLASSIFICATION FORCED " - "job-sheets=\"%s,%s\", " - "job-originating-user-name=\"%s\"", - job->id, attr->values[0].string.text, - attr->values[1].string.text, job->username); + cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED " + "job-sheets=\"%s,%s\", " + "job-originating-user-name=\"%s\"", + attr->values[0].string.text, + attr->values[1].string.text, job->username); } else if (strcmp(attr->values[0].string.text, Classification) && strcmp(attr->values[0].string.text, "none") && @@ -1701,18 +1788,18 @@ add_job(cupsd_client_t *con, /* I - Client connection */ strcmp(attr->values[1].string.text, "none")))) { if (attr->num_values == 1) - cupsdLogMessage(CUPSD_LOG_NOTICE, - "[Job %d] CLASSIFICATION OVERRIDDEN " - "job-sheets=\"%s\", " - "job-originating-user-name=\"%s\"", - job->id, attr->values[0].string.text, job->username); + cupsdLogJob(job, CUPSD_LOG_NOTICE, + "CLASSIFICATION OVERRIDDEN " + "job-sheets=\"%s\", " + "job-originating-user-name=\"%s\"", + attr->values[0].string.text, job->username); else - cupsdLogMessage(CUPSD_LOG_NOTICE, - "[Job %d] CLASSIFICATION OVERRIDDEN " - "job-sheets=\"%s,%s\",fffff " - "job-originating-user-name=\"%s\"", - job->id, attr->values[0].string.text, - attr->values[1].string.text, job->username); + cupsdLogJob(job, CUPSD_LOG_NOTICE, + "CLASSIFICATION OVERRIDDEN " + "job-sheets=\"%s,%s\",fffff " + "job-originating-user-name=\"%s\"", + attr->values[0].string.text, + attr->values[1].string.text, job->username); } } else if (strcmp(attr->values[0].string.text, Classification) && @@ -1741,18 +1828,18 @@ add_job(cupsd_client_t *con, /* I - Client connection */ } if (attr->num_values > 1) - cupsdLogMessage(CUPSD_LOG_NOTICE, - "[Job %d] CLASSIFICATION FORCED " - "job-sheets=\"%s,%s\", " - "job-originating-user-name=\"%s\"", - job->id, attr->values[0].string.text, - attr->values[1].string.text, job->username); + cupsdLogJob(job, CUPSD_LOG_NOTICE, + "CLASSIFICATION FORCED " + "job-sheets=\"%s,%s\", " + "job-originating-user-name=\"%s\"", + attr->values[0].string.text, + attr->values[1].string.text, job->username); else - cupsdLogMessage(CUPSD_LOG_NOTICE, - "[Job %d] CLASSIFICATION FORCED " - "job-sheets=\"%s\", " - "job-originating-user-name=\"%s\"", - job->id, Classification, job->username); + cupsdLogJob(job, CUPSD_LOG_NOTICE, + "CLASSIFICATION FORCED " + "job-sheets=\"%s\", " + "job-originating-user-name=\"%s\"", + Classification, job->username); } } @@ -1762,11 +1849,14 @@ add_job(cupsd_client_t *con, /* I - Client connection */ if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))) { - cupsdLogMessage(CUPSD_LOG_INFO, - "[Job %d] Adding start banner page \"%s\".", - job->id, attr->values[0].string.text); + cupsdLogJob(job, CUPSD_LOG_INFO, "Adding start banner page \"%s\".", + attr->values[0].string.text); - kbytes = copy_banner(con, job, attr->values[0].string.text); + if ((kbytes = copy_banner(con, job, attr->values[0].string.text)) < 0) + { + cupsdDeleteJob(job); + return (NULL); + } cupsdUpdateQuota(printer, job->username, 0, kbytes); } @@ -1890,7 +1980,7 @@ add_job_state_reasons( /* - * 'add_job_subscriptions()' - Add any subcriptions for a job. + * 'add_job_subscriptions()' - Add any subscriptions for a job. */ static void @@ -1915,9 +2005,7 @@ add_job_subscriptions( * none... */ - for (attr = job->attrs->attrs, prev = NULL; - attr; - prev = attr, attr = attr->next) + for (attr = job->attrs->attrs; attr; attr = attr->next) if (attr->group_tag == IPP_TAG_SUBSCRIPTION) break; @@ -1940,10 +2028,70 @@ add_job_subscriptions( { if (!strcmp(attr->name, "notify-recipient-uri") && attr->value_tag == IPP_TAG_URI) + { + /* + * Validate the recipient scheme against the ServerBin/notifier + * directory... + */ + + char notifier[1024], /* Notifier filename */ + scheme[HTTP_MAX_URI], /* Scheme portion of URI */ + userpass[HTTP_MAX_URI], /* Username portion of URI */ + host[HTTP_MAX_URI], /* Host portion of URI */ + resource[HTTP_MAX_URI]; /* Resource portion of URI */ + int port; /* Port portion of URI */ + + recipient = attr->values[0].string.text; + + if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient, + scheme, sizeof(scheme), userpass, sizeof(userpass), + host, sizeof(host), &port, + resource, sizeof(resource)) < HTTP_URI_OK) + { + send_ipp_status(con, IPP_NOT_POSSIBLE, + _("Bad notify-recipient-uri URI \"%s\"!"), recipient); + ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, + "notify-status-code", IPP_URI_SCHEME); + return; + } + + snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin, + scheme); + if (access(notifier, X_OK)) + { + send_ipp_status(con, IPP_NOT_POSSIBLE, + _("notify-recipient-uri URI \"%s\" uses unknown " + "scheme!"), recipient); + ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, + "notify-status-code", IPP_URI_SCHEME); + return; + } + + if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient)) + { + send_ipp_status(con, IPP_NOT_POSSIBLE, + _("notify-recipient-uri URI \"%s\" is already used!"), + recipient); + ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, + "notify-status-code", IPP_ATTRIBUTES); + return; + } + } else if (!strcmp(attr->name, "notify-pull-method") && attr->value_tag == IPP_TAG_KEYWORD) + { pullmethod = attr->values[0].string.text; + + if (strcmp(pullmethod, "ippget")) + { + send_ipp_status(con, IPP_NOT_POSSIBLE, + _("Bad notify-pull-method \"%s\"!"), pullmethod); + ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, + "notify-status-code", IPP_ATTRIBUTES); + return; + } + } else if (!strcmp(attr->name, "notify-charset") && attr->value_tag == IPP_TAG_CHARSET && strcmp(attr->values[0].string.text, "us-ascii") && @@ -2025,10 +2173,13 @@ add_job_subscriptions( attr = attr->next; } - cupsdSaveAllSubscriptions(); + cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS); /* * Remove all of the subscription attributes from the job request... + * + * TODO: Optimize this since subscription groups have to come at the + * end of the request... */ for (attr = job->attrs->attrs, prev = NULL; attr; attr = next) @@ -2177,16 +2328,6 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ return; } - /* - * Check policy... - */ - - if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) - { - send_http_error(con, status, NULL); - return; - } - /* * See if the printer already exists; if not, create a new printer... */ @@ -2211,18 +2352,31 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ } /* - * No, add the printer... + * No, check the default policy then add the printer... */ + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + printer = cupsdAddPrinter(resource + 10); modify = 0; } else if (printer->type & CUPS_PRINTER_IMPLICIT) { /* - * Rename the implicit printer to "AnyPrinter" or delete it... + * Check the default policy, then rename the implicit printer to + * "AnyPrinter" or delete it... */ + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + if (ImplicitAnyClasses) { snprintf(newname, sizeof(newname), "Any%s", resource + 10); @@ -2241,9 +2395,16 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ else if (printer->type & CUPS_PRINTER_DISCOVERED) { /* - * Rename the remote printer to "Printer@server"... + * Check the default policy, then rename the remote printer to + * "Printer@server"... */ + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + snprintf(newname, sizeof(newname), "%s@%s", resource + 10, printer->hostname); cupsdRenamePrinter(printer, newname); @@ -2255,6 +2416,12 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ printer = cupsdAddPrinter(resource + 10); modify = 0; } + else if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, + NULL)) != HTTP_OK) + { + send_http_error(con, status, printer); + return; + } else modify = 1; @@ -2281,11 +2448,28 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ * Do we have a valid device URI? */ + http_uri_status_t uri_status; /* URI separation status */ + char old_device_uri[1024]; + /* Old device URI */ + + need_restart_job = 1; - httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, scheme, - sizeof(scheme), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); + uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, + attr->values[0].string.text, + scheme, sizeof(scheme), + username, sizeof(username), + host, sizeof(host), &port, + resource, sizeof(resource)); + + if (uri_status < HTTP_URI_OK) + { + send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"!"), + attr->values[0].string.text); + cupsdLogMessage(CUPSD_LOG_DEBUG, + "add_printer: httpSeparateURI returned %d", uri_status); + return; + } if (!strcmp(scheme, "file")) { @@ -2320,21 +2504,25 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ * Could not find device in list! */ - send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"!"), - attr->values[0].string.text); + send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri scheme \"%s\"!"), + scheme); return; } } + if (printer->sanitized_device_uri) + strlcpy(old_device_uri, printer->sanitized_device_uri, + sizeof(old_device_uri)); + else + old_device_uri[0] = '\0'; + + cupsdSetDeviceURI(printer, attr->values[0].string.text); + cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s device-uri to \"%s\" (was \"%s\".)", - printer->name, - cupsdSanitizeURI(attr->values[0].string.text, line, - sizeof(line)), - cupsdSanitizeURI(printer->device_uri, resource, - sizeof(resource))); + printer->name, printer->sanitized_device_uri, + old_device_uri); - cupsdSetString(&printer->device_uri, attr->values[0].string.text); set_device_uri = 1; } @@ -2349,13 +2537,16 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ need_restart_job = 1; supported = ippFindAttribute(printer->attrs, "port-monitor-supported", - IPP_TAG_KEYWORD); - for (i = 0; i < supported->num_values; i ++) - if (!strcmp(supported->values[i].string.text, - attr->values[0].string.text)) - break; + IPP_TAG_NAME); + if (supported) + { + for (i = 0; i < supported->num_values; i ++) + if (!strcmp(supported->values[i].string.text, + attr->values[0].string.text)) + break; + } - if (i >= supported->num_values) + if (!supported || i >= supported->num_values) { send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\"!"), attr->values[0].string.text); @@ -2429,6 +2620,48 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ cupsdAddPrinterHistory(printer); } + if ((attr = ippFindAttribute(con->request, "printer-state-reasons", + IPP_TAG_KEYWORD)) != NULL) + { + if (attr->num_values > + (int)(sizeof(printer->reasons) / sizeof(printer->reasons[0]))) + { + send_ipp_status(con, IPP_NOT_POSSIBLE, + _("Too many printer-state-reasons values (%d > %d)!"), + attr->num_values, + (int)(sizeof(printer->reasons) / + sizeof(printer->reasons[0]))); + return; + } + + for (i = 0; i < printer->num_reasons; i ++) + _cupsStrFree(printer->reasons[i]); + + printer->num_reasons = 0; + for (i = 0; i < attr->num_values; i ++) + { + if (!strcmp(attr->values[i].string.text, "none")) + continue; + + printer->reasons[printer->num_reasons] = + _cupsStrAlloc(attr->values[i].string.text); + + if (!strcmp(printer->reasons[printer->num_reasons], "paused") && + printer->state != IPP_PRINTER_STOPPED) + { + cupsdLogMessage(CUPSD_LOG_INFO, + "Setting %s printer-state to %d (was %d.)", + printer->name, IPP_PRINTER_STOPPED, printer->state); + cupsdStopPrinter(printer, 0); + } + + printer->num_reasons ++; + } + + if (PrintcapFormat == PRINTCAP_PLIST) + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); + } + set_printer_defaults(con, printer); if ((attr = ippFindAttribute(con->request, "auth-info-required", @@ -2492,12 +2725,10 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ strerror(errno)); return; } - else - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Copied interface script successfully!"); - chmod(dstfile, 0755); - } + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "Copied interface script successfully!"); + chmod(dstfile, 0755); } snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot, @@ -2517,12 +2748,19 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ strerror(errno)); return; } - else - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Copied PPD file successfully!"); - chmod(dstfile, 0644); - } + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "Copied PPD file successfully!"); + chmod(dstfile, 0644); + +#ifdef __APPLE__ + /* + * (Re)register color profiles... + */ + + apple_unregister_profiles(printer); + apple_register_profiles(printer); +#endif /* __APPLE__ */ } else { @@ -2572,12 +2810,19 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file!")); return; } - else - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Copied PPD file successfully!"); - chmod(dstfile, 0644); - } + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "Copied PPD file successfully!"); + chmod(dstfile, 0644); + +#ifdef __APPLE__ + /* + * (Re)register color profiles... + */ + + apple_unregister_profiles(printer); + apple_register_profiles(printer); +#endif /* __APPLE__ */ } } @@ -2628,7 +2873,7 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ */ cupsdSetPrinterAttrs(printer); - cupsdSaveAllPrinters(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); if (need_restart_job && printer->job) { @@ -2649,7 +2894,7 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ if (need_restart_job) cupsdCheckJobs(); - cupsdWritePrintcap(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); if (modify) { @@ -2724,45 +2969,590 @@ add_queued_job_count( } +#ifdef __APPLE__ /* - * 'apply_printer_defaults()' - Apply printer default options to a job. + * 'apple_init_profile()' - Initialize a color profile. */ static void -apply_printer_defaults( - cupsd_printer_t *printer, /* I - Printer */ - cupsd_job_t *job) /* I - Job */ +apple_init_profile( + ppd_file_t *ppd, /* I - PPD file */ + cups_array_t *languages, /* I - Languages in the PPD file */ + CMDeviceProfileInfo *profile, /* I - Profile record */ + unsigned id, /* I - Profile ID */ + const char *name, /* I - Profile name */ + const char *text, /* I - Profile UI text */ + const char *iccfile) /* I - ICC filename */ { - int i, /* Looping var */ - num_options; /* Number of default options */ - cups_option_t *options, /* Default options */ - *option; /* Current option */ + char url[1024]; /* URL for profile filename */ + CFMutableDictionaryRef dict; /* Dictionary for name */ + char *language; /* Current language */ + ppd_attr_t *attr; /* Profile attribute */ + CFStringRef cflang, /* Language string */ + cftext; /* Localized text */ /* - * Collect all of the default options and add the missing ones to the - * job object... + * Build the profile name dictionary... */ - for (i = printer->num_options, num_options = 0, option = printer->options; - i > 0; - i --, option ++) - if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO)) - { - num_options = cupsAddOption(option->name, option->value, num_options, - &options); - } - - /* - * Encode these options as attributes in the job object... - */ + dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); - cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB); - cupsFreeOptions(num_options, options); -} + cftext = CFStringCreateWithCString(kCFAllocatorDefault, text, + kCFStringEncodingUTF8); + if (cftext) + { + CFDictionarySetValue(dict, CFSTR("en"), cftext); + CFRelease(cftext); + } -/* + if (languages) + { + /* + * Find localized names for the color profiles... + */ + + cupsArraySave(ppd->sorted_attrs); + + for (language = (char *)cupsArrayFirst(languages); + language; + language = (char *)cupsArrayNext(languages)) + { + if (iccfile) + { + if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name, + language)) == NULL) + attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language); + } + else + attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language); + + if (attr && attr->text[0]) + { + cflang = CFStringCreateWithCString(kCFAllocatorDefault, language, + kCFStringEncodingUTF8); + cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text, + kCFStringEncodingUTF8); + + if (cflang && cftext) + CFDictionarySetValue(dict, cflang, cftext); + + if (cflang) + CFRelease(cflang); + + if (cftext) + CFRelease(cftext); + } + } + + cupsArrayRestore(ppd->sorted_attrs); + } + + /* + * Fill in the profile data... + */ + + if (iccfile) + httpAssembleURI(HTTP_URI_CODING_ALL, url, sizeof(url), "file", NULL, "", 0, + iccfile); + + profile->dataVersion = cmDeviceProfileInfoVersion1; + profile->profileID = id; + profile->profileLoc.locType = iccfile ? cmPathBasedProfile : cmNoProfileBase; + profile->profileName = dict; + + if (iccfile) + strlcpy(profile->profileLoc.u.pathLoc.path, iccfile, + sizeof(profile->profileLoc.u.pathLoc.path)); +} + + +/* + * 'apple_register_profiles()' - Register color profiles for a printer. + */ + +static void +apple_register_profiles( + cupsd_printer_t *p) /* I - Printer */ +{ + int i; /* Looping var */ + char ppdfile[1024], /* PPD filename */ + iccfile[1024], /* ICC filename */ + selector[PPD_MAX_NAME]; + /* Profile selection string */ + ppd_file_t *ppd; /* PPD file */ + ppd_attr_t *attr, /* Profile attributes */ + *profileid_attr,/* cupsProfileID attribute */ + *q1_attr, /* ColorModel (or other) qualifier */ + *q2_attr, /* MediaType (or other) qualifier */ + *q3_attr; /* Resolution (or other) qualifier */ + char q_keyword[PPD_MAX_NAME]; + /* Qualifier keyword */ + const char *q1_choice, /* ColorModel (or other) choice */ + *q2_choice, /* MediaType (or other) choice */ + *q3_choice; /* Resolution (or other) choice */ + const char *profile_key; /* Profile keyword */ + ppd_option_t *cm_option; /* Color model option */ + ppd_choice_t *cm_choice; /* Color model choice */ + int num_profiles; /* Number of profiles */ + CMError error; /* Last error */ + unsigned device_id, /* Printer device ID */ + profile_id, /* Profile ID */ + default_profile_id = 0; + /* Default profile ID */ + CFMutableDictionaryRef device_name; /* Printer device name dictionary */ + CFStringRef printer_name; /* Printer name string */ + CMDeviceScope scope = /* Scope of the registration */ + { + kCFPreferencesAnyUser, + kCFPreferencesCurrentHost + }; + CMDeviceProfileArrayPtr profiles; /* Profiles */ + CMDeviceProfileInfo *profile; /* Current profile */ + cups_array_t *languages; /* Languages array */ + + + /* + * Make sure ColorSync is available... + */ + + if (CMRegisterColorDevice == NULL) + return; + + /* + * Try opening the PPD file for this printer... + */ + + snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name); + if ((ppd = ppdOpenFile(ppdfile)) == NULL) + return; + + /* + * See if we have any profiles... + */ + + if ((attr = ppdFindAttr(ppd, "APTiogaProfile", NULL)) != NULL) + profile_key = "APTiogaProfile"; + else + { + attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); + profile_key = "cupsICCProfile"; + } + + for (num_profiles = 0; attr; attr = ppdFindNextAttr(ppd, profile_key, NULL)) + if (attr->spec[0] && attr->value && attr->value[0]) + { + if (attr->value[0] != '/') + snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir, + attr->value); + else + strlcpy(iccfile, attr->value, sizeof(iccfile)); + + if (access(iccfile, 0)) + continue; + + num_profiles ++; + } + + + /* + * If we have profiles, add them... + */ + + if (num_profiles > 0) + { + if (profile_key[0] == 'A') + { + /* + * For Tioga PPDs, get the default profile using the DefaultAPTiogaProfile + * attribute... + */ + + if ((attr = ppdFindAttr(ppd, "DefaultAPTiogaProfile", NULL)) != NULL && + attr->value) + default_profile_id = atoi(attr->value); + + q1_choice = q2_choice = q3_choice = NULL; + } + else + { + /* + * For CUPS PPDs, figure out the default profile selector values... + */ + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL && + attr->value && attr->value[0]) + { + snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); + q1_attr = ppdFindAttr(ppd, q_keyword, NULL); + } + else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL) + q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL); + + if (q1_attr && q1_attr->value && q1_attr->value[0]) + q1_choice = q1_attr->value; + else + q1_choice = ""; + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL && + attr->value && attr->value[0]) + { + snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); + q2_attr = ppdFindAttr(ppd, q_keyword, NULL); + } + else + q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL); + + if (q2_attr && q2_attr->value && q2_attr->value[0]) + q2_choice = q2_attr->value; + else + q2_choice = NULL; + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL && + attr->value && attr->value[0]) + { + snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); + q3_attr = ppdFindAttr(ppd, q_keyword, NULL); + } + else + q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL); + + if (q3_attr && q3_attr->value && q3_attr->value[0]) + q3_choice = q3_attr->value; + else + q3_choice = NULL; + } + + /* + * Build the array of profiles... + * + * Note: This calloc actually requests slightly more memory than needed. + */ + + if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for %d profiles!", + num_profiles); + ppdClose(ppd); + return; + } + + profiles->profileCount = num_profiles; + languages = _ppdGetLanguages(ppd); + + for (profile = profiles->profiles, + attr = ppdFindAttr(ppd, profile_key, NULL); + attr; + attr = ppdFindNextAttr(ppd, profile_key, NULL)) + if (attr->spec[0] && attr->value && attr->value[0]) + { + /* + * Add this profile... + */ + + if (attr->value[0] != '/') + snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir, + attr->value); + else + strlcpy(iccfile, attr->value, sizeof(iccfile)); + + if (access(iccfile, 0)) + continue; + + if (profile_key[0] == 'c') + { + cupsArraySave(ppd->sorted_attrs); + + if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID", + attr->spec)) != NULL && + profileid_attr->value && isdigit(profileid_attr->value[0] & 255)) + profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10); + else + profile_id = _ppdHashName(attr->spec); + + cupsArrayRestore(ppd->sorted_attrs); + } + else + profile_id = atoi(attr->spec); + + apple_init_profile(ppd, languages, profile, profile_id, attr->spec, + attr->text[0] ? attr->text : attr->spec, iccfile); + + profile ++; + + /* + * See if this is the default profile... + */ + + if (!default_profile_id) + { + if (q2_choice) + { + if (q3_choice) + { + snprintf(selector, sizeof(selector), "%s.%s.%s", + q1_choice, q2_choice, q3_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + + if (!default_profile_id) + { + snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, + q2_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + } + + if (!default_profile_id && q3_choice) + { + snprintf(selector, sizeof(selector), "%s..%s", q1_choice, + q3_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + + if (!default_profile_id) + { + snprintf(selector, sizeof(selector), "%s..", q1_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + } + } + + _ppdFreeLanguages(languages); + } + else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL) + { + /* + * Extract profiles from ColorModel option... + */ + + const char *profile_name; /* Name of generic profile */ + + + num_profiles = cm_option->num_choices; + + if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for %d profiles!", + num_profiles); + ppdClose(ppd); + return; + } + + profiles->profileCount = num_profiles; + + for (profile = profiles->profiles, i = cm_option->num_choices, + cm_choice = cm_option->choices; + i > 0; + i --, cm_choice ++, profile ++) + { + if (!strcmp(cm_choice->choice, "Gray") || + !strcmp(cm_choice->choice, "Black")) + profile_name = "Gray"; + else if (!strcmp(cm_choice->choice, "RGB") || + !strcmp(cm_choice->choice, "CMY")) + profile_name = "RGB"; + else if (!strcmp(cm_choice->choice, "CMYK") || + !strcmp(cm_choice->choice, "KCMY")) + profile_name = "CMYK"; + else + profile_name = "DeviceN"; + + snprintf(selector, sizeof(selector), "%s..", profile_name); + profile_id = _ppdHashName(selector); + + apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice, + cm_choice->text, NULL); + + if (cm_choice->marked) + default_profile_id = profile_id; + } + } + else + { + /* + * Use the default colorspace... + */ + + num_profiles = 1 + ppd->colorspace != PPD_CS_GRAY; + + if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for %d profiles!", + num_profiles); + ppdClose(ppd); + return; + } + + profiles->profileCount = num_profiles; + + apple_init_profile(ppd, NULL, profiles->profiles, _ppdHashName("Gray.."), + "Gray", "Gray", NULL); + + switch (ppd->colorspace) + { + case PPD_CS_RGB : + case PPD_CS_CMY : + apple_init_profile(ppd, NULL, profiles->profiles + 1, + _ppdHashName("RGB.."), "RGB", "RGB", NULL); + break; + case PPD_CS_RGBK : + case PPD_CS_CMYK : + apple_init_profile(ppd, NULL, profiles->profiles + 1, + _ppdHashName("CMYK.."), "CMYK", "CMYK", NULL); + break; + + case PPD_CS_N : + apple_init_profile(ppd, NULL, profiles->profiles + 1, + _ppdHashName("DeviceN.."), "DeviceN", "DeviceN", + NULL); + break; + + default : + break; + } + } + + if (num_profiles > 0) + { + /* + * Make sure we have a default profile ID... + */ + + if (!default_profile_id) + default_profile_id = profiles->profiles[num_profiles - 1].profileID; + + /* + * Get the device ID hash and pathelogical name dictionary. + */ + + cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"", + p->name); + + device_id = _ppdHashName(p->name); + device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + printer_name = CFStringCreateWithCString(kCFAllocatorDefault, + p->name, kCFStringEncodingUTF8); + + if (device_name && printer_name) + { + CFDictionarySetValue(device_name, CFSTR("en"), printer_name); + + /* + * Register the device with ColorSync... + */ + + error = CMRegisterColorDevice(cmPrinterDeviceClass, device_id, + device_name, &scope); + + /* + * Register the profiles... + */ + + if (error == noErr) + error = CMSetDeviceFactoryProfiles(cmPrinterDeviceClass, device_id, + default_profile_id, profiles); + } + else + error = 1000; + + /* + * Clean up... + */ + + if (error != noErr) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to register ICC color profiles for \"%s\" - %d", + p->name, (int)error); + + for (profile = profiles->profiles; + num_profiles > 0; + profile ++, num_profiles --) + CFRelease(profile->profileName); + + free(profiles); + + if (printer_name) + CFRelease(printer_name); + + if (device_name) + CFRelease(device_name); + } + + ppdClose(ppd); +} + + +/* + * 'apple_unregister_profiles()' - Remove color profiles for the specified + * printer. + */ + +static void +apple_unregister_profiles( + cupsd_printer_t *p) /* I - Printer */ +{ + /* + * Make sure ColorSync is available... + */ + + if (CMUnregisterColorDevice != NULL) + CMUnregisterColorDevice(cmPrinterDeviceClass, _ppdHashName(p->name)); +} +#endif /* __APPLE__ */ + +/* + * 'apply_printer_defaults()' - Apply printer default options to a job. + */ + +static void +apply_printer_defaults( + cupsd_printer_t *printer, /* I - Printer */ + cupsd_job_t *job) /* I - Job */ +{ + int i, /* Looping var */ + num_options; /* Number of default options */ + cups_option_t *options, /* Default options */ + *option; /* Current option */ + + + /* + * Collect all of the default options and add the missing ones to the + * job object... + */ + + for (i = printer->num_options, num_options = 0, options = NULL, + option = printer->options; + i > 0; + i --, option ++) + if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO)) + { + num_options = cupsAddOption(option->name, option->value, num_options, + &options); + } + + /* + * Encode these options as attributes in the job object... + */ + + cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB); + cupsFreeOptions(num_options, options); +} + + +/* * 'authenticate_job()' - Set job authentication info. */ @@ -2877,8 +3667,22 @@ authenticate_job(cupsd_client_t *con, /* I - Client connection */ if (!con->username[0] && !auth_info) { - send_ipp_status(con, IPP_NOT_AUTHORIZED, - _("No authentication information provided!")); + cupsd_printer_t *printer; /* Job destination */ + + + /* + * No auth data. If we need to authenticate via Kerberos, send a + * HTTP auth challenge, otherwise just return an IPP error... + */ + + printer = cupsdFindDest(job->dest); + + if (printer && printer->num_auth_info_required > 0 && + !strcmp(printer->auth_info_required[0], "negotiate")) + send_http_error(con, HTTP_UNAUTHORIZED, printer); + else + send_ipp_status(con, IPP_NOT_AUTHORIZED, + _("No authentication information provided!")); return; } @@ -2888,7 +3692,7 @@ authenticate_job(cupsd_client_t *con, /* I - Client connection */ if (!validate_user(job, con, job->username, username, sizeof(username))) { - send_http_error(con, HTTP_UNAUTHORIZED, NULL); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -2918,8 +3722,7 @@ authenticate_job(cupsd_client_t *con, /* I - Client connection */ cupsdReleaseJob(job); - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Authenticated by \"%s\".", jobid, - con->username); + cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username); } @@ -3078,6 +3881,7 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ cupsd_job_t *job; /* Job information */ cups_ptype_t dtype; /* Destination type (printer/class) */ cupsd_printer_t *printer; /* Printer data */ + int purge; /* Purge the job? */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con, @@ -3185,6 +3989,16 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ jobid = atoi(resource + 6); } + /* + * Look for the "purge-job" attribute... + */ + + if ((attr = ippFindAttribute(con->request, "purge-job", + IPP_TAG_BOOLEAN)) != NULL) + purge = attr->values[0].boolean; + else + purge = 0; + /* * See if the job exists... */ @@ -3205,7 +4019,7 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ if (!validate_user(job, con, job->username, username, sizeof(username))) { - send_http_error(con, HTTP_UNAUTHORIZED, NULL); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -3214,7 +4028,7 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ * we can't cancel... */ - if (job->state_value >= IPP_JOB_CANCELED) + if (job->state_value >= IPP_JOB_CANCELED && !purge) { switch (job->state_value) { @@ -3244,11 +4058,15 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ * Cancel the job and return... */ - cupsdCancelJob(job, 0, IPP_JOB_CANCELED); + cupsdCancelJob(job, purge, IPP_JOB_CANCELED); cupsdCheckJobs(); - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid, - username); + if (purge) + cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid, + username); + else + cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid, + username); con->response->request.status.status_code = IPP_OK; } @@ -3308,6 +4126,40 @@ cancel_subscription( } +/* + * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI. + */ + +static int /* O - 1 if OK, 0 if not */ +check_rss_recipient( + const char *recipient) /* I - Recipient URI */ +{ + cupsd_subscription_t *sub; /* Current subscription */ + + + for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions); + sub; + sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions)) + if (sub->recipient) + { + /* + * Compare the URIs up to the first ?... + */ + + const char *r1, *r2; + + for (r1 = recipient, r2 = sub->recipient; + *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?'; + r1 ++, r2 ++); + + if (*r1 == *r2) + return (0); + } + + return (1); +} + + /* * 'check_quotas()' - Check quotas for a printer and user. */ @@ -3342,13 +4194,6 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])", con, con->http.fd, p, p->name); - /* - * Check input... - */ - - if (!con || !p) - return (0); - /* * Figure out who is printing... */ @@ -3433,8 +4278,13 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ */ #ifdef HAVE_MBR_UID_TO_UUID - if ((mbr_err = mbr_group_name_to_uuid((char *)p->users[i] + 1, - grp_uuid)) != 0) + if (p->users[i][1] == '#') + { + if (uuid_parse((char *)p->users[i] + 2, grp_uuid)) + uuid_clear(grp_uuid); + } + else if ((mbr_err = mbr_group_name_to_uuid((char *)p->users[i] + 1, + grp_uuid)) != 0) { /* * Invalid ACL entries are ignored for matching; just record a @@ -3448,28 +4298,27 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ "Access control entry \"%s\" not a valid group name; " "entry ignored", p->users[i]); } - else - { - if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid, - &is_member)) != 0) - { - /* - * At this point, there should be no errors, but check anyways... - */ - - cupsdLogMessage(CUPSD_LOG_DEBUG, - "check_quotas: group \"%s\" membership check " - "failed (err=%d)", p->users[i] + 1, mbr_err); - is_member = 0; - } - /* - * Stop if we found a match... + if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid, + &is_member)) != 0) + { + /* + * At this point, there should be no errors, but check anyways... */ - if (is_member) - break; + cupsdLogMessage(CUPSD_LOG_DEBUG, + "check_quotas: group \"%s\" membership check " + "failed (err=%d)", p->users[i] + 1, mbr_err); + is_member = 0; } + + /* + * Stop if we found a match... + */ + + if (is_member) + break; + #else if (cupsdCheckGroup(username, pw, p->users[i] + 1)) break; @@ -3478,8 +4327,13 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ #ifdef HAVE_MBR_UID_TO_UUID else { - if ((mbr_err = mbr_user_name_to_uuid((char *)p->users[i], - usr2_uuid)) != 0) + if (p->users[i][0] == '#') + { + if (uuid_parse((char *)p->users[i] + 1, usr2_uuid)) + uuid_clear(usr2_uuid); + } + else if ((mbr_err = mbr_user_name_to_uuid((char *)p->users[i], + usr2_uuid)) != 0) { /* * Invalid ACL entries are ignored for matching; just record a @@ -3493,20 +4347,18 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ "Access control entry \"%s\" not a valid user name; " "entry ignored", p->users[i]); } - else - { - if ((mbr_err = mbr_check_membership(usr_uuid, usr2_uuid, - &is_member)) != 0) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "check_quotas: User \"%s\" identity check failed " - "(err=%d)", p->users[i], mbr_err); - is_member = 0; - } - if (is_member) - break; + if ((mbr_err = mbr_check_membership(usr_uuid, usr2_uuid, + &is_member)) != 0) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, + "check_quotas: User \"%s\" identity check failed " + "(err=%d)", p->users[i], mbr_err); + is_member = 0; } + + if (is_member) + break; } #else else if (!strcasecmp(username, p->users[i])) @@ -3838,7 +4690,8 @@ copy_banner(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *attr; /* Attribute */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_banner(%p[%d], %p[%d], %s)", + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")", con, con ? con->http.fd : -1, job, job->id, name ? name : "(null)"); @@ -3855,14 +4708,14 @@ copy_banner(cupsd_client_t *con, /* I - Client connection */ */ if (add_file(con, job, banner->filetype, 0)) - return (0); + return (-1); snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id, job->num_files); if ((out = cupsFileOpen(filename, "w")) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, - "copy_banner: Unable to create banner job file %s - %s", + "Unable to create banner job file %s - %s", filename, strerror(errno)); job->num_files --; return (0); @@ -3918,7 +4771,7 @@ copy_banner(cupsd_client_t *con, /* I - Client connection */ cupsFileClose(out); unlink(filename); cupsdLogMessage(CUPSD_LOG_ERROR, - "copy_banner: Unable to open banner template file %s - %s", + "Unable to open banner template file %s - %s", filename, strerror(errno)); job->num_files --; return (0); @@ -4172,12 +5025,6 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ cups_option_t *defaults; /* Default options */ char cups_protocol[PPD_MAX_LINE]; /* cupsProtocol attribute */ - int have_letter, /* Have Letter size */ - have_a4; /* Have A4 size */ -#ifdef HAVE_LIBPAPER - char *paper_result; /* Paper size name from libpaper */ - char system_paper[64]; /* Paper size name buffer */ -#endif /* HAVE_LIBPAPER */ cupsdLogMessage(CUPSD_LOG_DEBUG2, @@ -4207,10 +5054,11 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ "copy_model: Running \"cups-driverd cat %s\"...", from); if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1], - -1, -1, 0, &temppid)) + -1, -1, 0, DefaultProfile, &temppid)) { close(tempfd); unlink(tempfile); + return (-1); } @@ -4233,8 +5081,6 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ * See if we have data ready... */ - bytes = 0; - FD_ZERO(&input); FD_SET(temppipe[0], &input); FD_SET(CGIPipes[0], &input); @@ -4303,9 +5149,6 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ return (-1); } - have_letter = ppdPageSize(ppd, "Letter") != NULL; - have_a4 = ppdPageSize(ppd, "A4") != NULL; - /* * Open the destination (if possible) and set the default options... */ @@ -4350,81 +5193,20 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ cupsFileClose(dst); } -#ifdef HAVE_LIBPAPER - else if ((paper_result = systempapername()) != NULL) - { - /* - * Set the default media sizes from the systemwide default... - */ - - strlcpy(system_paper, paper_result, sizeof(system_paper)); - system_paper[0] = toupper(system_paper[0] & 255); - - if ((!strcmp(system_paper, "Letter") && have_letter) || - (!strcmp(system_paper, "A4") && have_a4)) - { - num_defaults = cupsAddOption("PageSize", system_paper, - num_defaults, &defaults); - num_defaults = cupsAddOption("PageRegion", system_paper, - num_defaults, &defaults); - num_defaults = cupsAddOption("PaperDimension", system_paper, - num_defaults, &defaults); - num_defaults = cupsAddOption("ImageableArea", system_paper, - num_defaults, &defaults); - } - } -#endif /* HAVE_LIBPAPER */ - else + else if (ppdPageSize(ppd, DefaultPaperSize)) { /* * Add the default media sizes... - * - * Note: These values are generally not valid for large-format devices - * like plotters, however it is probably safe to say that those - * users will configure the media size after initially adding - * the device anyways... */ - if (!DefaultLanguage || - !strcasecmp(DefaultLanguage, "C") || - !strcasecmp(DefaultLanguage, "POSIX") || - !strcasecmp(DefaultLanguage, "en") || - !strncasecmp(DefaultLanguage, "en.", 3) || - !strncasecmp(DefaultLanguage, "en_US", 5) || - !strncasecmp(DefaultLanguage, "en_CA", 5) || - !strncasecmp(DefaultLanguage, "fr_CA", 5)) - { - /* - * These are the only locales that will default to "letter" size... - */ - - if (have_letter) - { - num_defaults = cupsAddOption("PageSize", "Letter", num_defaults, - &defaults); - num_defaults = cupsAddOption("PageRegion", "Letter", num_defaults, - &defaults); - num_defaults = cupsAddOption("PaperDimension", "Letter", num_defaults, - &defaults); - num_defaults = cupsAddOption("ImageableArea", "Letter", num_defaults, - &defaults); - } - } - else if (have_a4) - { - /* - * The rest default to "a4" size... - */ - - num_defaults = cupsAddOption("PageSize", "A4", num_defaults, - &defaults); - num_defaults = cupsAddOption("PageRegion", "A4", num_defaults, - &defaults); - num_defaults = cupsAddOption("PaperDimension", "A4", num_defaults, - &defaults); - num_defaults = cupsAddOption("ImageableArea", "A4", num_defaults, - &defaults); - } + num_defaults = cupsAddOption("PageSize", DefaultPaperSize, + num_defaults, &defaults); + num_defaults = cupsAddOption("PageRegion", DefaultPaperSize, + num_defaults, &defaults); + num_defaults = cupsAddOption("PaperDimension", DefaultPaperSize, + num_defaults, &defaults); + num_defaults = cupsAddOption("ImageableArea", DefaultPaperSize, + num_defaults, &defaults); } ppdClose(ppd); @@ -4521,6 +5303,14 @@ copy_job_attrs(cupsd_client_t *con, /* I - Client connection */ con->servername, con->serverport, "/jobs/%d", job->id); + if (!ra || cupsArrayFind(ra, "document-count")) + ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, + "document-count", job->num_files); + + if (!ra || cupsArrayFind(ra, "job-media-progress")) + ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, + "job-media-progress", job->progress); + if (!ra || cupsArrayFind(ra, "job-more-info")) ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-more-info", NULL, job_uri); @@ -4577,6 +5367,44 @@ copy_printer_attrs( printer->recoverable); #endif /* __APPLE__ */ + if (!ra || cupsArrayFind(ra, "marker-change-time")) + ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "marker-change-time", printer->marker_time); + + if (printer->num_printers > 0 && + (!ra || cupsArrayFind(ra, "member-uris"))) + { + ipp_attribute_t *member_uris; /* member-uris attribute */ + cupsd_printer_t *p2; /* Printer in class */ + ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */ + + + if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER, + IPP_TAG_URI, "member-uris", + printer->num_printers, NULL, + NULL)) != NULL) + { + for (i = 0; i < printer->num_printers; i ++) + { + p2 = printer->printers[i]; + + if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported", + IPP_TAG_URI)) != NULL) + member_uris->values[i].string.text = + _cupsStrAlloc(p2_uri->values[0].string.text); + else + { + httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, + sizeof(printer_uri), "ipp", NULL, con->servername, + con->serverport, + (p2->type & CUPS_PRINTER_CLASS) ? + "/classes/%s" : "/printers/%s", p2->name); + member_uris->values[i].string.text = _cupsStrAlloc(printer_uri); + } + } + } + } + if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert"))) ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING, "printer-alert", NULL, printer->alert); @@ -4591,6 +5419,18 @@ copy_printer_attrs( ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(curtime)); +#ifdef HAVE_DNSSD + if (!ra || cupsArrayFind(ra, "printer-dns-sd-name")) + { + if (printer->reg_name) + ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME, + "printer-dns-sd-name", NULL, printer->reg_name); + else + ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, + "printer-dns-sd-name", 0); + } +#endif /* HAVE_DNSSD */ + if (!ra || cupsArrayFind(ra, "printer-error-policy")) ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-error-policy", NULL, printer->error_policy); @@ -4834,10 +5674,8 @@ create_job(cupsd_client_t *con, /* I - Client connection */ * Save and log the job... */ - cupsdSaveJob(job); - - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Queued on \"%s\" by \"%s\".", - job->id, job->dest, job->username); + cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".", + job->dest, job->username); } @@ -4934,6 +5772,7 @@ create_requested_array(ipp_t *request) /* I - IPP request */ cupsArrayAdd(ra, "job-impressions-completed"); cupsArrayAdd(ra, "job-k-octets"); cupsArrayAdd(ra, "job-k-octets-processed"); + cupsArrayAdd(ra, "job-media-progress"); cupsArrayAdd(ra, "job-media-sheets"); cupsArrayAdd(ra, "job-media-sheets-completed"); cupsArrayAdd(ra, "job-message-from-operator"); @@ -4966,6 +5805,7 @@ create_requested_array(ipp_t *request) /* I - IPP request */ cupsArrayAdd(ra, "job-impressions-supported"); cupsArrayAdd(ra, "job-k-octets-supported"); cupsArrayAdd(ra, "job-media-sheets-supported"); + cupsArrayAdd(ra, "job-settable-attributes-supported"); cupsArrayAdd(ra, "multiple-document-jobs-supported"); cupsArrayAdd(ra, "multiple-operation-time-out"); cupsArrayAdd(ra, "natural-language-configured"); @@ -4983,8 +5823,10 @@ create_requested_array(ipp_t *request) /* I - IPP request */ cupsArrayAdd(ra, "pdl-override-supported"); cupsArrayAdd(ra, "printer-alert"); cupsArrayAdd(ra, "printer-alert-description"); + cupsArrayAdd(ra, "printer-commands"); cupsArrayAdd(ra, "printer-current-time"); cupsArrayAdd(ra, "printer-driver-installer"); + cupsArrayAdd(ra, "printer-dns-sd-name"); cupsArrayAdd(ra, "printer-info"); cupsArrayAdd(ra, "printer-is-accepting-jobs"); cupsArrayAdd(ra, "printer-location"); @@ -4996,6 +5838,8 @@ create_requested_array(ipp_t *request) /* I - IPP request */ cupsArrayAdd(ra, "printer-state"); cupsArrayAdd(ra, "printer-state-message"); cupsArrayAdd(ra, "printer-state-reasons"); + cupsArrayAdd(ra, "printer-settable-attributes-supported"); + cupsArrayAdd(ra, "printer-type"); cupsArrayAdd(ra, "printer-up-time"); cupsArrayAdd(ra, "printer-uri-supported"); cupsArrayAdd(ra, "queued-job-count"); @@ -5075,10 +5919,10 @@ create_subscription( for (attr = con->request->attrs; attr; attr = attr->next) { if (attr->group_tag != IPP_TAG_ZERO) - cupsdLogMessage(CUPSD_LOG_DEBUG, "g%04x v%04x %s", attr->group_tag, + cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag, attr->value_tag, attr->name); else - cupsdLogMessage(CUPSD_LOG_DEBUG, "----SEP----"); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----"); } #endif /* DEBUG */ @@ -5232,6 +6076,16 @@ create_subscription( "notify-status-code", IPP_URI_SCHEME); return; } + + if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient)) + { + send_ipp_status(con, IPP_NOT_POSSIBLE, + _("notify-recipient-uri URI \"%s\" is already used!"), + recipient); + ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, + "notify-status-code", IPP_ATTRIBUTES); + return; + } } else if (!strcmp(attr->name, "notify-pull-method") && attr->value_tag == IPP_TAG_KEYWORD) @@ -5382,8 +6236,7 @@ create_subscription( attr = attr->next; } - cupsdSaveAllSubscriptions(); - + cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS); } @@ -5458,45 +6311,246 @@ delete_printer(cupsd_client_t *con, /* I - Client connection */ printer->name); unlink(filename); +#ifdef __APPLE__ + /* + * Unregister color profiles... + */ + + apple_unregister_profiles(printer); +#endif /* __APPLE__ */ + if (dtype & CUPS_PRINTER_CLASS) { cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".", printer->name, get_username(con)); cupsdDeletePrinter(printer, 0); - cupsdSaveAllClasses(); + cupsdMarkDirty(CUPSD_DIRTY_CLASSES); } else { cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".", printer->name, get_username(con)); - cupsdDeletePrinter(printer, 0); - cupsdSaveAllPrinters(); + cupsdDeletePrinter(printer, 0); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); + } + + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); + + /* + * Return with no errors... + */ + + con->response->request.status.status_code = IPP_OK; +} + + +/* + * 'get_default()' - Get the default destination. + */ + +static void +get_default(cupsd_client_t *con) /* I - Client connection */ +{ + http_status_t status; /* Policy status */ + cups_array_t *ra; /* Requested attributes array */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd); + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + + if (DefaultPrinter) + { + ra = create_requested_array(con->request); + + copy_printer_attrs(con, DefaultPrinter, ra); + + cupsArrayDelete(ra); + + con->response->request.status.status_code = IPP_OK; + } + else + send_ipp_status(con, IPP_NOT_FOUND, _("No default printer")); +} + + +/* + * 'get_devices()' - Get the list of available devices on the local system. + */ + +static void +get_devices(cupsd_client_t *con) /* I - Client connection */ +{ + http_status_t status; /* Policy status */ + ipp_attribute_t *limit, /* limit attribute */ + *timeout, /* timeout attribute */ + *requested, /* requested-attributes attribute */ + *exclude; /* exclude-schemes attribute */ + char command[1024], /* cups-deviced command */ + options[1024], /* Options to pass to command */ + requested_str[256], + /* String for requested attributes */ + exclude_str[512]; + /* String for excluded attributes */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd); + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + + /* + * Run cups-deviced command with the given options... + */ + + limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER); + timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER); + requested = ippFindAttribute(con->request, "requested-attributes", + IPP_TAG_KEYWORD); + exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME); + + if (requested) + url_encode_attr(requested, requested_str, sizeof(requested_str)); + else + strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str)); + + if (exclude) + url_encode_attr(exclude, exclude_str, sizeof(exclude_str)); + else + exclude_str[0] = '\0'; + + snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin); + snprintf(options, sizeof(options), + "%d+%d+%d+%d+%s%s%s", + con->request->request.op.request_id, + limit ? limit->values[0].integer : 0, + timeout ? timeout->values[0].integer : 10, + (int)User, + requested_str, + exclude_str[0] ? "%20" : "", exclude_str); + + if (cupsdSendCommand(con, command, options, 1)) + { + /* + * Command started successfully, don't send an IPP response here... + */ + + ippDelete(con->response); + con->response = NULL; + } + else + { + /* + * Command failed, return "internal error" so the user knows something + * went wrong... + */ + + send_ipp_status(con, IPP_INTERNAL_ERROR, + _("cups-deviced failed to execute.")); + } +} + + +/* + * 'get_document()' - Get a copy of a job file. + */ + +static void +get_document(cupsd_client_t *con, /* I - Client connection */ + ipp_attribute_t *uri) /* I - Job URI */ +{ + http_status_t status; /* Policy status */ + ipp_attribute_t *attr; /* Current attribute */ + int jobid; /* Job ID */ + int docnum; /* Document number */ + cupsd_job_t *job; /* Current job */ + char method[HTTP_MAX_URI], /* Method portion of URI */ + username[HTTP_MAX_URI], /* Username portion of URI */ + host[HTTP_MAX_URI], /* Host portion of URI */ + resource[HTTP_MAX_URI]; /* Resource portion of URI */ + int port; /* Port portion of URI */ + char filename[1024], /* Filename for document */ + format[1024]; /* Format for document */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con, + con->http.fd, uri->values[0].string.text); + + /* + * See if we have a job URI or a printer URI... + */ + + if (!strcmp(uri->name, "printer-uri")) + { + /* + * Got a printer URI; see if we also have a job-id attribute... + */ + + if ((attr = ippFindAttribute(con->request, "job-id", + IPP_TAG_INTEGER)) == NULL) + { + send_ipp_status(con, IPP_BAD_REQUEST, + _("Got a printer-uri attribute but no job-id!")); + return; + } + + jobid = attr->values[0].integer; + } + else + { + /* + * Got a job URI; parse it to get the job ID... + */ + + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, + sizeof(method), username, sizeof(username), host, + sizeof(host), &port, resource, sizeof(resource)); + + if (strncmp(resource, "/jobs/", 6)) + { + /* + * Not a valid URI! + */ + + send_ipp_status(con, IPP_BAD_REQUEST, + _("Bad job-uri attribute \"%s\"!"), + uri->values[0].string.text); + return; + } + + jobid = atoi(resource + 6); } - cupsdWritePrintcap(); - /* - * Return with no errors... + * See if the job exists... */ - con->response->request.status.status_code = IPP_OK; -} - - -/* - * 'get_default()' - Get the default destination. - */ - -static void -get_default(cupsd_client_t *con) /* I - Client connection */ -{ - http_status_t status; /* Policy status */ - cups_array_t *ra; /* Requested attributes array */ - + if ((job = cupsdFindJob(jobid)) == NULL) + { + /* + * Nope - return a "not found" error... + */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd); + send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid); + return; + } /* * Check policy... @@ -5508,88 +6562,53 @@ get_default(cupsd_client_t *con) /* I - Client connection */ return; } - if (DefaultPrinter) - { - ra = create_requested_array(con->request); - - copy_printer_attrs(con, DefaultPrinter, ra); - - cupsArrayDelete(ra); - - con->response->request.status.status_code = IPP_OK; - } - else - send_ipp_status(con, IPP_NOT_FOUND, _("No default printer")); -} - - -/* - * 'get_devices()' - Get the list of available devices on the local system. - */ - -static void -get_devices(cupsd_client_t *con) /* I - Client connection */ -{ - http_status_t status; /* Policy status */ - ipp_attribute_t *limit, /* Limit attribute */ - *requested; /* requested-attributes attribute */ - char command[1024], /* cups-deviced command */ - options[1024], /* Options to pass to command */ - requested_str[256]; - /* String for requested attributes */ - - - cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd); - /* - * Check policy... + * Get the document number... */ - if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + if ((attr = ippFindAttribute(con->request, "document-number", + IPP_TAG_INTEGER)) == NULL) { - send_http_error(con, status, NULL); + send_ipp_status(con, IPP_BAD_REQUEST, + _("Missing document-number attribute!")); return; } - /* - * Run cups-deviced command with the given options... - */ - - limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER); - requested = ippFindAttribute(con->request, "requested-attributes", - IPP_TAG_KEYWORD); + if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files || + attr->num_values > 1) + { + send_ipp_status(con, IPP_NOT_FOUND, _("Document %d not found in job %d."), + docnum, jobid); + return; + } - if (requested) - url_encode_attr(requested, requested_str, sizeof(requested_str)); - else - strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str)); + snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid, + docnum); + if ((con->file = open(filename, O_RDONLY)) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to open document %d in job %d - %s", docnum, jobid, + strerror(errno)); + send_ipp_status(con, IPP_NOT_FOUND, + _("Unable to open document %d in job %d!"), docnum, jobid); + return; + } - snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin); - snprintf(options, sizeof(options), - "%d+%d+%d+%s", - con->request->request.op.request_id, - limit ? limit->values[0].integer : 0, (int)User, - requested_str); + fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); - if (cupsdSendCommand(con, command, options, 1)) - { - /* - * Command started successfully, don't send an IPP response here... - */ + cupsdLoadJob(job); - ippDelete(con->response); - con->response = NULL; - } - else - { - /* - * Command failed, return "internal error" so the user knows something - * went wrong... - */ + snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super, + job->filetypes[docnum - 1]->type); - send_ipp_status(con, IPP_INTERNAL_ERROR, - _("cups-deviced failed to execute.")); - } + ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format", + NULL, format); + ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number", + docnum); + if ((attr = ippFindAttribute(job->attrs, "document-name", + IPP_TAG_NAME)) != NULL) + ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name", + NULL, attr->values[0].string.text); } @@ -5812,6 +6831,11 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ completed = 0; list = Jobs; } + else if (attr && !strcmp(attr->values[0].string.text, "printing")) + { + completed = 0; + list = PrintingJobs; + } else { completed = 0; @@ -5826,7 +6850,7 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ IPP_TAG_INTEGER)) != NULL) limit = attr->values[0].integer; else - limit = 1000000; + limit = 0; if ((attr = ippFindAttribute(con->request, "first-job-id", IPP_TAG_INTEGER)) != NULL) @@ -5852,7 +6876,7 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ */ for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list); - count < limit && job; + (limit <= 0 || count < limit) && job; job = (cupsd_job_t *)cupsArrayNext(list)) { /* @@ -5861,6 +6885,12 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: job->id = %d", job->id); + if (!job->dest || !job->username) + cupsdLoadJob(job); + + if (!job->dest || !job->username) + continue; + if ((dest && strcmp(job->dest, dest)) && (!job->printer || !dest || strcmp(job->printer->name, dest))) continue; @@ -6113,7 +7143,7 @@ get_ppd(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK) { - send_http_error(con, status, NULL); + send_http_error(con, status, dest); return; } @@ -6496,8 +7526,8 @@ get_printers(cupsd_client_t *con, /* I - Client connection */ { if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) && (printer->type & printer_mask) == printer_type && - (!location || !printer->location || - !strcasecmp(printer->location, location))) + (!location || + (printer->location && !strcasecmp(printer->location, location)))) { /* * If HideImplicitMembers is enabled, see if this printer or class @@ -6513,7 +7543,8 @@ get_printers(cupsd_client_t *con, /* I - Client connection */ * access... */ - if (printer->num_users && username && !user_allowed(printer, username)) + if (!(printer->type & CUPS_PRINTER_AUTHENTICATED) && + printer->num_users && username && !user_allowed(printer, username)) continue; /* @@ -6850,7 +7881,7 @@ hold_job(cupsd_client_t *con, /* I - Client connection */ if (!validate_user(job, con, job->username, username, sizeof(username))) { - send_http_error(con, HTTP_UNAUTHORIZED, NULL); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -6860,7 +7891,7 @@ hold_job(cupsd_client_t *con, /* I - Client connection */ cupsdHoldJob(job); - cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, + cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job, "Job held by user."); if ((newattr = ippFindAttribute(con->request, "job-hold-until", @@ -6897,12 +7928,11 @@ hold_job(cupsd_client_t *con, /* I - Client connection */ cupsdSetJobHoldUntil(job, attr->values[0].string.text); - cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job, + cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job, "Job job-hold-until value changed by user."); } - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Held by \"%s\".", jobid, - username); + cupsdLogJob(job, CUPSD_LOG_INFO, "Held by \"%s\".", username); con->response->request.status.status_code = IPP_OK; } @@ -6962,17 +7992,6 @@ move_job(cupsd_client_t *con, /* I - Client connection */ return; } - /* - * Check policy... - */ - - if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con, - NULL)) != HTTP_OK) - { - send_http_error(con, status, dprinter); - return; - } - /* * See if we have a job URI or a printer URI... */ @@ -7080,6 +8099,17 @@ move_job(cupsd_client_t *con, /* I - Client connection */ } } + /* + * Check the policy of the destination printer... + */ + + if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con, + job ? job->username : NULL)) != HTTP_OK) + { + send_http_error(con, status, dprinter); + return; + } + /* * Now move the job or jobs... */ @@ -7108,7 +8138,7 @@ move_job(cupsd_client_t *con, /* I - Client connection */ if (!validate_user(job, con, job->username, username, sizeof(username))) { - send_http_error(con, HTTP_UNAUTHORIZED, NULL); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -7370,7 +8400,7 @@ print_job(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *doc_name; /* document-name attribute */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "print_job: auto-typing file..."); + cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file..."); doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME); filetype = mimeFileType(MimeDatabase, con->filename, @@ -7379,6 +8409,9 @@ print_job(cupsd_client_t *con, /* I - Client connection */ if (!filetype) filetype = mimeType(MimeDatabase, super, type); + + cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.", + filetype->super, filetype->type); } else filetype = mimeType(MimeDatabase, super, type); @@ -7424,8 +8457,9 @@ print_job(cupsd_client_t *con, /* I - Client connection */ */ if (!strcasecmp(filetype->super, "application") && - !strcasecmp(filetype->type, "postscript")) - read_ps_job_ticket(con); + (!strcasecmp(filetype->type, "postscript") || + !strcasecmp(filetype->type, "pdf"))) + read_job_ticket(con); /* * Create the job object... @@ -7434,9 +8468,6 @@ print_job(cupsd_client_t *con, /* I - Client connection */ if ((job = add_job(con, printer, filetype)) == NULL) return; - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Adding job file of type %s/%s.", - job->id, filetype->super, filetype->type); - /* * Update quota data... */ @@ -7468,18 +8499,19 @@ print_job(cupsd_client_t *con, /* I - Client connection */ * See if we need to add the ending sheet... */ - cupsdTimeoutJob(job); + if (cupsdTimeoutJob(job)) + return; /* * Log and save the job... */ - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Queued on \"%s\" by \"%s\".", - job->id, job->dest, job->username); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] hold_until = %d", job->id, - (int)job->hold_until); - - cupsdSaveJob(job); + cupsdLogJob(job, CUPSD_LOG_INFO, + "File of type %s/%s queued by \"%s\".", + filetype->super, filetype->type, job->username); + cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until); + cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".", + job->dest, job->username); /* * Start the job if possible... @@ -7490,16 +8522,16 @@ print_job(cupsd_client_t *con, /* I - Client connection */ /* - * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file. + * 'read_job_ticket()' - Read a job ticket embedded in a print file. * - * This function only gets called when printing a single PostScript + * This function only gets called when printing a single PDF or PostScript * file using the Print-Job operation. It doesn't work for Create-Job + * Send-File, since the job attributes need to be set at job creation - * time for banners to work. The embedded PS job ticket stuff is here - * only to allow the Windows printer driver for CUPS to pass in JCL + * time for banners to work. The embedded job ticket stuff is here + * primarily to allow the Windows printer driver for CUPS to pass in JCL * options and IPP attributes which otherwise would be lost. * - * The format of a PS job ticket is simple: + * The format of a job ticket is simple: * * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN * @@ -7509,8 +8541,8 @@ print_job(cupsd_client_t *con, /* I - Client connection */ * %cupsJobTicket: attrN=valueN * * Job ticket lines must appear immediately after the first line that - * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop - * looking for job ticket info when it finds a line that does not begin + * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS + * stops looking for job ticket info when it finds a line that does not begin * with "%cupsJobTicket:". * * The maximum length of a job ticket line, including the prefix, is @@ -7523,7 +8555,7 @@ print_job(cupsd_client_t *con, /* I - Client connection */ */ static void -read_ps_job_ticket(cupsd_client_t *con) /* I - Client connection */ +read_job_ticket(cupsd_client_t *con) /* I - Client connection */ { cups_file_t *fp; /* File to read from */ char line[256]; /* Line data */ @@ -7542,8 +8574,7 @@ read_ps_job_ticket(cupsd_client_t *con) /* I - Client connection */ if ((fp = cupsFileOpen(con->filename, "rb")) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, - "read_ps_job_ticket: Unable to open PostScript print file " - "- %s", + "Unable to open print file for job ticket - %s", strerror(errno)); return; } @@ -7555,14 +8586,13 @@ read_ps_job_ticket(cupsd_client_t *con) /* I - Client connection */ if (cupsFileGets(fp, line, sizeof(line)) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, - "read_ps_job_ticket: Unable to read from PostScript print " - "file - %s", + "Unable to read from print file for job ticket - %s", strerror(errno)); cupsFileClose(fp); return; } - if (strncmp(line, "%!PS-Adobe-", 11)) + if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5)) { /* * Not a DSC-compliant file, so no job ticket info will be available... @@ -7733,14 +8763,14 @@ reject_jobs(cupsd_client_t *con, /* I - Client connection */ if (dtype & CUPS_PRINTER_CLASS) { - cupsdSaveAllClasses(); + cupsdMarkDirty(CUPSD_DIRTY_CLASSES); cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").", printer->name, get_username(con)); } else { - cupsdSaveAllPrinters(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").", printer->name, get_username(con)); @@ -7854,7 +8884,7 @@ release_job(cupsd_client_t *con, /* I - Client connection */ if (!validate_user(job, con, job->username, username, sizeof(username))) { - send_http_error(con, HTTP_UNAUTHORIZED, NULL); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -7873,7 +8903,7 @@ release_job(cupsd_client_t *con, /* I - Client connection */ attr->value_tag = IPP_TAG_KEYWORD; attr->values[0].string.text = _cupsStrAlloc("no-hold"); - cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job, + cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job, "Job job-hold-until value changed by user."); } @@ -7883,13 +8913,14 @@ release_job(cupsd_client_t *con, /* I - Client connection */ cupsdReleaseJob(job); - cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, + cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job, "Job released by user."); - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Released by \"%s\".", jobid, - username); + cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username); con->response->request.status.status_code = IPP_OK; + + cupsdCheckJobs(); } @@ -7969,7 +9000,7 @@ renew_subscription( sub->expire = sub->lease ? time(NULL) + sub->lease : 0; - cupsdSaveAllSubscriptions(); + cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS); con->response->request.status.status_code = IPP_OK; @@ -8096,7 +9127,7 @@ restart_job(cupsd_client_t *con, /* I - Client connection */ if (!validate_user(job, con, job->username, username, sizeof(username))) { - send_http_error(con, HTTP_UNAUTHORIZED, NULL); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -8106,8 +9137,7 @@ restart_job(cupsd_client_t *con, /* I - Client connection */ cupsdRestartJob(job); - cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Restarted by \"%s\".", jobid, - username); + cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username); con->response->request.status.status_code = IPP_OK; } @@ -8255,14 +9285,20 @@ static void save_krb5_creds(cupsd_client_t *con, /* I - Client connection */ cupsd_job_t *job) /* I - Job */ { -# ifndef HAVE_KRB5_CC_NEW_UNIQUE - char cachename[1024]; /* Name of resolved cache */ -# endif /* !HAVE_KRB5_CC_NEW_UNIQUE */ - OM_uint32 major_status, /* Major status code */ - minor_status; /* Minor status code */ +# if !defined(HAVE_KRB5_CC_NEW_UNIQUE) && !defined(HAVE_HEIMDAL) + cupsdLogMessage(CUPSD_LOG_INFO, + "Sorry, your version of Kerberos does not support delegated " + "credentials!"); + return; + +# else + krb5_error_code error; /* Kerberos error code */ + OM_uint32 major_status, /* Major status code */ + minor_status; /* Minor status code */ + krb5_principal principal; /* Kerberos principal */ -# ifdef __APPLE__ +# ifdef __APPLE__ /* * If the weak-linked GSSAPI/Kerberos library is not present, don't try * to use it... @@ -8270,25 +9306,78 @@ save_krb5_creds(cupsd_client_t *con, /* I - Client connection */ if (krb5_init_context == NULL) return; -# endif /* __APPLE__ */ +# endif /* __APPLE__ */ + + if (!KerberosInitialized) + { + /* + * Setup a Kerberos context for the scheduler to use... + */ + + KerberosInitialized = 1; + + if (krb5_init_context(&KerberosContext)) + { + KerberosContext = NULL; + + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize Kerberos context"); + return; + } + } /* * We MUST create a file-based cache because memory-based caches are * only valid for the current process/address space. - */ + * + * Due to various bugs/features in different versions of Kerberos, we + * need either the krb5_cc_new_unique() function or Heimdal's version + * of krb5_cc_gen_new() to create a new FILE: credential cache that + * can be passed to the backend. These functions create a temporary + * file (typically in /tmp) containing the cached credentials, which + * are removed when we have successfully printed a job. + */ + +# ifdef HAVE_KRB5_CC_NEW_UNIQUE + if ((error = krb5_cc_new_unique(KerberosContext, "FILE", NULL, + &(job->ccache))) != 0) +# else /* HAVE_HEIMDAL */ + if ((error = krb5_cc_gen_new(KerberosContext, &krb5_fcc_ops, + &(job->ccache))) != 0) +# endif /* HAVE_KRB5_CC_NEW_UNIQUE */ + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to create new credentials cache (%d/%s)", + error, strerror(errno)); + job->ccache = NULL; + return; + } -# ifdef HAVE_KRB5_CC_NEW_UNIQUE - if (krb5_cc_new_unique(KerberosContext, "FILE", NULL, &(job->ccache))) -# else - snprintf(cachename, sizeof(cachename), "FILE:%s/k%05d", RequestRoot, job->id); + if ((error = krb5_parse_name(KerberosContext, con->username, &principal)) != 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to parse kerberos username (%d/%s)", + error, strerror(errno)); + krb5_cc_destroy(KerberosContext, job->ccache); + job->ccache = NULL; + return; + } - if (krb5_cc_resolve(KerberosContext, cachename, &(job->ccache))) -# endif /* HAVE_KRB5_CC_NEW_UNIQUE */ + if ((error = krb5_cc_initialize(KerberosContext, job->ccache, principal))) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create new credentials"); + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to initialize credentials cache (%d/%s)", error, + strerror(errno)); + krb5_cc_destroy(KerberosContext, job->ccache); + krb5_free_principal(KerberosContext, principal); + job->ccache = NULL; return; } + krb5_free_principal(KerberosContext, principal); + + /* + * Copy the user's credentials to the new cache file... + */ + major_status = gss_krb5_copy_ccache(&minor_status, con->gss_delegated_cred, job->ccache); @@ -8297,14 +9386,20 @@ save_krb5_creds(cupsd_client_t *con, /* I - Client connection */ cupsdLogGSSMessage(CUPSD_LOG_ERROR, major_status, minor_status, "Unable to import client credentials cache"); krb5_cc_destroy(KerberosContext, job->ccache); + job->ccache = NULL; return; } - cupsdSetStringf(&(job->ccname), "KRB5CCNAME=%s", + /* + * Add the KRB5CCNAME environment variable to the job so that the + * backend can use the credentials when printing. + */ + + cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s", krb5_cc_get_name(KerberosContext, job->ccache)); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] save_krb5_creds: %s", job->id, - job->ccname); + cupsdLogJob(job, CUPSD_LOG_DEBUG2, "save_krb5_creds: %s", job->ccname); +# endif /* HAVE_KRB5_CC_NEW_UNIQUE || HAVE_HEIMDAL */ } #endif /* HAVE_GSSAPI && HAVE_KRB5_H */ @@ -8318,7 +9413,8 @@ send_document(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer URI */ { ipp_attribute_t *attr; /* Current attribute */ - ipp_attribute_t *format; /* Document-format attribute */ + ipp_attribute_t *format; /* Request's document-format attribute */ + ipp_attribute_t *jformat; /* Job's document-format attribute */ const char *default_format;/* document-format-default value */ int jobid; /* Job ID number */ cupsd_job_t *job; /* Current job */ @@ -8345,6 +9441,7 @@ send_document(cupsd_client_t *con, /* I - Client connection */ struct stat fileinfo; /* File information */ int kbytes; /* Size of file */ int compression; /* Type of compression */ + int start_job; /* Start the job? */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con, @@ -8417,7 +9514,7 @@ send_document(cupsd_client_t *con, /* I - Client connection */ if (!validate_user(job, con, job->username, username, sizeof(username))) { - send_http_error(con, HTTP_UNAUTHORIZED, NULL); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -8514,7 +9611,7 @@ send_document(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *doc_name; /* document-name attribute */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "send_document: auto-typing file..."); + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file..."); doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME); filetype = mimeFileType(MimeDatabase, con->filename, @@ -8523,13 +9620,14 @@ send_document(cupsd_client_t *con, /* I - Client connection */ if (!filetype) filetype = mimeType(MimeDatabase, super, type); + + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.", + filetype->super, filetype->type); } else filetype = mimeType(MimeDatabase, super, type); - if (filetype && - (!format || - (!strcmp(super, "application") && !strcmp(type, "octet-stream")))) + if (filetype) { /* * Replace the document-format attribute value with the auto-typed or @@ -8539,14 +9637,15 @@ send_document(cupsd_client_t *con, /* I - Client connection */ snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, filetype->type); - if (format) + if ((jformat = ippFindAttribute(job->attrs, "document-format", + IPP_TAG_MIMETYPE)) != NULL) { - _cupsStrFree(format->values[0].string.text); + _cupsStrFree(jformat->values[0].string.text); - format->values[0].string.text = _cupsStrAlloc(mimetype); + jformat->values[0].string.text = _cupsStrAlloc(mimetype); } else - ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, + ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format", NULL, mimetype); } else if (!filetype) @@ -8577,10 +9676,6 @@ send_document(cupsd_client_t *con, /* I - Client connection */ return; } - cupsdLogMessage(CUPSD_LOG_DEBUG, - "send_document: request file type is %s/%s.", - filetype->super, filetype->type); - /* * Add the file to the job... */ @@ -8607,9 +9702,8 @@ send_document(cupsd_client_t *con, /* I - Client connection */ cupsdClearString(&con->filename); - cupsdLogMessage(CUPSD_LOG_INFO, - "File of type %s/%s queued in job #%d by \"%s\".", - filetype->super, filetype->type, job->id, job->username); + cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".", + filetype->super, filetype->type, job->username); /* * Start the job if this is the last document... @@ -8623,7 +9717,8 @@ send_document(cupsd_client_t *con, /* I - Client connection */ * See if we need to add the ending sheet... */ - cupsdTimeoutJob(job); + if (cupsdTimeoutJob(job)) + return; if (job->state_value == IPP_JOB_STOPPED) { @@ -8643,18 +9738,10 @@ send_document(cupsd_client_t *con, /* I - Client connection */ } } - cupsdSaveJob(job); - - /* - * Start the job if possible... Since cupsdCheckJobs() can cancel a - * job if it doesn't print, we need to re-find the job afterwards... - */ - - jobid = job->id; - - cupsdCheckJobs(); + job->dirty = 1; + cupsdMarkDirty(CUPSD_DIRTY_JOBS); - job = cupsdFindJob(jobid); + start_job = 1; } else { @@ -8667,8 +9754,12 @@ send_document(cupsd_client_t *con, /* I - Client connection */ job->state->values[0].integer = IPP_JOB_HELD; job->state_value = IPP_JOB_HELD; job->hold_until = time(NULL) + 60; - cupsdSaveJob(job); + job->dirty = 1; + + cupsdMarkDirty(CUPSD_DIRTY_JOBS); } + + start_job = 0; } /* @@ -8684,10 +9775,17 @@ send_document(cupsd_client_t *con, /* I - Client connection */ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid); ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", - job ? job->state_value : IPP_JOB_CANCELED); + job->state_value); add_job_state_reasons(con, job); con->response->request.status.status_code = IPP_OK; + + /* + * Start the job if necessary... + */ + + if (start_job) + cupsdCheckJobs(); } @@ -8701,16 +9799,76 @@ send_http_error( http_status_t status, /* I - HTTP status code */ cupsd_printer_t *printer) /* I - Printer, if any */ { - cupsdLogMessage(CUPSD_LOG_ERROR, "%s: %s", - ippOpString(con->request->request.op.operation_id), - httpStatus(status)); - - if (status == HTTP_UNAUTHORIZED && - printer && printer->num_auth_info_required > 0 && - !strcmp(printer->auth_info_required[0], "negotiate")) - cupsdSendError(con, status, AUTH_NEGOTIATE); + ipp_attribute_t *uri; /* Request URI, if any */ + + + if ((uri = ippFindAttribute(con->request, "printer-uri", + IPP_TAG_URI)) == NULL) + uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI); + + cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG, + "Returning HTTP %s for %s (%s) from %s", + httpStatus(status), + ippOpString(con->request->request.op.operation_id), + uri ? uri->values[0].string.text : "no URI", + con->http.hostname); + + if (printer) + { + int auth_type; /* Type of authentication required */ + + + auth_type = CUPSD_AUTH_NONE; + + if (status == HTTP_UNAUTHORIZED && + printer->num_auth_info_required > 0 && + !strcmp(printer->auth_info_required[0], "negotiate") && + con->request && + (con->request->request.op.operation_id == IPP_PRINT_JOB || + con->request->request.op.operation_id == IPP_CREATE_JOB || + con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB)) + { + /* + * Creating and authenticating jobs requires Kerberos... + */ + + auth_type = CUPSD_AUTH_NEGOTIATE; + } + else + { + /* + * Use policy/location-defined authentication requirements... + */ + + char resource[HTTP_MAX_URI]; /* Resource portion of URI */ + cupsd_location_t *auth; /* Pointer to authentication element */ + + + if (printer->type & CUPS_PRINTER_CLASS) + snprintf(resource, sizeof(resource), "/classes/%s", printer->name); + else + snprintf(resource, sizeof(resource), "/printers/%s", printer->name); + + if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL || + auth->type == CUPSD_AUTH_NONE) + auth = cupsdFindPolicyOp(printer->op_policy_ptr, + con->request ? + con->request->request.op.operation_id : + IPP_PRINT_JOB); + + if (auth) + { + if (auth->type == CUPSD_AUTH_DEFAULT) + auth_type = DefaultAuthType; + else + auth_type = auth->type; + } + } + + cupsdSendError(con, status, auth_type); + } else - cupsdSendError(con, status, AUTH_NONE); + cupsdSendError(con, status, CUPSD_AUTH_NONE); ippDelete(con->response); con->response = NULL; @@ -8725,9 +9883,9 @@ send_http_error( static void send_ipp_status(cupsd_client_t *con, /* I - Client connection */ - ipp_status_t status, /* I - IPP status code */ - const char *message, /* I - Status message */ - ...) /* I - Additional args as needed */ + ipp_status_t status, /* I - IPP status code */ + const char *message,/* I - Status message */ + ...) /* I - Additional args as needed */ { va_list ap; /* Pointer to additional args */ char formatted[1024]; /* Formatted errror message */ @@ -8769,7 +9927,8 @@ set_default(cupsd_client_t *con, /* I - Client connection */ { http_status_t status; /* Policy status */ cups_ptype_t dtype; /* Destination type (printer/class) */ - cupsd_printer_t *printer; /* Printer */ + cupsd_printer_t *printer, /* Printer */ + *oldprinter; /* Old default printer */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con, @@ -8804,12 +9963,18 @@ set_default(cupsd_client_t *con, /* I - Client connection */ * Set it as the default... */ + oldprinter = DefaultPrinter; DefaultPrinter = printer; - cupsdSaveAllPrinters(); - cupsdSaveAllClasses(); + if (oldprinter) + cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL, + "%s is no longer the default printer.", oldprinter->name); - cupsdWritePrintcap(); + cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL, + "%s is now the default printer.", printer->name); + + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES | + CUPSD_DIRTY_REMOTE | CUPSD_DIRTY_PRINTCAP); cupsdLogMessage(CUPSD_LOG_INFO, "Default destination set to \"%s\" by \"%s\".", @@ -8845,6 +10010,7 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ /* Resource portion of URI */ int port; /* Port portion of URI */ int event; /* Events? */ + int check_jobs; /* Check jobs? */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con, @@ -8936,7 +10102,7 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ if (!validate_user(job, con, job->username, username, sizeof(username))) { - send_http_error(con, HTTP_UNAUTHORIZED, NULL); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -8946,7 +10112,8 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ cupsdLoadJob(job); - event = 0; + check_jobs = 0; + event = 0; for (attr = con->request->attrs; attr; attr = attr->next) { @@ -8960,6 +10127,7 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ !strcmp(attr->name, "job-detailed-status-messages") || !strcmp(attr->name, "job-document-access-errors") || !strcmp(attr->name, "job-id") || + !strcmp(attr->name, "job-impressions-completed") || !strcmp(attr->name, "job-k-octets") || !strcmp(attr->name, "job-originating-host-name") || !strcmp(attr->name, "job-originating-user-name") || @@ -8973,7 +10141,6 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ !strcmp(attr->name, "number-of-intervening-jobs") || !strcmp(attr->name, "output-device-assigned") || !strncmp(attr->name, "date-time-at-", 13) || - !strncmp(attr->name, "job-impressions", 15) || !strncmp(attr->name, "job-k-octets", 12) || !strncmp(attr->name, "job-media-sheets", 16) || !strncmp(attr->name, "time-at-", 8)) @@ -9012,8 +10179,13 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ } else if (con->response->request.status.status_code == IPP_OK) { + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d", + attr->values[0].integer); cupsdSetJobPriority(job, attr->values[0].integer); - event |= CUPSD_EVENT_JOB_CONFIG_CHANGED; + + check_jobs = 1; + event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | + CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED; } } else if (!strcmp(attr->name, "job-state")) @@ -9043,10 +10215,14 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ } else if (con->response->request.status.status_code == IPP_OK) { + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d", + attr->values[0].integer); + job->state->values[0].integer = attr->values[0].integer; job->state_value = (ipp_jstate_t)attr->values[0].integer; event |= CUPSD_EVENT_JOB_STATE; + check_jobs = 1; } break; @@ -9070,7 +10246,13 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ return; } else if (con->response->request.status.status_code == IPP_OK) + { + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d", + attr->values[0].integer); cupsdCancelJob(job, 0, (ipp_jstate_t)attr->values[0].integer); + + check_jobs = 1; + } break; } } @@ -9106,6 +10288,8 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ if (!strcmp(attr->name, "job-hold-until")) { + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s", + attr->values[0].string.text); cupsdSetJobHoldUntil(job, attr->values[0].string.text); if (!strcmp(attr->values[0].string.text, "no-hold")) @@ -9113,7 +10297,8 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ else cupsdHoldJob(job); - event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE; + check_jobs = 1; + event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE; } } else if (attr->value_tag == IPP_TAG_DELETEATTR) @@ -9154,26 +10339,33 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ * Save the job... */ - cupsdSaveJob(job); + job->dirty = 1; + cupsdMarkDirty(CUPSD_DIRTY_JOBS); /* * Send events as needed... */ + if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED) + cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED, + cupsdFindDest(job->dest), job, + "Job priority changed by user."); + if (event & CUPSD_EVENT_JOB_STATE) - cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, + cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job, job->state_value == IPP_JOB_HELD ? "Job held by user." : "Job restarted by user."); if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED) - cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job, + cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job, "Job options changed by user."); /* * Start jobs if possible... */ - cupsdCheckJobs(); + if (check_jobs) + cupsdCheckJobs(); } @@ -9319,6 +10511,7 @@ set_printer_defaults( continue; if (strcmp(attr->values[0].string.text, "abort-job") && + strcmp(attr->values[0].string.text, "retry-current-job") && strcmp(attr->values[0].string.text, "retry-job") && strcmp(attr->values[0].string.text, "stop-printer")) { @@ -9666,6 +10859,8 @@ user_allowed(cupsd_printer_t *p, /* I - Printer or class */ { int i; /* Looping var */ struct passwd *pw; /* User password data */ + char baseuser[256], /* Base username */ + *baseptr; /* Pointer to "@" in base username */ if (p->num_users == 0) @@ -9674,6 +10869,20 @@ user_allowed(cupsd_printer_t *p, /* I - Printer or class */ if (!strcmp(username, "root")) return (1); + if (strchr(username, '@')) + { + /* + * Strip @REALM for username check... + */ + + strlcpy(baseuser, username, sizeof(baseuser)); + + if ((baseptr = strchr(baseuser, '@')) != NULL) + *baseptr = '\0'; + + username = baseuser; + } + pw = getpwnam(username); endpwent(); @@ -9688,6 +10897,15 @@ user_allowed(cupsd_printer_t *p, /* I - Printer or class */ if (cupsdCheckGroup(username, pw, p->users[i] + 1)) break; } + else if (p->users[i][0] == '#') + { + /* + * Check UUID... + */ + + if (cupsdCheckGroup(username, pw, p->users[i])) + break; + } else if (!strcasecmp(username, p->users[i])) break; } @@ -9840,8 +11058,8 @@ validate_user(cupsd_job_t *job, /* I - Job */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, " "userlen=%d)", - job ? job->id : 0, con->http.fd, owner ? owner : "(null)", - username, userlen); + job->id, con ? con->http.fd : 0, + owner ? owner : "(null)", username, userlen); /* * Validate input... @@ -9868,5 +11086,5 @@ validate_user(cupsd_job_t *job, /* I - Job */ /* - * End of "$Id: ipp.c 6755 2007-08-01 19:02:47Z mike $". + * End of "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $". */