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 */ 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 */ 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 */ 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 */ 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 */ 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