Index: doc/help/spec-ipp.html =================================================================== --- doc/help/spec-ipp.html (revision 6878) +++ doc/help/spec-ipp.html (revision 6879) @@ -39,7 +39,7 @@ 'fold',' trim', and 'bale' attribute values extension" specifications.

-

CUPS also provides 15 new operations and many new attributes +

CUPS also provides 16 new operations and many new attributes to support multiple IPP printers and printer classes on a single host.

@@ -80,7 +80,7 @@

CUPS IPP Operations

-

CUPS provides 14 extension operations in addition to most of the +

CUPS provides 16 extension operations in addition to most of the standard IPP and registered extension operations:

0x400F + + + + + +
Get a PPD file.
CUPS-Get-Document1.40x4027Get a document file from a job.
@@ -1786,7 +1792,91 @@

If the status code is successful-ok, the PPD file follows the end of the IPP response.

+

CUPS 1.4CUPS-Get-Document Operation

+

The CUPS-Get-Document operation (0x4027) gets a document file from a +job on the server. The document file is specified using the +document-number and either the job-uri or printer-uri +and job-id identifying the job.

+ +

If the document file is found, successful-ok is returned with +the document file following the response data.

+ +

If the document file does not exist, client-error-not-found is +returned.

+ +

If the requesting user does not have access to the document file, +client-error-not-authorized is returned. + +

CUPS-Get-Document Request

+ +

The following group of attributes is supplied as part of the +CUPS-Get-Document request: + +

Group 1: Operation Attributes + +

+ +
Natural Language and Character Set: + +
The "attributes-charset" and "attributes-natural-language" + attributes as described in section 3.1.4.1 of the IPP Model and + Semantics document. + +
"printer-uri" (uri) and "job-id" (integer) +
OR +
"job-uri" (uri): + +
The client MUST supply a printer URI and job ID or job URI. + +
"document-number" (integer(1:MAX)): + +
The client MUST supply a document number to retrieve. The + document-count attribute for the job defines the maximum + document number that can be specified. In the case of jobs with + banners (job-sheets is not "none"), document number 1 + will typically contain the start banner and document number N + will typically contain the end banner. + +
+ +

CUPS-Get-Document Response

+ +

The following group of attributes is sent as part of the +CUPS-Get-Document Response: + +

Group 1: Operation Attributes + +

+ +
Status Message: + +
The standard response status message. + +
Natural Language and Character Set: + +
The "attributes-charset" and "attributes-natural-language" + attributes as described in section 3.1.4.2 of the IPP Model and + Semantics document. + +
"document-format" (mimeType): + +
The format of the document file. + +
"document-number" (integer(1:MAX)): + +
The requested document number. + +
"document-name" (name(MAX)): + +
The name that was supplied with the document, if any. + +
+ +

If the status code is successful-ok, the document file follows +the end of the IPP response.

+ +

Attributes

CUPS provides many extension attributes to support multiple @@ -1914,6 +2004,11 @@ printing text files. Only the values 10, 12, and 17 are currently supported. The default value is 10. +

document-count (integer(1:MAX))

+ +

The document-count attribute specifies the number of documents that +are present in the job. +

fitplot (boolean)

