1 /*
2  * Destination job support for CUPS.
3  *
4  * Copyright 2012-2016 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_ERRPR_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
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   * Range check input...
88   */
89 
90   if (!http || !dest || !info || job_id <= 0)
91   {
92     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
93     DEBUG_puts("1cupsCloseDestJob: Bad arguments.");
94     return (IPP_STATUS_ERROR_INTERNAL);
95   }
96 
97  /*
98   * Build a Close-Job or empty Send-Document request...
99   */
100 
101   if ((attr = ippFindAttribute(info->attrs, "operations-supported",
102                                IPP_TAG_ENUM)) != NULL)
103   {
104     for (i = 0; i < attr->num_values; i ++)
105       if (attr->values[i].integer == IPP_OP_CLOSE_JOB)
106       {
107         request = ippNewRequest(IPP_OP_CLOSE_JOB);
108         break;
109       }
110   }
111 
112   if (!request)
113     request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
114 
115   if (!request)
116   {
117     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
118     DEBUG_puts("1cupsCloseDestJob: Unable to create Close-Job/Send-Document "
119                "request.");
120     return (IPP_STATUS_ERROR_INTERNAL);
121   }
122 
123   ippSetVersion(request, info->version / 10, info->version % 10);
124 
125   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
126                NULL, info->uri);
127   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
128                 job_id);
129   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
130                NULL, cupsUser());
131   if (ippGetOperation(request) == IPP_OP_SEND_DOCUMENT)
132     ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
133 
134  /*
135   * Send the request and return the status...
136   */
137 
138   ippDelete(cupsDoRequest(http, request, info->resource));
139 
140   DEBUG_printf(("1cupsCloseDestJob: %s (%s)", ippErrorString(cupsLastError()),
141                 cupsLastErrorString()));
142 
143   return (cupsLastError());
144 }
145 
146 
147 /*
148  * 'cupsCreateDestJob()' - Create a job on a destination.
149  *
150  * Returns @code IPP_STATUS_OK@ or @code IPP_STATUS_OK_SUBST@ on success, saving the job ID
151  * in the variable pointed to by "job_id".
152  *
153  * @since CUPS 1.6/macOS 10.8@
154  */
155 
156 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)157 cupsCreateDestJob(
158     http_t        *http,		/* I - Connection to destination */
159     cups_dest_t   *dest,		/* I - Destination */
160     cups_dinfo_t  *info, 		/* I - Destination information */
161     int           *job_id,		/* O - Job ID or 0 on error */
162     const char    *title,		/* I - Job name */
163     int           num_options,		/* I - Number of job options */
164     cups_option_t *options)		/* I - Job options */
165 {
166   ipp_t			*request,	/* Create-Job request */
167 			*response;	/* Create-Job response */
168   ipp_attribute_t	*attr;		/* job-id attribute */
169 
170 
171   DEBUG_printf(("cupsCreateDestJob(http=%p, dest=%p(%s/%s), info=%p, "
172                 "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));
173 
174  /*
175   * Range check input...
176   */
177 
178   if (job_id)
179     *job_id = 0;
180 
181   if (!http || !dest || !info || !job_id)
182   {
183     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
184     DEBUG_puts("1cupsCreateDestJob: Bad arguments.");
185     return (IPP_STATUS_ERROR_INTERNAL);
186   }
187 
188  /*
189   * Build a Create-Job request...
190   */
191 
192   if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
193   {
194     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
195     DEBUG_puts("1cupsCreateDestJob: Unable to create Create-Job request.");
196     return (IPP_STATUS_ERROR_INTERNAL);
197   }
198 
199   ippSetVersion(request, info->version / 10, info->version % 10);
200 
201   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
202                NULL, info->uri);
203   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
204                NULL, cupsUser());
205   if (title)
206     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
207                  title);
208 
209   cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
210   cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
211   cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
212 
213  /*
214   * Send the request and get the job-id...
215   */
216 
217   response = cupsDoRequest(http, request, info->resource);
218 
219   if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
220   {
221     *job_id = attr->values[0].integer;
222     DEBUG_printf(("1cupsCreateDestJob: job-id=%d", *job_id));
223   }
224 
225   ippDelete(response);
226 
227  /*
228   * Return the status code from the Create-Job request...
229   */
230 
231   DEBUG_printf(("1cupsCreateDestJob: %s (%s)", ippErrorString(cupsLastError()),
232                 cupsLastErrorString()));
233 
234   return (cupsLastError());
235 }
236 
237 
238 /*
239  * 'cupsFinishDestDocument()' - Finish the current document.
240  *
241  * Returns @code IPP_STATUS_OK@ or @code IPP_STATUS_OK_SUBST@ on success.
242  *
243  * @since CUPS 1.6/macOS 10.8@
244  */
245 
246 ipp_status_t				/* O - Status of document submission */
cupsFinishDestDocument(http_t * http,cups_dest_t * dest,cups_dinfo_t * info)247 cupsFinishDestDocument(
248     http_t       *http,			/* I - Connection to destination */
249     cups_dest_t  *dest,			/* I - Destination */
250     cups_dinfo_t *info) 		/* I - Destination information */
251 {
252   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));
253 
254  /*
255   * Range check input...
256   */
257 
258   if (!http || !dest || !info)
259   {
260     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
261     DEBUG_puts("1cupsFinishDestDocument: Bad arguments.");
262     return (IPP_STATUS_ERROR_INTERNAL);
263   }
264 
265  /*
266   * Get the response at the end of the document and return it...
267   */
268 
269   ippDelete(cupsGetResponse(http, info->resource));
270 
271   DEBUG_printf(("1cupsFinishDestDocument: %s (%s)",
272                 ippErrorString(cupsLastError()), cupsLastErrorString()));
273 
274   return (cupsLastError());
275 }
276 
277 
278 /*
279  * 'cupsStartDestDocument()' - Start a new document.
280  *
281  * "job_id" is the job ID returned by cupsCreateDestJob.  "docname" is the name
282  * of the document/file being printed, "format" is the MIME media type for the
283  * document (see CUPS_FORMAT_xxx constants), and "num_options" and "options"
284  * are the options do be applied to the document. "last_document" should be 1
285  * if this is the last document to be submitted in the job.  Returns
286  * @code HTTP_CONTINUE@ on success.
287  *
288  * @since CUPS 1.6/macOS 10.8@
289  */
290 
291 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)292 cupsStartDestDocument(
293     http_t        *http,		/* I - Connection to destination */
294     cups_dest_t   *dest,		/* I - Destination */
295     cups_dinfo_t  *info, 		/* I - Destination information */
296     int           job_id,		/* I - Job ID */
297     const char    *docname,		/* I - Document name */
298     const char    *format,		/* I - Document format */
299     int           num_options,		/* I - Number of document options */
300     cups_option_t *options,		/* I - Document options */
301     int           last_document)	/* I - 1 if this is the last document */
302 {
303   ipp_t		*request;		/* Send-Document request */
304   http_status_t	status;			/* HTTP status */
305 
306 
307   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));
308 
309  /*
310   * Range check input...
311   */
312 
313   if (!http || !dest || !info || job_id <= 0)
314   {
315     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
316     DEBUG_puts("1cupsStartDestDocument: Bad arguments.");
317     return (HTTP_STATUS_ERROR);
318   }
319 
320  /*
321   * Create a Send-Document request...
322   */
323 
324   if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL)
325   {
326     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
327     DEBUG_puts("1cupsStartDestDocument: Unable to create Send-Document "
328                "request.");
329     return (HTTP_STATUS_ERROR);
330   }
331 
332   ippSetVersion(request, info->version / 10, info->version % 10);
333 
334   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
335                NULL, info->uri);
336   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
337   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
338                NULL, cupsUser());
339   if (docname)
340     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
341                  NULL, docname);
342   if (format)
343     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
344                  "document-format", NULL, format);
345   ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last_document);
346 
347   cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
348   cupsEncodeOptions2(request, num_options, options, IPP_TAG_DOCUMENT);
349 
350  /*
351   * Send and delete the request, then return the status...
352   */
353 
354   status = cupsSendRequest(http, request, info->resource, CUPS_LENGTH_VARIABLE);
355 
356   ippDelete(request);
357 
358   return (status);
359 }
360