X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fcups.git;a=blobdiff_plain;f=scheduler%2Fipp.c;h=637d760b4fdf7120c6b68a88a485cbad2cfe0d7c;hp=7ed169a449d3d3bc8d09add475f75ef19a4ad45b;hb=f11a948a02771f78f50b530880a0269d4b4f58eb;hpb=f7faf1f5c3235dfa4f883522da7dc6446f028247 diff --git a/scheduler/ipp.c b/scheduler/ipp.c index 7ed169a44..637d760b4 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -1,47 +1,49 @@ /* - * "$Id: ipp.c 5686 2006-06-21 21:02:56Z mike $" + * "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $" * * IPP routines for the Common UNIX Printing System (CUPS) scheduler. * - * Copyright 1997-2006 by Easy Software Products, all rights reserved. + * Copyright 2007-2009 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * - * These coded instructions, statements, and computer programs are the - * property of Easy Software Products and are protected by Federal - * copyright law. Distribution and use rights are outlined in the file - * "LICENSE.txt" which should have been included with this file. If this - * file is missing or damaged please contact Easy Software Products - * at: - * - * Attn: CUPS Licensing Information - * Easy Software Products - * 44141 Airport View Drive, Suite 204 - * Hollywood, Maryland 20636 USA + * This file contains Kerberos support code, copyright 2006 by + * Jelmer Vernooij. * - * Voice: (301) 373-9600 - * EMail: cups-info@cups.org - * WWW: http://www.cups.org + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". * * Contents: * - * cupsdProcessIPPRequest() - Process an incoming IPP request... + * cupsdProcessIPPRequest() - Process an incoming IPP request. + * cupsdTimeoutJob() - Timeout a job waiting on job files. * accept_jobs() - Accept print jobs to a printer. * add_class() - Add a class to the system. * add_file() - Add a file to a job. * 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 @@ -59,36 +61,40 @@ * 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. + * get_ppd() - Get a named PPD from the local system. * 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. * hold_job() - Hold a print job. * move_job() - Move a job to a new destination. - * ppd_add_default() - Add a PPD default choice. * 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 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. * set_default() - Set the default destination... * set_job_attrs() - Set job attributes. + * set_printer_attrs() - Set printer attributes. * set_printer_defaults() - Set printer default options from a request. * start_printer() - Start a printer. * stop_printer() - Stop a printer. * url_encode_attr() - URL-encode a string attribute. + * url_encode_string() - URL-encode a string. * user_allowed() - See if a user is allowed to print to a queue. * validate_job() - Validate printer options and destination. * validate_name() - Make sure the printer name only contains @@ -101,21 +107,26 @@ */ #include "cupsd.h" +#include #ifdef HAVE_LIBPAPER # include #endif /* HAVE_LIBPAPER */ - -/* - * PPD default choice structure... - */ - -typedef struct -{ - char option[PPD_MAX_NAME]; /* Main keyword (option name) */ - char choice[PPD_MAX_NAME]; /* Option keyword (choice name) */ -} ppd_default_t; +#ifdef __APPLE__ +# include +# include +# ifdef HAVE_MEMBERSHIP_H +# include +# endif /* HAVE_MEMBERSHIP_H */ +# ifdef HAVE_MEMBERSHIPPRIV_H +# include +# else +extern int mbr_user_name_to_uuid(const char* name, uuid_t uu); +extern int mbr_group_name_to_uuid(const char* name, uuid_t uu); +extern int mbr_check_membership_by_id(uuid_t user, gid_t group, int* ismember); +# endif /* HAVE_MEMBERSHIPPRIV_H */ +#endif /* __APPLE__ */ /* @@ -126,8 +137,7 @@ static void accept_jobs(cupsd_client_t *con, ipp_attribute_t *uri); static void add_class(cupsd_client_t *con, ipp_attribute_t *uri); static int add_file(cupsd_client_t *con, cupsd_job_t *job, mime_type_t *filetype, int compression); -static cupsd_job_t *add_job(cupsd_client_t *con, ipp_attribute_t *uri, - cupsd_printer_t **dprinter, +static cupsd_job_t *add_job(cupsd_client_t *con, cupsd_printer_t *printer, mime_type_t *filetype); static void add_job_state_reasons(cupsd_client_t *con, cupsd_job_t *job); static void add_job_subscriptions(cupsd_client_t *con, cupsd_job_t *job); @@ -136,12 +146,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); @@ -167,30 +186,39 @@ 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); +static void get_ppd(cupsd_client_t *con, ipp_attribute_t *uri); static void get_ppds(cupsd_client_t *con); static void get_printers(cupsd_client_t *con, int type); static void get_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri); +static void get_printer_supported(cupsd_client_t *con, ipp_attribute_t *uri); static void get_subscription_attrs(cupsd_client_t *con, int sub_id); static void get_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri); static const char *get_username(cupsd_client_t *con); static void hold_job(cupsd_client_t *con, ipp_attribute_t *uri); +static void hold_new_jobs(cupsd_client_t *con, ipp_attribute_t *uri); static void move_job(cupsd_client_t *con, ipp_attribute_t *uri); -static int ppd_add_default(const char *option, const char *choice, - int num_defaults, ppd_default_t **defaults); 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_held_new_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); static void restart_job(cupsd_client_t *con, ipp_attribute_t *uri); -static void save_auth_info(cupsd_client_t *con, cupsd_job_t *job); +static void save_auth_info(cupsd_client_t *con, cupsd_job_t *job, + ipp_attribute_t *auth_info); +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H) +static void save_krb5_creds(cupsd_client_t *con, cupsd_job_t *job); +#endif /* HAVE_GSSAPI && HAVE_KRB5_H */ static void send_document(cupsd_client_t *con, ipp_attribute_t *uri); -static void send_http_error(cupsd_client_t *con, http_status_t status); +static void send_http_error(cupsd_client_t *con, http_status_t status, + cupsd_printer_t *printer); static void send_ipp_status(cupsd_client_t *con, ipp_status_t status, const char *message, ...) # ifdef __GNUC__ @@ -199,12 +227,14 @@ __attribute__ ((__format__ (__printf__, 3, 4))) ; static void set_default(cupsd_client_t *con, ipp_attribute_t *uri); static void set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri); +static void set_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri); static void set_printer_defaults(cupsd_client_t *con, cupsd_printer_t *printer); static void start_printer(cupsd_client_t *con, ipp_attribute_t *uri); static void stop_printer(cupsd_client_t *con, ipp_attribute_t *uri); static void url_encode_attr(ipp_attribute_t *attr, char *buffer, int bufsize); +static char *url_encode_string(const char *s, char *buffer, int bufsize); static int user_allowed(cupsd_printer_t *p, const char *username); static void validate_job(cupsd_client_t *con, ipp_attribute_t *uri); static int validate_name(const char *name); @@ -214,7 +244,7 @@ static int validate_user(cupsd_job_t *job, cupsd_client_t *con, /* - * 'cupsdProcessIPPRequest()' - Process an incoming IPP request... + * 'cupsdProcessIPPRequest()' - Process an incoming IPP request. */ int /* O - 1 on success, 0 on failure */ @@ -225,7 +255,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 */ @@ -240,18 +270,22 @@ cupsdProcessIPPRequest( con->response = ippNew(); - con->response->request.status.version[0] = con->request->request.op.version[0]; - con->response->request.status.version[1] = con->request->request.op.version[1]; - con->response->request.status.request_id = con->request->request.op.request_id; + con->response->request.status.version[0] = + con->request->request.op.version[0]; + con->response->request.status.version[1] = + con->request->request.op.version[1]; + con->response->request.status.request_id = + con->request->request.op.request_id; /* * Then validate the request header and required attributes... */ - - if (con->request->request.any.version[0] != 1) + + if (con->request->request.any.version[0] != 1 && + con->request->request.any.version[0] != 2) { /* - * Return an error, since we only support IPP 1.x. + * Return an error, since we only support IPP 1.x and 2.x. */ cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, @@ -264,7 +298,21 @@ cupsdProcessIPPRequest( _("Bad request version number %d.%d!"), con->request->request.any.version[0], con->request->request.any.version[1]); - } + } + else if (con->request->request.any.request_id < 1) + { + /* + * Return an error, since request IDs must be between 1 and 2^31-1 + */ + + cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, + "%04X %s Bad request ID %d", + IPP_BAD_REQUEST, con->http.hostname, + con->request->request.any.request_id); + + send_ipp_status(con, IPP_BAD_REQUEST, _("Bad request ID %d!"), + con->request->request.any.request_id); + } else if (!con->request->attrs) { cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, @@ -312,7 +360,8 @@ cupsdProcessIPPRequest( */ attr = con->request->attrs; - if (attr && !strcmp(attr->name, "attributes-charset") && + if (attr && attr->name && + !strcmp(attr->name, "attributes-charset") && (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET) charset = attr; else @@ -321,7 +370,8 @@ cupsdProcessIPPRequest( if (attr) attr = attr->next; - if (attr && !strcmp(attr->name, "attributes-natural-language") && + if (attr && attr->name && + !strcmp(attr->name, "attributes-natural-language") && (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE) language = attr; else @@ -333,12 +383,15 @@ cupsdProcessIPPRequest( else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL) uri = attr; + else if (con->request->request.op.operation_id == CUPS_GET_PPD) + uri = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME); else uri = NULL; if (charset) ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET, - "attributes-charset", NULL, charset->values[0].string.text); + "attributes-charset", NULL, + charset->values[0].string.text); else ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, DefaultCharset); @@ -351,13 +404,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, @@ -388,17 +459,18 @@ cupsdProcessIPPRequest( if (!uri) { cupsdLogMessage(CUPSD_LOG_ERROR, - "Missing printer-uri or job-uri attribute!"); + "Missing printer-uri, job-uri, or ppd-name " + "attribute!"); cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, - "%04X %s Missing printer-uri or job-uri attribute", - IPP_BAD_REQUEST, con->http.hostname); + "%04X %s Missing printer-uri, job-uri, or ppd-name " + "attribute", IPP_BAD_REQUEST, con->http.hostname); } cupsdLogMessage(CUPSD_LOG_DEBUG, "Request attributes follow..."); for (attr = con->request->attrs; attr; attr = attr->next) - cupsdLogMessage(CUPSD_LOG_DEBUG, + cupsdLogMessage(CUPSD_LOG_DEBUG, "attr \"%s\": group_tag = %x, value_tag = %x", attr->name ? attr->name : "(null)", attr->group_tag, attr->value_tag); @@ -487,6 +559,10 @@ cupsdProcessIPPRequest( get_printer_attrs(con, uri); break; + case IPP_GET_PRINTER_SUPPORTED_VALUES : + get_printer_supported(con, uri); + break; + case IPP_HOLD_JOB : hold_job(con, uri); break; @@ -515,6 +591,18 @@ cupsdProcessIPPRequest( set_job_attrs(con, uri); break; + case IPP_SET_PRINTER_ATTRIBUTES : + set_printer_attrs(con, uri); + break; + + case IPP_HOLD_NEW_JOBS : + hold_new_jobs(con, uri); + break; + + case IPP_RELEASE_HELD_NEW_JOBS : + release_held_new_jobs(con, uri); + break; + case CUPS_GET_DEFAULT : get_default(con); break; @@ -561,6 +649,14 @@ cupsdProcessIPPRequest( get_devices(con); break; + case CUPS_GET_DOCUMENT : + get_document(con, uri); + break; + + case CUPS_GET_PPD : + get_ppd(con, uri); + break; + case CUPS_GET_PPDS : get_ppds(con); break; @@ -607,7 +703,8 @@ cupsdProcessIPPRequest( send_ipp_status(con, IPP_OPERATION_NOT_SUPPORTED, _("%s not supported!"), - ippOpString(con->request->request.op.operation_id)); + ippOpString( + con->request->request.op.operation_id)); break; } } @@ -620,17 +717,27 @@ 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")) + if (LogLevel == CUPSD_LOG_DEBUG2) + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdProcessIPPRequest: ippLength(response)=%ld", + (long)ippLength(con->response)); + + if (cupsdSendHeader(con, HTTP_OK, "application/ipp", CUPSD_AUTH_NONE)) { #ifdef CUPSD_USE_CHUNKING /* * Because older versions of CUPS (1.1.17 and older) and some IPP - * clients do not implement chunking properly, we should not use + * clients do not implement chunking properly, we cannot use * chunking by default. This may become the default in future * CUPS releases, or we might add a configuration directive for * it. @@ -638,30 +745,49 @@ cupsdProcessIPPRequest( if (con->http.version == HTTP_1_1) { - con->http.data_encoding = HTTP_ENCODE_CHUNKED; + if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n") < 0) + return (0); + + if (cupsdFlushHeader(con) < 0) + return (0); - httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n"); + con->http.data_encoding = HTTP_ENCODE_CHUNKED; } else #endif /* CUPSD_USE_CHUNKING */ { + size_t length; /* Length of response */ + + + length = ippLength(con->response); + + if (con->file >= 0 && !con->pipe_pid) + { + struct stat fileinfo; /* File information */ + + + if (!fstat(con->file, &fileinfo)) + length += fileinfo.st_size; + } + + if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n", + CUPS_LLCAST length) < 0) + return (0); + + if (cupsdFlushHeader(con) < 0) + return (0); + con->http.data_encoding = HTTP_ENCODE_LENGTH; - con->http.data_remaining = ippLength(con->response); + con->http.data_remaining = length; - if (con->http.data_remaining < INT_MAX) + if (con->http.data_remaining <= INT_MAX) con->http._data_remaining = con->http.data_remaining; else con->http._data_remaining = INT_MAX; - - httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n", - CUPS_LLCAST con->http.data_remaining); } - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdProcessIPPRequest: Adding fd %d to OutputSet...", - con->http.fd); - - FD_SET(con->http.fd, OutputSet); + cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, + (cupsd_selfunc_t)cupsdWriteClient, con); /* * Tell the caller the response header was sent successfully... @@ -690,6 +816,48 @@ cupsdProcessIPPRequest( } +/* + * 'cupsdTimeoutJob()' - Timeout a job waiting on job files. + */ + +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 */ + ipp_attribute_t *attr; /* job-sheets attribute */ + int kbytes; /* Kilobytes in banner */ + + + job->pending_timeout = 0; + + /* + * See if we need to add the ending sheet... + */ + + printer = cupsdFindDest(job->dest); + attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME); + + if (printer && + !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) && + attr && attr->num_values > 1) + { + /* + * Yes... + */ + + cupsdLogJob(job, CUPSD_LOG_INFO, "Adding end banner page \"%s\".", + 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); +} + + /* * 'accept_jobs()' - Accept print jobs to a printer. */ @@ -699,13 +867,7 @@ accept_jobs(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer or class URI */ { http_status_t status; /* Policy status */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - 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 */ - const char *name; /* Printer name */ + cups_ptype_t dtype; /* Destination type (printer/class) */ cupsd_printer_t *printer; /* Printer data */ @@ -716,11 +878,7 @@ accept_jobs(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -737,7 +895,7 @@ accept_jobs(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } @@ -751,12 +909,20 @@ accept_jobs(cupsd_client_t *con, /* I - Client connection */ cupsdAddPrinterHistory(printer); 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\").", name, - get_username(con)); + cupsdLogMessage(CUPSD_LOG_INFO, + "Printer \"%s\" now accepting jobs (\"%s\").", + printer->name, get_username(con)); + } /* * Everything was ok, so return OK status... @@ -776,7 +942,7 @@ add_class(cupsd_client_t *con, /* I - Client connection */ { http_status_t status; /* Policy status */ int i; /* Looping var */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + char scheme[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 */ @@ -784,7 +950,6 @@ add_class(cupsd_client_t *con, /* I - Client connection */ cupsd_printer_t *pclass, /* Class */ *member; /* Member printer/class */ cups_ptype_t dtype; /* Destination type */ - const char *dest; /* Printer or class name */ ipp_attribute_t *attr; /* Printer attribute */ int modify; /* Non-zero if we just modified */ char newname[IPP_MAX_NAME]; /* New class name */ @@ -798,8 +963,8 @@ add_class(cupsd_client_t *con, /* I - Client connection */ * Do we have a valid URI? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); @@ -831,16 +996,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); - return; - } - /* * See if the class already exists; if not, create a new class... */ @@ -852,7 +1007,7 @@ add_class(cupsd_client_t *con, /* I - Client connection */ */ if ((pclass = cupsdFindPrinter(resource + 9)) != NULL && - !(pclass->type & CUPS_PRINTER_REMOTE)) + !(pclass->type & CUPS_PRINTER_DISCOVERED)) { /* * Yes, return an error... @@ -865,18 +1020,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); @@ -892,12 +1060,18 @@ add_class(cupsd_client_t *con, /* I - Client connection */ pclass = cupsdAddClass(resource + 9); modify = 0; } - else if (pclass->type & CUPS_PRINTER_REMOTE) + 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); @@ -908,6 +1082,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; @@ -928,8 +1108,9 @@ add_class(cupsd_client_t *con, /* I - Client connection */ if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL) { - cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)", - pclass->name, attr->values[0].boolean, pclass->accepting); + cupsdLogMessage(CUPSD_LOG_INFO, + "Setting %s printer-is-accepting-jobs to %d (was %d.)", + pclass->name, attr->values[0].boolean, pclass->accepting); pclass->accepting = attr->values[0].boolean; cupsdAddPrinterHistory(pclass); @@ -939,7 +1120,7 @@ add_class(cupsd_client_t *con, /* I - Client connection */ IPP_TAG_BOOLEAN)) != NULL) { if (pclass->shared && !attr->values[0].boolean) - cupsdSendBrowseDelete(pclass); + cupsdDeregisterPrinter(pclass, 1); cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-is-shared to %d (was %d.)", @@ -960,8 +1141,8 @@ add_class(cupsd_client_t *con, /* I - Client connection */ return; } - cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name, - attr->values[0].integer, pclass->state); + cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)", + pclass->name, attr->values[0].integer, pclass->state); if (attr->values[0].integer == IPP_PRINTER_STOPPED) cupsdStopPrinter(pclass, 0); @@ -1003,11 +1184,7 @@ add_class(cupsd_client_t *con, /* I - Client connection */ * Search for the printer or class URI... */ - httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((dest = cupsdValidateDest(host, resource, &dtype, &member)) == NULL) + if (!cupsdValidateDest(attr->values[i].string.text, &dtype, &member)) { /* * Bad URI... @@ -1028,33 +1205,28 @@ add_class(cupsd_client_t *con, /* I - Client connection */ set_printer_defaults(con, pclass); + if ((attr = ippFindAttribute(con->request, "auth-info-required", + IPP_TAG_KEYWORD)) != NULL) + cupsdSetAuthInfoRequired(pclass, NULL, attr); + /* * Update the printer class attributes and return... */ cupsdSetPrinterAttrs(pclass); - cupsdSaveAllClasses(); + cupsdMarkDirty(CUPSD_DIRTY_CLASSES); if (need_restart_job && pclass->job) { - cupsd_job_t *job; - /* - * Stop the current job and then restart it below... + * Reset the current job to a "pending" status... */ - job = (cupsd_job_t *)pclass->job; - - cupsdStopJob(job, 1); - - job->state->values[0].integer = IPP_JOB_PENDING; - job->state_value = IPP_JOB_PENDING; + cupsdSetJobState(pclass->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE, + "Job restarted because the class was modified."); } - if (need_restart_job) - cupsdCheckJobs(); - - cupsdWritePrintcap(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); if (modify) { @@ -1096,9 +1268,9 @@ add_file(cupsd_client_t *con, /* I - Connection to client */ cupsdLogMessage(CUPSD_LOG_DEBUG2, - "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)", - con, con->http.fd, job->id, filetype->super, filetype->type, - compression); + "add_file(con=%p[%d], job=%d, filetype=%s/%s, " + "compression=%d)", con, con ? con->http.fd : -1, job->id, + filetype->super, filetype->type, compression); /* * Add the file to the job... @@ -1120,10 +1292,13 @@ add_file(cupsd_client_t *con, /* I - Connection to client */ if (!compressions || !filetypes) { - cupsdCancelJob(job, 1); + cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE, + "Job aborted because the scheduler ran out of memory."); + + if (con) + send_ipp_status(con, IPP_INTERNAL_ERROR, + _("Unable to allocate memory for file types!")); - send_ipp_status(con, IPP_INTERNAL_ERROR, - _("Unable to allocate memory for file types!")); return (-1); } @@ -1134,6 +1309,9 @@ add_file(cupsd_client_t *con, /* I - Connection to client */ job->num_files ++; + job->dirty = 1; + cupsdMarkDirty(CUPSD_DIRTY_JOBS); + return (0); } @@ -1144,54 +1322,26 @@ add_file(cupsd_client_t *con, /* I - Connection to client */ static cupsd_job_t * /* O - Job object */ add_job(cupsd_client_t *con, /* I - Client connection */ - ipp_attribute_t *uri, /* I - printer-uri */ - cupsd_printer_t **dprinter, /* I - Destination printer */ + cupsd_printer_t *printer, /* I - Destination printer */ mime_type_t *filetype) /* I - First print file type, if any */ { http_status_t status; /* Policy status */ - ipp_attribute_t *attr; /* Current attribute */ - const char *dest; /* Destination */ - cups_ptype_t dtype; /* Destination type (printer or class) */ + ipp_attribute_t *attr, /* Current attribute */ + *auth_info; /* auth-info attribute */ const char *val; /* Default option value */ int priority; /* Job priority */ char *title; /* Job name/title */ cupsd_job_t *job; /* Current job */ - char job_uri[HTTP_MAX_URI], /* Job URI */ - 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 */ - cupsd_printer_t *printer; /* Printer data */ + char job_uri[HTTP_MAX_URI]; /* Job URI */ int kbytes; /* Size of print file */ int i; /* Looping var */ int lowerpagerange; /* Page range bound */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %s)", con, - con->http.fd, uri->values[0].string.text); - - /* - * Is the destination valid? - */ - - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) - { - /* - * Bad URI... - */ - - send_ipp_status(con, IPP_NOT_FOUND, - _("The printer or class was not found.")); - return (NULL); - } - - if (dprinter) - *dprinter = printer; + cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))", + con, con->http.fd, printer, printer->name, + filetype, filetype ? filetype->super : "none", + filetype ? filetype->type : "none"); /* * Check remote printing to non-shared printer... @@ -1210,16 +1360,32 @@ add_job(cupsd_client_t *con, /* I - Client connection */ * Check policy... */ + auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT); + if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); + return (NULL); + } + else if (printer->num_auth_info_required == 1 && + !strcmp(printer->auth_info_required[0], "negotiate") && + !con->username[0]) + { + send_http_error(con, HTTP_UNAUTHORIZED, printer); return (NULL); } - else if ((printer->type & CUPS_PRINTER_AUTHENTICATED) && !con->username[0]) +#ifdef HAVE_SSL + else if (auth_info && !con->http.tls && + !httpAddrLocalhost(con->http.hostaddr)) { - send_http_error(con, HTTP_UNAUTHORIZED); + /* + * Require encryption of auth-info over non-local connections... + */ + + send_http_error(con, HTTP_UPGRADE_REQUIRED, printer); return (NULL); } +#endif /* HAVE_SSL */ /* * See if the printer is accepting jobs... @@ -1229,13 +1395,13 @@ add_job(cupsd_client_t *con, /* I - Client connection */ { send_ipp_status(con, IPP_NOT_ACCEPTING, _("Destination \"%s\" is not accepting jobs."), - dest); + printer->name); return (NULL); } /* * Validate job template attributes; for now just document-format, - * copies, and page-ranges... + * copies, number-up, and page-ranges... */ if (filetype && printer->filetypes && @@ -1270,12 +1436,58 @@ 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) + { + if (attr->values[0].integer != 1 && + attr->values[0].integer != 2 && + attr->values[0].integer != 4 && + attr->values[0].integer != 6 && + attr->values[0].integer != 9 && + attr->values[0].integer != 16) + { + send_ipp_status(con, IPP_ATTRIBUTES, _("Bad number-up value %d."), + attr->values[0].integer); + ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER, + "number-up", attr->values[0].integer); + return (NULL); + } + } + if ((attr = ippFindAttribute(con->request, "page-ranges", IPP_TAG_RANGE)) != NULL) { for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++) { - if (attr->values[i].range.lower < lowerpagerange || + if (attr->values[i].range.lower < lowerpagerange || attr->values[i].range.lower > attr->values[i].range.upper) { send_ipp_status(con, IPP_BAD_REQUEST, @@ -1296,18 +1508,23 @@ add_job(cupsd_client_t *con, /* I - Client connection */ if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs) cupsdCleanJobs(); - if (cupsArrayCount(Jobs) >= MaxJobs && MaxJobs) + if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs) { send_ipp_status(con, IPP_NOT_POSSIBLE, _("Too many active jobs.")); return (NULL); } - if (!check_quotas(con, printer)) + if ((i = check_quotas(con, printer)) < 0) { send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached.")); return (NULL); } + else if (i == 0) + { + send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print.")); + return (NULL); + } /* * Create the job and set things up... @@ -1338,13 +1555,18 @@ add_job(cupsd_client_t *con, /* I - Client connection */ if ((job = cupsdAddJob(priority, printer->name)) == NULL) { send_ipp_status(con, IPP_INTERNAL_ERROR, - _("Unable to add job for destination \"%s\"!"), dest); + _("Unable to add job for destination \"%s\"!"), + printer->name); return (NULL); } - job->dtype = dtype; + job->dtype = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT | + CUPS_PRINTER_REMOTE); job->attrs = con->request; - con->request = NULL; + 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); @@ -1357,8 +1579,6 @@ add_job(cupsd_client_t *con, /* I - Client connection */ if (attr) cupsdSetString(&attr->values[0].string.text, con->username); - - save_auth_info(con, job); } else if (attr) { @@ -1381,6 +1601,18 @@ add_job(cupsd_client_t *con, /* I - Client connection */ attr->name = _cupsStrAlloc("job-originating-user-name"); } + if (con->username[0] || auth_info) + { + save_auth_info(con, job, auth_info); + + /* + * Remove the auth-info attribute from the attribute data... + */ + + if (auth_info) + ippDeleteAttribute(job->attrs, auth_info); + } + if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name", IPP_TAG_ZERO)) != NULL) { @@ -1447,7 +1679,7 @@ add_job(cupsd_client_t *con, /* I - Client connection */ * the connection... */ - ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, + ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-host-name", NULL, con->http.hostname); } @@ -1467,7 +1699,7 @@ add_job(cupsd_client_t *con, /* I - Client connection */ ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id); job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", IPP_JOB_STOPPED); - job->state_value = job->state->values[0].integer; + job->state_value = (ipp_jstate_t)job->state->values[0].integer; job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-media-sheets-completed", 0); ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, @@ -1479,8 +1711,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) @@ -1501,14 +1732,14 @@ add_job(cupsd_client_t *con, /* I - Client connection */ * Hold job until specified time... */ - cupsdSetJobHoldUntil(job, attr->values[0].string.text); + cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0); job->state->values[0].integer = IPP_JOB_HELD; job->state_value = IPP_JOB_HELD; } else if (job->attrs->request.op.operation_id == IPP_CREATE_JOB) { - job->hold_until = time(NULL) + 60; + job->hold_until = time(NULL) + MultipleOperationTimeout; job->state->values[0].integer = IPP_JOB_HELD; job->state_value = IPP_JOB_HELD; } @@ -1534,8 +1765,8 @@ add_job(cupsd_client_t *con, /* I - Client connection */ attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets", 2, NULL, NULL); - attr->values[0].string.text = _cupsStrAlloc(printer->job_sheets[0]); - attr->values[1].string.text = _cupsStrAlloc(printer->job_sheets[1]); + attr->values[0].string.text = _cupsStrRetain(printer->job_sheets[0]); + attr->values[1].string.text = _cupsStrRetain(printer->job_sheets[1]); } job->job_sheets = attr; @@ -1563,10 +1794,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, @@ -1580,11 +1811,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") && @@ -1593,18 +1824,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) && @@ -1633,18 +1864,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); } } @@ -1654,11 +1885,16 @@ add_job(cupsd_client_t *con, /* I - Client connection */ if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))) { - cupsdLogMessage(CUPSD_LOG_INFO, - "Adding start banner page \"%s\" to job %d.", - attr->values[0].string.text, job->id); + 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) + { + cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE, + "Aborting job because the start banner could not be " + "copied."); + return (NULL); + } cupsdUpdateQuota(printer, job->username, 0, kbytes); } @@ -1728,7 +1964,7 @@ add_job_state_reasons( cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job_state_reasons(%p[%d], %d)", con, con->http.fd, job ? job->id : 0); - switch (job ? job->state_value : IPP_JOB_CANCELLED) + switch (job ? job->state_value : IPP_JOB_CANCELED) { case IPP_JOB_PENDING : dest = cupsdFindDest(job->dest); @@ -1763,7 +1999,7 @@ add_job_state_reasons( "job-state-reasons", NULL, "job-stopped"); break; - case IPP_JOB_CANCELLED : + case IPP_JOB_CANCELED : ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-canceled-by-user"); break; @@ -1782,7 +2018,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 @@ -1807,9 +2043,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; @@ -1830,42 +2064,102 @@ add_job_subscriptions( while (attr && attr->group_tag != IPP_TAG_ZERO) { - if (!strcmp(attr->name, "notify-recipient") && + if (!strcmp(attr->name, "notify-recipient-uri") && attr->value_tag == IPP_TAG_URI) - recipient = attr->values[0].string.text; - else if (!strcmp(attr->name, "notify-pull-method") && - attr->value_tag == IPP_TAG_KEYWORD) - pullmethod = attr->values[0].string.text; - else if (!strcmp(attr->name, "notify-charset") && - attr->value_tag == IPP_TAG_CHARSET && - strcmp(attr->values[0].string.text, "us-ascii") && - strcmp(attr->values[0].string.text, "utf-8")) - { - send_ipp_status(con, IPP_CHARSET, - _("Character set \"%s\" not supported!"), - attr->values[0].string.text); - return; - } - else if (!strcmp(attr->name, "notify-natural-language") && - (attr->value_tag != IPP_TAG_LANGUAGE || - strcmp(attr->values[0].string.text, DefaultLanguage))) { - send_ipp_status(con, IPP_CHARSET, - _("Language \"%s\" not supported!"), - attr->values[0].string.text); - return; - } - else if (!strcmp(attr->name, "notify-user-data") && - attr->value_tag == IPP_TAG_STRING) - { - if (attr->num_values > 1 || attr->values[0].unknown.length > 63) - { - send_ipp_status(con, IPP_REQUEST_VALUE, - _("The notify-user-data value is too large " - "(%d > 63 octets)!"), - attr->values[0].unknown.length); - return; - } + /* + * 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") && + strcmp(attr->values[0].string.text, "utf-8")) + { + send_ipp_status(con, IPP_CHARSET, + _("Character set \"%s\" not supported!"), + attr->values[0].string.text); + return; + } + else if (!strcmp(attr->name, "notify-natural-language") && + (attr->value_tag != IPP_TAG_LANGUAGE || + strcmp(attr->values[0].string.text, DefaultLanguage))) + { + send_ipp_status(con, IPP_CHARSET, + _("Language \"%s\" not supported!"), + attr->values[0].string.text); + return; + } + else if (!strcmp(attr->name, "notify-user-data") && + attr->value_tag == IPP_TAG_STRING) + { + if (attr->num_values > 1 || attr->values[0].unknown.length > 63) + { + send_ipp_status(con, IPP_REQUEST_VALUE, + _("The notify-user-data value is too large " + "(%d > 63 octets)!"), + attr->values[0].unknown.length); + return; + } user_data = attr; } @@ -1895,32 +2189,36 @@ add_job_subscriptions( if (mask == CUPSD_EVENT_NONE) mask = CUPSD_EVENT_JOB_COMPLETED; - sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job, recipient, - 0); + if ((sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job, + recipient, 0)) != NULL) + { + sub->interval = interval; - sub->interval = interval; + cupsdSetString(&sub->owner, job->username); - cupsdSetString(&sub->owner, job->username); + if (user_data) + { + sub->user_data_len = user_data->values[0].unknown.length; + memcpy(sub->user_data, user_data->values[0].unknown.data, + sub->user_data_len); + } - if (user_data) - { - sub->user_data_len = user_data->values[0].unknown.length; - memcpy(sub->user_data, user_data->values[0].unknown.data, - sub->user_data_len); + ippAddSeparator(con->response); + ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, + "notify-subscription-id", sub->id); } - ippAddSeparator(con->response); - ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, - "notify-subscription-id", sub->id); - if (attr) 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) @@ -1961,7 +2259,6 @@ add_job_uuid(cupsd_client_t *con, /* I - Client connection */ cupsd_job_t *job) /* I - Job */ { char uuid[1024]; /* job-uuid string */ - ipp_attribute_t *attr; /* job-uuid attribute */ _cups_md5_state_t md5state; /* MD5 state */ unsigned char md5sum[16]; /* MD5 digest/sum */ @@ -1970,7 +2267,7 @@ add_job_uuid(cupsd_client_t *con, /* I - Client connection */ * First see if the job already has a job-uuid attribute; if so, return... */ - if ((attr = ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI)) != NULL) + if (ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI)) return; /* @@ -2013,7 +2310,7 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ { http_status_t status; /* Policy status */ int i; /* Looping var */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + char scheme[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 */ @@ -2027,6 +2324,8 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ int modify; /* Non-zero if we are modifying */ char newname[IPP_MAX_NAME]; /* New printer name */ int need_restart_job; /* Need to restart job? */ + int set_device_uri, /* Did we set the device URI? */ + set_port_monitor; /* Did we set the port monitor? */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", con, @@ -2036,8 +2335,8 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ * Do we have a valid URI? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (strncmp(resource, "/printers/", 10) || strlen(resource) == 10) @@ -2068,16 +2367,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); - return; - } - /* * See if the printer already exists; if not, create a new printer... */ @@ -2089,7 +2378,7 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ */ if ((printer = cupsdFindClass(resource + 10)) != NULL && - !(printer->type & CUPS_PRINTER_REMOTE)) + !(printer->type & CUPS_PRINTER_DISCOVERED)) { /* * Yes, return an error... @@ -2102,18 +2391,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); @@ -2129,12 +2431,19 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ printer = cupsdAddPrinter(resource + 10); modify = 0; } - else if (printer->type & CUPS_PRINTER_REMOTE) + 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); @@ -2146,6 +2455,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; @@ -2163,6 +2478,8 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ IPP_TAG_TEXT)) != NULL) cupsdSetString(&printer->info, attr->values[0].string.text); + set_device_uri = 0; + if ((attr = ippFindAttribute(con->request, "device-uri", IPP_TAG_URI)) != NULL) { @@ -2170,13 +2487,30 @@ 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, method, - sizeof(method), 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 (!strcmp(method, "file")) + 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")) { /* * See if the administrator has enabled file devices... @@ -2202,46 +2536,56 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ * See if the backend exists and is executable... */ - snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, method); + snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, scheme); if (access(srcfile, X_OK)) { /* * 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; } + set_port_monitor = 0; + if ((attr = ippFindAttribute(con->request, "port-monitor", - IPP_TAG_KEYWORD)) != NULL) + IPP_TAG_NAME)) != NULL) { ipp_attribute_t *supported; /* port-monitor-supported attribute */ 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; + supported = ippFindAttribute(printer->ppd_attrs, "port-monitor-supported", + 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); @@ -2251,12 +2595,14 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s port-monitor to \"%s\" (was \"%s\".)", printer->name, attr->values[0].string.text, - printer->port_monitor); + printer->port_monitor ? printer->port_monitor : "none"); if (strcmp(attr->values[0].string.text, "none")) cupsdSetString(&printer->port_monitor, attr->values[0].string.text); else cupsdClearString(&printer->port_monitor); + + set_port_monitor = 1; } if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", @@ -2274,7 +2620,7 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ IPP_TAG_BOOLEAN)) != NULL) { if (printer->shared && !attr->values[0].boolean) - cupsdSendBrowseDelete(printer); + cupsdDeregisterPrinter(printer, 1); cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-is-shared to %d (was %d.)", @@ -2294,8 +2640,8 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ return; } - cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)", printer->name, - attr->values[0].integer, printer->state); + cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)", + printer->name, attr->values[0].integer, printer->state); if (attr->values[0].integer == IPP_PRINTER_STOPPED) cupsdStopPrinter(printer, 0); @@ -2305,6 +2651,7 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ cupsdSetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0); } } + if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL) { @@ -2313,8 +2660,53 @@ 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] = + _cupsStrRetain(attr->values[i].string.text); + printer->num_reasons ++; + + if (!strcmp(attr->values[i].string.text, "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); + } + } + + if (PrintcapFormat == PRINTCAP_PLIST) + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); + } + set_printer_defaults(con, printer); + if ((attr = ippFindAttribute(con->request, "auth-info-required", + IPP_TAG_KEYWORD)) != NULL) + cupsdSetAuthInfoRequired(printer, NULL, attr); + /* * See if we have all required attributes... */ @@ -2372,12 +2764,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, @@ -2397,12 +2787,22 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ strerror(errno)); return; } - else + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "Copied PPD file successfully!"); + chmod(dstfile, 0644); + +#ifdef __APPLE__ + /* + * (Re)register color profiles... + */ + + if (!RunUser) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Copied PPD file successfully!"); - chmod(dstfile, 0644); - } + apple_unregister_profiles(printer); + apple_register_profiles(printer); + } +#endif /* __APPLE__ */ } else { @@ -2452,12 +2852,64 @@ 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); + +#ifdef __APPLE__ + /* + * (Re)register color profiles... + */ + + if (!RunUser) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Copied PPD file successfully!"); - chmod(dstfile, 0644); + apple_unregister_profiles(printer); + apple_register_profiles(printer); } +#endif /* __APPLE__ */ + } + } + + /* + * If we set the device URI but not the port monitor, check which port + * monitor to use by default... + */ + + if (set_device_uri && !set_port_monitor) + { + ppd_file_t *ppd; /* PPD file */ + ppd_attr_t *ppdattr; /* cupsPortMonitor attribute */ + + + httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme, + sizeof(scheme), username, sizeof(username), host, + sizeof(host), &port, resource, sizeof(resource)); + + snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot, + printer->name); + if ((ppd = ppdOpenFile(srcfile)) != NULL) + { + for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL); + ppdattr; + ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL)) + if (!strcmp(scheme, ppdattr->spec)) + { + cupsdLogMessage(CUPSD_LOG_INFO, + "Setting %s port-monitor to \"%s\" (was \"%s\".)", + printer->name, ppdattr->value, + printer->port_monitor ? printer->port_monitor + : "none"); + + if (strcmp(ppdattr->value, "none")) + cupsdSetString(&printer->port_monitor, ppdattr->value); + else + cupsdClearString(&printer->port_monitor); + + break; + } + + ppdClose(ppd); } } @@ -2466,28 +2918,19 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ */ cupsdSetPrinterAttrs(printer); - cupsdSaveAllPrinters(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); if (need_restart_job && printer->job) { - cupsd_job_t *job; - /* - * Stop the current job and then restart it below... + * Restart the current job... */ - job = (cupsd_job_t *)printer->job; - - cupsdStopJob(job, 1); - - job->state->values[0].integer = IPP_JOB_PENDING; - job->state_value = IPP_JOB_PENDING; + cupsdSetJobState(printer->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE, + "Job restarted because the printer was modified."); } - if (need_restart_job) - cupsdCheckJobs(); - - cupsdWritePrintcap(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); if (modify) { @@ -2530,8 +2973,7 @@ add_printer_state_reasons( if (p->num_reasons == 0) ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, - "printer-state-reasons", NULL, - p->state == IPP_PRINTER_STOPPED ? "paused" : "none"); + "printer-state-reasons", NULL, "none"); else ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", p->num_reasons, NULL, @@ -2555,12 +2997,559 @@ add_queued_job_count( cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])", con, con->http.fd, p, p->name); - count = cupsdGetPrinterJobCount(p->name); + count = cupsdGetPrinterJobCount(p->name); + + ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, + "queued-job-count", count); +} + + +#ifdef __APPLE__ +/* + * 'apple_init_profile()' - Initialize a color profile. + */ + +static void +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 */ +{ + 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 */ + + + /* + * Build the profile name dictionary... + */ + + dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + 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... + */ + + attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL); + + num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2; + + 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_GRAY : + if (attr) + break; + + case PPD_CS_N : + apple_init_profile(ppd, NULL, profiles->profiles + 1, + _ppdHashName("DeviceN.."), "DeviceN", "DeviceN", + NULL); + 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. + */ - ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, - "queued-job-count", count); -} +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. @@ -2574,7 +3563,7 @@ apply_printer_defaults( int i, /* Looping var */ num_options; /* Number of default options */ cups_option_t *options, /* Default options */ - *option; /* Current option */ + *option; /* Current option */ /* @@ -2582,7 +3571,8 @@ apply_printer_defaults( * job object... */ - for (i = printer->num_options, num_options = 0, option = printer->options; + 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)) @@ -2608,10 +3598,11 @@ static void authenticate_job(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Job URI */ { - ipp_attribute_t *attr; /* Job-id attribute */ + ipp_attribute_t *attr, /* job-id attribute */ + *auth_info; /* auth-info attribute */ int jobid; /* Job ID */ cupsd_job_t *job; /* Current job */ - char method[HTTP_MAX_URI], + char scheme[HTTP_MAX_URI], /* Method portion of URI */ username[HTTP_MAX_URI], /* Username portion of URI */ @@ -2657,10 +3648,10 @@ authenticate_job(cupsd_client_t *con, /* I - Client connection */ * 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, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); - + if (strncmp(resource, "/jobs/", 6)) { /* @@ -2710,10 +3701,26 @@ authenticate_job(cupsd_client_t *con, /* I - Client connection */ * See if we have already authenticated... */ - if (!con->username[0]) + auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT); + + 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; } @@ -2723,7 +3730,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); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -2731,7 +3738,7 @@ authenticate_job(cupsd_client_t *con, /* I - Client connection */ * Save the authentication information for this job... */ - save_auth_info(con, job); + save_auth_info(con, job, auth_info); /* * Reset the job-hold-until value to "no-hold"... @@ -2753,8 +3760,7 @@ authenticate_job(cupsd_client_t *con, /* I - Client connection */ cupsdReleaseJob(job); - cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was authenticated by \"%s\".", jobid, - con->username); + cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username); } @@ -2767,11 +3773,10 @@ cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Job or Printer URI */ { http_status_t status; /* Policy status */ - const char *dest; /* Destination */ cups_ptype_t dtype; /* Destination type */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + char scheme[HTTP_MAX_URI], /* Scheme portion of URI */ userpass[HTTP_MAX_URI], /* Username portion of URI */ - host[HTTP_MAX_URI], /* Host portion of URI */ + hostname[HTTP_MAX_URI], /* Host portion of URI */ resource[HTTP_MAX_URI]; /* Resource portion of URI */ int port; /* Port portion of URI */ ipp_attribute_t *attr; /* Attribute in request */ @@ -2830,16 +3835,17 @@ cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */ * And if the destination is valid... */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), userpass, sizeof(userpass), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI? */ + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, + scheme, sizeof(scheme), userpass, sizeof(userpass), + hostname, sizeof(hostname), &port, + resource, sizeof(resource)); + if ((!strncmp(resource, "/printers/", 10) && resource[10]) || (!strncmp(resource, "/classes/", 9) && resource[9])) { @@ -2847,13 +3853,6 @@ cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */ _("The printer or class was not found.")); return; } - else if (strcmp(resource, "/printers/")) - { - send_ipp_status(con, IPP_NOT_FOUND, - _("The printer-uri \"%s\" is not valid."), - uri->values[0].string.text); - return; - } /* * Check policy... @@ -2861,7 +3860,7 @@ cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -2872,7 +3871,7 @@ cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */ cupsdCancelJobs(NULL, username, purge); cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".", - purge ? "purged" : "cancelled", get_username(con)); + purge ? "purged" : "canceled", get_username(con)); } else { @@ -2880,9 +3879,10 @@ cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */ * Check policy... */ - if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) + if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, + NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } @@ -2890,10 +3890,11 @@ cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */ * Cancel all of the jobs on the named printer... */ - cupsdCancelJobs(dest, username, purge); + cupsdCancelJobs(printer->name, username, purge); cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".", - dest, purge ? "purged" : "cancelled", get_username(con)); + printer->name, purge ? "purged" : "canceled", + get_username(con)); } con->response->request.status.status_code = IPP_OK; @@ -2910,15 +3911,15 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ { ipp_attribute_t *attr; /* Current attribute */ int jobid; /* Job ID */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + char scheme[HTTP_MAX_URI], /* Scheme 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 */ cupsd_job_t *job; /* Job information */ - const char *dest; /* Destination */ - cups_ptype_t dtype; /* Destination type (printer or class) */ + 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, @@ -2948,11 +3949,7 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ * Find the current job on the specified printer... */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -2964,22 +3961,29 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ } /* - * See if the printer is currently printing a job... + * See if there are any pending jobs... */ - if (printer->job) - jobid = ((cupsd_job_t *)printer->job)->id; + for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); + job; + job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) + if (job->state_value <= IPP_JOB_PROCESSING && + !strcasecmp(job->dest, printer->name)) + break; + + if (job) + jobid = job->id; else { /* - * No, see if there are any pending jobs... + * No, try stopped jobs... */ - - for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); + + for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) - if (job->state_value <= IPP_JOB_PROCESSING && - !strcasecmp(job->dest, dest)) + if (job->state_value == IPP_JOB_STOPPED && + !strcasecmp(job->dest, printer->name)) break; if (job) @@ -2987,7 +3991,7 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ else { send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s!"), - dest); + printer->name); return; } } @@ -2999,10 +4003,10 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ * 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, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); - + if (strncmp(resource, "/jobs/", 6)) { /* @@ -3018,6 +4022,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... */ @@ -3038,22 +4052,22 @@ 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); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } /* - * See if the job is already completed, cancelled, or aborted; if so, + * See if the job is already completed, canceled, or aborted; if so, * we can't cancel... */ - if (job->state_value >= IPP_JOB_CANCELLED) + if (job->state_value >= IPP_JOB_CANCELED && !purge) { switch (job->state_value) { - case IPP_JOB_CANCELLED : + case IPP_JOB_CANCELED : send_ipp_status(con, IPP_NOT_POSSIBLE, - _("Job #%d is already cancelled - can\'t cancel."), + _("Job #%d is already canceled - can\'t cancel."), jobid); break; @@ -3077,14 +4091,17 @@ cancel_job(cupsd_client_t *con, /* I - Client connection */ * Cancel the job and return... */ - cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, - "Job cancelled by \"%s\".", username); - - cupsdCancelJob(job, 0); + cupsdSetJobState(job, IPP_JOB_CANCELED, purge, + purge ? "Job purged by \"%s\"" : "Job canceled by \"%s\"", + username); cupsdCheckJobs(); - cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was cancelled 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; } @@ -3130,7 +4147,7 @@ cancel_subscription( DefaultPolicyPtr, con, sub->owner)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, sub->dest); return; } @@ -3144,6 +4161,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. */ @@ -3155,19 +4206,29 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ int i; /* Looping var */ char username[33]; /* Username */ cupsd_quota_t *q; /* Quota data */ +#ifdef HAVE_MBR_UID_TO_UUID + /* + * Use Apple membership APIs which require that all names represent + * valid user account or group records accessible by the server. + */ + + uuid_t usr_uuid; /* UUID for job requesting user */ + uuid_t usr2_uuid; /* UUID for ACL user name entry */ + uuid_t grp_uuid; /* UUID for ACL group name entry */ + int mbr_err; /* Error from membership function */ + int is_member; /* Is this user a member? */ +#else + /* + * Use standard POSIX APIs for checking users and groups... + */ + struct passwd *pw; /* User password data */ +#endif /* HAVE_MBR_UID_TO_UUID */ 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... */ @@ -3215,8 +4276,34 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ if (p->num_users) { +#ifdef HAVE_MBR_UID_TO_UUID + /* + * Get UUID for job requesting user... + */ + + if (mbr_user_name_to_uuid((char *)username, usr_uuid)) + { + /* + * Unknown user... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "check_quotas: UUID lookup failed for user \"%s\"", + username); + cupsdLogMessage(CUPSD_LOG_INFO, + "Denying user \"%s\" access to printer \"%s\" " + "(unknown user)...", + username, p->name); + return (0); + } +#else + /* + * Get UID and GID of requesting user... + */ + pw = getpwnam(username); endpwent(); +#endif /* HAVE_MBR_UID_TO_UUID */ for (i = 0; i < p->num_users; i ++) if (p->users[i][0] == '@') @@ -3225,11 +4312,84 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ * Check group membership... */ +#ifdef HAVE_MBR_UID_TO_UUID + 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 + * warning in the log... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "check_quotas: UUID lookup failed for ACL entry " + "\"%s\" (err=%d)", p->users[i], mbr_err); + cupsdLogMessage(CUPSD_LOG_WARN, + "Access control entry \"%s\" not a valid group name; " + "entry ignored", p->users[i]); + } + + 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 (is_member) + break; + +#else if (cupsdCheckGroup(username, pw, p->users[i] + 1)) break; +#endif /* HAVE_MBR_UID_TO_UUID */ } +#ifdef HAVE_MBR_UID_TO_UUID + else + { + 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 + * warning in the log... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG, + "check_quotas: UUID lookup failed for ACL entry " + "\"%s\" (err=%d)", p->users[i], mbr_err); + cupsdLogMessage(CUPSD_LOG_WARN, + "Access control entry \"%s\" not a valid user name; " + "entry ignored", p->users[i]); + } + + if (!uuid_compare(usr_uuid, usr2_uuid)) + break; + } +#else else if (!strcasecmp(username, p->users[i])) break; +#endif /* HAVE_MBR_UID_TO_UUID */ if ((i < p->num_users) == p->deny_users) { @@ -3244,6 +4404,66 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ * Check quotas... */ +#ifdef __APPLE__ + if (AppleQuotas && (q = cupsdFindQuota(p, username)) != NULL) + { + /* + * TODO: Define these special page count values as constants! + */ + + if (q->page_count == -4) /* special case: unlimited user */ + { + cupsdLogMessage(CUPSD_LOG_INFO, + "User \"%s\" request approved for printer %s (%s): " + "unlimited quota.", + username, p->name, p->info); + q->page_count = 0; /* allow user to print */ + return (1); + } + else if (q->page_count == -3) /* quota exceeded */ + { + cupsdLogMessage(CUPSD_LOG_INFO, + "User \"%s\" request denied for printer %s (%s): " + "quota limit exceeded.", + username, p->name, p->info); + q->page_count = 2; /* force quota exceeded failure */ + return (-1); + } + else if (q->page_count == -2) /* quota disabled for user */ + { + cupsdLogMessage(CUPSD_LOG_INFO, + "User \"%s\" request denied for printer %s (%s): " + "printing disabled for user.", + username, p->name, p->info); + q->page_count = 2; /* force quota exceeded failure */ + return (-1); + } + else if (q->page_count == -1) /* quota access error */ + { + cupsdLogMessage(CUPSD_LOG_INFO, + "User \"%s\" request denied for printer %s (%s): " + "unable to determine quota limit.", + username, p->name, p->info); + q->page_count = 2; /* force quota exceeded failure */ + return (-1); + } + else if (q->page_count < 0) /* user not found or other error */ + { + cupsdLogMessage(CUPSD_LOG_INFO, + "User \"%s\" request denied for printer %s (%s): " + "user disabled / missing quota.", + username, p->name, p->info); + q->page_count = 2; /* force quota exceeded failure */ + return (-1); + } + else /* page within user limits */ + { + q->page_count = 0; /* allow user to print */ + return (1); + } + } + else +#endif /* __APPLE__ */ if (p->k_limit || p->page_limit) { if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL) @@ -3251,7 +4471,7 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate quota data for user \"%s\"!", username); - return (0); + return (-1); } if ((q->k_count >= p->k_limit && p->k_limit) || @@ -3259,7 +4479,7 @@ check_quotas(cupsd_client_t *con, /* I - Client connection */ { cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...", username); - return (0); + return (-1); } } @@ -3331,10 +4551,17 @@ copy_attribute( for (i = 0; i < attr->num_values; i ++) toattr->values[i].string.text = attr->values[i].string.text; } + else if (attr->value_tag & IPP_TAG_COPY) + { + for (i = 0; i < attr->num_values; i ++) + toattr->values[i].string.text = + _cupsStrAlloc(attr->values[i].string.text); + } else { for (i = 0; i < attr->num_values; i ++) - toattr->values[i].string.text = _cupsStrAlloc(attr->values[i].string.text); + toattr->values[i].string.text = + _cupsStrRetain(attr->values[i].string.text); } break; @@ -3381,7 +4608,7 @@ copy_attribute( toattr->values[i].string.text = attr->values[i].string.text; } } - else + else if (attr->value_tag & IPP_TAG_COPY) { for (i = 0; i < attr->num_values; i ++) { @@ -3392,7 +4619,23 @@ copy_attribute( toattr->values[i].string.charset = toattr->values[0].string.charset; - toattr->values[i].string.text = _cupsStrAlloc(attr->values[i].string.text); + toattr->values[i].string.text = + _cupsStrAlloc(attr->values[i].string.text); + } + } + else + { + for (i = 0; i < attr->num_values; i ++) + { + if (!i) + toattr->values[i].string.charset = + _cupsStrRetain(attr->values[i].string.charset); + else + toattr->values[i].string.charset = + toattr->values[0].string.charset; + + toattr->values[i].string.text = + _cupsStrRetain(attr->values[i].string.text); } } break; @@ -3462,12 +4705,23 @@ copy_attrs(ipp_t *to, /* I - Destination request */ * Filter attributes as needed... */ - if (group != IPP_TAG_ZERO && fromattr->group_tag != group && - fromattr->group_tag != IPP_TAG_ZERO && !fromattr->name) + if ((group != IPP_TAG_ZERO && fromattr->group_tag != group && + fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name) continue; if (!ra || cupsArrayFind(ra, fromattr->name)) + { + /* + * Don't send collection attributes by default to IPP/1.x clients + * since many do not support collections... + */ + + if (fromattr->value_tag == IPP_TAG_BEGIN_COLLECTION && + !ra && to->request.status.version[0] == 1) + continue; + copy_attribute(to, fromattr, quickcopy); + } } } @@ -3494,8 +4748,10 @@ 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)", - con, con->http.fd, job, job->id, name ? name : "(null)"); + 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)"); /* * Find the banner; return if not found or "none"... @@ -3510,14 +4766,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); @@ -3573,7 +4829,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); @@ -3656,7 +4912,10 @@ copy_banner(cupsd_client_t *con, /* I - Client connection */ case IPP_TAG_INTEGER : case IPP_TAG_ENUM : if (!strncmp(s, "time-at-", 8)) - cupsFilePuts(out, cupsdGetDateTime(attr->values[i].integer)); + { + struct timeval tv = { attr->values[i].integer, 0 }; + cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD)); + } else cupsFilePrintf(out, "%d", attr->values[i].integer); break; @@ -3805,9 +5064,9 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ const char *from, /* I - Source file */ const char *to) /* I - Destination file */ { - fd_set *input; /* select() input set */ + fd_set input; /* select() input set */ struct timeval timeout; /* select() timeout */ - int maxfd; /* Maximum file descriptor for select() */ + int maxfd; /* Max file descriptor for select() */ char tempfile[1024]; /* Temporary PPD file */ int tempfd; /* Temporary PPD file descriptor */ int temppid; /* Process ID of cups-driverd */ @@ -3816,23 +5075,17 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ *envp[MAX_ENV]; /* Environment */ cups_file_t *src, /* Source file */ *dst; /* Destination file */ + ppd_file_t *ppd; /* PPD file */ int bytes, /* Bytes from pipe */ total; /* Total bytes from pipe */ - char buffer[2048], /* Copy buffer */ - *ptr; /* Pointer into buffer */ + char buffer[2048]; /* Copy buffer */ int i; /* Looping var */ char option[PPD_MAX_NAME], /* Option name */ choice[PPD_MAX_NAME]; /* Choice name */ int num_defaults; /* Number of default options */ - ppd_default_t *defaults; /* Default options */ + 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, @@ -3858,26 +5111,15 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ cupsdOpenPipe(temppipe); - if ((input = calloc(1, SetSize)) == NULL) - { - close(tempfd); - unlink(tempfile); - - cupsdLogMessage(CUPSD_LOG_ERROR, - "copy_model: Unable to allocate %d bytes for select()...", - SetSize); - return (-1); - } - cupsdLogMessage(CUPSD_LOG_DEBUG, "copy_model: Running \"cups-driverd cat %s\"...", from); if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1], - -1, 0, &temppid)) + -1, -1, 0, DefaultProfile, NULL, &temppid)) { - free(input); close(tempfd); unlink(tempfile); + return (-1); } @@ -3900,15 +5142,14 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ * See if we have data ready... */ - bytes = 0; - - FD_SET(temppipe[0], input); - FD_SET(CGIPipes[0], input); + FD_ZERO(&input); + FD_SET(temppipe[0], &input); + FD_SET(CGIPipes[0], &input); timeout.tv_sec = 30; timeout.tv_usec = 0; - if ((i = select(maxfd, input, NULL, NULL, &timeout)) < 0) + if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0) { if (errno == EINTR) continue; @@ -3924,7 +5165,7 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ break; } - if (FD_ISSET(temppipe[0], input)) + if (FD_ISSET(temppipe[0], &input)) { /* * Read the PPD file from the pipe, and write it to the PPD file. @@ -3941,15 +5182,13 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ break; } - if (FD_ISSET(CGIPipes[0], input)) + if (FD_ISSET(CGIPipes[0], &input)) cupsdUpdateCGI(); } close(temppipe[0]); close(tempfd); - free(input); - if (!total) { /* @@ -3965,42 +5204,12 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ * Read the source file and see what page sizes are supported... */ - if ((src = cupsFileOpen(tempfile, "rb")) == NULL) + if ((ppd = ppdOpenFile(tempfile)) == NULL) { unlink(tempfile); return (-1); } - have_letter = 0; - have_a4 = 0; - - while (cupsFileGets(src, buffer, sizeof(buffer))) - if (!strncmp(buffer, "*PageSize ", 10)) - { - /* - * Strip UI text and command data from the end of the line... - */ - - if ((ptr = strchr(buffer + 10, '/')) != NULL) - *ptr = '\0'; - if ((ptr = strchr(buffer + 10, ':')) != NULL) - *ptr = '\0'; - - for (ptr = buffer + 10; isspace(*ptr); ptr ++); - - /* - * Look for Letter and A4 page sizes... - */ - - if (!strcmp(ptr, "Letter")) - have_letter = 1; - - if (!strcmp(ptr, "A4")) - have_a4 = 1; - } - - cupsFileRewind(src); - /* * Open the destination (if possible) and set the default options... */ @@ -4024,88 +5233,54 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ if (!ppd_parse_line(buffer, option, sizeof(option), choice, sizeof(choice))) - num_defaults = ppd_add_default(option, choice, num_defaults, + { + ppd_option_t *ppdo; /* PPD option */ + + + /* + * Only add the default if the default hasn't already been + * set and the choice exists in the new PPD... + */ + + if (!cupsGetOption(option, num_defaults, defaults) && + (ppdo = ppdFindOption(ppd, option)) != NULL && + ppdFindChoice(ppdo, choice)) + num_defaults = cupsAddOption(option, choice, num_defaults, &defaults); + } } else if (!strncmp(buffer, "*cupsProtocol:", 14)) strlcpy(cups_protocol, buffer, sizeof(cups_protocol)); cupsFileClose(dst); } -#ifdef HAVE_LIBPAPER - else if ((paper_result = systempapername()) != NULL) + else if (ppdPageSize(ppd, DefaultPaperSize)) { /* - * Set the default media sizes from the systemwide default... + * Add the default media sizes... */ - 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 = ppd_add_default("PageSize", system_paper, - num_defaults, &defaults); - num_defaults = ppd_add_default("PageRegion", system_paper, - num_defaults, &defaults); - num_defaults = ppd_add_default("PaperDimension", system_paper, - num_defaults, &defaults); - num_defaults = ppd_add_default("ImageableArea", system_paper, - 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); } -#endif /* HAVE_LIBPAPER */ - else - { - /* - * 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_US", 5) || - !strncasecmp(DefaultLanguage, "en_CA", 5) || - !strncasecmp(DefaultLanguage, "fr_CA", 5)) - { - /* - * These are the only locales that will default to "letter" size... - */ + ppdClose(ppd); - if (have_letter) - { - num_defaults = ppd_add_default("PageSize", "Letter", num_defaults, - &defaults); - num_defaults = ppd_add_default("PageRegion", "Letter", num_defaults, - &defaults); - num_defaults = ppd_add_default("PaperDimension", "Letter", num_defaults, - &defaults); - num_defaults = ppd_add_default("ImageableArea", "Letter", num_defaults, - &defaults); - } - } - else if (have_a4) - { - /* - * The rest default to "a4" size... - */ + /* + * Open the source file for a copy... + */ - num_defaults = ppd_add_default("PageSize", "A4", num_defaults, - &defaults); - num_defaults = ppd_add_default("PageRegion", "A4", num_defaults, - &defaults); - num_defaults = ppd_add_default("PaperDimension", "A4", num_defaults, - &defaults); - num_defaults = ppd_add_default("ImageableArea", "A4", num_defaults, - &defaults); - } + if ((src = cupsFileOpen(tempfile, "rb")) == NULL) + { + cupsFreeOptions(num_defaults, defaults); + unlink(tempfile); + return (-1); } /* @@ -4114,9 +5289,7 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ if ((dst = cupsFileOpen(to, "wb")) == NULL) { - if (num_defaults > 0) - free(defaults); - + cupsFreeOptions(num_defaults, defaults); cupsFileClose(src); unlink(tempfile); return (-1); @@ -4136,18 +5309,18 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ if (!ppd_parse_line(buffer, option, sizeof(option), choice, sizeof(choice))) - { - for (i = 0; i < num_defaults; i ++) - if (!strcmp(option, defaults[i].option)) - { - /* - * Substitute the previous choice... - */ + { + const char *val; /* Default option value */ - snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, - defaults[i].choice); - break; - } + + if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL) + { + /* + * Substitute the previous choice... + */ + + snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val); + } } } @@ -4157,8 +5330,7 @@ copy_model(cupsd_client_t *con, /* I - Client connection */ if (cups_protocol[0]) cupsFilePrintf(dst, "%s\n", cups_protocol); - if (num_defaults > 0) - free(defaults); + cupsFreeOptions(num_defaults, defaults); /* * Close both files and return... @@ -4192,6 +5364,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); @@ -4248,14 +5428,93 @@ 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 = + _cupsStrRetain(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); + + if (printer->alert_description && + (!ra || cupsArrayFind(ra, "printer-alert-description"))) + ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, + "printer-alert-description", NULL, + printer->alert_description); + if (!ra || cupsArrayFind(ra, "printer-current-time")) 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); + if (!ra || cupsArrayFind(ra, "printer-error-policy-supported")) + { + static const char * const errors[] =/* printer-error-policy-supported values */ + { + "abort-job", + "retry-current-job", + "retry-job", + "stop-printer" + }; + + if (printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS)) + ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY, + "printer-error-policy-supported", NULL, "retry-current-job"); + else + ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY, + "printer-error-policy-supported", + sizeof(errors) / sizeof(errors[0]), NULL, errors); + } + if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs")) ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", printer->accepting); @@ -4264,6 +5523,17 @@ copy_printer_attrs( ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared", printer->shared); + if ((!ra || cupsArrayFind(ra, "printer-more-info")) && + !(printer->type & CUPS_PRINTER_DISCOVERED)) + { + httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), + "http", NULL, con->servername, con->serverport, + (printer->type & CUPS_PRINTER_CLASS) ? + "/classes/%s" : "/printers/%s", printer->name); + ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, + "printer-more-info", NULL, printer_uri); + } + if (!ra || cupsArrayFind(ra, "printer-op-policy")) ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-op-policy", NULL, printer->op_policy); @@ -4275,7 +5545,7 @@ copy_printer_attrs( if (!ra || cupsArrayFind(ra, "printer-state-change-time")) ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", printer->state_time); - + if (MaxPrinterHistory > 0 && printer->num_history > 0 && cupsArrayFind(ra, "printer-state-history")) { @@ -4329,12 +5599,12 @@ copy_printer_attrs( "printer-up-time", curtime); if ((!ra || cupsArrayFind(ra, "printer-uri-supported")) && - !ippFindAttribute(printer->attrs, "printer-uri-supported", - IPP_TAG_URI)) + !(printer->type & CUPS_PRINTER_DISCOVERED)) { httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", NULL, con->servername, con->serverport, - "/printers/%s", printer->name); + (printer->type & CUPS_PRINTER_CLASS) ? + "/classes/%s" : "/printers/%s", printer->name); ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, printer_uri); cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"", @@ -4345,6 +5615,8 @@ copy_printer_attrs( add_queued_job_count(con, printer); copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0); + if (printer->ppd_attrs) + copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0); copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY); } @@ -4381,7 +5653,7 @@ copy_subscription_attrs( */ ippAddString(con->response, IPP_TAG_SUBSCRIPTION, - IPP_TAG_KEYWORD | IPP_TAG_COPY, + (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY), "notify-events", NULL, name); } else @@ -4395,7 +5667,7 @@ copy_subscription_attrs( count ++; attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION, - IPP_TAG_KEYWORD | IPP_TAG_COPY, + (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY), "notify-events", count, NULL, NULL); for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1) @@ -4459,27 +5731,43 @@ static void create_job(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer URI */ { - cupsd_job_t *job; /* New job */ + cupsd_printer_t *printer; /* Printer */ + cupsd_job_t *job; /* New job */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con, con->http.fd, uri->values[0].string.text); + /* + * Is the destination valid? + */ + + if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer)) + { + /* + * Bad URI... + */ + + send_ipp_status(con, IPP_NOT_FOUND, + _("The printer or class was not found.")); + return; + } + /* * Create the job object... */ - if ((job = add_job(con, uri, NULL, NULL)) == NULL) + if ((job = add_job(con, printer, NULL)) == NULL) return; + job->pending_timeout = 1; + /* * Save and log the job... */ - - cupsdSaveJob(job); - cupsdLogMessage(CUPSD_LOG_INFO, "Job %d created on \"%s\" by \"%s\".", - job->id, job->dest, job->username); + cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".", + job->dest, job->username); } @@ -4576,6 +5864,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"); @@ -4608,6 +5897,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"); @@ -4623,8 +5913,12 @@ create_requested_array(ipp_t *request) /* I - IPP request */ cupsArrayAdd(ra, "pages-per-minute"); cupsArrayAdd(ra, "pages-per-minute-color"); 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"); @@ -4636,6 +5930,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"); @@ -4643,6 +5939,16 @@ create_requested_array(ipp_t *request) /* I - IPP request */ cupsArrayAdd(ra, "uri-authentication-supported"); cupsArrayAdd(ra, "uri-security-supported"); } + else if (!strcmp(value, "printer-defaults")) + { + char *name; /* Option name */ + + + for (name = (char *)cupsArrayFirst(CommonDefaults); + name; + name = (char *)cupsArrayNext(CommonDefaults)) + cupsArrayAdd(ra, name); + } else if (!strcmp(value, "subscription-template")) { cupsArrayAdd(ra, "notify-attributes"); @@ -4675,8 +5981,7 @@ create_subscription( http_status_t status; /* Policy status */ int i; /* Looping var */ ipp_attribute_t *attr; /* Current attribute */ - const char *dest; /* Destination */ - cups_ptype_t dtype; /* Destination type (printer or class) */ + cups_ptype_t dtype; /* Destination type (printer/class) */ char scheme[HTTP_MAX_URI], /* Scheme portion of URI */ userpass[HTTP_MAX_URI], @@ -4690,23 +5995,26 @@ create_subscription( cupsd_job_t *job; /* Job */ int jobid; /* Job ID */ cupsd_subscription_t *sub; /* Subscription object */ - const char *username, /* requesting-user-name or authenticated username */ + const char *username, /* requesting-user-name or + authenticated username */ *recipient, /* notify-recipient-uri */ *pullmethod; /* notify-pull-method */ ipp_attribute_t *user_data; /* notify-user-data */ int interval, /* notify-time-interval */ lease; /* notify-lease-duration */ unsigned mask; /* notify-events */ + ipp_attribute_t *notify_events,/* notify-events(-default) */ + *notify_lease; /* notify-lease-duration(-default) */ #ifdef DEBUG 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 */ @@ -4724,23 +6032,20 @@ create_subscription( if (!strcmp(resource, "/")) { - dest = NULL; dtype = (cups_ptype_t)0; printer = NULL; } else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) { - dest = NULL; dtype = (cups_ptype_t)0; printer = NULL; } else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9) { - dest = NULL; dtype = CUPS_PRINTER_CLASS; printer = NULL; } - else if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -4757,15 +6062,16 @@ create_subscription( if (printer) { - if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) + if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, + NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } } else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -4807,9 +6113,26 @@ create_subscription( jobid = 0; mask = CUPSD_EVENT_NONE; + if (printer) + { + notify_events = ippFindAttribute(printer->attrs, "notify-events-default", + IPP_TAG_KEYWORD); + notify_lease = ippFindAttribute(printer->attrs, + "notify-lease-duration-default", + IPP_TAG_INTEGER); + + if (notify_lease) + lease = notify_lease->values[0].integer; + } + else + { + notify_events = NULL; + notify_lease = NULL; + } + while (attr && attr->group_tag != IPP_TAG_ZERO) { - if (!strcmp(attr->name, "notify-recipient") && + if (!strcmp(attr->name, "notify-recipient-uri") && attr->value_tag == IPP_TAG_URI) { /* @@ -4822,12 +6145,13 @@ create_subscription( 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))) + 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 \"%s\"!"), recipient); + _("Bad notify-recipient-uri URI \"%s\"!"), recipient); ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", IPP_URI_SCHEME); return; @@ -4838,12 +6162,22 @@ create_subscription( if (access(notifier, X_OK)) { send_ipp_status(con, IPP_NOT_POSSIBLE, - _("notify-recipient URI \"%s\" uses unknown scheme!"), - recipient); + _("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) @@ -4894,10 +6228,7 @@ create_subscription( } else if (!strcmp(attr->name, "notify-events") && attr->value_tag == IPP_TAG_KEYWORD) - { - for (i = 0; i < attr->num_values; i ++) - mask |= cupsdEventValue(attr->values[i].string.text); - } + notify_events = attr; else if (!strcmp(attr->name, "notify-lease-duration") && attr->value_tag == IPP_TAG_INTEGER) lease = attr->values[0].integer; @@ -4911,6 +6242,12 @@ create_subscription( attr = attr->next; } + if (notify_events) + { + for (i = 0; i < notify_events->num_values; i ++) + mask |= cupsdEventValue(notify_events->values[i].string.text); + } + if (recipient) cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient); if (pullmethod) @@ -4955,7 +6292,12 @@ create_subscription( else job = NULL; - sub = cupsdAddSubscription(mask, printer, job, recipient, 0); + if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL) + { + send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS, + _("There are too many subscriptions.")); + return; + } if (job) cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d", @@ -4991,8 +6333,7 @@ create_subscription( attr = attr->next; } - cupsdSaveAllSubscriptions(); - + cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS); } @@ -5005,13 +6346,7 @@ delete_printer(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - URI of printer or class */ { http_status_t status; /* Policy status */ - const char *dest; /* Destination */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - 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 */ + cups_ptype_t dtype; /* Destination type (printer/class) */ cupsd_printer_t *printer; /* Printer/class */ char filename[1024]; /* Script/PPD filename */ @@ -5023,11 +6358,7 @@ delete_printer(cupsd_client_t *con, /* I - Client connection */ * Do we have a valid URI? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -5044,7 +6375,7 @@ delete_printer(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -5052,7 +6383,7 @@ delete_printer(cupsd_client_t *con, /* I - Client connection */ * Remove old jobs... */ - cupsdCancelJobs(dest, NULL, 1); + cupsdCancelJobs(printer->name, NULL, 1); /* * Remove old subscriptions and send a "deleted printer" event... @@ -5061,38 +6392,51 @@ delete_printer(cupsd_client_t *con, /* I - Client connection */ cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL, "%s \"%s\" deleted by \"%s\".", (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer", - dest, get_username(con)); + printer->name, get_username(con)); cupsdExpireSubscriptions(printer, NULL); - + /* * Remove any old PPD or script files... */ - snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest); + snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, + printer->name); + unlink(filename); + + snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, + printer->name); unlink(filename); - snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest); + snprintf(filename, sizeof(filename), "%s/%s.ipp", CacheDir, 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\".", dest, - get_username(con)); + 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\".", dest, - get_username(con)); + cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".", + printer->name, get_username(con)); cupsdDeletePrinter(printer, 0); - cupsdSaveAllPrinters(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); } - cupsdWritePrintcap(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); /* * Return with no errors... @@ -5121,7 +6465,7 @@ get_default(cupsd_client_t *con) /* I - Client connection */ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -5148,12 +6492,19 @@ 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 */ + ipp_attribute_t *limit, /* limit attribute */ + *timeout, /* timeout attribute */ + *requested, /* requested-attributes attribute */ + *exclude, /* exclude-schemes attribute */ + *include; /* include-schemes attribute */ char command[1024], /* cups-deviced command */ - options[1024], /* Options to pass to command */ - requested_str[256]; + options[2048], /* Options to pass to command */ + requested_str[256], /* String for requested attributes */ + exclude_str[512], + /* String for excluded schemes */ + include_str[512]; + /* String for included schemes */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd); @@ -5164,7 +6515,7 @@ get_devices(cupsd_client_t *con) /* I - Client connection */ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -5172,21 +6523,38 @@ get_devices(cupsd_client_t *con) /* I - Client connection */ * Run cups-deviced command with the given options... */ - limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER); + 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); + include = ippFindAttribute(con->request, "include-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'; + + if (include) + url_encode_attr(include, include_str, sizeof(include_str)); + else + include_str[0] = '\0'; + snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin); snprintf(options, sizeof(options), - "%d+%d+%d+%s", + "%d+%d+%d+%d+%s%s%s%s%s", con->request->request.op.request_id, - limit ? limit->values[0].integer : 0, (int)User, - requested_str); + limit ? limit->values[0].integer : 0, + timeout ? timeout->values[0].integer : 10, + (int)User, + requested_str, + exclude_str[0] ? "%20" : "", exclude_str, + include_str[0] ? "%20" : "", include_str); if (cupsdSendCommand(con, command, options, 1)) { @@ -5210,6 +6578,150 @@ get_devices(cupsd_client_t *con) /* I - Client connection */ } +/* + * '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 scheme[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, scheme, + sizeof(scheme), 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); + } + + /* + * See if the job exists... + */ + + if ((job = cupsdFindJob(jobid)) == NULL) + { + /* + * Nope - return a "not found" error... + */ + + send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid); + return; + } + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + + /* + * Get the document number... + */ + + if ((attr = ippFindAttribute(con->request, "document-number", + IPP_TAG_INTEGER)) == NULL) + { + send_ipp_status(con, IPP_BAD_REQUEST, + _("Missing document-number attribute!")); + return; + } + + 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; + } + + 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; + } + + fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); + + cupsdLoadJob(job); + + snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super, + job->filetypes[docnum - 1]->type); + + 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); +} + + /* * 'get_job_attrs()' - Get job attributes. */ @@ -5222,7 +6734,7 @@ get_job_attrs(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *attr; /* Current attribute */ int jobid; /* Job ID */ cupsd_job_t *job; /* Current job */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + char scheme[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 */ @@ -5259,8 +6771,8 @@ get_job_attrs(cupsd_client_t *con, /* I - Client connection */ * 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, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (strncmp(resource, "/jobs/", 6)) @@ -5298,7 +6810,7 @@ get_job_attrs(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -5327,9 +6839,9 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ http_status_t status; /* Policy status */ ipp_attribute_t *attr; /* Current attribute */ const char *dest; /* Destination */ - cups_ptype_t dtype; /* Destination type (printer or class) */ + cups_ptype_t dtype; /* Destination type (printer/class) */ cups_ptype_t dmask; /* Destination type mask */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + char scheme[HTTP_MAX_URI], /* Scheme 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 */ @@ -5351,12 +6863,17 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, + if (strcmp(uri->name, "printer-uri")) + { + send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request!")); + return; + } + + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); - if (!strcmp(resource, "/") || - (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6)) + if (!strcmp(resource, "/") || !strcmp(resource, "/jobs")) { dest = NULL; dtype = (cups_ptype_t)0; @@ -5377,7 +6894,8 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ dmask = CUPS_PRINTER_CLASS; printer = NULL; } - else if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype, + &printer)) == NULL) { /* * Bad URI... @@ -5388,7 +6906,10 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ return; } else + { + dtype &= CUPS_PRINTER_CLASS; dmask = CUPS_PRINTER_CLASS; + } /* * Check policy... @@ -5396,15 +6917,16 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ if (printer) { - if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) + if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, + NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } } else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -5424,6 +6946,11 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ completed = 0; list = Jobs; } + else if (attr && !strcmp(attr->values[0].string.text, "processing")) + { + completed = 0; + list = PrintingJobs; + } else { completed = 0; @@ -5438,7 +6965,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) @@ -5464,14 +6991,23 @@ 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)) { /* * Filter out jobs that don't match... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: job->id = %d", job->id); + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", " + "state_value=%d, attrs=%p", job->id, job->dest, + job->username, job->state_value, job->attrs); + + 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))) @@ -5479,9 +7015,6 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ if ((job->dtype & dmask) != dtype && (!job->printer || (job->printer->type & dmask) != dtype)) continue; - if (username[0] && strcasecmp(username, job->username)) - continue; - if (completed && job->state_value <= IPP_JOB_STOPPED) continue; @@ -5491,6 +7024,13 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ cupsdLoadJob(job); if (!job->attrs) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d!", + job->id); + continue; + } + + if (username[0] && strcasecmp(username, job->username)) continue; if (count > 0) @@ -5498,11 +7038,11 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ count ++; - cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count = %d", count); - copy_job_attrs(con, job, ra); } + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count); + cupsArrayDelete(ra); con->response->request.status.status_code = IPP_OK; @@ -5525,7 +7065,7 @@ get_notifications(cupsd_client_t *con) /* I - Client connection */ int interval; /* Poll interval */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_subscription_attrs(con=%p[%d])", + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])", con, con->http.fd); /* @@ -5570,7 +7110,7 @@ get_notifications(cupsd_client_t *con) /* I - Client connection */ DefaultPolicyPtr, con, sub->owner)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, sub->dest); return; } @@ -5646,6 +7186,152 @@ get_notifications(cupsd_client_t *con) /* I - Client connection */ } +/* + * 'get_ppd()' - Get a named PPD from the local system. + */ + +static void +get_ppd(cupsd_client_t *con, /* I - Client connection */ + ipp_attribute_t *uri) /* I - Printer URI or PPD name */ +{ + http_status_t status; /* Policy status */ + cupsd_printer_t *dest; /* Destination */ + cups_ptype_t dtype; /* Destination type */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con, + con->http.fd, uri, uri->name, uri->values[0].string.text); + + if (!strcmp(uri->name, "ppd-name")) + { + /* + * Return a PPD file from cups-driverd... + */ + + char command[1024], /* cups-driverd command */ + options[1024], /* Options to pass to command */ + ppd_name[1024]; /* ppd-name */ + + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, NULL); + return; + } + + /* + * Run cups-driverd command with the given options... + */ + + snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin); + url_encode_string(uri->values[0].string.text, ppd_name, sizeof(ppd_name)); + snprintf(options, sizeof(options), "get+%d+%s", + con->request->request.op.request_id, ppd_name); + + if (cupsdSendCommand(con, command, options, 0)) + { + /* + * 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-driverd failed to execute.")); + } + } + else if (!strcmp(uri->name, "printer-uri") && + cupsdValidateDest(uri->values[0].string.text, &dtype, &dest)) + { + int i; /* Looping var */ + char filename[1024]; /* PPD filename */ + + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, dest); + return; + } + + /* + * See if we need the PPD for a class or remote printer... + */ + + snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, + dest->name); + + if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0)) + { + con->response->request.status.status_code = CUPS_SEE_OTHER; + ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, dest->uri); + return; + } + else if (dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) + { + for (i = 0; i < dest->num_printers; i ++) + if (!(dest->printers[i]->type & + (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))) + { + snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, + dest->printers[i]->name); + + if (!access(filename, 0)) + break; + } + + if (i < dest->num_printers) + dest = dest->printers[i]; + else + { + con->response->request.status.status_code = CUPS_SEE_OTHER; + ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, dest->printers[0]->uri); + return; + } + } + + /* + * Found the printer with the PPD file, now see if there is one... + */ + + if ((con->file = open(filename, O_RDONLY)) < 0) + { + send_ipp_status(con, IPP_NOT_FOUND, + _("The PPD file \"%s\" could not be opened: %s"), + uri->values[0].string.text, strerror(errno)); + return; + } + + fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); + + con->pipe_pid = 0; + + con->response->request.status.status_code = IPP_OK; + } + else + send_ipp_status(con, IPP_NOT_FOUND, + _("The PPD file \"%s\" could not be found."), + uri->values[0].string.text); +} + + /* * 'get_ppds()' - Get the list of PPD files on the local system. */ @@ -5655,13 +7341,37 @@ get_ppds(cupsd_client_t *con) /* I - Client connection */ { http_status_t status; /* Policy status */ ipp_attribute_t *limit, /* Limit attribute */ + *device, /* ppd-device-id attribute */ + *language, /* ppd-natural-language attribute */ *make, /* ppd-make attribute */ - *requested; /* requested-attributes attribute */ - char command[1024], /* cups-deviced command */ - options[1024], /* Options to pass to command */ + *model, /* ppd-make-and-model attribute */ + *model_number, /* ppd-model-number attribute */ + *product, /* ppd-product attribute */ + *psversion, /* ppd-psverion attribute */ + *type, /* ppd-type attribute */ + *requested, /* requested-attributes attribute */ + *exclude, /* exclude-schemes attribute */ + *include; /* include-schemes attribute */ + char command[1024], /* cups-driverd command */ + options[4096], /* Options to pass to command */ + device_str[256],/* Escaped ppd-device-id string */ + language_str[256], + /* Escaped ppd-natural-language */ + make_str[256], /* Escaped ppd-make string */ + model_str[256], /* Escaped ppd-make-and-model string */ + model_number_str[256], + /* ppd-model-number string */ + product_str[256], + /* Escaped ppd-product string */ + psversion_str[256], + /* Escaped ppd-psversion string */ + type_str[256], /* Escaped ppd-type string */ requested_str[256], /* String for requested attributes */ - make_str[256]; /* Escaped ppd-make string */ + exclude_str[512], + /* String for excluded schemes */ + include_str[512]; + /* String for included schemes */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd); @@ -5672,7 +7382,7 @@ get_ppds(cupsd_client_t *con) /* I - Client connection */ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -5680,26 +7390,97 @@ get_ppds(cupsd_client_t *con) /* I - Client connection */ * Run cups-driverd command with the given options... */ - limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER); - make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT); - requested = ippFindAttribute(con->request, "requested-attributes", - IPP_TAG_KEYWORD); + limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER); + device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT); + language = ippFindAttribute(con->request, "ppd-natural-language", + IPP_TAG_LANGUAGE); + make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT); + model = ippFindAttribute(con->request, "ppd-make-and-model", + IPP_TAG_TEXT); + model_number = ippFindAttribute(con->request, "ppd-model-number", + IPP_TAG_INTEGER); + product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT); + psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT); + type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD); + requested = ippFindAttribute(con->request, "requested-attributes", + IPP_TAG_KEYWORD); + exclude = ippFindAttribute(con->request, "exclude-schemes", + IPP_TAG_NAME); + include = ippFindAttribute(con->request, "include-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 (device) + url_encode_attr(device, device_str, sizeof(device_str)); + else + device_str[0] = '\0'; + + if (language) + url_encode_attr(language, language_str, sizeof(language_str)); + else + language_str[0] = '\0'; + if (make) url_encode_attr(make, make_str, sizeof(make_str)); else make_str[0] = '\0'; + if (model) + url_encode_attr(model, model_str, sizeof(model_str)); + else + model_str[0] = '\0'; + + if (model_number) + snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d", + model_number->values[0].integer); + else + model_number_str[0] = '\0'; + + if (product) + url_encode_attr(product, product_str, sizeof(product_str)); + else + product_str[0] = '\0'; + + if (psversion) + url_encode_attr(psversion, psversion_str, sizeof(psversion_str)); + else + psversion_str[0] = '\0'; + + if (type) + url_encode_attr(type, type_str, sizeof(type_str)); + else + type_str[0] = '\0'; + + if (exclude) + url_encode_attr(exclude, exclude_str, sizeof(exclude_str)); + else + exclude_str[0] = '\0'; + + if (include) + url_encode_attr(include, include_str, sizeof(include_str)); + else + include_str[0] = '\0'; + snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin); - snprintf(options, sizeof(options), "list+%d+%d+%s%s%s", + snprintf(options, sizeof(options), + "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", con->request->request.op.request_id, limit ? limit->values[0].integer : 0, - requested_str, make ? "%20" : "", make_str); + requested_str, + device ? "%20" : "", device_str, + language ? "%20" : "", language_str, + make ? "%20" : "", make_str, + model ? "%20" : "", model_str, + model_number ? "%20" : "", model_number_str, + product ? "%20" : "", product_str, + psversion ? "%20" : "", psversion_str, + type ? "%20" : "", type_str, + exclude_str[0] ? "%20" : "", exclude_str, + include_str[0] ? "%20" : "", include_str); if (cupsdSendCommand(con, command, options, 0)) { @@ -5732,17 +7513,7 @@ get_printer_attrs(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer URI */ { http_status_t status; /* Policy status */ - const char *dest; /* Destination */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - 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 */ + cups_ptype_t dtype; /* Destination type (printer/class) */ cupsd_printer_t *printer; /* Printer/class */ cups_array_t *ra; /* Requested attributes array */ @@ -5754,11 +7525,7 @@ get_printer_attrs(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -5775,7 +7542,7 @@ get_printer_attrs(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } @@ -5793,6 +7560,61 @@ get_printer_attrs(cupsd_client_t *con, /* I - Client connection */ } +/* + * 'get_printer_supported()' - Get printer supported values. + */ + +static void +get_printer_supported( + cupsd_client_t *con, /* I - Client connection */ + ipp_attribute_t *uri) /* I - Printer URI */ +{ + http_status_t status; /* Policy status */ + cups_ptype_t dtype; /* Destination type (printer/class) */ + cupsd_printer_t *printer; /* Printer/class */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con, + con->http.fd, uri->values[0].string.text); + + /* + * Is the destination valid? + */ + + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) + { + /* + * Bad URI... + */ + + send_ipp_status(con, IPP_NOT_FOUND, + _("The printer or class was not found.")); + return; + } + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, printer); + return; + } + + /* + * Return a list of attributes that can be set via Set-Printer-Attributes. + */ + + ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE, + "printer-info", 0); + ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE, + "printer-location", 0); + + con->response->request.status.status_code = IPP_OK; +} + + /* * 'get_printers()' - Get a list of printers or classes. */ @@ -5803,7 +7625,7 @@ get_printers(cupsd_client_t *con, /* I - Client connection */ { http_status_t status; /* Policy status */ ipp_attribute_t *attr; /* Current attribute */ - int limit; /* Maximum number of printers to return */ + int limit; /* Max number of printers to return */ int count; /* Number of printers that match */ cupsd_printer_t *printer; /* Current printer pointer */ int printer_type, /* printer-type attribute */ @@ -5812,6 +7634,7 @@ get_printers(cupsd_client_t *con, /* I - Client connection */ const char *username; /* Current user */ char *first_printer_name; /* first-printer-name attribute */ cups_array_t *ra; /* Requested attributes array */ + int local; /* Local connection? */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con, @@ -5823,7 +7646,7 @@ get_printers(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -5869,6 +7692,8 @@ get_printers(cupsd_client_t *con, /* I - Client connection */ else printer_mask = 0; + local = httpAddrLocalhost(&(con->clientaddr)); + if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL) location = attr->values[0].string.text; @@ -5901,10 +7726,13 @@ get_printers(cupsd_client_t *con, /* I - Client connection */ count < limit && printer; printer = (cupsd_printer_t *)cupsArrayNext(Printers)) { + if (!local && !printer->shared) + continue; + 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 @@ -5920,7 +7748,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; /* @@ -5987,7 +7816,7 @@ get_subscription_attrs( DefaultPolicyPtr, con, sub->owner)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, sub->dest); return; } @@ -6020,9 +7849,9 @@ get_subscriptions(cupsd_client_t *con, /* I - Client connection */ cupsd_subscription_t *sub; /* Subscription */ cups_array_t *ra; /* Requested attributes array */ ipp_attribute_t *attr; /* Attribute */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - char method[HTTP_MAX_URI], - /* Method portion of URI */ + cups_ptype_t dtype; /* Destination type (printer/class) */ + char scheme[HTTP_MAX_URI], + /* Scheme portion of URI */ username[HTTP_MAX_URI], /* Username portion of URI */ host[HTTP_MAX_URI], @@ -6042,8 +7871,8 @@ get_subscriptions(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (!strcmp(resource, "/") || @@ -6066,7 +7895,7 @@ get_subscriptions(cupsd_client_t *con, /* I - Client connection */ return; } } - else if (!cupsdValidateDest(host, resource, &dtype, &printer)) + else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -6099,7 +7928,7 @@ get_subscriptions(cupsd_client_t *con, /* I - Client connection */ DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } @@ -6178,10 +8007,10 @@ static void hold_job(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Job or Printer URI */ { - ipp_attribute_t *attr, /* Current job-hold-until */ - *newattr; /* New job-hold-until */ + ipp_attribute_t *attr; /* Current job-hold-until */ + const char *when; /* New value */ int jobid; /* Job ID */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + char scheme[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 */ @@ -6218,8 +8047,8 @@ hold_job(cupsd_client_t *con, /* I - Client connection */ * 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, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (strncmp(resource, "/jobs/", 6)) @@ -6257,7 +8086,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); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -6265,45 +8094,90 @@ hold_job(cupsd_client_t *con, /* I - Client connection */ * Hold the job and return... */ - cupsdHoldJob(job); - - if ((newattr = ippFindAttribute(con->request, "job-hold-until", - IPP_TAG_KEYWORD)) == NULL) - newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME); - - if ((attr = ippFindAttribute(job->attrs, "job-hold-until", - IPP_TAG_KEYWORD)) == NULL) - attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME); + if ((attr = ippFindAttribute(con->request, "job-hold-until", + IPP_TAG_KEYWORD)) == NULL) + attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME); if (attr) { - /* - * Free the old hold value and copy the new one over... - */ + when = attr->values[0].string.text; - _cupsStrFree(attr->values[0].string.text); + cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job, + "Job job-hold-until value changed by user."); + } + else + when = "indefinite"; - if (newattr) - { - attr->value_tag = newattr->value_tag; - attr->values[0].string.text = - _cupsStrAlloc(newattr->values[0].string.text); - } - else - { - attr->value_tag = IPP_TAG_KEYWORD; - attr->values[0].string.text = _cupsStrAlloc("indefinite"); - } + cupsdSetJobHoldUntil(job, when, 1); + cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".", + username); + + con->response->request.status.status_code = IPP_OK; +} + + +/* + * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class. + */ + +static void +hold_new_jobs(cupsd_client_t *con, /* I - Connection */ + ipp_attribute_t *uri) /* I - Printer URI */ +{ + http_status_t status; /* Policy status */ + cups_ptype_t dtype; /* Destination type (printer/class) */ + cupsd_printer_t *printer; /* Printer data */ + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con, + con->http.fd, uri->values[0].string.text); + + /* + * Is the destination valid? + */ + + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) + { /* - * Hold job until specified time... + * Bad URI... */ - cupsdSetJobHoldUntil(job, attr->values[0].string.text); + send_ipp_status(con, IPP_NOT_FOUND, + _("The printer or class was not found.")); + return; + } + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, printer); + return; } - cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was held by \"%s\".", jobid, - username); + /* + * Hold pending/new jobs sent to the printer... + */ + + printer->holding_new_jobs = 1; + + cupsdSetPrinterReasons(printer, "+hold-new-jobs"); + cupsdAddPrinterHistory(printer); + + if (dtype & CUPS_PRINTER_CLASS) + cupsdLogMessage(CUPSD_LOG_INFO, + "Class \"%s\" now holding pending/new jobs (\"%s\").", + printer->name, get_username(con)); + else + cupsdLogMessage(CUPSD_LOG_INFO, + "Printer \"%s\" now holding pending/new jobs (\"%s\").", + printer->name, get_username(con)); + + /* + * Everything was ok, so return OK status... + */ con->response->request.status.status_code = IPP_OK; } @@ -6321,11 +8195,10 @@ move_job(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *attr; /* Current attribute */ int jobid; /* Job ID */ cupsd_job_t *job; /* Current job */ - const char *src, /* Source printer/class */ - *dest; /* Destination */ + const char *src; /* Source printer/class */ cups_ptype_t stype, /* Source type (printer or class) */ - dtype; /* Destination type (printer or class) */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + dtype; /* Destination type (printer/class) */ + char scheme[HTTP_MAX_URI], /* Scheme 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 */ @@ -6352,12 +8225,8 @@ move_job(cupsd_client_t *con, /* I - Client connection */ _("job-printer-uri attribute missing!")); return; } - - httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - if ((dest = cupsdValidateDest(host, resource, &dtype, &dprinter)) == NULL) + if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter)) { /* * Bad URI... @@ -6368,22 +8237,12 @@ 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); - return; - } - /* * See if we have a job URI or a printer URI... */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (!strcmp(uri->name, "printer-uri")) @@ -6399,7 +8258,8 @@ move_job(cupsd_client_t *con, /* I - Client connection */ * Move all jobs... */ - if ((src = cupsdValidateDest(host, resource, &stype, &sprinter)) == NULL) + if ((src = cupsdValidateDest(uri->values[0].string.text, &stype, + &sprinter)) == NULL) { /* * Bad URI... @@ -6479,9 +8339,20 @@ move_job(cupsd_client_t *con, /* I - Client connection */ * Job found, initialize source pointers... */ - src = NULL; - sprinter = NULL; - } + src = NULL; + sprinter = NULL; + } + } + + /* + * 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; } /* @@ -6512,7 +8383,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); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -6570,56 +8441,6 @@ move_job(cupsd_client_t *con, /* I - Client connection */ } -/* - * 'ppd_add_default()' - Add a PPD default choice. - */ - -static int /* O - Number of defaults */ -ppd_add_default( - const char *option, /* I - Option name */ - const char *choice, /* I - Choice name */ - int num_defaults, /* I - Number of defaults */ - ppd_default_t **defaults) /* IO - Defaults */ -{ - int i; /* Looping var */ - ppd_default_t *temp; /* Temporary defaults array */ - - - /* - * First check if the option already has a default value; the PPD spec - * says that the first one is used... - */ - - for (i = 0, temp = *defaults; i < num_defaults; i ++) - if (!strcmp(option, temp[i].option)) - return (num_defaults); - - /* - * Now add the option... - */ - - if (num_defaults == 0) - temp = malloc(sizeof(ppd_default_t)); - else - temp = realloc(*defaults, (num_defaults + 1) * sizeof(ppd_default_t)); - - if (!temp) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "ppd_add_default: Unable to add default value for \"%s\" - %s", - option, strerror(errno)); - return (num_defaults); - } - - *defaults = temp; - temp += num_defaults; - - strlcpy(temp->option, option, sizeof(temp->option)); - strlcpy(temp->choice, choice, sizeof(temp->choice)); - - return (num_defaults + 1); -} - - /* * 'ppd_parse_line()' - Parse a PPD default line. */ @@ -6697,6 +8518,7 @@ print_job(cupsd_client_t *con, /* I - Client connection */ { ipp_attribute_t *attr; /* Current attribute */ ipp_attribute_t *format; /* Document-format attribute */ + const char *default_format; /* document-format-default value */ cupsd_job_t *job; /* New job */ char filename[1024]; /* Job filename */ mime_type_t *filetype; /* Type of file */ @@ -6753,6 +8575,21 @@ print_job(cupsd_client_t *con, /* I - Client connection */ return; } + /* + * Is the destination valid? + */ + + if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer)) + { + /* + * Bad URI... + */ + + send_ipp_status(con, IPP_NOT_FOUND, + _("The printer or class was not found.")); + return; + } + /* * Is it a format we support? */ @@ -6764,7 +8601,8 @@ print_job(cupsd_client_t *con, /* I - Client connection */ * Grab format from client... */ - if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2) + if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, + type) != 2) { send_ipp_status(con, IPP_BAD_REQUEST, _("Could not scan type \"%s\"!"), @@ -6772,10 +8610,26 @@ print_job(cupsd_client_t *con, /* I - Client connection */ return; } } + else if ((default_format = cupsGetOption("document-format", + printer->num_options, + printer->options)) != NULL) + { + /* + * Use default document format... + */ + + if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2) + { + send_ipp_status(con, IPP_BAD_REQUEST, + _("Could not scan type \"%s\"!"), + default_format); + return; + } + } else { /* - * No document format attribute? Auto-type it! + * Auto-type it! */ strcpy(super, "application"); @@ -6791,39 +8645,45 @@ 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, doc_name ? doc_name->values[0].string.text : NULL, &compression); - if (filetype) - { - /* - * Replace the document-format attribute value with the auto-typed one. - */ + if (!filetype) + filetype = mimeType(MimeDatabase, super, type); - snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, - filetype->type); + cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.", + filetype->super, filetype->type); + } + else + filetype = mimeType(MimeDatabase, super, type); - if (format) - { - _cupsStrFree(format->values[0].string.text); + if (filetype && + (!format || + (!strcmp(super, "application") && !strcmp(type, "octet-stream")))) + { + /* + * Replace the document-format attribute value with the auto-typed or + * default one. + */ - format->values[0].string.text = _cupsStrAlloc(mimetype); - } - else - ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, - "document-format", NULL, mimetype); + snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, + filetype->type); + + if (format) + { + _cupsStrFree(format->values[0].string.text); + + format->values[0].string.text = _cupsStrAlloc(mimetype); } else - filetype = mimeType(MimeDatabase, super, type); + ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, + "document-format", NULL, mimetype); } - else - filetype = mimeType(MimeDatabase, super, type); - - if (!filetype) + else if (!filetype) { send_ipp_status(con, IPP_DOCUMENT_FORMAT, _("Unsupported format \'%s/%s\'!"), super, type); @@ -6837,22 +8697,20 @@ print_job(cupsd_client_t *con, /* I - Client connection */ return; } - cupsdLogMessage(CUPSD_LOG_DEBUG, "print_job: request file type is %s/%s.", - filetype->super, filetype->type); - /* * Read any embedded job ticket info from PS files... */ 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... */ - if ((job = add_job(con, uri, &printer, filetype)) == NULL) + if ((job = add_job(con, printer, filetype)) == NULL) return; /* @@ -6886,33 +8744,19 @@ print_job(cupsd_client_t *con, /* I - Client connection */ * See if we need to add the ending sheet... */ - attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME); - - if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) && - attr && attr->num_values > 1) - { - /* - * Yes... - */ - - cupsdLogMessage(CUPSD_LOG_INFO, "Adding end banner page \"%s\" to job %d.", - attr->values[1].string.text, job->id); - - kbytes = copy_banner(con, job, attr->values[1].string.text); - - cupsdUpdateQuota(printer, job->username, 0, kbytes); - } + 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... @@ -6923,16 +8767,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 * @@ -6942,8 +8786,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 @@ -6956,7 +8800,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 */ @@ -6975,8 +8819,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; } @@ -6988,14 +8831,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... @@ -7116,13 +8958,7 @@ reject_jobs(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer or class URI */ { http_status_t status; /* Policy status */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - 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 */ - const char *name; /* Printer name */ + cups_ptype_t dtype; /* Destination type (printer/class) */ cupsd_printer_t *printer; /* Printer data */ ipp_attribute_t *attr; /* printer-state-message text */ @@ -7134,11 +8970,7 @@ reject_jobs(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -7155,7 +8987,7 @@ reject_jobs(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } @@ -7176,19 +9008,87 @@ 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\").", - name, get_username(con)); + printer->name, get_username(con)); } else { - cupsdSaveAllPrinters(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").", - name, get_username(con)); + printer->name, get_username(con)); + } + + /* + * Everything was ok, so return OK status... + */ + + con->response->request.status.status_code = IPP_OK; +} + + +/* + * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class. + */ + +static void +release_held_new_jobs( + cupsd_client_t *con, /* I - Connection */ + ipp_attribute_t *uri) /* I - Printer URI */ +{ + http_status_t status; /* Policy status */ + cups_ptype_t dtype; /* Destination type (printer/class) */ + cupsd_printer_t *printer; /* Printer data */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con, + con->http.fd, uri->values[0].string.text); + + /* + * Is the destination valid? + */ + + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) + { + /* + * Bad URI... + */ + + send_ipp_status(con, IPP_NOT_FOUND, + _("The printer or class was not found.")); + return; + } + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, printer); + return; } + /* + * Hold pending/new jobs sent to the printer... + */ + + printer->holding_new_jobs = 0; + + cupsdSetPrinterReasons(printer, "-hold-new-jobs"); + cupsdAddPrinterHistory(printer); + + if (dtype & CUPS_PRINTER_CLASS) + cupsdLogMessage(CUPSD_LOG_INFO, + "Class \"%s\" now printing pending/new jobs (\"%s\").", + printer->name, get_username(con)); + else + cupsdLogMessage(CUPSD_LOG_INFO, + "Printer \"%s\" now printing pending/new jobs (\"%s\").", + printer->name, get_username(con)); + /* * Everything was ok, so return OK status... */ @@ -7207,7 +9107,7 @@ release_job(cupsd_client_t *con, /* I - Client connection */ { ipp_attribute_t *attr; /* Current attribute */ int jobid; /* Job ID */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + char scheme[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 */ @@ -7244,8 +9144,8 @@ release_job(cupsd_client_t *con, /* I - Client connection */ * 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, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (strncmp(resource, "/jobs/", 6)) @@ -7297,7 +9197,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); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -7315,6 +9215,9 @@ 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, cupsdFindDest(job->dest), job, + "Job job-hold-until value changed by user."); } /* @@ -7323,10 +9226,14 @@ release_job(cupsd_client_t *con, /* I - Client connection */ cupsdReleaseJob(job); - cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was released by \"%s\".", jobid, - username); + cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job, + "Job released by user."); + + cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username); con->response->request.status.status_code = IPP_OK; + + cupsdCheckJobs(); } @@ -7382,7 +9289,7 @@ renew_subscription( DefaultPolicyPtr, con, sub->owner)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, sub->dest); return; } @@ -7406,9 +9313,12 @@ renew_subscription( sub->expire = sub->lease ? time(NULL) + sub->lease : 0; - cupsdSaveAllSubscriptions(); + cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS); con->response->request.status.status_code = IPP_OK; + + ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, + "notify-lease-duration", sub->lease); } @@ -7422,12 +9332,12 @@ restart_job(cupsd_client_t *con, /* I - Client connection */ { ipp_attribute_t *attr; /* Current attribute */ int jobid; /* Job ID */ - char method[HTTP_MAX_URI], /* Method portion of URI */ + cupsd_job_t *job; /* Job information */ + char scheme[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 */ - cupsd_job_t *job; /* Job information */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con, @@ -7459,8 +9369,8 @@ restart_job(cupsd_client_t *con, /* I - Client connection */ * 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, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (strncmp(resource, "/jobs/", 6)) @@ -7513,7 +9423,7 @@ restart_job(cupsd_client_t *con, /* I - Client connection */ cupsdLoadJob(job); - if (!job->attrs ||job->num_files == 0) + if (!job->attrs || job->num_files == 0) { /* * Nope - return a "not possible" error... @@ -7530,18 +9440,44 @@ 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); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } /* - * Restart the job and return... + * See if the job-hold-until attribute is specified... */ - cupsdRestartJob(job); + if ((attr = ippFindAttribute(con->request, "job-hold-until", + IPP_TAG_KEYWORD)) == NULL) + attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME); + + if (attr && strcmp(attr->values[0].string.text, "no-hold")) + { + /* + * Return the job to a held state... + */ + + cupsdLogJob(job, CUPSD_LOG_DEBUG, + "Restarted by \"%s\" with job-hold-until=%s.", + username, attr->values[0].string.text); + cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0); + + cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE, + NULL, job, "Job restarted by user with job-hold-until=%s", + attr->values[0].string.text); + } + else + { + /* + * Restart the job... + */ + + cupsdRestartJob(job); + cupsdCheckJobs(); + } - cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was restarted by \"%s\".", jobid, - username); + cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username); con->response->request.status.status_code = IPP_OK; } @@ -7552,22 +9488,25 @@ restart_job(cupsd_client_t *con, /* I - Client connection */ */ static void -save_auth_info(cupsd_client_t *con, /* I - Client connection */ - cupsd_job_t *job) /* I - Job */ +save_auth_info( + cupsd_client_t *con, /* I - Client connection */ + cupsd_job_t *job, /* I - Job */ + ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */ { - int i; /* Looping var */ - char filename[1024]; /* Job authentication filename */ - cups_file_t *fp; /* Job authentication file */ - char line[1024]; /* Line for file */ + int i; /* Looping var */ + char filename[1024]; /* Job authentication filename */ + cups_file_t *fp; /* Job authentication file */ + char line[2048]; /* Line for file */ + cupsd_printer_t *dest; /* Destination printer/class */ /* * This function saves the in-memory authentication information for * a job so that it can be used to authenticate with a remote host. * The information is stored in a file that is readable only by the - * root user. The username and password are Base-64 encoded, each - * on a separate line, followed by random number (up to 1024) of - * newlines to limit the amount of information that is exposed. + * root user. The fields are Base-64 encoded, each on a separate line, + * followed by random number (up to 1024) of newlines to limit the + * amount of information that is exposed. * * Because of the potential for exposing of authentication information, * this functionality is only enabled when running cupsd as root. @@ -7587,6 +9526,9 @@ save_auth_info(cupsd_client_t *con, /* I - Client connection */ if (RunUser) return; + if ((dest = cupsdFindDest(job->dest)) == NULL) + return; + /* * Create the authentication file and change permissions... */ @@ -7603,19 +9545,54 @@ save_auth_info(cupsd_client_t *con, /* I - Client connection */ fchown(cupsFileNumber(fp), 0, 0); fchmod(cupsFileNumber(fp), 0400); - /* - * Write the authenticated username... - */ + if (auth_info && auth_info->num_values == dest->num_auth_info_required) + { + /* + * Write 1 to 3 auth values... + */ - httpEncode64_2(line, sizeof(line), con->username, strlen(con->username)); - cupsFilePrintf(fp, "%s\n", line); + cupsdClearString(&job->auth_username); + cupsdClearString(&job->auth_domain); + cupsdClearString(&job->auth_password); - /* - * Write the authenticated password... - */ + for (i = 0; i < auth_info->num_values; i ++) + { + httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text, + strlen(auth_info->values[i].string.text)); + cupsFilePrintf(fp, "%s\n", line); + + if (!strcmp(dest->auth_info_required[i], "username")) + cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", + auth_info->values[i].string.text); + else if (!strcmp(dest->auth_info_required[i], "domain")) + cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s", + auth_info->values[i].string.text); + else if (!strcmp(dest->auth_info_required[i], "password")) + cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", + auth_info->values[i].string.text); + } + } + else if (con->username[0]) + { + /* + * Write the authenticated username... + */ + + httpEncode64_2(line, sizeof(line), con->username, strlen(con->username)); + cupsFilePrintf(fp, "%s\n", line); + + cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", con->username); + cupsdClearString(&job->auth_domain); + + /* + * Write the authenticated password... + */ + + httpEncode64_2(line, sizeof(line), con->password, strlen(con->password)); + cupsFilePrintf(fp, "%s\n", line); - httpEncode64_2(line, sizeof(line), con->password, strlen(con->password)); - cupsFilePrintf(fp, "%s\n", line); + cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", con->password); + } /* * Write a random number of newlines to the end of the file... @@ -7629,7 +9606,47 @@ save_auth_info(cupsd_client_t *con, /* I - Client connection */ */ cupsFileClose(fp); + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H) + if (con->gss_creds) + save_krb5_creds(con, job); + else if (job->ccname) + cupsdClearString(&(job->ccname)); +#endif /* HAVE_GSSAPI && HAVE_KRB5_H */ +} + + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H) +/* + * 'save_krb5_creds()' - Save Kerberos credentials for the job. + */ + +static void +save_krb5_creds(cupsd_client_t *con, /* I - Client connection */ + cupsd_job_t *job) /* I - Job */ +{ + /* + * Get the credentials... + */ + + job->ccache = cupsdCopyKrb5Creds(con); + + /* + * Add the KRB5CCNAME environment variable to the job so that the + * backend can use the credentials when printing. + */ + + if (job->ccache) + { + cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s", + krb5_cc_get_name(KerberosContext, job->ccache)); + + cupsdLogJob(job, CUPSD_LOG_DEBUG2, "save_krb5_creds: %s", job->ccname); + } + else + cupsdClearString(&(job->ccname)); } +#endif /* HAVE_GSSAPI && HAVE_KRB5_H */ /* @@ -7641,12 +9658,14 @@ 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 */ char job_uri[HTTP_MAX_URI], /* Job URI */ - method[HTTP_MAX_URI], + scheme[HTTP_MAX_URI], /* Method portion of URI */ username[HTTP_MAX_URI], /* Username portion of URI */ @@ -7667,6 +9686,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, @@ -7698,8 +9718,8 @@ send_document(cupsd_client_t *con, /* I - Client connection */ * 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, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (strncmp(resource, "/jobs/", 6)) @@ -7739,7 +9759,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); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -7793,13 +9813,30 @@ send_document(cupsd_client_t *con, /* I - Client connection */ * Grab format from client... */ - if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2) + if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", + super, type) != 2) { send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"), format->values[0].string.text); return; } } + else if ((default_format = cupsGetOption("document-format", + printer->num_options, + printer->options)) != NULL) + { + /* + * Use default document format... + */ + + if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2) + { + send_ipp_status(con, IPP_BAD_REQUEST, + _("Could not scan type \"%s\"!"), + default_format); + return; + } + } else { /* @@ -7819,38 +9856,44 @@ 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, doc_name ? doc_name->values[0].string.text : NULL, &compression); - if (filetype) - { - /* - * Replace the document-format attribute value with the auto-typed one. - */ - - snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, - filetype->type); - - if (format) - { - _cupsStrFree(format->values[0].string.text); - format->values[0].string.text = _cupsStrAlloc(mimetype); - } - else - ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, - "document-format", NULL, mimetype); - } - else + 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) + if (filetype) + { + /* + * Replace the document-format attribute value with the auto-typed or + * default one. + */ + + snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, + filetype->type); + + if ((jformat = ippFindAttribute(job->attrs, "document-format", + IPP_TAG_MIMETYPE)) != NULL) + { + _cupsStrFree(jformat->values[0].string.text); + + jformat->values[0].string.text = _cupsStrAlloc(mimetype); + } + else + ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE, + "document-format", NULL, mimetype); + } + else if (!filetype) { send_ipp_status(con, IPP_DOCUMENT_FORMAT, _("Unsupported format \'%s/%s\'!"), super, type); @@ -7878,10 +9921,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... */ @@ -7908,9 +9947,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... @@ -7924,24 +9962,8 @@ send_document(cupsd_client_t *con, /* I - Client connection */ * See if we need to add the ending sheet... */ - if (printer && - !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) && - (attr = ippFindAttribute(job->attrs, "job-sheets", - IPP_TAG_ZERO)) != NULL && - attr->num_values > 1) - { - /* - * Yes... - */ - - cupsdLogMessage(CUPSD_LOG_INFO, - "Adding end banner page \"%s\" to job %d.", - attr->values[1].string.text, job->id); - - kbytes = copy_banner(con, job, attr->values[1].string.text); - - cupsdUpdateQuota(printer, job->username, 0, kbytes); - } + if (cupsdTimeoutJob(job)) + return; if (job->state_value == IPP_JOB_STOPPED) { @@ -7961,18 +9983,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 { @@ -7984,9 +9998,13 @@ 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->hold_until = time(NULL) + MultipleOperationTimeout; + job->dirty = 1; + + cupsdMarkDirty(CUPSD_DIRTY_JOBS); } + + start_job = 0; } /* @@ -8002,10 +10020,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_CANCELLED); + 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(); } @@ -8014,14 +10039,81 @@ send_document(cupsd_client_t *con, /* I - Client connection */ */ static void -send_http_error(cupsd_client_t *con, /* I - Client connection */ - http_status_t status) /* I - HTTP status code */ +send_http_error( + cupsd_client_t *con, /* I - Client connection */ + 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)); + 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 */ - cupsdSendError(con, status); + + 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, CUPSD_AUTH_NONE); ippDelete(con->response); con->response = NULL; @@ -8036,31 +10128,22 @@ send_http_error(cupsd_client_t *con, /* I - Client connection */ 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 */ - if (message) - { - va_start(ap, message); - vsnprintf(formatted, sizeof(formatted), - _cupsLangString(con->language, message), ap); - va_end(ap); + va_start(ap, message); + vsnprintf(formatted, sizeof(formatted), + _cupsLangString(con->language, message), ap); + va_end(ap); - cupsdLogMessage(status >= IPP_BAD_REQUEST ? CUPSD_LOG_ERROR : CUPSD_LOG_INFO, - "%s %s: %s", - ippOpString(con->request->request.op.operation_id), - ippErrorString(status), formatted); - } - else - cupsdLogMessage(status >= IPP_BAD_REQUEST ? CUPSD_LOG_ERROR : CUPSD_LOG_INFO, - "%s %s", - ippOpString(con->request->request.op.operation_id), - ippErrorString(status)); + cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s", + ippOpString(con->request->request.op.operation_id), + ippErrorString(status), formatted); con->response->request.status.status_code = status; @@ -8074,9 +10157,8 @@ send_ipp_status(cupsd_client_t *con, /* I - Client connection */ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, DefaultLanguage); - if (message) - ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, - "status-message", NULL, formatted); + ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, + "status-message", NULL, formatted); } @@ -8089,18 +10171,9 @@ set_default(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer URI */ { http_status_t status; /* Policy status */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - 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 */ - const char *name; /* Printer name */ - cupsd_printer_t *printer; /* Printer */ + cups_ptype_t dtype; /* Destination type (printer/class) */ + cupsd_printer_t *printer, /* Printer */ + *oldprinter; /* Old default printer */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con, @@ -8110,11 +10183,7 @@ set_default(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -8131,7 +10200,7 @@ set_default(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, NULL); return; } @@ -8139,16 +10208,22 @@ 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); + + cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL, + "%s is now the default printer.", printer->name); - cupsdWritePrintcap(); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES | + CUPSD_DIRTY_REMOTE | CUPSD_DIRTY_PRINTCAP); cupsdLogMessage(CUPSD_LOG_INFO, - "Default destination set to \"%s\" by \"%s\".", name, - get_username(con)); + "Default destination set to \"%s\" by \"%s\".", + printer->name, get_username(con)); /* * Everything was ok, so return OK status... @@ -8170,7 +10245,7 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ *attr2; /* Job attribute */ int jobid; /* Job ID */ cupsd_job_t *job; /* Current job */ - char method[HTTP_MAX_URI], + char scheme[HTTP_MAX_URI], /* Method portion of URI */ username[HTTP_MAX_URI], /* Username portion of URI */ @@ -8179,6 +10254,8 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ resource[HTTP_MAX_URI]; /* 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, @@ -8216,8 +10293,8 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ * 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, + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme, + sizeof(scheme), username, sizeof(username), host, sizeof(host), &port, resource, sizeof(resource)); if (strncmp(resource, "/jobs/", 6)) @@ -8270,7 +10347,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); + send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest)); return; } @@ -8280,6 +10357,9 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ cupsdLoadJob(job); + check_jobs = 0; + event = 0; + for (attr = con->request->attrs; attr; attr = attr->next) { if (attr->group_tag != IPP_TAG_JOB || !attr->name) @@ -8292,6 +10372,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") || @@ -8305,7 +10386,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)) @@ -8343,7 +10423,15 @@ 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-priority to %d", + attr->values[0].integer); cupsdSetJobPriority(job, attr->values[0].integer); + + check_jobs = 1; + event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | + CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED; + } } else if (!strcmp(attr->name, "job-state")) { @@ -8372,8 +10460,12 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ } else if (con->response->request.status.status_code == IPP_OK) { - job->state->values[0].integer = attr->values[0].integer; - job->state_value = attr->values[0].integer; + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d", + attr->values[0].integer); + cupsdSetJobState(job, attr->values[0].integer, + CUPSD_JOB_DEFAULT, + "Job state changed by \"%s\"", username); + check_jobs = 1; } break; @@ -8387,7 +10479,7 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ } break; - case IPP_JOB_CANCELLED : + case IPP_JOB_CANCELED : case IPP_JOB_ABORTED : case IPP_JOB_COMPLETED : if (job->state_value > IPP_JOB_PROCESSING) @@ -8398,14 +10490,12 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ } else if (con->response->request.status.status_code == IPP_OK) { - cupsdCancelJob(job, 0); - - if (JobHistory) - { - job->state->values[0].integer = attr->values[0].integer; - job->state_value = attr->values[0].integer; - cupsdSaveJob(job); - } + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d", + attr->values[0].integer); + cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer, + CUPSD_JOB_DEFAULT, + "Job state changed by \"%s\"", username); + check_jobs = 1; } break; } @@ -8442,12 +10532,20 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ if (!strcmp(attr->name, "job-hold-until")) { - cupsdSetJobHoldUntil(job, attr->values[0].string.text); + cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s", + attr->values[0].string.text); + cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0); if (!strcmp(attr->values[0].string.text, "no-hold")) + { cupsdReleaseJob(job); + check_jobs = 1; + } else - cupsdHoldJob(job); + cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, + "Job held by \"%s\".", username); + + event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE; } } else if (attr->value_tag == IPP_TAG_DELETEATTR) @@ -8468,6 +10566,8 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ job->attrs->last = job->attrs->prev; _ippFreeAttr(attr2); + + event |= CUPSD_EVENT_JOB_CONFIG_CHANGED; } } else @@ -8477,6 +10577,8 @@ set_job_attrs(cupsd_client_t *con, /* I - Client connection */ */ copy_attribute(job->attrs, attr, 0); + + event |= CUPSD_EVENT_JOB_CONFIG_CHANGED; } } @@ -8484,13 +10586,116 @@ 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, 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, cupsdFindDest(job->dest), job, + "Job options changed by user."); /* * Start jobs if possible... */ - cupsdCheckJobs(); + if (check_jobs) + cupsdCheckJobs(); +} + + +/* + * 'set_printer_attrs()' - Set printer attributes. + */ + +static void +set_printer_attrs(cupsd_client_t *con, /* I - Client connection */ + ipp_attribute_t *uri) /* I - Printer */ +{ + http_status_t status; /* Policy status */ + cups_ptype_t dtype; /* Destination type (printer/class) */ + cupsd_printer_t *printer; /* Printer/class */ + ipp_attribute_t *attr; /* Printer attribute */ + int changed = 0; /* Was anything changed? */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con, + con->http.fd, uri->values[0].string.text); + + /* + * Is the destination valid? + */ + + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) + { + /* + * Bad URI... + */ + + send_ipp_status(con, IPP_NOT_FOUND, + _("The printer or class was not found.")); + return; + } + + /* + * Check policy... + */ + + if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) + { + send_http_error(con, status, printer); + return; + } + + /* + * Return a list of attributes that can be set via Set-Printer-Attributes. + */ + + if ((attr = ippFindAttribute(con->request, "printer-location", + IPP_TAG_TEXT)) != NULL) + { + cupsdSetString(&printer->location, attr->values[0].string.text); + changed = 1; + } + + if ((attr = ippFindAttribute(con->request, "printer-info", + IPP_TAG_TEXT)) != NULL) + { + cupsdSetString(&printer->info, attr->values[0].string.text); + changed = 1; + } + + /* + * Update the printer attributes and return... + */ + + if (changed) + { + cupsdSetPrinterAttrs(printer); + cupsdMarkDirty(CUPSD_DIRTY_PRINTERS); + + cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL, + "Printer \"%s\" description or location changed by \"%s\".", + printer->name, get_username(con)); + + cupsdLogMessage(CUPSD_LOG_INFO, + "Printer \"%s\" description or location changed by \"%s\".", + printer->name, get_username(con)); + } + + con->response->request.status.status_code = IPP_OK; } @@ -8635,9 +10840,11 @@ set_printer_defaults( if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD) continue; - if (strcmp(attr->values[0].string.text, "abort-job") && - strcmp(attr->values[0].string.text, "retry-job") && - strcmp(attr->values[0].string.text, "stop-printer")) + if (strcmp(attr->values[0].string.text, "retry-current-job") && + ((printer->type & (CUPS_PRINTER_IMPLICIT | CUPS_PRINTER_CLASS)) || + (strcmp(attr->values[0].string.text, "abort-job") && + strcmp(attr->values[0].string.text, "retry-job") && + strcmp(attr->values[0].string.text, "stop-printer")))) { send_ipp_status(con, IPP_NOT_POSSIBLE, _("Unknown printer-error-policy \"%s\"."), @@ -8650,10 +10857,6 @@ set_printer_defaults( attr->values[0].string.text); cupsdSetString(&printer->error_policy, attr->values[0].string.text); } - else if (!strcmp(attr->name, "document-format-default") || - !strcmp(attr->name, "notify-lease-duration-default") || - !strcmp(attr->name, "notify-events-default")) - continue; /* * Skip any other non-default attributes... @@ -8753,17 +10956,7 @@ start_printer(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer URI */ { http_status_t status; /* Policy status */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - 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 */ - const char *name; /* Printer name */ + cups_ptype_t dtype; /* Destination type (printer/class) */ cupsd_printer_t *printer; /* Printer data */ @@ -8774,11 +10967,7 @@ start_printer(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -8795,7 +10984,7 @@ start_printer(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } @@ -8808,19 +10997,11 @@ start_printer(cupsd_client_t *con, /* I - Client connection */ cupsdStartPrinter(printer, 1); if (dtype & CUPS_PRINTER_CLASS) - { - cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".", name, - get_username(con)); - cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL, - "Class \"%s\" started by \"%s\".", name, get_username(con)); - } + cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".", + printer->name, get_username(con)); else - { - cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".", name, - get_username(con)); - cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL, - "Printer \"%s\" started by \"%s\".", name, get_username(con)); - } + cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".", + printer->name, get_username(con)); cupsdCheckJobs(); @@ -8841,17 +11022,7 @@ stop_printer(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer URI */ { http_status_t status; /* Policy status */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - 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 */ - const char *name; /* Printer name */ + cups_ptype_t dtype; /* Destination type (printer/class) */ cupsd_printer_t *printer; /* Printer data */ ipp_attribute_t *attr; /* printer-state-message attribute */ @@ -8863,11 +11034,7 @@ stop_printer(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -8884,7 +11051,7 @@ stop_printer(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } @@ -8904,19 +11071,11 @@ stop_printer(cupsd_client_t *con, /* I - Client connection */ cupsdStopPrinter(printer, 1); if (dtype & CUPS_PRINTER_CLASS) - { - cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".", name, - get_username(con)); - cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL, - "Class \"%s\" stopped by \"%s\".", name, get_username(con)); - } + cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".", + printer->name, get_username(con)); else - { - cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".", name, - get_username(con)); - cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL, - "Printer \"%s\" stopped by \"%s\".", name, get_username(con)); - } + cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".", + printer->name, get_username(con)); /* * Everything was ok, so return OK status... @@ -8937,8 +11096,7 @@ url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */ { int i; /* Looping var */ char *bufptr, /* Pointer into buffer */ - *bufend, /* End of buffer */ - *valptr; /* Pointer into value */ + *bufend; /* End of buffer */ strlcpy(buffer, attr->name, bufsize); @@ -8960,25 +11118,8 @@ url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */ *bufptr++ = '\''; - for (valptr = attr->values[i].string.text; - *valptr && bufptr < bufend; - valptr ++) - if (*valptr == ' ') - { - if (bufptr >= (bufend - 2)) - break; - - *bufptr++ = '%'; - *bufptr++ = '2'; - *bufptr++ = '0'; - } - else if (*valptr == '\'' || *valptr == '\\') - { - *bufptr++ = '\\'; - *bufptr++ = *valptr; - } - else - *bufptr++ = *valptr; + bufptr = url_encode_string(attr->values[i].string.text, + bufptr, bufend - bufptr + 1); if (bufptr >= bufend) break; @@ -8990,6 +11131,55 @@ url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */ } +/* + * 'url_encode_string()' - URL-encode a string. + */ + +static char * /* O - End of string */ +url_encode_string(const char *s, /* I - String */ + char *buffer, /* I - String buffer */ + int bufsize) /* I - Size of buffer */ +{ + char *bufptr, /* Pointer into buffer */ + *bufend; /* End of buffer */ + static const char *hex = "0123456789ABCDEF"; + /* Hex digits */ + + + bufptr = buffer; + bufend = buffer + bufsize - 1; + + while (*s && bufptr < bufend) + { + if (*s == ' ' || *s == '%') + { + if (bufptr >= (bufend - 2)) + break; + + *bufptr++ = '%'; + *bufptr++ = hex[(*s >> 4) & 15]; + *bufptr++ = hex[*s & 15]; + + s ++; + } + else if (*s == '\'' || *s == '\\') + { + if (bufptr >= (bufend - 1)) + break; + + *bufptr++ = '\\'; + *bufptr++ = *s++; + } + else + *bufptr++ = *s++; + } + + *bufptr = '\0'; + + return (bufptr); +} + + /* * 'user_allowed()' - See if a user is allowed to print to a queue. */ @@ -9000,6 +11190,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) @@ -9008,6 +11200,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(); @@ -9022,6 +11228,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; } @@ -9041,16 +11256,7 @@ validate_job(cupsd_client_t *con, /* I - Client connection */ http_status_t status; /* Policy status */ ipp_attribute_t *attr; /* Current attribute */ ipp_attribute_t *format; /* Document-format attribute */ - cups_ptype_t dtype; /* Destination type (printer or class) */ - 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 */ + cups_ptype_t dtype; /* Destination type (printer/class) */ char super[MIME_MAX_SUPER], /* Supertype of file */ type[MIME_MAX_TYPE]; @@ -9085,7 +11291,8 @@ validate_job(cupsd_client_t *con, /* I - Client connection */ if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL) { - if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2) + if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", + super, type) != 2) { send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"), format->values[0].string.text); @@ -9110,11 +11317,7 @@ validate_job(cupsd_client_t *con, /* I - Client connection */ * Is the destination valid? */ - httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, - sizeof(method), username, sizeof(username), host, - sizeof(host), &port, resource, sizeof(resource)); - - if (cupsdValidateDest(host, resource, &dtype, &printer) == NULL) + if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer)) { /* * Bad URI... @@ -9131,7 +11334,7 @@ validate_job(cupsd_client_t *con, /* I - Client connection */ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK) { - send_http_error(con, status); + send_http_error(con, status, printer); return; } @@ -9147,7 +11350,7 @@ validate_job(cupsd_client_t *con, /* I - Client connection */ * 'validate_name()' - Make sure the printer name only contains valid chars. */ -static int /* O - 0 if name is no good, 1 if name is good */ +static int /* O - 0 if name is no good, 1 if good */ validate_name(const char *name) /* I - Name to check */ { const char *ptr; /* Pointer into name */ @@ -9158,7 +11361,7 @@ validate_name(const char *name) /* I - Name to check */ */ for (ptr = name; *ptr; ptr ++) - if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#') + if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#') return (0); /* @@ -9186,8 +11389,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... @@ -9214,5 +11417,5 @@ validate_user(cupsd_job_t *job, /* I - Job */ /* - * End of "$Id: ipp.c 5686 2006-06-21 21:02:56Z mike $". + * End of "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $". */