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