1 /*
2  * Destination job support for CUPS.
3  *
4  * Copyright 2012-2017 by Apple Inc.
5  *
6  * These coded instructions, statements, and computer programs are the
7  * property of Apple Inc. and are protected by Federal copyright
8  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
9  * which should have been included with this file.  If this file is
10  * missing or damaged, see the license at "http://www.cups.org/".
11  *
12  * This file is subject to the Apple OS-Developed Software exception.
13  */
14 
15 /*
16  * Include necessary headers...
17  */
18 
19 #include "cups-private.h"
20 
21 
22 /*
23  * 'cupsCancelDestJob()' - Cancel a job on a destination.
24  *
25  * The "job_id" is the number returned by cupsCreateDestJob.
26  *
27  * Returns @code IPP_STATUS_OK@ on success and
28  * @code IPP_STATUS_ERROR_NOT_AUTHORIZED@ or
29  * @code IPP_STATUS_ERROR_FORBIDDEN@ on failure.
30  *
31  * @since CUPS 1.6/macOS 10.8@
32  */
33 
34 ipp_status_t                            /* O - Status of cancel operation */
cupsCancelDestJob(http_t * http,cups_dest_t * dest,int job_id)35 cupsCancelDestJob(http_t      *http,	/* I - Connection to destination */
36                   cups_dest_t *dest,	/* I - Destination */
37                   int         job_id)	/* I - Job ID */
38 {
39   cups_dinfo_t	*info;			/* Destination information */
40 
41 
42   if ((info = cupsCopyDestInfo(http, dest)) != NULL)
43   {
44     ipp_t	*request;		/* Cancel-Job request */
45 
46     request = ippNewRequest(IPP_OP_CANCEL_JOB);
47 
48     ippSetVersion(request, info->version / 10, info->version % 10);
49 
50     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, info->uri);
51     ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
52     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
53 
54     ippDelete(cupsDoRequest(http, request, info->resource));
55     cupsFreeDestInfo(info);
56   }
57 
58   return (cupsLastError());
59 }
60 
61 
62 /*
63  * 'cupsCloseDestJob()' - Close a job and start printing.
64  *
65  * Use when the last call to cupsStartDocument passed 0 for "last_document".
66  * "job_id" is the job ID returned by cupsCreateDestJob. Returns @code IPP_STATUS_OK@
67  * on success.
68  *
69  * @since CUPS 1.6/macOS 10.8@
70  */
71 
72 ipp_status_t				/* O - IPP status code */
cupsCloseDestJob(http_t * http,cups_dest_t * dest,cups_dinfo_t * info,int job_id)73 cupsCloseDestJob(
74     http_t       *http,			/* I - Connection to destination */
75     cups_dest_t  *dest,			/* I - Destination */
76     cups_dinfo_t *info, 		/* I - Destination information */
77     int          job_id)		/* I - Job ID */
78 {
79   int			i;		/* Looping var */
80   ipp_t			*request = NULL;/* Close-Job/Send-Document request */
81   ipp_attribute_t	*attr;		/* operations-supported attribute */
82 
83 
84   DEBUG_printf(("cupsCloseDestJob(http=%p, dest=%p(%s/%s), info=%p, job_id=%d)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, job_id));
85 
86  /*
87   * Get the default connection as needed...
88   */
89 
90   if (!http)
91     http = _cupsConnect();
92 
93  /*
94   * Range check input...
95   */
96 
97   if (!http || !dest || !info || job_id <= 0)
98   {
99     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
100     DEBUG_puts("1cupsCloseDestJob: Bad arguments.");
101     return (IPP_STATUS_ERROR_INTERNAL);
102   }
103 
104  /*
105   * Build a Close-Job or empty Send-Document request...
106   */
107 
108   if ((attr = ippFindAttribute(info->attrs, "operations-supported",
109                                IPP_TAG_ENUM)) != NULL)
110   {
111     for (i = 0; i < attr->num_values; i ++)
112       if (attr->values[i].integer == IPP_OP_CLOSE_JOB)
113       {
114         request = ippNewRequest(IPP_OP_CLOSE_JOB);
115         break;
116       }
117   }
118 
119   if (!request)
120     request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
121 
122   if (!request)
123   {
124     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
125     DEBUG_puts("1cupsCloseDestJob: Unable to create Close-Job/Send-Document "
126                "request.");
127     return (IPP_STATUS_ERROR_INTERNAL);
128   }
129 
130   ippSetVersion(request, info->version / 10, info->version % 10);
131 
132   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
133                NULL, info->uri);
134   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
135                 job_id);
136   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
137                NULL, cupsUser());
138   if (ippGetOperation(request) == IPP_OP_SEND_DOCUMENT)
139     ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
140 
141  /*
142   * Send the request and return the status...
143   */
144 
145   ippDelete(cupsDoRequest(http, request, info->resource));
146 
147   DEBUG_printf(("1cupsCloseDestJob: %s (%s)", ippErrorString(cupsLastError()),
148                 cupsLastErrorString()));
149 
150   return (cupsLastError());
151 }
152 
153 
154 /*
155  * 'cupsCreateDestJob()' - Create a job on a destination.
156  *
157  * Returns @code IPP_STATUS_OK@ or @code IPP_STATUS_OK_SUBST@ on success, saving the job ID
158  * in the variable pointed to by "job_id".
159  *
160  * @since CUPS 1.6/macOS 10.8@
161  */
162 
163 ipp_status_t				/* O - IPP status code */
cupsCreateDestJob(http_t * http,cups_dest_t * dest,cups_dinfo_t * info,int * job_id,const char * title,int num_options,cups_option_t * options)164 cupsCreateDestJob(
165     http_t        *http,		/* I - Connection to destination */
166     cups_dest_t   *dest,		/* I - Destination */
167     cups_dinfo_t  *info, 		/* I - Destination information */
168     int           *job_id,		/* O - Job ID or 0 on error */
169     const char    *title,		/* I - Job name */
170     int           num_options,		/* I - Number of job options */
171     cups_option_t *options)		/* I - Job options */
172 {
173   ipp_t			*request,	/* Create-Job request */
174 			*response;	/* Create-Job response */
175   ipp_attribute_t	*attr;		/* job-id attribute */
176 
177 
178   DEBUG_printf(("cupsCreateDestJob(http=%p, dest=%p(%s/%s), info=%p, "
179                 "job_id=%p, title=\"%s\", num_options=%d, options=%p)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, (void *)job_id, title, num_options, (void *)options));
180 
181  /*
182   * Get the default connection as needed...
183   */
184 
185   if (!http)
186     http = _cupsConnect();
187 
188  /*
189   * Range check input...
190   */
191 
192   if (job_id)
193     *job_id = 0;
194 
195   if (!http || !dest || !info || !job_id)
196   {
197     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
198     DEBUG_puts("1cupsCreateDestJob: Bad arguments.");
199     return (IPP_STATUS_ERROR_INTERNAL);
200   }
201 
202  /*
203   * Build a Create-Job request...
204   */
205 
206   if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
207   {
208     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
209     DEBUG_puts("1cupsCreateDestJob: Unable to create Create-Job request.");
210     return (IPP_STATUS_ERROR_INTERNAL);
211   }
212 
213   ippSetVersion(request, info->version / 10, info->version % 10);
214 
215   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
216                NULL, info->uri);
217   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
218                NULL, cupsUser());
219   if (title)
220     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
221                  title);
222 
223   cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
224   cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
225   cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
226 
227  /*
228   * Send the request and get the job-id...
229   */
230 
231   response = cupsDoRequest(http, request, info->resource);
232 
233   if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
234   {
235     *job_id = attr->values[0].integer;
236     DEBUG_printf(("1cupsCreateDestJob: job-id=%d", *job_id));
237   }
238 
239   ippDelete(response);
240 
241  /*
242   * Return the status code from the Create-Job request...
243   */
244 
245   DEBUG_printf(("1cupsCreateDestJob: %s (%s)", ippErrorString(cupsLastError()),
246                 cupsLastErrorString()));
247 
248   return (cupsLastError());
249 }
250 
251 
252 /*
253  * 'cupsFinishDestDocument()' - Finish the current document.
254  *
255  * Returns @code IPP_STATUS_OK@ or @code IPP_STATUS_OK_SUBST@ on success.
256  *
257  * @since CUPS 1.6/macOS 10.8@
258  */
259 
260 ipp_status_t				/* O - Status of document submission */
cupsFinishDestDocument(http_t * http,cups_dest_t * dest,cups_dinfo_t * info)261 cupsFinishDestDocument(
262     http_t       *http,			/* I - Connection to destination */
263     cups_dest_t  *dest,			/* I - Destination */
264     cups_dinfo_t *info) 		/* I - Destination information */
265 {
266   DEBUG_printf(("cupsFinishDestDocument(http=%p, dest=%p(%s/%s), info=%p)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info));
267 
268  /*
269   * Get the default connection as needed...
270   */
271 
272   if (!http)
273     http = _cupsConnect();
274 
275  /*
276   * Range check input...
277   */
278 
279   if (!http || !dest || !info)
280   {
281     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
282     DEBUG_puts("1cupsFinishDestDocument: Bad arguments.");
283     return (IPP_STATUS_ERROR_INTERNAL);
284   }
285 
286  /*
287   * Get the response at the end of the document and return it...
288   */
289 
290   ippDelete(cupsGetResponse(http, info->resource));
291 
292   DEBUG_printf(("1cupsFinishDestDocument: %s (%s)",
293                 ippErrorString(cupsLastError()), cupsLastErrorString()));
294 
295   return (cupsLastError());
296 }
297 
298 
299 /*
300  * 'cupsStartDestDocument()' - Start a new document.
301  *
302  * "job_id" is the job ID returned by cupsCreateDestJob.  "docname" is the name
303  * of the document/file being printed, "format" is the MIME media type for the
304  * document (see CUPS_FORMAT_xxx constants), and "num_options" and "options"
305  * are the options do be applied to the document. "last_document" should be 1
306  * if this is the last document to be submitted in the job.  Returns
307  * @code HTTP_CONTINUE@ on success.
308  *
309  * @since CUPS 1.6/macOS 10.8@
310  */
311 
312 http_status_t				/* O - Status of document creation */
cupsStartDestDocument(http_t * http,cups_dest_t * dest,cups_dinfo_t * info,int job_id,const char * docname,const char * format,int num_options,cups_option_t * options,int last_document)313 cupsStartDestDocument(
314     http_t        *http,		/* I - Connection to destination */
315     cups_dest_t   *dest,		/* I - Destination */
316     cups_dinfo_t  *info, 		/* I - Destination information */
317     int           job_id,		/* I - Job ID */
318     const char    *docname,		/* I - Document name */
319     const char    *format,		/* I - Document format */
320     int           num_options,		/* I - Number of document options */
321     cups_option_t *options,		/* I - Document options */
322     int           last_document)	/* I - 1 if this is the last document */
323 {
324   ipp_t		*request;		/* Send-Document request */
325   http_status_t	status;			/* HTTP status */
326 
327 
328   DEBUG_printf(("cupsStartDestDocument(http=%p, dest=%p(%s/%s), info=%p, job_id=%d, docname=\"%s\", format=\"%s\", num_options=%d, options=%p, last_document=%d)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, job_id, docname, format, num_options, (void *)options, last_document));
329 
330  /*
331   * Get the default connection as needed...
332   */
333 
334   if (!http)
335     http = _cupsConnect();
336 
337  /*
338   * Range check input...
339   */
340 
341   if (!http || !dest || !info || job_id <= 0)
342   {
343     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
344     DEBUG_puts("1cupsStartDestDocument: Bad arguments.");
345     return (HTTP_STATUS_ERROR);
346   }
347 
348  /*
349   * Create a Send-Document request...
350   */
351 
352   if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL)
353   {
354     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
355     DEBUG_puts("1cupsStartDestDocument: Unable to create Send-Document "
356                "request.");
357     return (HTTP_STATUS_ERROR);
358   }
359 
360   ippSetVersion(request, info->version / 10, info->version % 10);
361 
362   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
363                NULL, info->uri);
364   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
365   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
366                NULL, cupsUser());
367   if (docname)
368     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
369                  NULL, docname);
370   if (format)
371     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
372                  "document-format", NULL, format);
373   ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last_document);
374 
375   cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
376   cupsEncodeOptions2(request, num_options, options, IPP_TAG_DOCUMENT);
377 
378  /*
379   * Send and delete the request, then return the status...
380   */
381 
382   status = cupsSendRequest(http, request, info->resource, CUPS_LENGTH_VARIABLE);
383 
384   ippDelete(request);
385 
386   return (status);
387 }
388