1/* 2 * "$Id: util.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * Printing utilities for CUPS. 5 * 6 * Copyright 2007-2013 by Apple Inc. 7 * Copyright 1997-2006 by Easy Software Products. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 * 15 * This file is subject to the Apple OS-Developed Software exception. 16 * 17 * Contents: 18 * 19 * cupsCancelJob() - Cancel a print job on the default server. 20 * cupsCancelJob2() - Cancel or purge a print job. 21 * cupsCreateJob() - Create an empty job for streaming. 22 * cupsFinishDocument() - Finish sending a document. 23 * cupsFreeJobs() - Free memory used by job data. 24 * cupsGetClasses() - Get a list of printer classes from the default 25 * server. 26 * cupsGetDefault() - Get the default printer or class for the default 27 * server. 28 * cupsGetDefault2() - Get the default printer or class for the specified 29 * server. 30 * cupsGetJobs() - Get the jobs from the default server. 31 * cupsGetJobs2() - Get the jobs from the specified server. 32 * cupsGetPPD() - Get the PPD file for a printer on the default 33 * server. 34 * cupsGetPPD2() - Get the PPD file for a printer from the specified 35 * server. 36 * cupsGetPPD3() - Get the PPD file for a printer on the specified 37 * server if it has changed. 38 * cupsGetPrinters() - Get a list of printers from the default server. 39 * cupsGetServerPPD() - Get an available PPD file from the server. 40 * cupsPrintFile() - Print a file to a printer or class on the default 41 * server. 42 * cupsPrintFile2() - Print a file to a printer or class on the 43 * specified server. 44 * cupsPrintFiles() - Print one or more files to a printer or class on 45 * the default server. 46 * cupsPrintFiles2() - Print one or more files to a printer or class on 47 * the specified server. 48 * cupsStartDocument() - Add a document to a job created with 49 * cupsCreateJob(). 50 * cups_get_printer_uri() - Get the printer-uri-supported attribute for the 51 * first printer in a class. 52 */ 53 54/* 55 * Include necessary headers... 56 */ 57 58#include "cups-private.h" 59#include <fcntl.h> 60#include <sys/stat.h> 61#if defined(WIN32) || defined(__EMX__) 62# include <io.h> 63#else 64# include <unistd.h> 65#endif /* WIN32 || __EMX__ */ 66 67 68/* 69 * Local functions... 70 */ 71 72static int cups_get_printer_uri(http_t *http, const char *name, 73 char *host, int hostsize, int *port, 74 char *resource, int resourcesize, 75 int depth); 76 77 78/* 79 * 'cupsCancelJob()' - Cancel a print job on the default server. 80 * 81 * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@ 82 * to cancel the current job on the named destination. 83 * 84 * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get 85 * the cause of any failure. 86 */ 87 88int /* O - 1 on success, 0 on failure */ 89cupsCancelJob(const char *name, /* I - Name of printer or class */ 90 int job_id) /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */ 91{ 92 return (cupsCancelJob2(CUPS_HTTP_DEFAULT, name, job_id, 0) 93 < IPP_STATUS_REDIRECTION_OTHER_SITE); 94} 95 96 97/* 98 * 'cupsCancelJob2()' - Cancel or purge a print job. 99 * 100 * Canceled jobs remain in the job history while purged jobs are removed 101 * from the job history. 102 * 103 * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@ 104 * to cancel the current job on the named destination. 105 * 106 * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get 107 * the cause of any failure. 108 * 109 * @since CUPS 1.4/OS X 10.6@ 110 */ 111 112ipp_status_t /* O - IPP status */ 113cupsCancelJob2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 114 const char *name, /* I - Name of printer or class */ 115 int job_id, /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */ 116 int purge) /* I - 1 to purge, 0 to cancel */ 117{ 118 char uri[HTTP_MAX_URI]; /* Job/printer URI */ 119 ipp_t *request; /* IPP request */ 120 121 122 /* 123 * Range check input... 124 */ 125 126 if (job_id < -1 || (!name && job_id == 0)) 127 { 128 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 129 return (0); 130 } 131 132 /* 133 * Connect to the default server as needed... 134 */ 135 136 if (!http) 137 if ((http = _cupsConnect()) == NULL) 138 return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE); 139 140 /* 141 * Build an IPP_CANCEL_JOB or IPP_PURGE_JOBS request, which requires the following 142 * attributes: 143 * 144 * attributes-charset 145 * attributes-natural-language 146 * job-uri or printer-uri + job-id 147 * requesting-user-name 148 * [purge-job] or [purge-jobs] 149 */ 150 151 request = ippNewRequest(job_id < 0 ? IPP_OP_PURGE_JOBS : IPP_OP_CANCEL_JOB); 152 153 if (name) 154 { 155 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 156 "localhost", ippPort(), "/printers/%s", name); 157 158 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, 159 uri); 160 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", 161 job_id); 162 } 163 else if (job_id > 0) 164 { 165 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id); 166 167 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); 168 } 169 170 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 171 NULL, cupsUser()); 172 173 if (purge && job_id >= 0) 174 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1); 175 else if (!purge && job_id < 0) 176 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 0); 177 178 /* 179 * Do the request... 180 */ 181 182 ippDelete(cupsDoRequest(http, request, "/jobs/")); 183 184 return (cupsLastError()); 185} 186 187 188/* 189 * 'cupsCreateJob()' - Create an empty job for streaming. 190 * 191 * Use this function when you want to stream print data using the 192 * @link cupsStartDocument@, @link cupsWriteRequestData@, and 193 * @link cupsFinishDocument@ functions. If you have one or more files to 194 * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function 195 * instead. 196 * 197 * @since CUPS 1.4/OS X 10.6@ 198 */ 199 200int /* O - Job ID or 0 on error */ 201cupsCreateJob( 202 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 203 const char *name, /* I - Destination name */ 204 const char *title, /* I - Title of job */ 205 int num_options, /* I - Number of options */ 206 cups_option_t *options) /* I - Options */ 207{ 208 char printer_uri[1024], /* Printer URI */ 209 resource[1024]; /* Printer resource */ 210 ipp_t *request, /* Create-Job request */ 211 *response; /* Create-Job response */ 212 ipp_attribute_t *attr; /* job-id attribute */ 213 int job_id = 0; /* job-id value */ 214 215 216 DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", " 217 "num_options=%d, options=%p)", 218 http, name, title, num_options, options)); 219 220 /* 221 * Range check input... 222 */ 223 224 if (!name) 225 { 226 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 227 return (0); 228 } 229 230 /* 231 * Build a Create-Job request... 232 */ 233 234 if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL) 235 { 236 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0); 237 return (0); 238 } 239 240 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", 241 NULL, "localhost", ippPort(), "/printers/%s", name); 242 snprintf(resource, sizeof(resource), "/printers/%s", name); 243 244 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 245 NULL, printer_uri); 246 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 247 NULL, cupsUser()); 248 if (title) 249 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, 250 title); 251 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION); 252 cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB); 253 cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION); 254 255 /* 256 * Send the request and get the job-id... 257 */ 258 259 response = cupsDoRequest(http, request, resource); 260 261 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL) 262 job_id = attr->values[0].integer; 263 264 ippDelete(response); 265 266 /* 267 * Return it... 268 */ 269 270 return (job_id); 271} 272 273 274/* 275 * 'cupsFinishDocument()' - Finish sending a document. 276 * 277 * The document must have been started using @link cupsStartDocument@. 278 * 279 * @since CUPS 1.4/OS X 10.6@ 280 */ 281 282ipp_status_t /* O - Status of document submission */ 283cupsFinishDocument(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 284 const char *name) /* I - Destination name */ 285{ 286 char resource[1024]; /* Printer resource */ 287 288 289 snprintf(resource, sizeof(resource), "/printers/%s", name); 290 291 ippDelete(cupsGetResponse(http, resource)); 292 293 return (cupsLastError()); 294} 295 296 297/* 298 * 'cupsFreeJobs()' - Free memory used by job data. 299 */ 300 301void 302cupsFreeJobs(int num_jobs, /* I - Number of jobs */ 303 cups_job_t *jobs) /* I - Jobs */ 304{ 305 int i; /* Looping var */ 306 cups_job_t *job; /* Current job */ 307 308 309 if (num_jobs <= 0 || !jobs) 310 return; 311 312 for (i = num_jobs, job = jobs; i > 0; i --, job ++) 313 { 314 _cupsStrFree(job->dest); 315 _cupsStrFree(job->user); 316 _cupsStrFree(job->format); 317 _cupsStrFree(job->title); 318 } 319 320 free(jobs); 321} 322 323 324/* 325 * 'cupsGetClasses()' - Get a list of printer classes from the default server. 326 * 327 * This function is deprecated - use @link cupsGetDests@ instead. 328 * 329 * @deprecated@ 330 */ 331 332int /* O - Number of classes */ 333cupsGetClasses(char ***classes) /* O - Classes */ 334{ 335 int n; /* Number of classes */ 336 ipp_t *request, /* IPP Request */ 337 *response; /* IPP Response */ 338 ipp_attribute_t *attr; /* Current attribute */ 339 char **temp; /* Temporary pointer */ 340 http_t *http; /* Connection to server */ 341 342 343 if (!classes) 344 { 345 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 346 347 return (0); 348 } 349 350 *classes = NULL; 351 352 if ((http = _cupsConnect()) == NULL) 353 return (0); 354 355 /* 356 * Build a CUPS_GET_CLASSES request, which requires the following 357 * attributes: 358 * 359 * attributes-charset 360 * attributes-natural-language 361 * requested-attributes 362 */ 363 364 request = ippNewRequest(IPP_OP_CUPS_GET_CLASSES); 365 366 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 367 "requested-attributes", NULL, "printer-name"); 368 369 /* 370 * Do the request and get back a response... 371 */ 372 373 n = 0; 374 375 if ((response = cupsDoRequest(http, request, "/")) != NULL) 376 { 377 for (attr = response->attrs; attr != NULL; attr = attr->next) 378 if (attr->name != NULL && 379 _cups_strcasecmp(attr->name, "printer-name") == 0 && 380 attr->value_tag == IPP_TAG_NAME) 381 { 382 if (n == 0) 383 temp = malloc(sizeof(char *)); 384 else 385 temp = realloc(*classes, sizeof(char *) * (n + 1)); 386 387 if (temp == NULL) 388 { 389 /* 390 * Ran out of memory! 391 */ 392 393 while (n > 0) 394 { 395 n --; 396 free((*classes)[n]); 397 } 398 399 free(*classes); 400 ippDelete(response); 401 return (0); 402 } 403 404 *classes = temp; 405 temp[n] = strdup(attr->values[0].string.text); 406 n ++; 407 } 408 409 ippDelete(response); 410 } 411 412 return (n); 413} 414 415 416/* 417 * 'cupsGetDefault()' - Get the default printer or class for the default server. 418 * 419 * This function returns the default printer or class as defined by 420 * the LPDEST or PRINTER environment variables. If these environment 421 * variables are not set, the server default destination is returned. 422 * Applications should use the @link cupsGetDests@ and @link cupsGetDest@ 423 * functions to get the user-defined default printer, as this function does 424 * not support the lpoptions-defined default printer. 425 */ 426 427const char * /* O - Default printer or @code NULL@ */ 428cupsGetDefault(void) 429{ 430 /* 431 * Return the default printer... 432 */ 433 434 return (cupsGetDefault2(CUPS_HTTP_DEFAULT)); 435} 436 437 438/* 439 * 'cupsGetDefault2()' - Get the default printer or class for the specified server. 440 * 441 * This function returns the default printer or class as defined by 442 * the LPDEST or PRINTER environment variables. If these environment 443 * variables are not set, the server default destination is returned. 444 * Applications should use the @link cupsGetDests@ and @link cupsGetDest@ 445 * functions to get the user-defined default printer, as this function does 446 * not support the lpoptions-defined default printer. 447 * 448 * @since CUPS 1.1.21/OS X 10.4@ 449 */ 450 451const char * /* O - Default printer or @code NULL@ */ 452cupsGetDefault2(http_t *http) /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 453{ 454 ipp_t *request, /* IPP Request */ 455 *response; /* IPP Response */ 456 ipp_attribute_t *attr; /* Current attribute */ 457 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 458 459 460 /* 461 * See if we have a user default printer set... 462 */ 463 464 if (_cupsUserDefault(cg->def_printer, sizeof(cg->def_printer))) 465 return (cg->def_printer); 466 467 /* 468 * Connect to the server as needed... 469 */ 470 471 if (!http) 472 if ((http = _cupsConnect()) == NULL) 473 return (NULL); 474 475 /* 476 * Build a CUPS_GET_DEFAULT request, which requires the following 477 * attributes: 478 * 479 * attributes-charset 480 * attributes-natural-language 481 */ 482 483 request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT); 484 485 /* 486 * Do the request and get back a response... 487 */ 488 489 if ((response = cupsDoRequest(http, request, "/")) != NULL) 490 { 491 if ((attr = ippFindAttribute(response, "printer-name", 492 IPP_TAG_NAME)) != NULL) 493 { 494 strlcpy(cg->def_printer, attr->values[0].string.text, 495 sizeof(cg->def_printer)); 496 ippDelete(response); 497 return (cg->def_printer); 498 } 499 500 ippDelete(response); 501 } 502 503 return (NULL); 504} 505 506 507/* 508 * 'cupsGetJobs()' - Get the jobs from the default server. 509 * 510 * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless 511 * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are 512 * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns 513 * jobs that are stopped, canceled, aborted, or completed. 514 */ 515 516int /* O - Number of jobs */ 517cupsGetJobs(cups_job_t **jobs, /* O - Job data */ 518 const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */ 519 int myjobs, /* I - 0 = all users, 1 = mine */ 520 int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */ 521{ 522 /* 523 * Return the jobs... 524 */ 525 526 return (cupsGetJobs2(CUPS_HTTP_DEFAULT, jobs, name, myjobs, whichjobs)); 527} 528 529 530 531/* 532 * 'cupsGetJobs2()' - Get the jobs from the specified server. 533 * 534 * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless 535 * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are 536 * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns 537 * jobs that are stopped, canceled, aborted, or completed. 538 * 539 * @since CUPS 1.1.21/OS X 10.4@ 540 */ 541 542int /* O - Number of jobs */ 543cupsGetJobs2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 544 cups_job_t **jobs, /* O - Job data */ 545 const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */ 546 int myjobs, /* I - 0 = all users, 1 = mine */ 547 int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */ 548{ 549 int n; /* Number of jobs */ 550 ipp_t *request, /* IPP Request */ 551 *response; /* IPP Response */ 552 ipp_attribute_t *attr; /* Current attribute */ 553 cups_job_t *temp; /* Temporary pointer */ 554 int id, /* job-id */ 555 priority, /* job-priority */ 556 size; /* job-k-octets */ 557 ipp_jstate_t state; /* job-state */ 558 time_t completed_time, /* time-at-completed */ 559 creation_time, /* time-at-creation */ 560 processing_time; /* time-at-processing */ 561 const char *dest, /* job-printer-uri */ 562 *format, /* document-format */ 563 *title, /* job-name */ 564 *user; /* job-originating-user-name */ 565 char uri[HTTP_MAX_URI]; /* URI for jobs */ 566 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 567 static const char * const attrs[] = /* Requested attributes */ 568 { 569 "document-format", 570 "job-id", 571 "job-k-octets", 572 "job-name", 573 "job-originating-user-name", 574 "job-printer-uri", 575 "job-priority", 576 "job-state", 577 "time-at-completed", 578 "time-at-creation", 579 "time-at-processing" 580 }; 581 582 583 /* 584 * Range check input... 585 */ 586 587 if (!jobs) 588 { 589 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 590 591 return (-1); 592 } 593 594 /* 595 * Get the right URI... 596 */ 597 598 if (name) 599 { 600 if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 601 "localhost", 0, "/printers/%s", 602 name) < HTTP_URI_STATUS_OK) 603 { 604 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, 605 _("Unable to create printer-uri"), 1); 606 607 return (-1); 608 } 609 } 610 else 611 strlcpy(uri, "ipp://localhost/", sizeof(uri)); 612 613 if (!http) 614 if ((http = _cupsConnect()) == NULL) 615 return (-1); 616 617 /* 618 * Build an IPP_GET_JOBS request, which requires the following 619 * attributes: 620 * 621 * attributes-charset 622 * attributes-natural-language 623 * printer-uri 624 * requesting-user-name 625 * which-jobs 626 * my-jobs 627 * requested-attributes 628 */ 629 630 request = ippNewRequest(IPP_OP_GET_JOBS); 631 632 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, 633 "printer-uri", NULL, uri); 634 635 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, 636 "requesting-user-name", NULL, cupsUser()); 637 638 if (myjobs) 639 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); 640 641 if (whichjobs == CUPS_WHICHJOBS_COMPLETED) 642 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 643 "which-jobs", NULL, "completed"); 644 else if (whichjobs == CUPS_WHICHJOBS_ALL) 645 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 646 "which-jobs", NULL, "all"); 647 648 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 649 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]), 650 NULL, attrs); 651 652 /* 653 * Do the request and get back a response... 654 */ 655 656 n = 0; 657 *jobs = NULL; 658 659 if ((response = cupsDoRequest(http, request, "/")) != NULL) 660 { 661 for (attr = response->attrs; attr; attr = attr->next) 662 { 663 /* 664 * Skip leading attributes until we hit a job... 665 */ 666 667 while (attr && attr->group_tag != IPP_TAG_JOB) 668 attr = attr->next; 669 670 if (!attr) 671 break; 672 673 /* 674 * Pull the needed attributes from this job... 675 */ 676 677 id = 0; 678 size = 0; 679 priority = 50; 680 state = IPP_JSTATE_PENDING; 681 user = "unknown"; 682 dest = NULL; 683 format = "application/octet-stream"; 684 title = "untitled"; 685 creation_time = 0; 686 completed_time = 0; 687 processing_time = 0; 688 689 while (attr && attr->group_tag == IPP_TAG_JOB) 690 { 691 if (!strcmp(attr->name, "job-id") && 692 attr->value_tag == IPP_TAG_INTEGER) 693 id = attr->values[0].integer; 694 else if (!strcmp(attr->name, "job-state") && 695 attr->value_tag == IPP_TAG_ENUM) 696 state = (ipp_jstate_t)attr->values[0].integer; 697 else if (!strcmp(attr->name, "job-priority") && 698 attr->value_tag == IPP_TAG_INTEGER) 699 priority = attr->values[0].integer; 700 else if (!strcmp(attr->name, "job-k-octets") && 701 attr->value_tag == IPP_TAG_INTEGER) 702 size = attr->values[0].integer; 703 else if (!strcmp(attr->name, "time-at-completed") && 704 attr->value_tag == IPP_TAG_INTEGER) 705 completed_time = attr->values[0].integer; 706 else if (!strcmp(attr->name, "time-at-creation") && 707 attr->value_tag == IPP_TAG_INTEGER) 708 creation_time = attr->values[0].integer; 709 else if (!strcmp(attr->name, "time-at-processing") && 710 attr->value_tag == IPP_TAG_INTEGER) 711 processing_time = attr->values[0].integer; 712 else if (!strcmp(attr->name, "job-printer-uri") && 713 attr->value_tag == IPP_TAG_URI) 714 { 715 if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL) 716 dest ++; 717 } 718 else if (!strcmp(attr->name, "job-originating-user-name") && 719 attr->value_tag == IPP_TAG_NAME) 720 user = attr->values[0].string.text; 721 else if (!strcmp(attr->name, "document-format") && 722 attr->value_tag == IPP_TAG_MIMETYPE) 723 format = attr->values[0].string.text; 724 else if (!strcmp(attr->name, "job-name") && 725 (attr->value_tag == IPP_TAG_TEXT || 726 attr->value_tag == IPP_TAG_NAME)) 727 title = attr->values[0].string.text; 728 729 attr = attr->next; 730 } 731 732 /* 733 * See if we have everything needed... 734 */ 735 736 if (!dest || !id) 737 { 738 if (!attr) 739 break; 740 else 741 continue; 742 } 743 744 /* 745 * Allocate memory for the job... 746 */ 747 748 if (n == 0) 749 temp = malloc(sizeof(cups_job_t)); 750 else 751 temp = realloc(*jobs, sizeof(cups_job_t) * (n + 1)); 752 753 if (!temp) 754 { 755 /* 756 * Ran out of memory! 757 */ 758 759 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0); 760 761 cupsFreeJobs(n, *jobs); 762 *jobs = NULL; 763 764 ippDelete(response); 765 766 return (-1); 767 } 768 769 *jobs = temp; 770 temp += n; 771 n ++; 772 773 /* 774 * Copy the data over... 775 */ 776 777 temp->dest = _cupsStrAlloc(dest); 778 temp->user = _cupsStrAlloc(user); 779 temp->format = _cupsStrAlloc(format); 780 temp->title = _cupsStrAlloc(title); 781 temp->id = id; 782 temp->priority = priority; 783 temp->state = state; 784 temp->size = size; 785 temp->completed_time = completed_time; 786 temp->creation_time = creation_time; 787 temp->processing_time = processing_time; 788 789 if (!attr) 790 break; 791 } 792 793 ippDelete(response); 794 } 795 796 if (n == 0 && cg->last_error >= IPP_STATUS_ERROR_BAD_REQUEST) 797 return (-1); 798 else 799 return (n); 800} 801 802 803/* 804 * 'cupsGetPPD()' - Get the PPD file for a printer on the default server. 805 * 806 * For classes, @code cupsGetPPD@ returns the PPD file for the first printer 807 * in the class. 808 * 809 * The returned filename is stored in a static buffer and is overwritten with 810 * each call to @code cupsGetPPD@ or @link cupsGetPPD2@. The caller "owns" the 811 * file that is created and must @code unlink@ the returned filename. 812 */ 813 814const char * /* O - Filename for PPD file */ 815cupsGetPPD(const char *name) /* I - Destination name */ 816{ 817 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 818 time_t modtime = 0; /* Modification time */ 819 820 821 /* 822 * Return the PPD file... 823 */ 824 825 cg->ppd_filename[0] = '\0'; 826 827 if (cupsGetPPD3(CUPS_HTTP_DEFAULT, name, &modtime, cg->ppd_filename, 828 sizeof(cg->ppd_filename)) == HTTP_STATUS_OK) 829 return (cg->ppd_filename); 830 else 831 return (NULL); 832} 833 834 835/* 836 * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server. 837 * 838 * For classes, @code cupsGetPPD2@ returns the PPD file for the first printer 839 * in the class. 840 * 841 * The returned filename is stored in a static buffer and is overwritten with 842 * each call to @link cupsGetPPD@ or @code cupsGetPPD2@. The caller "owns" the 843 * file that is created and must @code unlink@ the returned filename. 844 * 845 * @since CUPS 1.1.21/OS X 10.4@ 846 */ 847 848const char * /* O - Filename for PPD file */ 849cupsGetPPD2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 850 const char *name) /* I - Destination name */ 851{ 852 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 853 time_t modtime = 0; /* Modification time */ 854 855 856 cg->ppd_filename[0] = '\0'; 857 858 if (cupsGetPPD3(http, name, &modtime, cg->ppd_filename, 859 sizeof(cg->ppd_filename)) == HTTP_STATUS_OK) 860 return (cg->ppd_filename); 861 else 862 return (NULL); 863} 864 865 866/* 867 * 'cupsGetPPD3()' - Get the PPD file for a printer on the specified 868 * server if it has changed. 869 * 870 * The "modtime" parameter contains the modification time of any 871 * locally-cached content and is updated with the time from the PPD file on 872 * the server. 873 * 874 * The "buffer" parameter contains the local PPD filename. If it contains 875 * the empty string, a new temporary file is created, otherwise the existing 876 * file will be overwritten as needed. The caller "owns" the file that is 877 * created and must @code unlink@ the returned filename. 878 * 879 * On success, @code HTTP_STATUS_OK@ is returned for a new PPD file and 880 * @code HTTP_STATUS_NOT_MODIFIED@ if the existing PPD file is up-to-date. Any other 881 * status is an error. 882 * 883 * For classes, @code cupsGetPPD3@ returns the PPD file for the first printer 884 * in the class. 885 * 886 * @since CUPS 1.4/OS X 10.6@ 887 */ 888 889http_status_t /* O - HTTP status */ 890cupsGetPPD3(http_t *http, /* I - HTTP connection or @code CUPS_HTTP_DEFAULT@ */ 891 const char *name, /* I - Destination name */ 892 time_t *modtime, /* IO - Modification time */ 893 char *buffer, /* I - Filename buffer */ 894 size_t bufsize) /* I - Size of filename buffer */ 895{ 896 int http_port; /* Port number */ 897 char http_hostname[HTTP_MAX_HOST]; 898 /* Hostname associated with connection */ 899 http_t *http2; /* Alternate HTTP connection */ 900 int fd; /* PPD file */ 901 char localhost[HTTP_MAX_URI],/* Local hostname */ 902 hostname[HTTP_MAX_URI], /* Hostname */ 903 resource[HTTP_MAX_URI]; /* Resource name */ 904 int port; /* Port number */ 905 http_status_t status; /* HTTP status from server */ 906 char tempfile[1024] = ""; /* Temporary filename */ 907 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 908 909 910 /* 911 * Range check input... 912 */ 913 914 DEBUG_printf(("cupsGetPPD3(http=%p, name=\"%s\", modtime=%p(%d), buffer=%p, " 915 "bufsize=%d)", http, name, modtime, 916 modtime ? (int)*modtime : 0, buffer, (int)bufsize)); 917 918 if (!name) 919 { 920 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer name"), 1); 921 return (HTTP_STATUS_NOT_ACCEPTABLE); 922 } 923 924 if (!modtime) 925 { 926 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No modification time"), 1); 927 return (HTTP_STATUS_NOT_ACCEPTABLE); 928 } 929 930 if (!buffer || bufsize <= 1) 931 { 932 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad filename buffer"), 1); 933 return (HTTP_STATUS_NOT_ACCEPTABLE); 934 } 935 936#ifndef WIN32 937 /* 938 * See if the PPD file is available locally... 939 */ 940 941 if (http) 942 httpGetHostname(http, hostname, sizeof(hostname)); 943 else 944 { 945 strlcpy(hostname, cupsServer(), sizeof(hostname)); 946 if (hostname[0] == '/') 947 strlcpy(hostname, "localhost", sizeof(hostname)); 948 } 949 950 if (!_cups_strcasecmp(hostname, "localhost")) 951 { 952 char ppdname[1024]; /* PPD filename */ 953 struct stat ppdinfo; /* PPD file information */ 954 955 956 snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", cg->cups_serverroot, 957 name); 958 if (!stat(ppdname, &ppdinfo)) 959 { 960 /* 961 * OK, the file exists, use it! 962 */ 963 964 if (buffer[0]) 965 { 966 unlink(buffer); 967 968 if (symlink(ppdname, buffer) && errno != EEXIST) 969 { 970 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0); 971 972 return (HTTP_STATUS_SERVER_ERROR); 973 } 974 } 975 else 976 { 977 int tries; /* Number of tries */ 978 const char *tmpdir; /* TMPDIR environment variable */ 979 struct timeval curtime; /* Current time */ 980 981 /* 982 * Previously we put root temporary files in the default CUPS temporary 983 * directory under /var/spool/cups. However, since the scheduler cleans 984 * out temporary files there and runs independently of the user apps, we 985 * don't want to use it unless specifically told to by cupsd. 986 */ 987 988 if ((tmpdir = getenv("TMPDIR")) == NULL) 989# ifdef __APPLE__ 990 tmpdir = "/private/tmp"; /* /tmp is a symlink to /private/tmp */ 991# else 992 tmpdir = "/tmp"; 993# endif /* __APPLE__ */ 994 995 /* 996 * Make the temporary name using the specified directory... 997 */ 998 999 tries = 0; 1000 1001 do 1002 { 1003 /* 1004 * Get the current time of day... 1005 */ 1006 1007 gettimeofday(&curtime, NULL); 1008 1009 /* 1010 * Format a string using the hex time values... 1011 */ 1012 1013 snprintf(buffer, bufsize, "%s/%08lx%05lx", tmpdir, 1014 (unsigned long)curtime.tv_sec, 1015 (unsigned long)curtime.tv_usec); 1016 1017 /* 1018 * Try to make a symlink... 1019 */ 1020 1021 if (!symlink(ppdname, buffer)) 1022 break; 1023 1024 tries ++; 1025 } 1026 while (tries < 1000); 1027 1028 if (tries >= 1000) 1029 { 1030 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0); 1031 1032 return (HTTP_STATUS_SERVER_ERROR); 1033 } 1034 } 1035 1036 if (*modtime >= ppdinfo.st_mtime) 1037 return (HTTP_STATUS_NOT_MODIFIED); 1038 else 1039 { 1040 *modtime = ppdinfo.st_mtime; 1041 return (HTTP_STATUS_OK); 1042 } 1043 } 1044 } 1045#endif /* !WIN32 */ 1046 1047 /* 1048 * Try finding a printer URI for this printer... 1049 */ 1050 1051 if (!http) 1052 if ((http = _cupsConnect()) == NULL) 1053 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 1054 1055 if (!cups_get_printer_uri(http, name, hostname, sizeof(hostname), &port, 1056 resource, sizeof(resource), 0)) 1057 return (HTTP_STATUS_NOT_FOUND); 1058 1059 DEBUG_printf(("2cupsGetPPD3: Printer hostname=\"%s\", port=%d", hostname, 1060 port)); 1061 1062 /* 1063 * Remap local hostname to localhost... 1064 */ 1065 1066 httpGetHostname(NULL, localhost, sizeof(localhost)); 1067 1068 DEBUG_printf(("2cupsGetPPD3: Local hostname=\"%s\"", localhost)); 1069 1070 if (!_cups_strcasecmp(localhost, hostname)) 1071 strlcpy(hostname, "localhost", sizeof(hostname)); 1072 1073 /* 1074 * Get the hostname and port number we are connected to... 1075 */ 1076 1077 httpGetHostname(http, http_hostname, sizeof(http_hostname)); 1078 http_port = httpAddrPort(http->hostaddr); 1079 1080 DEBUG_printf(("2cupsGetPPD3: Connection hostname=\"%s\", port=%d", 1081 http_hostname, http_port)); 1082 1083 /* 1084 * Reconnect to the correct server as needed... 1085 */ 1086 1087 if (!_cups_strcasecmp(http_hostname, hostname) && port == http_port) 1088 http2 = http; 1089 else if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC, 1090 cupsEncryption(), 1, 30000, NULL)) == NULL) 1091 { 1092 DEBUG_puts("1cupsGetPPD3: Unable to connect to server"); 1093 1094 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 1095 } 1096 1097 /* 1098 * Get a temp file... 1099 */ 1100 1101 if (buffer[0]) 1102 fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0600); 1103 else 1104 fd = cupsTempFd(tempfile, sizeof(tempfile)); 1105 1106 if (fd < 0) 1107 { 1108 /* 1109 * Can't open file; close the server connection and return NULL... 1110 */ 1111 1112 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0); 1113 1114 if (http2 != http) 1115 httpClose(http2); 1116 1117 return (HTTP_STATUS_SERVER_ERROR); 1118 } 1119 1120 /* 1121 * And send a request to the HTTP server... 1122 */ 1123 1124 strlcat(resource, ".ppd", sizeof(resource)); 1125 1126 if (*modtime > 0) 1127 httpSetField(http2, HTTP_FIELD_IF_MODIFIED_SINCE, 1128 httpGetDateString(*modtime)); 1129 1130 status = cupsGetFd(http2, resource, fd); 1131 1132 close(fd); 1133 1134 /* 1135 * See if we actually got the file or an error... 1136 */ 1137 1138 if (status == HTTP_STATUS_OK) 1139 { 1140 *modtime = httpGetDateTime(httpGetField(http2, HTTP_FIELD_DATE)); 1141 1142 if (tempfile[0]) 1143 strlcpy(buffer, tempfile, bufsize); 1144 } 1145 else if (status != HTTP_STATUS_NOT_MODIFIED) 1146 { 1147 _cupsSetHTTPError(status); 1148 1149 if (buffer[0]) 1150 unlink(buffer); 1151 else if (tempfile[0]) 1152 unlink(tempfile); 1153 } 1154 else if (tempfile[0]) 1155 unlink(tempfile); 1156 1157 if (http2 != http) 1158 httpClose(http2); 1159 1160 /* 1161 * Return the PPD file... 1162 */ 1163 1164 DEBUG_printf(("1cupsGetPPD3: Returning status %d", status)); 1165 1166 return (status); 1167} 1168 1169 1170/* 1171 * 'cupsGetPrinters()' - Get a list of printers from the default server. 1172 * 1173 * This function is deprecated - use @link cupsGetDests@ instead. 1174 * 1175 * @deprecated@ 1176 */ 1177 1178int /* O - Number of printers */ 1179cupsGetPrinters(char ***printers) /* O - Printers */ 1180{ 1181 int n; /* Number of printers */ 1182 ipp_t *request, /* IPP Request */ 1183 *response; /* IPP Response */ 1184 ipp_attribute_t *attr; /* Current attribute */ 1185 char **temp; /* Temporary pointer */ 1186 http_t *http; /* Connection to server */ 1187 1188 1189 /* 1190 * Range check input... 1191 */ 1192 1193 if (!printers) 1194 { 1195 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 1196 1197 return (0); 1198 } 1199 1200 *printers = NULL; 1201 1202 /* 1203 * Try to connect to the server... 1204 */ 1205 1206 if ((http = _cupsConnect()) == NULL) 1207 return (0); 1208 1209 /* 1210 * Build a CUPS_GET_PRINTERS request, which requires the following 1211 * attributes: 1212 * 1213 * attributes-charset 1214 * attributes-natural-language 1215 * requested-attributes 1216 */ 1217 1218 request = ippNewRequest(IPP_OP_CUPS_GET_PRINTERS); 1219 1220 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 1221 "requested-attributes", NULL, "printer-name"); 1222 1223 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, 1224 "printer-type", 0); 1225 1226 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, 1227 "printer-type-mask", CUPS_PRINTER_CLASS); 1228 1229 /* 1230 * Do the request and get back a response... 1231 */ 1232 1233 n = 0; 1234 1235 if ((response = cupsDoRequest(http, request, "/")) != NULL) 1236 { 1237 for (attr = response->attrs; attr != NULL; attr = attr->next) 1238 if (attr->name != NULL && 1239 _cups_strcasecmp(attr->name, "printer-name") == 0 && 1240 attr->value_tag == IPP_TAG_NAME) 1241 { 1242 if (n == 0) 1243 temp = malloc(sizeof(char *)); 1244 else 1245 temp = realloc(*printers, sizeof(char *) * (n + 1)); 1246 1247 if (temp == NULL) 1248 { 1249 /* 1250 * Ran out of memory! 1251 */ 1252 1253 while (n > 0) 1254 { 1255 n --; 1256 free((*printers)[n]); 1257 } 1258 1259 free(*printers); 1260 ippDelete(response); 1261 return (0); 1262 } 1263 1264 *printers = temp; 1265 temp[n] = strdup(attr->values[0].string.text); 1266 n ++; 1267 } 1268 1269 ippDelete(response); 1270 } 1271 1272 return (n); 1273} 1274 1275 1276/* 1277 * 'cupsGetServerPPD()' - Get an available PPD file from the server. 1278 * 1279 * This function returns the named PPD file from the server. The 1280 * list of available PPDs is provided by the IPP @code CUPS_GET_PPDS@ 1281 * operation. 1282 * 1283 * You must remove (unlink) the PPD file when you are finished with 1284 * it. The PPD filename is stored in a static location that will be 1285 * overwritten on the next call to @link cupsGetPPD@, @link cupsGetPPD2@, 1286 * or @link cupsGetServerPPD@. 1287 * 1288 * @since CUPS 1.3/OS X 10.5@ 1289 */ 1290 1291char * /* O - Name of PPD file or @code NULL@ on error */ 1292cupsGetServerPPD(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 1293 const char *name) /* I - Name of PPD file ("ppd-name") */ 1294{ 1295 int fd; /* PPD file descriptor */ 1296 ipp_t *request; /* IPP request */ 1297 _cups_globals_t *cg = _cupsGlobals(); 1298 /* Pointer to library globals */ 1299 1300 1301 /* 1302 * Range check input... 1303 */ 1304 1305 if (!name) 1306 { 1307 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No PPD name"), 1); 1308 1309 return (NULL); 1310 } 1311 1312 if (!http) 1313 if ((http = _cupsConnect()) == NULL) 1314 return (NULL); 1315 1316 /* 1317 * Get a temp file... 1318 */ 1319 1320 if ((fd = cupsTempFd(cg->ppd_filename, sizeof(cg->ppd_filename))) < 0) 1321 { 1322 /* 1323 * Can't open file; close the server connection and return NULL... 1324 */ 1325 1326 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0); 1327 1328 return (NULL); 1329 } 1330 1331 /* 1332 * Get the PPD file... 1333 */ 1334 1335 request = ippNewRequest(IPP_OP_CUPS_GET_PPD); 1336 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", NULL, 1337 name); 1338 1339 ippDelete(cupsDoIORequest(http, request, "/", -1, fd)); 1340 1341 close(fd); 1342 1343 if (cupsLastError() != IPP_STATUS_OK) 1344 { 1345 unlink(cg->ppd_filename); 1346 return (NULL); 1347 } 1348 else 1349 return (cg->ppd_filename); 1350} 1351 1352 1353/* 1354 * 'cupsPrintFile()' - Print a file to a printer or class on the default server. 1355 */ 1356 1357int /* O - Job ID or 0 on error */ 1358cupsPrintFile(const char *name, /* I - Destination name */ 1359 const char *filename, /* I - File to print */ 1360 const char *title, /* I - Title of job */ 1361 int num_options,/* I - Number of options */ 1362 cups_option_t *options) /* I - Options */ 1363{ 1364 DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", " 1365 "title=\"%s\", num_options=%d, options=%p)", 1366 name, filename, title, num_options, options)); 1367 1368 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title, 1369 num_options, options)); 1370} 1371 1372 1373/* 1374 * 'cupsPrintFile2()' - Print a file to a printer or class on the specified 1375 * server. 1376 * 1377 * @since CUPS 1.1.21/OS X 10.4@ 1378 */ 1379 1380int /* O - Job ID or 0 on error */ 1381cupsPrintFile2( 1382 http_t *http, /* I - Connection to server */ 1383 const char *name, /* I - Destination name */ 1384 const char *filename, /* I - File to print */ 1385 const char *title, /* I - Title of job */ 1386 int num_options, /* I - Number of options */ 1387 cups_option_t *options) /* I - Options */ 1388{ 1389 DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", " 1390 "title=\"%s\", num_options=%d, options=%p)", 1391 http, name, filename, title, num_options, options)); 1392 1393 return (cupsPrintFiles2(http, name, 1, &filename, title, num_options, 1394 options)); 1395} 1396 1397 1398/* 1399 * 'cupsPrintFiles()' - Print one or more files to a printer or class on the 1400 * default server. 1401 */ 1402 1403int /* O - Job ID or 0 on error */ 1404cupsPrintFiles( 1405 const char *name, /* I - Destination name */ 1406 int num_files, /* I - Number of files */ 1407 const char **files, /* I - File(s) to print */ 1408 const char *title, /* I - Title of job */ 1409 int num_options, /* I - Number of options */ 1410 cups_option_t *options) /* I - Options */ 1411{ 1412 DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, " 1413 "files=%p, title=\"%s\", num_options=%d, options=%p)", 1414 name, num_files, files, title, num_options, options)); 1415 1416 /* 1417 * Print the file(s)... 1418 */ 1419 1420 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title, 1421 num_options, options)); 1422} 1423 1424 1425/* 1426 * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the 1427 * specified server. 1428 * 1429 * @since CUPS 1.1.21/OS X 10.4@ 1430 */ 1431 1432int /* O - Job ID or 0 on error */ 1433cupsPrintFiles2( 1434 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 1435 const char *name, /* I - Destination name */ 1436 int num_files, /* I - Number of files */ 1437 const char **files, /* I - File(s) to print */ 1438 const char *title, /* I - Title of job */ 1439 int num_options, /* I - Number of options */ 1440 cups_option_t *options) /* I - Options */ 1441{ 1442 int i; /* Looping var */ 1443 int job_id; /* New job ID */ 1444 const char *docname; /* Basename of current filename */ 1445 const char *format; /* Document format */ 1446 cups_file_t *fp; /* Current file */ 1447 char buffer[8192]; /* Copy buffer */ 1448 ssize_t bytes; /* Bytes in buffer */ 1449 http_status_t status; /* Status of write */ 1450 _cups_globals_t *cg = _cupsGlobals(); /* Global data */ 1451 ipp_status_t cancel_status; /* Status code to preserve */ 1452 char *cancel_message; /* Error message to preserve */ 1453 1454 1455 DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, " 1456 "files=%p, title=\"%s\", num_options=%d, options=%p)", 1457 http, name, num_files, files, title, num_options, options)); 1458 1459 /* 1460 * Range check input... 1461 */ 1462 1463 if (!name || num_files < 1 || !files) 1464 { 1465 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 1466 1467 return (0); 1468 } 1469 1470 /* 1471 * Create the print job... 1472 */ 1473 1474 if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0) 1475 return (0); 1476 1477 /* 1478 * Send each of the files... 1479 */ 1480 1481 if (cupsGetOption("raw", num_options, options)) 1482 format = CUPS_FORMAT_RAW; 1483 else if ((format = cupsGetOption("document-format", num_options, 1484 options)) == NULL) 1485 format = CUPS_FORMAT_AUTO; 1486 1487 for (i = 0; i < num_files; i ++) 1488 { 1489 /* 1490 * Start the next file... 1491 */ 1492 1493 if ((docname = strrchr(files[i], '/')) != NULL) 1494 docname ++; 1495 else 1496 docname = files[i]; 1497 1498 if ((fp = cupsFileOpen(files[i], "rb")) == NULL) 1499 { 1500 /* 1501 * Unable to open print file, cancel the job and return... 1502 */ 1503 1504 _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_ACCESS, NULL, 0); 1505 goto cancel_job; 1506 } 1507 1508 status = cupsStartDocument(http, name, job_id, docname, format, 1509 i == (num_files - 1)); 1510 1511 while (status == HTTP_STATUS_CONTINUE && 1512 (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0) 1513 status = cupsWriteRequestData(http, buffer, bytes); 1514 1515 cupsFileClose(fp); 1516 1517 if (status != HTTP_STATUS_CONTINUE || cupsFinishDocument(http, name) != IPP_STATUS_OK) 1518 { 1519 /* 1520 * Unable to queue, cancel the job and return... 1521 */ 1522 1523 goto cancel_job; 1524 } 1525 } 1526 1527 return (job_id); 1528 1529 /* 1530 * If we get here, something happened while sending the print job so we need 1531 * to cancel the job without setting the last error (since we need to preserve 1532 * the current error... 1533 */ 1534 1535 cancel_job: 1536 1537 cancel_status = cg->last_error; 1538 cancel_message = cg->last_status_message ? 1539 _cupsStrRetain(cg->last_status_message) : NULL; 1540 1541 cupsCancelJob2(http, name, job_id, 0); 1542 1543 cg->last_error = cancel_status; 1544 cg->last_status_message = cancel_message; 1545 1546 return (0); 1547} 1548 1549 1550/* 1551 * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob(). 1552 * 1553 * Use @link cupsWriteRequestData@ to write data for the document and 1554 * @link cupsFinishDocument@ to finish the document and get the submission status. 1555 * 1556 * The MIME type constants @code CUPS_FORMAT_AUTO@, @code CUPS_FORMAT_PDF@, 1557 * @code CUPS_FORMAT_POSTSCRIPT@, @code CUPS_FORMAT_RAW@, and 1558 * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although 1559 * any supported MIME type string can be supplied. 1560 * 1561 * @since CUPS 1.4/OS X 10.6@ 1562 */ 1563 1564http_status_t /* O - HTTP status of request */ 1565cupsStartDocument( 1566 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 1567 const char *name, /* I - Destination name */ 1568 int job_id, /* I - Job ID from @link cupsCreateJob@ */ 1569 const char *docname, /* I - Name of document */ 1570 const char *format, /* I - MIME type or @code CUPS_FORMAT_foo@ */ 1571 int last_document) /* I - 1 for last document in job, 0 otherwise */ 1572{ 1573 char resource[1024], /* Resource for destinatio */ 1574 printer_uri[1024]; /* Printer URI */ 1575 ipp_t *request; /* Send-Document request */ 1576 http_status_t status; /* HTTP status */ 1577 1578 1579 /* 1580 * Create a Send-Document request... 1581 */ 1582 1583 if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL) 1584 { 1585 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0); 1586 return (HTTP_STATUS_ERROR); 1587 } 1588 1589 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", 1590 NULL, "localhost", ippPort(), "/printers/%s", name); 1591 snprintf(resource, sizeof(resource), "/printers/%s", name); 1592 1593 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 1594 NULL, printer_uri); 1595 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); 1596 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 1597 NULL, cupsUser()); 1598 if (docname) 1599 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", 1600 NULL, docname); 1601 if (format) 1602 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, 1603 "document-format", NULL, format); 1604 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", last_document); 1605 1606 /* 1607 * Send and delete the request, then return the status... 1608 */ 1609 1610 status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE); 1611 1612 ippDelete(request); 1613 1614 return (status); 1615} 1616 1617 1618/* 1619 * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the 1620 * first printer in a class. 1621 */ 1622 1623static int /* O - 1 on success, 0 on failure */ 1624cups_get_printer_uri( 1625 http_t *http, /* I - Connection to server */ 1626 const char *name, /* I - Name of printer or class */ 1627 char *host, /* I - Hostname buffer */ 1628 int hostsize, /* I - Size of hostname buffer */ 1629 int *port, /* O - Port number */ 1630 char *resource, /* I - Resource buffer */ 1631 int resourcesize, /* I - Size of resource buffer */ 1632 int depth) /* I - Depth of query */ 1633{ 1634 int i; /* Looping var */ 1635 int http_port; /* Port number */ 1636 http_t *http2; /* Alternate HTTP connection */ 1637 ipp_t *request, /* IPP request */ 1638 *response; /* IPP response */ 1639 ipp_attribute_t *attr; /* Current attribute */ 1640 char uri[HTTP_MAX_URI], /* printer-uri attribute */ 1641 scheme[HTTP_MAX_URI], /* Scheme name */ 1642 username[HTTP_MAX_URI], /* Username:password */ 1643 classname[255], /* Temporary class name */ 1644 http_hostname[HTTP_MAX_HOST]; 1645 /* Hostname associated with connection */ 1646 static const char * const requested_attrs[] = 1647 { /* Requested attributes */ 1648 "device-uri", 1649 "member-uris", 1650 "printer-uri-supported", 1651 "printer-type" 1652 }; 1653 1654 1655 DEBUG_printf(("7cups_get_printer_uri(http=%p, name=\"%s\", host=%p, " 1656 "hostsize=%d, resource=%p, resourcesize=%d, depth=%d)", 1657 http, name, host, hostsize, resource, resourcesize, depth)); 1658 1659 /* 1660 * Setup the printer URI... 1661 */ 1662 1663 if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 1664 "localhost", 0, "/printers/%s", 1665 name) < HTTP_URI_STATUS_OK) 1666 { 1667 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer-uri"), 1668 1); 1669 1670 *host = '\0'; 1671 *resource = '\0'; 1672 1673 return (0); 1674 } 1675 1676 DEBUG_printf(("9cups_get_printer_uri: printer-uri=\"%s\"", uri)); 1677 1678 /* 1679 * Get the hostname and port number we are connected to... 1680 */ 1681 1682 httpGetHostname(http, http_hostname, sizeof(http_hostname)); 1683 http_port = httpAddrPort(http->hostaddr); 1684 1685 /* 1686 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following 1687 * attributes: 1688 * 1689 * attributes-charset 1690 * attributes-natural-language 1691 * printer-uri 1692 * requested-attributes 1693 */ 1694 1695 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); 1696 1697 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", 1698 NULL, uri); 1699 1700 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME, 1701 "requested-attributes", 1702 sizeof(requested_attrs) / sizeof(requested_attrs[0]), 1703 NULL, requested_attrs); 1704 1705 /* 1706 * Do the request and get back a response... 1707 */ 1708 1709 snprintf(resource, resourcesize, "/printers/%s", name); 1710 1711 if ((response = cupsDoRequest(http, request, resource)) != NULL) 1712 { 1713 const char *device_uri = NULL; /* device-uri value */ 1714 1715 if ((attr = ippFindAttribute(response, "device-uri", 1716 IPP_TAG_URI)) != NULL) 1717 device_uri = attr->values[0].string.text; 1718 1719 if (device_uri && 1720 (!strncmp(device_uri, "ipp://", 6) || 1721 !strncmp(device_uri, "ipps://", 7) || 1722 ((strstr(device_uri, "._ipp.") != NULL || 1723 strstr(device_uri, "._ipps.") != NULL) && 1724 !strcmp(device_uri + strlen(device_uri) - 5, "/cups")))) 1725 { 1726 /* 1727 * Statically-configured shared printer. 1728 */ 1729 1730 httpSeparateURI(HTTP_URI_CODING_ALL, 1731 _httpResolveURI(device_uri, uri, sizeof(uri), 1732 _HTTP_RESOLVE_DEFAULT, NULL, NULL), 1733 scheme, sizeof(scheme), username, sizeof(username), 1734 host, hostsize, port, resource, resourcesize); 1735 ippDelete(response); 1736 1737 return (1); 1738 } 1739 else if ((attr = ippFindAttribute(response, "member-uris", 1740 IPP_TAG_URI)) != NULL) 1741 { 1742 /* 1743 * Get the first actual printer name in the class... 1744 */ 1745 1746 for (i = 0; i < attr->num_values; i ++) 1747 { 1748 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, 1749 scheme, sizeof(scheme), username, sizeof(username), 1750 host, hostsize, port, resource, resourcesize); 1751 if (!strncmp(resource, "/printers/", 10)) 1752 { 1753 /* 1754 * Found a printer! 1755 */ 1756 1757 ippDelete(response); 1758 1759 return (1); 1760 } 1761 } 1762 1763 /* 1764 * No printers in this class - try recursively looking for a printer, 1765 * but not more than 3 levels deep... 1766 */ 1767 1768 if (depth < 3) 1769 { 1770 for (i = 0; i < attr->num_values; i ++) 1771 { 1772 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, 1773 scheme, sizeof(scheme), username, sizeof(username), 1774 host, hostsize, port, resource, resourcesize); 1775 if (!strncmp(resource, "/classes/", 9)) 1776 { 1777 /* 1778 * Found a class! Connect to the right server... 1779 */ 1780 1781 if (!_cups_strcasecmp(http_hostname, host) && *port == http_port) 1782 http2 = http; 1783 else if ((http2 = httpConnect2(host, *port, NULL, AF_UNSPEC, 1784 cupsEncryption(), 1, 30000, 1785 NULL)) == NULL) 1786 { 1787 DEBUG_puts("8cups_get_printer_uri: Unable to connect to server"); 1788 1789 continue; 1790 } 1791 1792 /* 1793 * Look up printers on that server... 1794 */ 1795 1796 strlcpy(classname, resource + 9, sizeof(classname)); 1797 1798 cups_get_printer_uri(http2, classname, host, hostsize, port, 1799 resource, resourcesize, depth + 1); 1800 1801 /* 1802 * Close the connection as needed... 1803 */ 1804 1805 if (http2 != http) 1806 httpClose(http2); 1807 1808 if (*host) 1809 return (1); 1810 } 1811 } 1812 } 1813 } 1814 else if ((attr = ippFindAttribute(response, "printer-uri-supported", 1815 IPP_TAG_URI)) != NULL) 1816 { 1817 httpSeparateURI(HTTP_URI_CODING_ALL, 1818 _httpResolveURI(attr->values[0].string.text, uri, 1819 sizeof(uri), _HTTP_RESOLVE_DEFAULT, 1820 NULL, NULL), 1821 scheme, sizeof(scheme), username, sizeof(username), 1822 host, hostsize, port, resource, resourcesize); 1823 ippDelete(response); 1824 1825 if (!strncmp(resource, "/classes/", 9)) 1826 { 1827 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, 1828 _("No printer-uri found for class"), 1); 1829 1830 *host = '\0'; 1831 *resource = '\0'; 1832 1833 return (0); 1834 } 1835 1836 return (1); 1837 } 1838 1839 ippDelete(response); 1840 } 1841 1842 if (cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) 1843 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri found"), 1); 1844 1845 *host = '\0'; 1846 *resource = '\0'; 1847 1848 return (0); 1849} 1850 1851 1852/* 1853 * End of "$Id: util.c 11093 2013-07-03 20:48:42Z msweet $". 1854 */ 1855