1 /* 2 * Simulated client test program for CUPS. 3 * 4 * Copyright 2017 by Apple Inc. 5 * 6 * These coded instructions, statements, and computer programs are the 7 * property of Apple Inc. and are protected by Federal copyright 8 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 9 * which should have been included with this file. If this file is 10 * missing or damaged, see the license at "http://www.cups.org/". 11 * 12 * This file is subject to the Apple OS-Developed Software exception. 13 */ 14 15 /* 16 * Include necessary headers... 17 */ 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <cups/cups.h> 22 #include <cups/raster.h> 23 #include <cups/string-private.h> 24 #include <cups/thread-private.h> 25 26 27 /* 28 * Local types... 29 */ 30 31 typedef struct _client_monitor_s 32 { 33 const char *uri, /* Printer URI */ 34 *hostname, /* Hostname */ 35 *user, /* Username */ 36 *resource; /* Resource path */ 37 int port; /* Port number */ 38 http_encryption_t encryption; /* Use encryption? */ 39 ipp_pstate_t printer_state; /* Current printer state */ 40 char printer_state_reasons[1024]; 41 /* Current printer-state-reasons */ 42 int job_id; /* Job ID for submitted job */ 43 ipp_jstate_t job_state; /* Current job state */ 44 char job_state_reasons[1024]; 45 /* Current job-state-reasons */ 46 } _client_monitor_t; 47 48 49 /* 50 * Local functions... 51 */ 52 53 static const char *make_raster_file(ipp_t *response, int grayscale, char *tempname, size_t tempsize, const char **format); 54 static void *monitor_printer(_client_monitor_t *monitor); 55 static void show_attributes(const char *title, int request, ipp_t *ipp); 56 static void show_capabilities(ipp_t *response); 57 static void usage(void); 58 59 60 /* 61 * 'main()' - Main entry. 62 */ 63 64 int /* O - Exit status */ 65 main(int argc, /* I - Number of command-line arguments */ 66 char *argv[]) /* I - Command-line arguments */ 67 { 68 int i; /* Looping var */ 69 const char *opt, /* Current option */ 70 *uri = NULL, /* Printer URI */ 71 *printfile = NULL, 72 /* Print file */ 73 *printformat = NULL; 74 /* Print format */ 75 int keepfile = 0, /* Keep temp file? */ 76 grayscale = 0, /* Force grayscale? */ 77 verbosity = 0; /* Verbosity */ 78 char tempfile[1024] = "", 79 /* Temporary file (if any) */ 80 scheme[32], /* URI scheme */ 81 userpass[256], /* Username:password */ 82 hostname[256], /* Hostname */ 83 resource[256]; /* Resource path */ 84 int port; /* Port number */ 85 http_encryption_t encryption; /* Encryption mode */ 86 _client_monitor_t monitor; /* Monitoring data */ 87 http_t *http; /* HTTP connection */ 88 ipp_t *request, /* IPP request */ 89 *response; /* IPP response */ 90 ipp_attribute_t *attr; /* IPP attribute */ 91 static const char * const pattrs[] = /* Printer attributes we are interested in */ 92 { 93 "job-template", 94 "printer-defaults", 95 "printer-description", 96 "media-col-database", 97 "media-col-ready" 98 }; 99 100 101 /* 102 * Parse command-line options... 103 */ 104 105 for (i = 1; i < argc; i ++) 106 { 107 if (argv[i][0] == '-') 108 { 109 for (opt = argv[i] + 1; *opt; opt ++) 110 { 111 switch (*opt) 112 { 113 case 'd' : /* -d document-format */ 114 if (printformat) 115 { 116 puts("Document format can only be specified once."); 117 usage(); 118 return (1); 119 } 120 121 i ++; 122 if (i >= argc) 123 { 124 puts("Expected document format after '-d'."); 125 usage(); 126 return (1); 127 } 128 129 printformat = argv[i]; 130 break; 131 132 case 'f' : /* -f print-file */ 133 if (printfile) 134 { 135 puts("Print file can only be specified once."); 136 usage(); 137 return (1); 138 } 139 140 i ++; 141 if (i >= argc) 142 { 143 puts("Expected print file after '-f'."); 144 usage(); 145 return (1); 146 } 147 148 printfile = argv[i]; 149 break; 150 151 case 'g' : 152 grayscale = 1; 153 break; 154 155 case 'k' : 156 keepfile = 1; 157 break; 158 159 case 'v' : 160 verbosity ++; 161 break; 162 163 default : 164 printf("Unknown option '-%c'.\n", *opt); 165 usage(); 166 return (1); 167 } 168 } 169 } 170 else if (uri || (strncmp(argv[i], "ipp://", 6) && strncmp(argv[i], "ipps://", 7))) 171 { 172 printf("Unknown command-line argument '%s'.\n", argv[i]); 173 usage(); 174 return (1); 175 } 176 else 177 uri = argv[i]; 178 } 179 180 /* 181 * Make sure we have everything we need. 182 */ 183 184 if (!uri) 185 { 186 puts("Expected printer URI."); 187 usage(); 188 return (1); 189 } 190 191 /* 192 * Connect to the printer... 193 */ 194 195 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) 196 { 197 printf("Bad printer URI '%s'.\n", uri); 198 return (1); 199 } 200 201 if (!port) 202 port = IPP_PORT; 203 204 if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps")) 205 encryption = HTTP_ENCRYPTION_ALWAYS; 206 else 207 encryption = HTTP_ENCRYPTION_IF_REQUESTED; 208 209 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 0, NULL)) == NULL) 210 { 211 printf("Unable to connect to '%s' on port %d: %s\n", hostname, port, cupsLastErrorString()); 212 return (1); 213 } 214 215 /* 216 * Query printer status and capabilities... 217 */ 218 219 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); 220 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); 221 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); 222 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs); 223 224 response = cupsDoRequest(http, request, resource); 225 226 if (verbosity) 227 show_capabilities(response); 228 229 /* 230 * Now figure out what we will be printing... 231 */ 232 233 if (printfile) 234 { 235 /* 236 * User specified a print file, figure out the format... 237 */ 238 239 if ((opt = strrchr(printfile, '.')) != NULL) 240 { 241 /* 242 * Guess the format from the extension... 243 */ 244 245 if (!strcmp(opt, ".jpg")) 246 printformat = "image/jpeg"; 247 else if (!strcmp(opt, ".pdf")) 248 printformat = "application/pdf"; 249 else if (!strcmp(opt, ".ps")) 250 printformat = "application/postscript"; 251 else if (!strcmp(opt, ".pwg")) 252 printformat = "image/pwg-raster"; 253 else if (!strcmp(opt, ".urf")) 254 printformat = "image/urf"; 255 else 256 printformat = "application/octet-stream"; 257 } 258 else 259 { 260 /* 261 * Tell the printer to auto-detect... 262 */ 263 264 printformat = "application/octet-stream"; 265 } 266 } 267 else 268 { 269 /* 270 * No file specified, make something to test with... 271 */ 272 273 if ((printfile = make_raster_file(response, grayscale, tempfile, sizeof(tempfile), &printformat)) == NULL) 274 return (1); 275 } 276 277 ippDelete(response); 278 279 /* 280 * Start monitoring the printer in the background... 281 */ 282 283 memset(&monitor, 0, sizeof(monitor)); 284 285 monitor.uri = uri; 286 monitor.hostname = hostname; 287 monitor.resource = resource; 288 monitor.port = port; 289 monitor.encryption = encryption; 290 291 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor); 292 293 /* 294 * Create the job and wait for completion... 295 */ 296 297 request = ippNewRequest(IPP_OP_CREATE_JOB); 298 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); 299 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); 300 301 if ((opt = strrchr(printfile, '/')) != NULL) 302 opt ++; 303 else 304 opt = printfile; 305 306 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, opt); 307 308 if (verbosity) 309 show_attributes("Create-Job request", 1, request); 310 311 response = cupsDoRequest(http, request, resource); 312 313 if (verbosity) 314 show_attributes("Create-Job response", 0, response); 315 316 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE) 317 { 318 printf("Unable to create print job: %s\n", cupsLastErrorString()); 319 320 monitor.job_state = IPP_JSTATE_ABORTED; 321 322 goto cleanup; 323 } 324 325 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL) 326 { 327 puts("No job-id returned in Create-Job request."); 328 329 monitor.job_state = IPP_JSTATE_ABORTED; 330 331 goto cleanup; 332 } 333 334 monitor.job_id = ippGetInteger(attr, 0); 335 336 printf("CREATED JOB %d, sending %s of type %s\n", monitor.job_id, printfile, printformat); 337 338 ippDelete(response); 339 340 request = ippNewRequest(IPP_OP_SEND_DOCUMENT); 341 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); 342 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor.job_id); 343 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); 344 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, printformat); 345 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); 346 347 if (verbosity) 348 show_attributes("Send-Document request", 1, request); 349 350 response = cupsDoFileRequest(http, request, resource, printfile); 351 352 if (verbosity) 353 show_attributes("Send-Document response", 0, response); 354 355 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE) 356 { 357 printf("Unable to print file: %s\n", cupsLastErrorString()); 358 359 monitor.job_state = IPP_JSTATE_ABORTED; 360 361 goto cleanup; 362 } 363 364 puts("WAITING FOR JOB TO COMPLETE"); 365 366 while (monitor.job_state < IPP_JSTATE_CANCELED) 367 sleep(1); 368 369 /* 370 * Cleanup after ourselves... 371 */ 372 373 cleanup: 374 375 httpClose(http); 376 377 if (tempfile[0] && !keepfile) 378 unlink(tempfile); 379 380 return (monitor.job_state == IPP_JSTATE_COMPLETED); 381 } 382 383 384 /* 385 * 'make_raster_file()' - Create a temporary raster file. 386 */ 387 388 static const char * /* O - Print filename */ 389 make_raster_file(ipp_t *response, /* I - Printer attributes */ 390 int grayscale, /* I - Force grayscale? */ 391 char *tempname, /* I - Temporary filename buffer */ 392 size_t tempsize, /* I - Size of temp file buffer */ 393 const char **format) /* O - Print format */ 394 { 395 int i, /* Looping var */ 396 count; /* Number of values */ 397 ipp_attribute_t *attr; /* Printer attribute */ 398 const char *type = NULL; /* Raster type (colorspace + bits) */ 399 pwg_media_t *media = NULL; /* Media size */ 400 int xdpi = 0, /* Horizontal resolution */ 401 ydpi = 0; /* Vertical resolution */ 402 int fd; /* Temporary file */ 403 cups_mode_t mode; /* Raster mode */ 404 cups_raster_t *ras; /* Raster stream */ 405 cups_page_header2_t header; /* Page header */ 406 unsigned char *line, /* Line of raster data */ 407 *lineptr; /* Pointer into line */ 408 unsigned y, /* Current position on page */ 409 xcount, ycount, /* Current count for X and Y */ 410 xrep, yrep, /* Repeat count for X and Y */ 411 xoff, yoff, /* Offsets for X and Y */ 412 yend; /* End Y value */ 413 int temprow, /* Row in template */ 414 tempcolor; /* Template color */ 415 const char *template; /* Pointer into template */ 416 const unsigned char *color; /* Current color */ 417 static const unsigned char colors[][3] = 418 { /* Colors for test */ 419 { 191, 191, 191 }, 420 { 127, 127, 127 }, 421 { 63, 63, 63 }, 422 { 0, 0, 0 }, 423 { 255, 0, 0 }, 424 { 255, 127, 0 }, 425 { 255, 255, 0 }, 426 { 127, 255, 0 }, 427 { 0, 255, 0 }, 428 { 0, 255, 127 }, 429 { 0, 255, 255 }, 430 { 0, 127, 255 }, 431 { 0, 0, 255 }, 432 { 127, 0, 255 }, 433 { 255, 0, 255 } 434 }; 435 static const char * const templates[] = 436 { /* Raster template */ 437 " CCC U U PPPP SSS TTTTT EEEEE SSS TTTTT 000 1 222 333 4 55555 66 77777 888 999 ", 438 "C C U U P P S S T E S S T 0 0 11 2 2 3 3 4 4 5 6 7 8 8 9 9 ", 439 "C U U P P S T E S T 0 0 1 2 3 4 4 5 6 7 8 8 9 9 ", 440 "C U U PPPP SSS ----- T EEEE SSS T 0 0 0 1 22 333 44444 555 6666 7 888 9999 ", 441 "C U U P S T E S T 0 0 1 2 3 4 5 6 6 7 8 8 9 ", 442 "C C U U P S S T E S S T 0 0 1 2 3 3 4 5 5 6 6 7 8 8 9 ", 443 " CCC UUU P SSS T EEEEE SSS T 000 111 22222 333 4 555 666 7 888 99 ", 444 " " 445 }; 446 447 448 /* 449 * Figure out the output format... 450 */ 451 452 if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) == NULL) 453 { 454 puts("No supported document formats, aborting."); 455 return (NULL); 456 } 457 458 if (*format) 459 { 460 if (!ippContainsString(attr, *format)) 461 { 462 printf("Printer does not support document-format '%s'.\n", *format); 463 return (NULL); 464 } 465 466 if (!strcmp(*format, "image/urf")) 467 mode = CUPS_RASTER_WRITE_APPLE; 468 else if (!strcmp(*format, "image/pwg-raster")) 469 mode = CUPS_RASTER_WRITE_PWG; 470 else 471 { 472 printf("Unable to generate document-format '%s'.\n", *format); 473 return (NULL); 474 } 475 } 476 else if (ippContainsString(attr, "image/urf")) 477 { 478 /* 479 * Apple Raster format... 480 */ 481 482 *format = "image/urf"; 483 mode = CUPS_RASTER_WRITE_APPLE; 484 } 485 else if (ippContainsString(attr, "image/pwg-raster")) 486 { 487 /* 488 * PWG Raster format... 489 */ 490 491 *format = "image/pwg-raster"; 492 mode = CUPS_RASTER_WRITE_PWG; 493 } 494 else 495 { 496 /* 497 * No supported raster format... 498 */ 499 500 puts("Printer does not support Apple or PWG raster files, aborting."); 501 return (NULL); 502 } 503 504 /* 505 * Figure out the the media, resolution, and color mode... 506 */ 507 508 if ((attr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL) 509 { 510 /* 511 * Use default media... 512 */ 513 514 media = pwgMediaForPWG(ippGetString(attr, 0, NULL)); 515 } 516 else if ((attr = ippFindAttribute(response, "media-ready", IPP_TAG_KEYWORD)) != NULL) 517 { 518 /* 519 * Use ready media... 520 */ 521 522 if (ippContainsString(attr, "na_letter_8.5x11in")) 523 media = pwgMediaForPWG("na_letter_8.5x11in"); 524 else if (ippContainsString(attr, "iso_a4_210x297mm")) 525 media = pwgMediaForPWG("iso_a4_210x297mm"); 526 else 527 media = pwgMediaForPWG(ippGetString(attr, 0, NULL)); 528 } 529 else 530 { 531 puts("No default or ready media reported by printer, aborting."); 532 return (NULL); 533 } 534 535 if (mode == CUPS_RASTER_WRITE_APPLE && (attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL) 536 { 537 for (i = 0, count = ippGetCount(attr); i < count; i ++) 538 { 539 const char *val = ippGetString(attr, i, NULL); 540 541 if (!strncmp(val, "RS", 2)) 542 xdpi = ydpi = atoi(val + 2); 543 else if (!strncmp(val, "W8", 2) && !type) 544 type = "sgray_8"; 545 else if (!strncmp(val, "SRGB24", 6) && !grayscale) 546 type = "srgb_8"; 547 } 548 } 549 else if (mode == CUPS_RASTER_WRITE_PWG && (attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL) 550 { 551 for (i = 0, count = ippGetCount(attr); i < count; i ++) 552 { 553 int tempxdpi, tempydpi; 554 ipp_res_t tempunits; 555 556 tempxdpi = ippGetResolution(attr, 0, &tempydpi, &tempunits); 557 558 if (i == 0 || tempxdpi < xdpi || tempydpi < ydpi) 559 { 560 xdpi = tempxdpi; 561 ydpi = tempydpi; 562 } 563 } 564 565 if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) != NULL) 566 { 567 if (!grayscale && ippContainsString(attr, "srgb_8")) 568 type = "srgb_8"; 569 else if (ippContainsString(attr, "sgray_8")) 570 type = "sgray_8"; 571 } 572 } 573 574 if (xdpi < 72 || ydpi < 72) 575 { 576 puts("No supported raster resolutions, aborting."); 577 return (NULL); 578 } 579 580 if (!type) 581 { 582 puts("No supported color spaces or bit depths, aborting."); 583 return (NULL); 584 } 585 586 /* 587 * Make the raster context and details... 588 */ 589 590 if (!cupsRasterInitPWGHeader(&header, media, type, xdpi, ydpi, "one-sided", NULL)) 591 { 592 printf("Unable to initialize raster context: %s\n", cupsRasterErrorString()); 593 return (NULL); 594 } 595 596 header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount] = 1; 597 598 if (header.cupsWidth > (4 * header.HWResolution[0])) 599 { 600 xoff = header.HWResolution[0] / 2; 601 yoff = header.HWResolution[1] / 2; 602 } 603 else 604 { 605 xoff = 0; 606 yoff = 0; 607 } 608 609 xrep = (header.cupsWidth - 2 * xoff) / 140; 610 yrep = xrep * header.HWResolution[1] / header.HWResolution[0]; 611 yend = header.cupsHeight - yoff; 612 613 /* 614 * Prepare the raster file... 615 */ 616 617 if ((line = malloc(header.cupsBytesPerLine)) == NULL) 618 { 619 printf("Unable to allocate %u bytes for raster output: %s\n", header.cupsBytesPerLine, strerror(errno)); 620 return (NULL); 621 } 622 623 if ((fd = cupsTempFd(tempname, (int)tempsize)) < 0) 624 { 625 printf("Unable to create temporary print file: %s\n", strerror(errno)); 626 return (NULL); 627 } 628 629 if ((ras = cupsRasterOpen(fd, mode)) == NULL) 630 { 631 printf("Unable to open raster stream: %s\n", cupsRasterErrorString()); 632 close(fd); 633 return (NULL); 634 } 635 636 /* 637 * Write a single page consisting of the template dots repeated over the page. 638 */ 639 640 cupsRasterWriteHeader2(ras, &header); 641 642 memset(line, 0xff, header.cupsBytesPerLine); 643 644 for (y = 0; y < yoff; y ++) 645 cupsRasterWritePixels(ras, line, header.cupsBytesPerLine); 646 647 for (temprow = 0, tempcolor = 0; y < yend;) 648 { 649 template = templates[temprow]; 650 color = colors[tempcolor]; 651 652 temprow ++; 653 if (temprow >= (int)(sizeof(templates) / sizeof(templates[0]))) 654 { 655 temprow = 0; 656 tempcolor ++; 657 if (tempcolor >= (int)(sizeof(colors) / sizeof(colors[0]))) 658 tempcolor = 0; 659 else if (tempcolor > 3 && header.cupsColorSpace == CUPS_CSPACE_SW) 660 tempcolor = 0; 661 } 662 663 memset(line, 0xff, header.cupsBytesPerLine); 664 665 if (header.cupsColorSpace == CUPS_CSPACE_SW) 666 { 667 /* 668 * Do grayscale output... 669 */ 670 671 for (lineptr = line + xoff; *template; template ++) 672 { 673 if (*template != ' ') 674 { 675 for (xcount = xrep; xcount > 0; xcount --) 676 *lineptr++ = *color; 677 } 678 else 679 { 680 lineptr += xrep; 681 } 682 } 683 } 684 else 685 { 686 /* 687 * Do color output... 688 */ 689 690 for (lineptr = line + 3 * xoff; *template; template ++) 691 { 692 if (*template != ' ') 693 { 694 for (xcount = xrep; xcount > 0; xcount --, lineptr += 3) 695 memcpy(lineptr, color, 3); 696 } 697 else 698 { 699 lineptr += 3 * xrep; 700 } 701 } 702 } 703 704 for (ycount = yrep; ycount > 0 && y < yend; ycount --, y ++) 705 cupsRasterWritePixels(ras, line, header.cupsBytesPerLine); 706 } 707 708 memset(line, 0xff, header.cupsBytesPerLine); 709 710 for (y = 0; y < header.cupsHeight; y ++) 711 cupsRasterWritePixels(ras, line, header.cupsBytesPerLine); 712 713 cupsRasterClose(ras); 714 715 close(fd); 716 717 printf("PRINT FILE: %s\n", tempname); 718 719 return (tempname); 720 } 721 722 723 /* 724 * 'monitor_printer()' - Monitor the job and printer states. 725 */ 726 727 static void * /* O - Thread exit code */ 728 monitor_printer( 729 _client_monitor_t *monitor) /* I - Monitoring data */ 730 { 731 http_t *http; /* Connection to printer */ 732 ipp_t *request, /* IPP request */ 733 *response; /* IPP response */ 734 ipp_attribute_t *attr; /* Attribute in response */ 735 ipp_pstate_t printer_state; /* Printer state */ 736 char printer_state_reasons[1024]; 737 /* Printer state reasons */ 738 ipp_jstate_t job_state; /* Job state */ 739 char job_state_reasons[1024];/* Printer state reasons */ 740 static const char * const jattrs[] = /* Job attributes we want */ 741 { 742 "job-state", 743 "job-state-reasons" 744 }; 745 static const char * const pattrs[] = /* Printer attributes we want */ 746 { 747 "printer-state", 748 "printer-state-reasons" 749 }; 750 751 752 /* 753 * Open a connection to the printer... 754 */ 755 756 http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC, monitor->encryption, 1, 0, NULL); 757 758 /* 759 * Loop until the job is canceled, aborted, or completed. 760 */ 761 762 printer_state = (ipp_pstate_t)0; 763 printer_state_reasons[0] = '\0'; 764 765 job_state = (ipp_jstate_t)0; 766 job_state_reasons[0] = '\0'; 767 768 while (monitor->job_state < IPP_JSTATE_CANCELED) 769 { 770 /* 771 * Reconnect to the printer as needed... 772 */ 773 774 if (httpGetFd(http) < 0) 775 httpReconnect2(http, 30000, NULL); 776 777 if (httpGetFd(http) >= 0) 778 { 779 /* 780 * Connected, so check on the printer state... 781 */ 782 783 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); 784 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri); 785 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); 786 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs); 787 788 response = cupsDoRequest(http, request, monitor->resource); 789 790 if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL) 791 printer_state = (ipp_pstate_t)ippGetInteger(attr, 0); 792 793 if ((attr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL) 794 ippAttributeString(attr, printer_state_reasons, sizeof(printer_state_reasons)); 795 796 if (printer_state != monitor->printer_state || strcmp(printer_state_reasons, monitor->printer_state_reasons)) 797 { 798 printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state), printer_state_reasons); 799 800 monitor->printer_state = printer_state; 801 strlcpy(monitor->printer_state_reasons, printer_state_reasons, sizeof(monitor->printer_state_reasons)); 802 } 803 804 ippDelete(response); 805 806 if (monitor->job_id > 0) 807 { 808 /* 809 * Check the status of the job itself... 810 */ 811 812 request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES); 813 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri); 814 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor->job_id); 815 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); 816 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs); 817 818 response = cupsDoRequest(http, request, monitor->resource); 819 820 if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL) 821 job_state = (ipp_jstate_t)ippGetInteger(attr, 0); 822 823 if ((attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD)) != NULL) 824 ippAttributeString(attr, job_state_reasons, sizeof(job_state_reasons)); 825 826 if (job_state != monitor->job_state || strcmp(job_state_reasons, monitor->job_state_reasons)) 827 { 828 printf("JOB %d: %s (%s)\n", monitor->job_id, ippEnumString("job-state", job_state), job_state_reasons); 829 830 monitor->job_state = job_state; 831 strlcpy(monitor->job_state_reasons, job_state_reasons, sizeof(monitor->job_state_reasons)); 832 } 833 834 ippDelete(response); 835 } 836 } 837 838 if (monitor->job_state < IPP_JSTATE_CANCELED) 839 { 840 /* 841 * Sleep for 5 seconds... 842 */ 843 844 sleep(5); 845 } 846 } 847 848 /* 849 * Cleanup and return... 850 */ 851 852 httpClose(http); 853 854 return (NULL); 855 } 856 857 858 /* 859 * 'show_attributes()' - Show attributes in a request or response. 860 */ 861 862 static void 863 show_attributes(const char *title, /* I - Title */ 864 int request, /* I - 1 for request, 0 for response */ 865 ipp_t *ipp) /* I - IPP request/response */ 866 { 867 int minor, major = ippGetVersion(ipp, &minor); 868 /* IPP version number */ 869 ipp_tag_t group = IPP_TAG_ZERO; 870 /* Current group tag */ 871 ipp_attribute_t *attr; /* Current attribute */ 872 const char *name; /* Attribute name */ 873 char buffer[1024]; /* Value */ 874 875 876 printf("%s:\n", title); 877 printf(" version=%d.%d\n", major, minor); 878 printf(" request-id=%d\n", ippGetRequestId(ipp)); 879 if (!request) 880 printf(" status-code=%s\n", ippErrorString(ippGetStatusCode(ipp))); 881 882 for (attr = ippFirstAttribute(ipp); attr; attr = ippNextAttribute(ipp)) 883 { 884 if (group != ippGetGroupTag(attr)) 885 { 886 group = ippGetGroupTag(attr); 887 if (group) 888 printf(" %s:\n", ippTagString(group)); 889 } 890 891 if ((name = ippGetName(attr)) != NULL) 892 { 893 ippAttributeString(attr, buffer, sizeof(buffer)); 894 printf(" %s(%s%s)=%s\n", name, ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), buffer); 895 } 896 } 897 } 898 899 900 /* 901 * 'show_capabilities()' - Show printer capabilities. 902 */ 903 904 static void 905 show_capabilities(ipp_t *response) /* I - Printer attributes */ 906 { 907 int i; /* Looping var */ 908 ipp_attribute_t *attr; /* Attribute */ 909 char buffer[1024]; /* Attribute value buffer */ 910 static const char * const pattrs[] = /* Attributes we want to show */ 911 { 912 "copies-default", 913 "copies-supported", 914 "finishings-default", 915 "finishings-ready", 916 "finishings-supported", 917 "media-default", 918 "media-ready", 919 "media-supported", 920 "output-bin-default", 921 "output-bin-supported", 922 "print-color-mode-default", 923 "print-color-mode-supported", 924 "sides-default", 925 "sides-supported", 926 "document-format-default", 927 "document-format-supported", 928 "pwg-raster-document-resolution-supported", 929 "pwg-raster-document-type-supported", 930 "urf-supported" 931 }; 932 933 934 puts("CAPABILITIES:"); 935 for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++) 936 { 937 if ((attr = ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) != NULL) 938 { 939 ippAttributeString(attr, buffer, sizeof(buffer)); 940 printf(" %s=%s\n", pattrs[i], buffer); 941 } 942 } 943 } 944 945 946 /* 947 * 'usage()' - Show program usage... 948 */ 949 950 static void 951 usage(void) 952 { 953 puts("Usage: ./testclient printer-uri [options]"); 954 puts("Options:"); 955 puts(" -d document-format Generate the specified format"); 956 puts(" -f print-file Print the named file"); 957 puts(" -g Force grayscale printing"); 958 puts(" -k Keep temporary files"); 959 puts(" -v Be more verbose"); 960 } 961