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