| /* |
| * Printing utilities for CUPS. |
| * |
| * Copyright 2007-2017 by Apple Inc. |
| * Copyright 1997-2006 by Easy Software Products. |
| * |
| * 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 |
| * missing or damaged, see the license at "http://www.cups.org/". |
| * |
| * This file is subject to the Apple OS-Developed Software exception. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include "cups-private.h" |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #if defined(WIN32) || defined(__EMX__) |
| # include <io.h> |
| #else |
| # include <unistd.h> |
| #endif /* WIN32 || __EMX__ */ |
| |
| |
| /* |
| * Enumeration data and callback... |
| */ |
| |
| typedef struct _cups_createdata_s |
| { |
| const char *name; /* Destination name */ |
| cups_dest_t *dest; /* Matching destination */ |
| } _cups_createdata_t; |
| |
| static int cups_create_cb(_cups_createdata_t *data, unsigned flags, cups_dest_t *dest); |
| |
| |
| /* |
| * 'cupsCancelJob()' - Cancel a print job on the default server. |
| * |
| * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@ |
| * to cancel the current job on the named destination. |
| * |
| * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get |
| * the cause of any failure. |
| * |
| * @exclude all@ |
| */ |
| |
| int /* O - 1 on success, 0 on failure */ |
| cupsCancelJob(const char *name, /* I - Name of printer or class */ |
| int job_id) /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */ |
| { |
| return (cupsCancelJob2(CUPS_HTTP_DEFAULT, name, job_id, 0) |
| < IPP_STATUS_REDIRECTION_OTHER_SITE); |
| } |
| |
| |
| /* |
| * 'cupsCancelJob2()' - Cancel or purge a print job. |
| * |
| * Canceled jobs remain in the job history while purged jobs are removed |
| * from the job history. |
| * |
| * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@ |
| * to cancel the current job on the named destination. |
| * |
| * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get |
| * the cause of any failure. |
| * |
| * @since CUPS 1.4/macOS 10.6@ @exclude all@ |
| */ |
| |
| ipp_status_t /* O - IPP status */ |
| cupsCancelJob2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
| const char *name, /* I - Name of printer or class */ |
| int job_id, /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */ |
| int purge) /* I - 1 to purge, 0 to cancel */ |
| { |
| char uri[HTTP_MAX_URI]; /* Job/printer URI */ |
| ipp_t *request; /* IPP request */ |
| |
| |
| /* |
| * Range check input... |
| */ |
| |
| if (job_id < -1 || (!name && job_id == 0)) |
| { |
| _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
| return (0); |
| } |
| |
| /* |
| * Connect to the default server as needed... |
| */ |
| |
| if (!http) |
| if ((http = _cupsConnect()) == NULL) |
| return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE); |
| |
| /* |
| * Build an IPP_CANCEL_JOB or IPP_PURGE_JOBS request, which requires the following |
| * attributes: |
| * |
| * attributes-charset |
| * attributes-natural-language |
| * job-uri or printer-uri + job-id |
| * requesting-user-name |
| * [purge-job] or [purge-jobs] |
| */ |
| |
| request = ippNewRequest(job_id < 0 ? IPP_OP_PURGE_JOBS : IPP_OP_CANCEL_JOB); |
| |
| if (name) |
| { |
| httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, |
| "localhost", ippPort(), "/printers/%s", name); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, |
| uri); |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", |
| job_id); |
| } |
| else if (job_id > 0) |
| { |
| snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); |
| } |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", |
| NULL, cupsUser()); |
| |
| if (purge && job_id >= 0) |
| ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1); |
| else if (!purge && job_id < 0) |
| ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 0); |
| |
| /* |
| * Do the request... |
| */ |
| |
| ippDelete(cupsDoRequest(http, request, "/jobs/")); |
| |
| return (cupsLastError()); |
| } |
| |
| |
| /* |
| * 'cupsCreateJob()' - Create an empty job for streaming. |
| * |
| * Use this function when you want to stream print data using the |
| * @link cupsStartDocument@, @link cupsWriteRequestData@, and |
| * @link cupsFinishDocument@ functions. If you have one or more files to |
| * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function |
| * instead. |
| * |
| * @since CUPS 1.4/macOS 10.6@ @exclude all@ |
| */ |
| |
| int /* O - Job ID or 0 on error */ |
| cupsCreateJob( |
| http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
| const char *name, /* I - Destination name */ |
| const char *title, /* I - Title of job */ |
| int num_options, /* I - Number of options */ |
| cups_option_t *options) /* I - Options */ |
| { |
| int job_id = 0; /* job-id value */ |
| ipp_status_t status; /* Create-Job status */ |
| _cups_createdata_t data; /* Enumeration data */ |
| cups_dinfo_t *info; /* Destination information */ |
| |
| |
| DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", num_options=%d, options=%p)", (void *)http, name, title, num_options, (void *)options)); |
| |
| /* |
| * Range check input... |
| */ |
| |
| if (!name) |
| { |
| _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
| return (0); |
| } |
| |
| /* |
| * Lookup the destination... |
| */ |
| |
| data.name = name; |
| data.dest = NULL; |
| |
| cupsEnumDests(0, 1000, NULL, 0, 0, (cups_dest_cb_t)cups_create_cb, &data); |
| |
| if (!data.dest) |
| { |
| DEBUG_puts("1cupsCreateJob: Destination not found."); |
| _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0); |
| return (0); |
| } |
| |
| /* |
| * Query dest information and create the job... |
| */ |
| |
| DEBUG_puts("1cupsCreateJob: Querying destination info."); |
| if ((info = cupsCopyDestInfo(http, data.dest)) == NULL) |
| { |
| DEBUG_puts("1cupsCreateJob: Query failed."); |
| cupsFreeDests(1, data.dest); |
| return (0); |
| } |
| |
| status = cupsCreateDestJob(http, data.dest, info, &job_id, title, num_options, options); |
| DEBUG_printf(("1cupsCreateJob: cupsCreateDestJob returned %04x (%s)", status, ippErrorString(status))); |
| |
| cupsFreeDestInfo(info); |
| cupsFreeDests(1, data.dest); |
| |
| /* |
| * Return the job... |
| */ |
| |
| if (status >= IPP_STATUS_REDIRECTION_OTHER_SITE) |
| return (0); |
| else |
| return (job_id); |
| } |
| |
| |
| /* |
| * 'cupsFinishDocument()' - Finish sending a document. |
| * |
| * The document must have been started using @link cupsStartDocument@. |
| * |
| * @since CUPS 1.4/macOS 10.6@ @exclude all@ |
| */ |
| |
| ipp_status_t /* O - Status of document submission */ |
| cupsFinishDocument(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
| const char *name) /* I - Destination name */ |
| { |
| char resource[1024]; /* Printer resource */ |
| |
| |
| snprintf(resource, sizeof(resource), "/printers/%s", name); |
| |
| ippDelete(cupsGetResponse(http, resource)); |
| |
| return (cupsLastError()); |
| } |
| |
| |
| /* |
| * 'cupsFreeJobs()' - Free memory used by job data. |
| */ |
| |
| void |
| cupsFreeJobs(int num_jobs, /* I - Number of jobs */ |
| cups_job_t *jobs) /* I - Jobs */ |
| { |
| int i; /* Looping var */ |
| cups_job_t *job; /* Current job */ |
| |
| |
| if (num_jobs <= 0 || !jobs) |
| return; |
| |
| for (i = num_jobs, job = jobs; i > 0; i --, job ++) |
| { |
| _cupsStrFree(job->dest); |
| _cupsStrFree(job->user); |
| _cupsStrFree(job->format); |
| _cupsStrFree(job->title); |
| } |
| |
| free(jobs); |
| } |
| |
| |
| /* |
| * 'cupsGetClasses()' - Get a list of printer classes from the default server. |
| * |
| * This function is deprecated and no longer returns a list of printer |
| * classes - use @link cupsGetDests@ instead. |
| * |
| * @deprecated@ @exclude all@ |
| */ |
| |
| int /* O - Number of classes */ |
| cupsGetClasses(char ***classes) /* O - Classes */ |
| { |
| if (classes) |
| *classes = NULL; |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'cupsGetDefault()' - Get the default printer or class for the default server. |
| * |
| * This function returns the default printer or class as defined by |
| * the LPDEST or PRINTER environment variables. If these environment |
| * variables are not set, the server default destination is returned. |
| * Applications should use the @link cupsGetDests@ and @link cupsGetDest@ |
| * functions to get the user-defined default printer, as this function does |
| * not support the lpoptions-defined default printer. |
| * |
| * @exclude all@ |
| */ |
| |
| const char * /* O - Default printer or @code NULL@ */ |
| cupsGetDefault(void) |
| { |
| /* |
| * Return the default printer... |
| */ |
| |
| return (cupsGetDefault2(CUPS_HTTP_DEFAULT)); |
| } |
| |
| |
| /* |
| * 'cupsGetDefault2()' - Get the default printer or class for the specified server. |
| * |
| * This function returns the default printer or class as defined by |
| * the LPDEST or PRINTER environment variables. If these environment |
| * variables are not set, the server default destination is returned. |
| * Applications should use the @link cupsGetDests@ and @link cupsGetDest@ |
| * functions to get the user-defined default printer, as this function does |
| * not support the lpoptions-defined default printer. |
| * |
| * @since CUPS 1.1.21/macOS 10.4@ @exclude all@ |
| */ |
| |
| const char * /* O - Default printer or @code NULL@ */ |
| cupsGetDefault2(http_t *http) /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
| { |
| ipp_t *request, /* IPP Request */ |
| *response; /* IPP Response */ |
| ipp_attribute_t *attr; /* Current attribute */ |
| _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ |
| |
| |
| /* |
| * See if we have a user default printer set... |
| */ |
| |
| if (_cupsUserDefault(cg->def_printer, sizeof(cg->def_printer))) |
| return (cg->def_printer); |
| |
| /* |
| * Connect to the server as needed... |
| */ |
| |
| if (!http) |
| if ((http = _cupsConnect()) == NULL) |
| return (NULL); |
| |
| /* |
| * Build a CUPS_GET_DEFAULT request, which requires the following |
| * attributes: |
| * |
| * attributes-charset |
| * attributes-natural-language |
| */ |
| |
| request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT); |
| |
| /* |
| * Do the request and get back a response... |
| */ |
| |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) |
| { |
| if ((attr = ippFindAttribute(response, "printer-name", |
| IPP_TAG_NAME)) != NULL) |
| { |
| strlcpy(cg->def_printer, attr->values[0].string.text, |
| sizeof(cg->def_printer)); |
| ippDelete(response); |
| return (cg->def_printer); |
| } |
| |
| ippDelete(response); |
| } |
| |
| return (NULL); |
| } |
| |
| |
| /* |
| * 'cupsGetJobs()' - Get the jobs from the default server. |
| * |
| * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless |
| * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are |
| * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns |
| * jobs that are stopped, canceled, aborted, or completed. |
| * |
| * @exclude all@ |
| */ |
| |
| int /* O - Number of jobs */ |
| cupsGetJobs(cups_job_t **jobs, /* O - Job data */ |
| const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */ |
| int myjobs, /* I - 0 = all users, 1 = mine */ |
| int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */ |
| { |
| /* |
| * Return the jobs... |
| */ |
| |
| return (cupsGetJobs2(CUPS_HTTP_DEFAULT, jobs, name, myjobs, whichjobs)); |
| } |
| |
| |
| |
| /* |
| * 'cupsGetJobs2()' - Get the jobs from the specified server. |
| * |
| * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless |
| * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are |
| * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns |
| * jobs that are stopped, canceled, aborted, or completed. |
| * |
| * @since CUPS 1.1.21/macOS 10.4@ |
| */ |
| |
| int /* O - Number of jobs */ |
| cupsGetJobs2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
| cups_job_t **jobs, /* O - Job data */ |
| const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */ |
| int myjobs, /* I - 0 = all users, 1 = mine */ |
| int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */ |
| { |
| int n; /* Number of jobs */ |
| ipp_t *request, /* IPP Request */ |
| *response; /* IPP Response */ |
| ipp_attribute_t *attr; /* Current attribute */ |
| cups_job_t *temp; /* Temporary pointer */ |
| int id, /* job-id */ |
| priority, /* job-priority */ |
| size; /* job-k-octets */ |
| ipp_jstate_t state; /* job-state */ |
| time_t completed_time, /* time-at-completed */ |
| creation_time, /* time-at-creation */ |
| processing_time; /* time-at-processing */ |
| const char *dest, /* job-printer-uri */ |
| *format, /* document-format */ |
| *title, /* job-name */ |
| *user; /* job-originating-user-name */ |
| char uri[HTTP_MAX_URI]; /* URI for jobs */ |
| _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ |
| static const char * const attrs[] = /* Requested attributes */ |
| { |
| "document-format", |
| "job-id", |
| "job-k-octets", |
| "job-name", |
| "job-originating-user-name", |
| "job-printer-uri", |
| "job-priority", |
| "job-state", |
| "time-at-completed", |
| "time-at-creation", |
| "time-at-processing" |
| }; |
| |
| |
| /* |
| * Range check input... |
| */ |
| |
| if (!jobs) |
| { |
| _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
| |
| return (-1); |
| } |
| |
| /* |
| * Get the right URI... |
| */ |
| |
| if (name) |
| { |
| if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, |
| "localhost", 0, "/printers/%s", |
| name) < HTTP_URI_STATUS_OK) |
| { |
| _cupsSetError(IPP_STATUS_ERROR_INTERNAL, |
| _("Unable to create printer-uri"), 1); |
| |
| return (-1); |
| } |
| } |
| else |
| strlcpy(uri, "ipp://localhost/", sizeof(uri)); |
| |
| if (!http) |
| if ((http = _cupsConnect()) == NULL) |
| return (-1); |
| |
| /* |
| * Build an IPP_GET_JOBS request, which requires the following |
| * attributes: |
| * |
| * attributes-charset |
| * attributes-natural-language |
| * printer-uri |
| * requesting-user-name |
| * which-jobs |
| * my-jobs |
| * requested-attributes |
| */ |
| |
| request = ippNewRequest(IPP_OP_GET_JOBS); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, |
| "printer-uri", NULL, uri); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, |
| "requesting-user-name", NULL, cupsUser()); |
| |
| if (myjobs) |
| ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); |
| |
| if (whichjobs == CUPS_WHICHJOBS_COMPLETED) |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
| "which-jobs", NULL, "completed"); |
| else if (whichjobs == CUPS_WHICHJOBS_ALL) |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
| "which-jobs", NULL, "all"); |
| |
| ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
| "requested-attributes", sizeof(attrs) / sizeof(attrs[0]), |
| NULL, attrs); |
| |
| /* |
| * Do the request and get back a response... |
| */ |
| |
| n = 0; |
| *jobs = NULL; |
| |
| if ((response = cupsDoRequest(http, request, "/")) != NULL) |
| { |
| for (attr = response->attrs; attr; attr = attr->next) |
| { |
| /* |
| * Skip leading attributes until we hit a job... |
| */ |
| |
| while (attr && attr->group_tag != IPP_TAG_JOB) |
| attr = attr->next; |
| |
| if (!attr) |
| break; |
| |
| /* |
| * Pull the needed attributes from this job... |
| */ |
| |
| id = 0; |
| size = 0; |
| priority = 50; |
| state = IPP_JSTATE_PENDING; |
| user = "unknown"; |
| dest = NULL; |
| format = "application/octet-stream"; |
| title = "untitled"; |
| creation_time = 0; |
| completed_time = 0; |
| processing_time = 0; |
| |
| while (attr && attr->group_tag == IPP_TAG_JOB) |
| { |
| if (!strcmp(attr->name, "job-id") && |
| attr->value_tag == IPP_TAG_INTEGER) |
| id = attr->values[0].integer; |
| else if (!strcmp(attr->name, "job-state") && |
| attr->value_tag == IPP_TAG_ENUM) |
| state = (ipp_jstate_t)attr->values[0].integer; |
| else if (!strcmp(attr->name, "job-priority") && |
| attr->value_tag == IPP_TAG_INTEGER) |
| priority = attr->values[0].integer; |
| else if (!strcmp(attr->name, "job-k-octets") && |
| attr->value_tag == IPP_TAG_INTEGER) |
| size = attr->values[0].integer; |
| else if (!strcmp(attr->name, "time-at-completed") && |
| attr->value_tag == IPP_TAG_INTEGER) |
| completed_time = attr->values[0].integer; |
| else if (!strcmp(attr->name, "time-at-creation") && |
| attr->value_tag == IPP_TAG_INTEGER) |
| creation_time = attr->values[0].integer; |
| else if (!strcmp(attr->name, "time-at-processing") && |
| attr->value_tag == IPP_TAG_INTEGER) |
| processing_time = attr->values[0].integer; |
| else if (!strcmp(attr->name, "job-printer-uri") && |
| attr->value_tag == IPP_TAG_URI) |
| { |
| if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL) |
| dest ++; |
| } |
| else if (!strcmp(attr->name, "job-originating-user-name") && |
| attr->value_tag == IPP_TAG_NAME) |
| user = attr->values[0].string.text; |
| else if (!strcmp(attr->name, "document-format") && |
| attr->value_tag == IPP_TAG_MIMETYPE) |
| format = attr->values[0].string.text; |
| else if (!strcmp(attr->name, "job-name") && |
| (attr->value_tag == IPP_TAG_TEXT || |
| attr->value_tag == IPP_TAG_NAME)) |
| title = attr->values[0].string.text; |
| |
| attr = attr->next; |
| } |
| |
| /* |
| * See if we have everything needed... |
| */ |
| |
| if (!dest || !id) |
| { |
| if (!attr) |
| break; |
| else |
| continue; |
| } |
| |
| /* |
| * Allocate memory for the job... |
| */ |
| |
| if (n == 0) |
| temp = malloc(sizeof(cups_job_t)); |
| else |
| temp = realloc(*jobs, sizeof(cups_job_t) * (size_t)(n + 1)); |
| |
| if (!temp) |
| { |
| /* |
| * Ran out of memory! |
| */ |
| |
| _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0); |
| |
| cupsFreeJobs(n, *jobs); |
| *jobs = NULL; |
| |
| ippDelete(response); |
| |
| return (-1); |
| } |
| |
| *jobs = temp; |
| temp += n; |
| n ++; |
| |
| /* |
| * Copy the data over... |
| */ |
| |
| temp->dest = _cupsStrAlloc(dest); |
| temp->user = _cupsStrAlloc(user); |
| temp->format = _cupsStrAlloc(format); |
| temp->title = _cupsStrAlloc(title); |
| temp->id = id; |
| temp->priority = priority; |
| temp->state = state; |
| temp->size = size; |
| temp->completed_time = completed_time; |
| temp->creation_time = creation_time; |
| temp->processing_time = processing_time; |
| |
| if (!attr) |
| break; |
| } |
| |
| ippDelete(response); |
| } |
| |
| if (n == 0 && cg->last_error >= IPP_STATUS_ERROR_BAD_REQUEST) |
| return (-1); |
| else |
| return (n); |
| } |
| |
| |
| /* |
| * 'cupsGetPrinters()' - Get a list of printers from the default server. |
| * |
| * This function is deprecated and no longer returns a list of printers - use |
| * @link cupsGetDests@ instead. |
| * |
| * @deprecated@ @exclude all@ |
| */ |
| |
| int /* O - Number of printers */ |
| cupsGetPrinters(char ***printers) /* O - Printers */ |
| { |
| if (printers) |
| *printers = NULL; |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'cupsPrintFile()' - Print a file to a printer or class on the default server. |
| * |
| * @exclude all@ |
| */ |
| |
| int /* O - Job ID or 0 on error */ |
| cupsPrintFile(const char *name, /* I - Destination name */ |
| const char *filename, /* I - File to print */ |
| const char *title, /* I - Title of job */ |
| int num_options,/* I - Number of options */ |
| cups_option_t *options) /* I - Options */ |
| { |
| DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", title=\"%s\", num_options=%d, options=%p)", name, filename, title, num_options, (void *)options)); |
| |
| return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title, |
| num_options, options)); |
| } |
| |
| |
| /* |
| * 'cupsPrintFile2()' - Print a file to a printer or class on the specified |
| * server. |
| * |
| * @since CUPS 1.1.21/macOS 10.4@ @exclude all@ |
| */ |
| |
| int /* O - Job ID or 0 on error */ |
| cupsPrintFile2( |
| http_t *http, /* I - Connection to server */ |
| const char *name, /* I - Destination name */ |
| const char *filename, /* I - File to print */ |
| const char *title, /* I - Title of job */ |
| int num_options, /* I - Number of options */ |
| cups_option_t *options) /* I - Options */ |
| { |
| DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", title=\"%s\", num_options=%d, options=%p)", (void *)http, name, filename, title, num_options, (void *)options)); |
| |
| return (cupsPrintFiles2(http, name, 1, &filename, title, num_options, |
| options)); |
| } |
| |
| |
| /* |
| * 'cupsPrintFiles()' - Print one or more files to a printer or class on the |
| * default server. |
| * |
| * @exclude all@ |
| */ |
| |
| int /* O - Job ID or 0 on error */ |
| cupsPrintFiles( |
| const char *name, /* I - Destination name */ |
| int num_files, /* I - Number of files */ |
| const char **files, /* I - File(s) to print */ |
| const char *title, /* I - Title of job */ |
| int num_options, /* I - Number of options */ |
| cups_option_t *options) /* I - Options */ |
| { |
| DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, files=%p, title=\"%s\", num_options=%d, options=%p)", name, num_files, (void *)files, title, num_options, (void *)options)); |
| |
| /* |
| * Print the file(s)... |
| */ |
| |
| return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title, |
| num_options, options)); |
| } |
| |
| |
| /* |
| * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the |
| * specified server. |
| * |
| * @since CUPS 1.1.21/macOS 10.4@ @exclude all@ |
| */ |
| |
| int /* O - Job ID or 0 on error */ |
| cupsPrintFiles2( |
| http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
| const char *name, /* I - Destination name */ |
| int num_files, /* I - Number of files */ |
| const char **files, /* I - File(s) to print */ |
| const char *title, /* I - Title of job */ |
| int num_options, /* I - Number of options */ |
| cups_option_t *options) /* I - Options */ |
| { |
| int i; /* Looping var */ |
| int job_id; /* New job ID */ |
| const char *docname; /* Basename of current filename */ |
| const char *format; /* Document format */ |
| cups_file_t *fp; /* Current file */ |
| char buffer[8192]; /* Copy buffer */ |
| ssize_t bytes; /* Bytes in buffer */ |
| http_status_t status; /* Status of write */ |
| _cups_globals_t *cg = _cupsGlobals(); /* Global data */ |
| ipp_status_t cancel_status; /* Status code to preserve */ |
| char *cancel_message; /* Error message to preserve */ |
| |
| |
| DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, files=%p, title=\"%s\", num_options=%d, options=%p)", (void *)http, name, num_files, (void *)files, title, num_options, (void *)options)); |
| |
| /* |
| * Range check input... |
| */ |
| |
| if (!name || num_files < 1 || !files) |
| { |
| _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
| |
| return (0); |
| } |
| |
| /* |
| * Create the print job... |
| */ |
| |
| if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0) |
| return (0); |
| |
| /* |
| * Send each of the files... |
| */ |
| |
| if (cupsGetOption("raw", num_options, options)) |
| format = CUPS_FORMAT_RAW; |
| else if ((format = cupsGetOption("document-format", num_options, |
| options)) == NULL) |
| format = CUPS_FORMAT_AUTO; |
| |
| for (i = 0; i < num_files; i ++) |
| { |
| /* |
| * Start the next file... |
| */ |
| |
| if ((docname = strrchr(files[i], '/')) != NULL) |
| docname ++; |
| else |
| docname = files[i]; |
| |
| if ((fp = cupsFileOpen(files[i], "rb")) == NULL) |
| { |
| /* |
| * Unable to open print file, cancel the job and return... |
| */ |
| |
| _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_ACCESS, NULL, 0); |
| goto cancel_job; |
| } |
| |
| status = cupsStartDocument(http, name, job_id, docname, format, |
| i == (num_files - 1)); |
| |
| while (status == HTTP_STATUS_CONTINUE && |
| (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0) |
| status = cupsWriteRequestData(http, buffer, (size_t)bytes); |
| |
| cupsFileClose(fp); |
| |
| if (status != HTTP_STATUS_CONTINUE || cupsFinishDocument(http, name) != IPP_STATUS_OK) |
| { |
| /* |
| * Unable to queue, cancel the job and return... |
| */ |
| |
| goto cancel_job; |
| } |
| } |
| |
| return (job_id); |
| |
| /* |
| * If we get here, something happened while sending the print job so we need |
| * to cancel the job without setting the last error (since we need to preserve |
| * the current error... |
| */ |
| |
| cancel_job: |
| |
| cancel_status = cg->last_error; |
| cancel_message = cg->last_status_message ? |
| _cupsStrRetain(cg->last_status_message) : NULL; |
| |
| cupsCancelJob2(http, name, job_id, 0); |
| |
| cg->last_error = cancel_status; |
| cg->last_status_message = cancel_message; |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob(). |
| * |
| * Use @link cupsWriteRequestData@ to write data for the document and |
| * @link cupsFinishDocument@ to finish the document and get the submission status. |
| * |
| * The MIME type constants @code CUPS_FORMAT_AUTO@, @code CUPS_FORMAT_PDF@, |
| * @code CUPS_FORMAT_POSTSCRIPT@, @code CUPS_FORMAT_RAW@, and |
| * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although |
| * any supported MIME type string can be supplied. |
| * |
| * @since CUPS 1.4/macOS 10.6@ @exclude all@ |
| */ |
| |
| http_status_t /* O - HTTP status of request */ |
| cupsStartDocument( |
| http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
| const char *name, /* I - Destination name */ |
| int job_id, /* I - Job ID from @link cupsCreateJob@ */ |
| const char *docname, /* I - Name of document */ |
| const char *format, /* I - MIME type or @code CUPS_FORMAT_foo@ */ |
| int last_document) /* I - 1 for last document in job, 0 otherwise */ |
| { |
| char resource[1024], /* Resource for destinatio */ |
| printer_uri[1024]; /* Printer URI */ |
| ipp_t *request; /* Send-Document request */ |
| http_status_t status; /* HTTP status */ |
| |
| |
| /* |
| * Create a Send-Document request... |
| */ |
| |
| if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL) |
| { |
| _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0); |
| return (HTTP_STATUS_ERROR); |
| } |
| |
| httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", |
| NULL, "localhost", ippPort(), "/printers/%s", name); |
| snprintf(resource, sizeof(resource), "/printers/%s", name); |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", |
| NULL, printer_uri); |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", |
| NULL, cupsUser()); |
| if (docname) |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", |
| NULL, docname); |
| if (format) |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, |
| "document-format", NULL, format); |
| ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last_document); |
| |
| /* |
| * Send and delete the request, then return the status... |
| */ |
| |
| status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE); |
| |
| ippDelete(request); |
| |
| return (status); |
| } |
| |
| |
| /* |
| * 'cups_create_cb()' - Find the destination for printing. |
| */ |
| |
| static int /* O - 0 on match */ |
| cups_create_cb( |
| _cups_createdata_t *data, /* I - Data from cupsCreateJob call */ |
| unsigned flags, /* I - Enumeration flags */ |
| cups_dest_t *dest) /* I - Destination */ |
| { |
| DEBUG_printf(("2cups_create_cb(data=%p(%s), flags=%08x, dest=%p(%s))", (void *)data, data->name, flags, (void *)dest, dest->name)); |
| |
| (void)flags; |
| |
| if (dest->instance || strcasecmp(data->name, dest->name)) |
| return (1); |
| |
| cupsCopyDest(dest, 0, &data->dest); |
| |
| return (0); |
| } |