1 /* 2 * Printing utilities for CUPS. 3 * 4 * Copyright 2007-2015 by Apple Inc. 5 * Copyright 1997-2006 by Easy Software Products. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16 /* 17 * Include necessary headers... 18 */ 19 20 #include "cups-private.h" 21 #include <fcntl.h> 22 #include <sys/stat.h> 23 #if defined(WIN32) || defined(__EMX__) 24 # include <io.h> 25 #else 26 # include <unistd.h> 27 #endif /* WIN32 || __EMX__ */ 28 29 30 /* 31 * 'cupsCancelJob()' - Cancel a print job on the default server. 32 * 33 * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@ 34 * to cancel the current job on the named destination. 35 * 36 * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get 37 * the cause of any failure. 38 */ 39 40 int /* O - 1 on success, 0 on failure */ 41 cupsCancelJob(const char *name, /* I - Name of printer or class */ 42 int job_id) /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */ 43 { 44 return (cupsCancelJob2(CUPS_HTTP_DEFAULT, name, job_id, 0) 45 < IPP_STATUS_REDIRECTION_OTHER_SITE); 46 } 47 48 49 /* 50 * 'cupsCancelJob2()' - Cancel or purge a print job. 51 * 52 * Canceled jobs remain in the job history while purged jobs are removed 53 * from the job history. 54 * 55 * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@ 56 * to cancel the current job on the named destination. 57 * 58 * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get 59 * the cause of any failure. 60 * 61 * @since CUPS 1.4/macOS 10.6@ 62 */ 63 64 ipp_status_t /* O - IPP status */ 65 cupsCancelJob2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 66 const char *name, /* I - Name of printer or class */ 67 int job_id, /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */ 68 int purge) /* I - 1 to purge, 0 to cancel */ 69 { 70 char uri[HTTP_MAX_URI]; /* Job/printer URI */ 71 ipp_t *request; /* IPP request */ 72 73 74 /* 75 * Range check input... 76 */ 77 78 if (job_id < -1 || (!name && job_id == 0)) 79 { 80 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 81 return (0); 82 } 83 84 /* 85 * Connect to the default server as needed... 86 */ 87 88 if (!http) 89 if ((http = _cupsConnect()) == NULL) 90 return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE); 91 92 /* 93 * Build an IPP_CANCEL_JOB or IPP_PURGE_JOBS request, which requires the following 94 * attributes: 95 * 96 * attributes-charset 97 * attributes-natural-language 98 * job-uri or printer-uri + job-id 99 * requesting-user-name 100 * [purge-job] or [purge-jobs] 101 */ 102 103 request = ippNewRequest(job_id < 0 ? IPP_OP_PURGE_JOBS : IPP_OP_CANCEL_JOB); 104 105 if (name) 106 { 107 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 108 "localhost", ippPort(), "/printers/%s", name); 109 110 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, 111 uri); 112 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", 113 job_id); 114 } 115 else if (job_id > 0) 116 { 117 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id); 118 119 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); 120 } 121 122 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 123 NULL, cupsUser()); 124 125 if (purge && job_id >= 0) 126 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1); 127 else if (!purge && job_id < 0) 128 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 0); 129 130 /* 131 * Do the request... 132 */ 133 134 ippDelete(cupsDoRequest(http, request, "/jobs/")); 135 136 return (cupsLastError()); 137 } 138 139 140 /* 141 * 'cupsCreateJob()' - Create an empty job for streaming. 142 * 143 * Use this function when you want to stream print data using the 144 * @link cupsStartDocument@, @link cupsWriteRequestData@, and 145 * @link cupsFinishDocument@ functions. If you have one or more files to 146 * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function 147 * instead. 148 * 149 * @since CUPS 1.4/macOS 10.6@ 150 */ 151 152 int /* O - Job ID or 0 on error */ 153 cupsCreateJob( 154 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 155 const char *name, /* I - Destination name */ 156 const char *title, /* I - Title of job */ 157 int num_options, /* I - Number of options */ 158 cups_option_t *options) /* I - Options */ 159 { 160 char printer_uri[1024], /* Printer URI */ 161 resource[1024]; /* Printer resource */ 162 ipp_t *request, /* Create-Job request */ 163 *response; /* Create-Job response */ 164 ipp_attribute_t *attr; /* job-id attribute */ 165 int job_id = 0; /* job-id value */ 166 167 168 DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", num_options=%d, options=%p)", (void *)http, name, title, num_options, (void *)options)); 169 170 /* 171 * Range check input... 172 */ 173 174 if (!name) 175 { 176 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 177 return (0); 178 } 179 180 /* 181 * Build a Create-Job request... 182 */ 183 184 if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL) 185 { 186 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0); 187 return (0); 188 } 189 190 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", 191 NULL, "localhost", ippPort(), "/printers/%s", name); 192 snprintf(resource, sizeof(resource), "/printers/%s", name); 193 194 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 195 NULL, printer_uri); 196 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 197 NULL, cupsUser()); 198 if (title) 199 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, 200 title); 201 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION); 202 cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB); 203 cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION); 204 205 /* 206 * Send the request and get the job-id... 207 */ 208 209 response = cupsDoRequest(http, request, resource); 210 211 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL) 212 job_id = attr->values[0].integer; 213 214 ippDelete(response); 215 216 /* 217 * Return it... 218 */ 219 220 return (job_id); 221 } 222 223 224 /* 225 * 'cupsFinishDocument()' - Finish sending a document. 226 * 227 * The document must have been started using @link cupsStartDocument@. 228 * 229 * @since CUPS 1.4/macOS 10.6@ 230 */ 231 232 ipp_status_t /* O - Status of document submission */ 233 cupsFinishDocument(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 234 const char *name) /* I - Destination name */ 235 { 236 char resource[1024]; /* Printer resource */ 237 238 239 snprintf(resource, sizeof(resource), "/printers/%s", name); 240 241 ippDelete(cupsGetResponse(http, resource)); 242 243 return (cupsLastError()); 244 } 245 246 247 /* 248 * 'cupsFreeJobs()' - Free memory used by job data. 249 */ 250 251 void 252 cupsFreeJobs(int num_jobs, /* I - Number of jobs */ 253 cups_job_t *jobs) /* I - Jobs */ 254 { 255 int i; /* Looping var */ 256 cups_job_t *job; /* Current job */ 257 258 259 if (num_jobs <= 0 || !jobs) 260 return; 261 262 for (i = num_jobs, job = jobs; i > 0; i --, job ++) 263 { 264 _cupsStrFree(job->dest); 265 _cupsStrFree(job->user); 266 _cupsStrFree(job->format); 267 _cupsStrFree(job->title); 268 } 269 270 free(jobs); 271 } 272 273 274 /* 275 * 'cupsGetClasses()' - Get a list of printer classes from the default server. 276 * 277 * This function is deprecated and no longer returns a list of printer 278 * classes - use @link cupsGetDests@ instead. 279 * 280 * @deprecated@ 281 */ 282 283 int /* O - Number of classes */ 284 cupsGetClasses(char ***classes) /* O - Classes */ 285 { 286 if (classes) 287 *classes = NULL; 288 289 return (0); 290 } 291 292 293 /* 294 * 'cupsGetDefault()' - Get the default printer or class for the default server. 295 * 296 * This function returns the default printer or class as defined by 297 * the LPDEST or PRINTER environment variables. If these environment 298 * variables are not set, the server default destination is returned. 299 * Applications should use the @link cupsGetDests@ and @link cupsGetDest@ 300 * functions to get the user-defined default printer, as this function does 301 * not support the lpoptions-defined default printer. 302 */ 303 304 const char * /* O - Default printer or @code NULL@ */ 305 cupsGetDefault(void) 306 { 307 /* 308 * Return the default printer... 309 */ 310 311 return (cupsGetDefault2(CUPS_HTTP_DEFAULT)); 312 } 313 314 315 /* 316 * 'cupsGetDefault2()' - Get the default printer or class for the specified server. 317 * 318 * This function returns the default printer or class as defined by 319 * the LPDEST or PRINTER environment variables. If these environment 320 * variables are not set, the server default destination is returned. 321 * Applications should use the @link cupsGetDests@ and @link cupsGetDest@ 322 * functions to get the user-defined default printer, as this function does 323 * not support the lpoptions-defined default printer. 324 * 325 * @since CUPS 1.1.21/macOS 10.4@ 326 */ 327 328 const char * /* O - Default printer or @code NULL@ */ 329 cupsGetDefault2(http_t *http) /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 330 { 331 ipp_t *request, /* IPP Request */ 332 *response; /* IPP Response */ 333 ipp_attribute_t *attr; /* Current attribute */ 334 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 335 336 337 /* 338 * See if we have a user default printer set... 339 */ 340 341 if (_cupsUserDefault(cg->def_printer, sizeof(cg->def_printer))) 342 return (cg->def_printer); 343 344 /* 345 * Connect to the server as needed... 346 */ 347 348 if (!http) 349 if ((http = _cupsConnect()) == NULL) 350 return (NULL); 351 352 /* 353 * Build a CUPS_GET_DEFAULT request, which requires the following 354 * attributes: 355 * 356 * attributes-charset 357 * attributes-natural-language 358 */ 359 360 request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT); 361 362 /* 363 * Do the request and get back a response... 364 */ 365 366 if ((response = cupsDoRequest(http, request, "/")) != NULL) 367 { 368 if ((attr = ippFindAttribute(response, "printer-name", 369 IPP_TAG_NAME)) != NULL) 370 { 371 strlcpy(cg->def_printer, attr->values[0].string.text, 372 sizeof(cg->def_printer)); 373 ippDelete(response); 374 return (cg->def_printer); 375 } 376 377 ippDelete(response); 378 } 379 380 return (NULL); 381 } 382 383 384 /* 385 * 'cupsGetJobs()' - Get the jobs from the default server. 386 * 387 * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless 388 * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are 389 * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns 390 * jobs that are stopped, canceled, aborted, or completed. 391 */ 392 393 int /* O - Number of jobs */ 394 cupsGetJobs(cups_job_t **jobs, /* O - Job data */ 395 const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */ 396 int myjobs, /* I - 0 = all users, 1 = mine */ 397 int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */ 398 { 399 /* 400 * Return the jobs... 401 */ 402 403 return (cupsGetJobs2(CUPS_HTTP_DEFAULT, jobs, name, myjobs, whichjobs)); 404 } 405 406 407 408 /* 409 * 'cupsGetJobs2()' - Get the jobs from the specified server. 410 * 411 * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless 412 * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are 413 * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns 414 * jobs that are stopped, canceled, aborted, or completed. 415 * 416 * @since CUPS 1.1.21/macOS 10.4@ 417 */ 418 419 int /* O - Number of jobs */ 420 cupsGetJobs2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 421 cups_job_t **jobs, /* O - Job data */ 422 const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */ 423 int myjobs, /* I - 0 = all users, 1 = mine */ 424 int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */ 425 { 426 int n; /* Number of jobs */ 427 ipp_t *request, /* IPP Request */ 428 *response; /* IPP Response */ 429 ipp_attribute_t *attr; /* Current attribute */ 430 cups_job_t *temp; /* Temporary pointer */ 431 int id, /* job-id */ 432 priority, /* job-priority */ 433 size; /* job-k-octets */ 434 ipp_jstate_t state; /* job-state */ 435 time_t completed_time, /* time-at-completed */ 436 creation_time, /* time-at-creation */ 437 processing_time; /* time-at-processing */ 438 const char *dest, /* job-printer-uri */ 439 *format, /* document-format */ 440 *title, /* job-name */ 441 *user; /* job-originating-user-name */ 442 char uri[HTTP_MAX_URI]; /* URI for jobs */ 443 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 444 static const char * const attrs[] = /* Requested attributes */ 445 { 446 "document-format", 447 "job-id", 448 "job-k-octets", 449 "job-name", 450 "job-originating-user-name", 451 "job-printer-uri", 452 "job-priority", 453 "job-state", 454 "time-at-completed", 455 "time-at-creation", 456 "time-at-processing" 457 }; 458 459 460 /* 461 * Range check input... 462 */ 463 464 if (!jobs) 465 { 466 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 467 468 return (-1); 469 } 470 471 /* 472 * Get the right URI... 473 */ 474 475 if (name) 476 { 477 if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 478 "localhost", 0, "/printers/%s", 479 name) < HTTP_URI_STATUS_OK) 480 { 481 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, 482 _("Unable to create printer-uri"), 1); 483 484 return (-1); 485 } 486 } 487 else 488 strlcpy(uri, "ipp://localhost/", sizeof(uri)); 489 490 if (!http) 491 if ((http = _cupsConnect()) == NULL) 492 return (-1); 493 494 /* 495 * Build an IPP_GET_JOBS request, which requires the following 496 * attributes: 497 * 498 * attributes-charset 499 * attributes-natural-language 500 * printer-uri 501 * requesting-user-name 502 * which-jobs 503 * my-jobs 504 * requested-attributes 505 */ 506 507 request = ippNewRequest(IPP_OP_GET_JOBS); 508 509 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, 510 "printer-uri", NULL, uri); 511 512 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, 513 "requesting-user-name", NULL, cupsUser()); 514 515 if (myjobs) 516 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); 517 518 if (whichjobs == CUPS_WHICHJOBS_COMPLETED) 519 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 520 "which-jobs", NULL, "completed"); 521 else if (whichjobs == CUPS_WHICHJOBS_ALL) 522 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 523 "which-jobs", NULL, "all"); 524 525 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 526 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]), 527 NULL, attrs); 528 529 /* 530 * Do the request and get back a response... 531 */ 532 533 n = 0; 534 *jobs = NULL; 535 536 if ((response = cupsDoRequest(http, request, "/")) != NULL) 537 { 538 for (attr = response->attrs; attr; attr = attr->next) 539 { 540 /* 541 * Skip leading attributes until we hit a job... 542 */ 543 544 while (attr && attr->group_tag != IPP_TAG_JOB) 545 attr = attr->next; 546 547 if (!attr) 548 break; 549 550 /* 551 * Pull the needed attributes from this job... 552 */ 553 554 id = 0; 555 size = 0; 556 priority = 50; 557 state = IPP_JSTATE_PENDING; 558 user = "unknown"; 559 dest = NULL; 560 format = "application/octet-stream"; 561 title = "untitled"; 562 creation_time = 0; 563 completed_time = 0; 564 processing_time = 0; 565 566 while (attr && attr->group_tag == IPP_TAG_JOB) 567 { 568 if (!strcmp(attr->name, "job-id") && 569 attr->value_tag == IPP_TAG_INTEGER) 570 id = attr->values[0].integer; 571 else if (!strcmp(attr->name, "job-state") && 572 attr->value_tag == IPP_TAG_ENUM) 573 state = (ipp_jstate_t)attr->values[0].integer; 574 else if (!strcmp(attr->name, "job-priority") && 575 attr->value_tag == IPP_TAG_INTEGER) 576 priority = attr->values[0].integer; 577 else if (!strcmp(attr->name, "job-k-octets") && 578 attr->value_tag == IPP_TAG_INTEGER) 579 size = attr->values[0].integer; 580 else if (!strcmp(attr->name, "time-at-completed") && 581 attr->value_tag == IPP_TAG_INTEGER) 582 completed_time = attr->values[0].integer; 583 else if (!strcmp(attr->name, "time-at-creation") && 584 attr->value_tag == IPP_TAG_INTEGER) 585 creation_time = attr->values[0].integer; 586 else if (!strcmp(attr->name, "time-at-processing") && 587 attr->value_tag == IPP_TAG_INTEGER) 588 processing_time = attr->values[0].integer; 589 else if (!strcmp(attr->name, "job-printer-uri") && 590 attr->value_tag == IPP_TAG_URI) 591 { 592 if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL) 593 dest ++; 594 } 595 else if (!strcmp(attr->name, "job-originating-user-name") && 596 attr->value_tag == IPP_TAG_NAME) 597 user = attr->values[0].string.text; 598 else if (!strcmp(attr->name, "document-format") && 599 attr->value_tag == IPP_TAG_MIMETYPE) 600 format = attr->values[0].string.text; 601 else if (!strcmp(attr->name, "job-name") && 602 (attr->value_tag == IPP_TAG_TEXT || 603 attr->value_tag == IPP_TAG_NAME)) 604 title = attr->values[0].string.text; 605 606 attr = attr->next; 607 } 608 609 /* 610 * See if we have everything needed... 611 */ 612 613 if (!dest || !id) 614 { 615 if (!attr) 616 break; 617 else 618 continue; 619 } 620 621 /* 622 * Allocate memory for the job... 623 */ 624 625 if (n == 0) 626 temp = malloc(sizeof(cups_job_t)); 627 else 628 temp = realloc(*jobs, sizeof(cups_job_t) * (size_t)(n + 1)); 629 630 if (!temp) 631 { 632 /* 633 * Ran out of memory! 634 */ 635 636 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0); 637 638 cupsFreeJobs(n, *jobs); 639 *jobs = NULL; 640 641 ippDelete(response); 642 643 return (-1); 644 } 645 646 *jobs = temp; 647 temp += n; 648 n ++; 649 650 /* 651 * Copy the data over... 652 */ 653 654 temp->dest = _cupsStrAlloc(dest); 655 temp->user = _cupsStrAlloc(user); 656 temp->format = _cupsStrAlloc(format); 657 temp->title = _cupsStrAlloc(title); 658 temp->id = id; 659 temp->priority = priority; 660 temp->state = state; 661 temp->size = size; 662 temp->completed_time = completed_time; 663 temp->creation_time = creation_time; 664 temp->processing_time = processing_time; 665 666 if (!attr) 667 break; 668 } 669 670 ippDelete(response); 671 } 672 673 if (n == 0 && cg->last_error >= IPP_STATUS_ERROR_BAD_REQUEST) 674 return (-1); 675 else 676 return (n); 677 } 678 679 680 /* 681 * 'cupsGetPrinters()' - Get a list of printers from the default server. 682 * 683 * This function is deprecated and no longer returns a list of printers - use 684 * @link cupsGetDests@ instead. 685 * 686 * @deprecated@ 687 */ 688 689 int /* O - Number of printers */ 690 cupsGetPrinters(char ***printers) /* O - Printers */ 691 { 692 if (printers) 693 *printers = NULL; 694 695 return (0); 696 } 697 698 699 /* 700 * 'cupsPrintFile()' - Print a file to a printer or class on the default server. 701 */ 702 703 int /* O - Job ID or 0 on error */ 704 cupsPrintFile(const char *name, /* I - Destination name */ 705 const char *filename, /* I - File to print */ 706 const char *title, /* I - Title of job */ 707 int num_options,/* I - Number of options */ 708 cups_option_t *options) /* I - Options */ 709 { 710 DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", title=\"%s\", num_options=%d, options=%p)", name, filename, title, num_options, (void *)options)); 711 712 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title, 713 num_options, options)); 714 } 715 716 717 /* 718 * 'cupsPrintFile2()' - Print a file to a printer or class on the specified 719 * server. 720 * 721 * @since CUPS 1.1.21/macOS 10.4@ 722 */ 723 724 int /* O - Job ID or 0 on error */ 725 cupsPrintFile2( 726 http_t *http, /* I - Connection to server */ 727 const char *name, /* I - Destination name */ 728 const char *filename, /* I - File to print */ 729 const char *title, /* I - Title of job */ 730 int num_options, /* I - Number of options */ 731 cups_option_t *options) /* I - Options */ 732 { 733 DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", title=\"%s\", num_options=%d, options=%p)", (void *)http, name, filename, title, num_options, (void *)options)); 734 735 return (cupsPrintFiles2(http, name, 1, &filename, title, num_options, 736 options)); 737 } 738 739 740 /* 741 * 'cupsPrintFiles()' - Print one or more files to a printer or class on the 742 * default server. 743 */ 744 745 int /* O - Job ID or 0 on error */ 746 cupsPrintFiles( 747 const char *name, /* I - Destination name */ 748 int num_files, /* I - Number of files */ 749 const char **files, /* I - File(s) to print */ 750 const char *title, /* I - Title of job */ 751 int num_options, /* I - Number of options */ 752 cups_option_t *options) /* I - Options */ 753 { 754 DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, files=%p, title=\"%s\", num_options=%d, options=%p)", name, num_files, (void *)files, title, num_options, (void *)options)); 755 756 /* 757 * Print the file(s)... 758 */ 759 760 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title, 761 num_options, options)); 762 } 763 764 765 /* 766 * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the 767 * specified server. 768 * 769 * @since CUPS 1.1.21/macOS 10.4@ 770 */ 771 772 int /* O - Job ID or 0 on error */ 773 cupsPrintFiles2( 774 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 775 const char *name, /* I - Destination name */ 776 int num_files, /* I - Number of files */ 777 const char **files, /* I - File(s) to print */ 778 const char *title, /* I - Title of job */ 779 int num_options, /* I - Number of options */ 780 cups_option_t *options) /* I - Options */ 781 { 782 int i; /* Looping var */ 783 int job_id; /* New job ID */ 784 const char *docname; /* Basename of current filename */ 785 const char *format; /* Document format */ 786 cups_file_t *fp; /* Current file */ 787 char buffer[8192]; /* Copy buffer */ 788 ssize_t bytes; /* Bytes in buffer */ 789 http_status_t status; /* Status of write */ 790 _cups_globals_t *cg = _cupsGlobals(); /* Global data */ 791 ipp_status_t cancel_status; /* Status code to preserve */ 792 char *cancel_message; /* Error message to preserve */ 793 794 795 DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, files=%p, title=\"%s\", num_options=%d, options=%p)", (void *)http, name, num_files, (void *)files, title, num_options, (void *)options)); 796 797 /* 798 * Range check input... 799 */ 800 801 if (!name || num_files < 1 || !files) 802 { 803 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 804 805 return (0); 806 } 807 808 /* 809 * Create the print job... 810 */ 811 812 if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0) 813 return (0); 814 815 /* 816 * Send each of the files... 817 */ 818 819 if (cupsGetOption("raw", num_options, options)) 820 format = CUPS_FORMAT_RAW; 821 else if ((format = cupsGetOption("document-format", num_options, 822 options)) == NULL) 823 format = CUPS_FORMAT_AUTO; 824 825 for (i = 0; i < num_files; i ++) 826 { 827 /* 828 * Start the next file... 829 */ 830 831 if ((docname = strrchr(files[i], '/')) != NULL) 832 docname ++; 833 else 834 docname = files[i]; 835 836 if ((fp = cupsFileOpen(files[i], "rb")) == NULL) 837 { 838 /* 839 * Unable to open print file, cancel the job and return... 840 */ 841 842 _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_ACCESS, NULL, 0); 843 goto cancel_job; 844 } 845 846 status = cupsStartDocument(http, name, job_id, docname, format, 847 i == (num_files - 1)); 848 849 while (status == HTTP_STATUS_CONTINUE && 850 (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0) 851 status = cupsWriteRequestData(http, buffer, (size_t)bytes); 852 853 cupsFileClose(fp); 854 855 if (status != HTTP_STATUS_CONTINUE || cupsFinishDocument(http, name) != IPP_STATUS_OK) 856 { 857 /* 858 * Unable to queue, cancel the job and return... 859 */ 860 861 goto cancel_job; 862 } 863 } 864 865 return (job_id); 866 867 /* 868 * If we get here, something happened while sending the print job so we need 869 * to cancel the job without setting the last error (since we need to preserve 870 * the current error... 871 */ 872 873 cancel_job: 874 875 cancel_status = cg->last_error; 876 cancel_message = cg->last_status_message ? 877 _cupsStrRetain(cg->last_status_message) : NULL; 878 879 cupsCancelJob2(http, name, job_id, 0); 880 881 cg->last_error = cancel_status; 882 cg->last_status_message = cancel_message; 883 884 return (0); 885 } 886 887 888 /* 889 * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob(). 890 * 891 * Use @link cupsWriteRequestData@ to write data for the document and 892 * @link cupsFinishDocument@ to finish the document and get the submission status. 893 * 894 * The MIME type constants @code CUPS_FORMAT_AUTO@, @code CUPS_FORMAT_PDF@, 895 * @code CUPS_FORMAT_POSTSCRIPT@, @code CUPS_FORMAT_RAW@, and 896 * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although 897 * any supported MIME type string can be supplied. 898 * 899 * @since CUPS 1.4/macOS 10.6@ 900 */ 901 902 http_status_t /* O - HTTP status of request */ 903 cupsStartDocument( 904 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 905 const char *name, /* I - Destination name */ 906 int job_id, /* I - Job ID from @link cupsCreateJob@ */ 907 const char *docname, /* I - Name of document */ 908 const char *format, /* I - MIME type or @code CUPS_FORMAT_foo@ */ 909 int last_document) /* I - 1 for last document in job, 0 otherwise */ 910 { 911 char resource[1024], /* Resource for destinatio */ 912 printer_uri[1024]; /* Printer URI */ 913 ipp_t *request; /* Send-Document request */ 914 http_status_t status; /* HTTP status */ 915 916 917 /* 918 * Create a Send-Document request... 919 */ 920 921 if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL) 922 { 923 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0); 924 return (HTTP_STATUS_ERROR); 925 } 926 927 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", 928 NULL, "localhost", ippPort(), "/printers/%s", name); 929 snprintf(resource, sizeof(resource), "/printers/%s", name); 930 931 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 932 NULL, printer_uri); 933 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); 934 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 935 NULL, cupsUser()); 936 if (docname) 937 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", 938 NULL, docname); 939 if (format) 940 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, 941 "document-format", NULL, format); 942 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last_document); 943 944 /* 945 * Send and delete the request, then return the status... 946 */ 947 948 status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE); 949 950 ippDelete(request); 951 952 return (status); 953 } 954