The fitplot attribute specifies whether to scale HP-GL/2 plot files to Index: cups/ipp.h =================================================================== --- cups/ipp.h (revision 6878) +++ cups/ipp.h (revision 6879) @@ -246,7 +246,8 @@ CUPS_GET_PPDS, /* Get a list of supported drivers */ CUPS_MOVE_JOB, /* Move a job to a different printer */ CUPS_AUTHENTICATE_JOB, /* Authenticate a job @since CUPS 1.2@ */ - CUPS_GET_PPD /* Get a PPD file @since CUPS 1.3@ */ + CUPS_GET_PPD, /* Get a PPD file @since CUPS 1.3@ */ + CUPS_GET_DOCUMENT = 0x4027 /* Get a document file @since CUPS 1.4@ */ } ipp_op_t; /* Old names for the operations */ Index: cups/ipp-support.c =================================================================== --- cups/ipp-support.c (revision 6878) +++ cups/ipp-support.c (revision 6879) @@ -150,6 +150,10 @@ "CUPS-Move-Job", "CUPS-Authenticate-Job", "CUPS-Get-PPD" + }, + * const ipp_cups_ops2[] = + { + "CUPS-Get-Document" }; @@ -244,6 +248,8 @@ return ("windows-ext"); else if (op >= CUPS_GET_DEFAULT && op <= CUPS_GET_PPD) return (ipp_cups_ops[op - CUPS_GET_DEFAULT]); + else if (op == CUPS_GET_DOCUMENT) + return (ipp_cups_ops2[0]); /* * No, build an "unknown-xxxx" operation string... @@ -278,6 +284,10 @@ if (!strcasecmp(name, ipp_cups_ops[i])) return ((ipp_op_t)(i + 0x4001)); + for (i = 0; i < (sizeof(ipp_cups_ops2) / sizeof(ipp_cups_ops2[0])); i ++) + if (!strcasecmp(name, ipp_cups_ops2[i])) + return ((ipp_op_t)(i + 0x4027)); + if (!strcasecmp(name, "CUPS-Add-Class")) return (CUPS_ADD_MODIFY_CLASS); Index: cups/request.c =================================================================== --- cups/request.c (revision 6878) +++ cups/request.c (revision 6879) @@ -17,6 +17,7 @@ * Contents: * * cupsDoFileRequest() - Do an IPP request with a file. + * cupsDoIORequest() - Do an IPP request with file descriptors. * cupsDoRequest() - Do an IPP request. * _cupsSetError() - Set the last IPP status code and status-message. * _cupsSetHTTPError() - Set the last error using the HTTP status. Index: man/cupsfilter.man =================================================================== --- man/cupsfilter.man (revision 6878) +++ man/cupsfilter.man (revision 6879) @@ -11,14 +11,16 @@ .\" 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/". .\" -.TH cupsfilter 8 "Common UNIX Printing System" "12 July 2007" "Apple Inc." +.TH cupsfilter 8 "Common UNIX Printing System" "29 August 2007" "Apple Inc." .SH NAME cupsfilter \- convert a file to another format using cups filters .SH SYNOPSIS .B cupsfilter [ -c .I config-file -] -m +] -j +.I job-id[,N] +-m .I mime/type [ -n .I copies @@ -40,6 +42,11 @@ .br Uses the named cupsd.conf configuration file. .TP 5 +-j job-id[,N] +.br +Converts document N from the specified job. If N is omitted, document 1 is +converted. +.TP 5 -m mime/type .br Specifies the destination file type. Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 6878) +++ CHANGES.txt (revision 6879) @@ -1,8 +1,12 @@ -CHANGES.txt - 2007-08-27 +CHANGES.txt - 2007-08-29 ------------------------ CHANGES IN CUPS V1.4b1 + - The scheduler now supports a CUPS-Get-Document operation + that returns the specified print job document (STR #118) + - The cupsfilter utility now supports a "-J jobid" option + to filter the document from the specified job. - The scheduler (cupsd) now supports a new option (-t) to do a syntax check of the cupsd.conf file (STR #2003) - Added new cupsGetPPD3() API to allow applications to Index: scheduler/cupsfilter.c =================================================================== --- scheduler/cupsfilter.c (revision 6878) +++ scheduler/cupsfilter.c (revision 6879) @@ -19,6 +19,7 @@ * escape_options() - Convert an options array to a string. * exec_filter() - Execute a single filter. * exec_filters() - Execute filters for the given file and options. + * get_job_file() - Get the specified job file. * open_pipe() - Create a pipe which is closed on exec. * read_cupsd_conf() - Read the cupsd.conf file to get the filter settings. * set_string() - Copy and set a string. @@ -35,8 +36,10 @@ #include #include "mime.h" #include +#include #include #include +#include #include #if defined(__APPLE__) # include @@ -64,6 +67,8 @@ /* CUPS_SERVERROOT environment variable */ static char *RIPCache = NULL; /* RIP_CACHE environment variable */ +static char TempFile[1024] = ""; + /* Temporary file */ /* @@ -79,9 +84,11 @@ const char *printer, const char *user, const char *title, int num_options, cups_option_t *options); +static void get_job_file(const char *job); static int open_pipe(int *fds); static int read_cupsd_conf(const char *filename); static void set_string(char **s, const char *val); +static void sighandler(int sig); static void usage(const char *command, const char *opt); @@ -214,7 +221,21 @@ usage(command, opt); break; - case 'j' : /* Specify destination MIME type... */ + case 'j' : /* Get job file or specify destination MIME type... */ + if (strcmp(command, "convert")) + { + i ++; + if (i < argc) + { + get_job_file(argv[i]); + infile = TempFile; + } + else + usage(command, opt); + + break; + } + case 'm' : /* Specify destination MIME type... */ i ++; if (i < argc) @@ -405,6 +426,9 @@ * Remove files as needed, then exit... */ + if (TempFile[0]) + unlink(TempFile); + if (removeppd && ppdfile) unlink(ppdfile); @@ -811,10 +835,102 @@ /* + * 'get_job_file()' - Get the specified job file. + */ + +static void +get_job_file(const char *job) /* I - Job ID */ +{ + long jobid, /* Job ID */ + docnum; /* Document number */ + const char *jobptr; /* Pointer into job ID string */ + char uri[1024]; /* job-uri */ + http_t *http; /* Connection to server */ + ipp_t *request; /* Request data */ + int tempfd; /* Temporary file */ + + + /* + * Get the job ID and document number, if any... + */ + + if ((jobptr = strrchr(job, '-')) != NULL) + jobptr ++; + else + jobptr = job; + + jobid = strtol(jobptr, (char **)&jobptr, 10); + + if (*jobptr == ',') + docnum = strtol(jobptr + 1, NULL, 10); + else + docnum = 1; + + if (jobid < 1 || jobid > INT_MAX) + { + _cupsLangPrintf(stderr, _("cupsfilter: Invalid job ID %d!\n"), (int)jobid); + exit(1); + } + + if (docnum < 1 || docnum > INT_MAX) + { + _cupsLangPrintf(stderr, _("cupsfilter: Invalid document number %d!\n"), + (int)docnum); + exit(1); + } + + /* + * Ask the server for the document file... + */ + + if ((http = httpConnectEncrypt(cupsServer(), ippPort(), + cupsEncryption())) == NULL) + { + _cupsLangPrintf(stderr, _("%s: Unable to connect to server\n"), + "cupsfilter"); + exit(1); + } + + request = ippNewRequest(CUPS_GET_DOCUMENT); + + snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", (int)jobid); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "document-number", + (int)docnum); + + if ((tempfd = cupsTempFd(TempFile, sizeof(TempFile))) == -1) + { + _cupsLangPrintf(stderr, + _("cupsfilter: Unable to create temporary file: %s\n"), + strerror(errno)); + httpClose(http); + exit(1); + } + + signal(SIGTERM, sighandler); + + ippDelete(cupsDoIORequest(http, request, "/", -1, tempfd)); + + close(tempfd); + + httpClose(http); + + if (cupsLastError() != IPP_OK) + { + _cupsLangPrintf(stderr, _("cupsfilter: Unable to get job file - %s\n"), + cupsLastErrorString()); + unlink(TempFile); + exit(1); + } +} + + +/* * 'open_pipe()' - Create a pipe which is closed on exec. */ -int /* O - 0 on success, -1 on error */ +static int /* O - 0 on success, -1 on error */ open_pipe(int *fds) /* O - Pipe file descriptors (2) */ { /* @@ -948,6 +1064,28 @@ /* + * 'sighandler()' - Signal catcher for when we print from stdin... + */ + +static void +sighandler(int s) /* I - Signal number */ +{ + /* + * Remove the temporary file we're using to print a job file... + */ + + if (TempFile[0]) + unlink(TempFile); + + /* + * Exit... + */ + + exit(s); +} + + +/* * 'usage()' - Show program usage... */ @@ -965,6 +1103,7 @@ "Options:\n" "\n" " -c cupsd.conf Set cupsd.conf file to use\n" + " -j job-id[,N] Filter file N from the specified job (default is file 1)\n" " -n copies Set number of copies\n" " -o name=value Set option(s)\n" " -p filename.ppd Set PPD file\n" Index: scheduler/conf.c =================================================================== --- scheduler/conf.c (revision 6878) +++ scheduler/conf.c (revision 6879) @@ -958,7 +958,7 @@ "Renew-Subscription Cancel-Subscription " "Get-Notifications Reprocess-Job Cancel-Current-Job " "Suspend-Current-Job Resume-Job CUPS-Move-Job " - "CUPS-Authenticate-Job>"); + "CUPS-Authenticate-Job CUPS-Get-Document>"); cupsdLogMessage(CUPSD_LOG_INFO, "Order Deny,Allow"); po = cupsdAddPolicyOp(p, NULL, IPP_SEND_DOCUMENT); @@ -986,6 +986,7 @@ cupsdAddPolicyOp(p, po, IPP_RESUME_JOB); cupsdAddPolicyOp(p, po, CUPS_MOVE_JOB); cupsdAddPolicyOp(p, po, CUPS_AUTHENTICATE_JOB); + cupsdAddPolicyOp(p, po, CUPS_GET_DOCUMENT); cupsdLogMessage(CUPSD_LOG_INFO, ""); @@ -3245,6 +3246,36 @@ "Missing before on line %d!", linenum); + /* + * Verify that we have an explicit policy for CUPS-Get-Document + * (ensures that upgrades do not introduce new security issues...) + */ + + for (i = 0; i < pol->num_ops; i ++) + if (pol->ops[i]->op == CUPS_GET_DOCUMENT) + break; + + if (i >= pol->num_ops) + { + for (i = 0; i < pol->num_ops; i ++) + if (pol->ops[i]->op == IPP_SEND_DOCUMENT) + break; + + if (i < pol->num_ops) + { + /* + * Add a new limit for CUPS-Get-Document using the Send-Document + * limit as a template... + */ + + cupsdLogMessage(CUPSD_LOG_WARN, + "No limit for CUPS-Get-Document defined in policy %s " + "- using Send-Document's policy", pol->name); + + cupsdAddPolicyOp(pol, pol->ops[i], CUPS_GET_DOCUMENT); + } + } + return (linenum); } else if (!strcasecmp(line, "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-more-info")) ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-more-info", NULL, job_uri); @@ -5608,6 +5618,150 @@ /* + * 'get_document()' - Get a copy of a job file. + */ + +static void +get_document(cupsd_client_t *con, /* I - Client connection */ + ipp_attribute_t *uri) /* I - Job URI */ +{ + http_status_t status; /* Policy status */ + ipp_attribute_t *attr; /* Current attribute */ + int jobid; /* Job ID */ + int docnum; /* Document number */ + cupsd_job_t *job; /* Current job */ + char method[HTTP_MAX_URI], /* Method portion of URI */ + username[HTTP_MAX_URI], /* Username portion of URI */ + host[HTTP_MAX_URI], /* Host portion of URI */ + resource[HTTP_MAX_URI]; /* Resource portion of URI */ + int port; /* Port portion of URI */ + char filename[1024], /* Filename for document */ + format[1024]; /* Format for document */ + + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con, + con->http.fd, uri->values[0].string.text); + + /* + * See if we have a job URI or a printer URI... + */ + + if (!strcmp(uri->name, "printer-uri")) + { + /* + * Got a printer URI; see if we also have a job-id attribute... + */ + + if ((attr = ippFindAttribute(con->request, "job-id", + IPP_TAG_INTEGER)) == NULL) + { + send_ipp_status(con, IPP_BAD_REQUEST, + _("Got a printer-uri attribute but no job-id!")); + return; + } + + jobid = attr->values[0].integer; + } + else + { + /* + * Got a job URI; parse it to get the job ID... + */ + + httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method, + sizeof(method), username, sizeof(username), host, + sizeof(host), &port, resource, sizeof(resource)); + + if (strncmp(resource, "/jobs/", 6)) + { + /* + * Not a valid URI! + */ + + send_ipp_status(con, IPP_BAD_REQUEST, + _("Bad job-uri attribute \"%s\"!"), + uri->values[0].string.text); + return; + } + + jobid = atoi(resource + 6); + } + + /* + * 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. */ Index: scheduler/printers.c =================================================================== --- scheduler/printers.c (revision 6878) +++ scheduler/printers.c (revision 6879) @@ -321,6 +321,8 @@ CUPS_GET_PPDS, CUPS_MOVE_JOB, CUPS_AUTHENTICATE_JOB, + CUPS_GET_PPD, + CUPS_GET_DOCUMENT, IPP_RESTART_JOB }; static const char * const charsets[] =/* charset-supported values */