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