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
34ipp_status_t                            /* O - Status of cancel operation */
35cupsCancelDestJob(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
72ipp_status_t				/* O - IPP status code */
73cupsCloseDestJob(
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
163ipp_status_t				/* O - IPP status code */
164cupsCreateDestJob(
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
260ipp_status_t				/* O - Status of document submission */
261cupsFinishDestDocument(
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
312http_status_t				/* O - Status of document creation */
313cupsStartDestDocument(
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