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