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