1 /* 2 * CUPS destination API test program for CUPS. 3 * 4 * Copyright 2012-2016 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 <errno.h> 21 #include "cups.h" 22 23 24 /* 25 * Local functions... 26 */ 27 28 static int enum_cb(void *user_data, unsigned flags, cups_dest_t *dest); 29 static void localize(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option, const char *value); 30 static void print_file(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *filename, int num_options, cups_option_t *options); 31 static void show_conflicts(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, int num_options, cups_option_t *options); 32 static void show_default(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option); 33 static void show_media(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, unsigned flags, const char *name); 34 static void show_supported(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option, const char *value); 35 static void usage(const char *arg) __attribute__((noreturn)); 36 37 38 /* 39 * 'main()' - Main entry. 40 */ 41 42 int /* O - Exit status */ 43 main(int argc, /* I - Number of command-line arguments */ 44 char *argv[]) /* I - Command-line arguments */ 45 { 46 http_t *http; /* Connection to destination */ 47 cups_dest_t *dest = NULL; /* Destination */ 48 cups_dinfo_t *dinfo; /* Destination info */ 49 50 51 if (argc < 2) 52 usage(NULL); 53 54 if (!strcmp(argv[1], "--enum")) 55 { 56 int i; /* Looping var */ 57 cups_ptype_t type = 0, /* Printer type filter */ 58 mask = 0; /* Printer type mask */ 59 60 61 for (i = 2; i < argc; i ++) 62 { 63 if (!strcmp(argv[i], "grayscale")) 64 { 65 type |= CUPS_PRINTER_BW; 66 mask |= CUPS_PRINTER_BW; 67 } 68 else if (!strcmp(argv[i], "color")) 69 { 70 type |= CUPS_PRINTER_COLOR; 71 mask |= CUPS_PRINTER_COLOR; 72 } 73 else if (!strcmp(argv[i], "duplex")) 74 { 75 type |= CUPS_PRINTER_DUPLEX; 76 mask |= CUPS_PRINTER_DUPLEX; 77 } 78 else if (!strcmp(argv[i], "staple")) 79 { 80 type |= CUPS_PRINTER_STAPLE; 81 mask |= CUPS_PRINTER_STAPLE; 82 } 83 else if (!strcmp(argv[i], "small")) 84 { 85 type |= CUPS_PRINTER_SMALL; 86 mask |= CUPS_PRINTER_SMALL; 87 } 88 else if (!strcmp(argv[i], "medium")) 89 { 90 type |= CUPS_PRINTER_MEDIUM; 91 mask |= CUPS_PRINTER_MEDIUM; 92 } 93 else if (!strcmp(argv[i], "large")) 94 { 95 type |= CUPS_PRINTER_LARGE; 96 mask |= CUPS_PRINTER_LARGE; 97 } 98 else 99 usage(argv[i]); 100 } 101 102 cupsEnumDests(CUPS_DEST_FLAGS_NONE, 5000, NULL, type, mask, enum_cb, NULL); 103 104 return (0); 105 } 106 else if (!strncmp(argv[1], "ipp://", 6) || !strncmp(argv[1], "ipps://", 7)) 107 dest = cupsGetDestWithURI(NULL, argv[1]); 108 else 109 dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, argv[1], NULL); 110 111 if (!dest) 112 { 113 printf("testdest: Unable to get destination \"%s\": %s\n", argv[1], cupsLastErrorString()); 114 return (1); 115 } 116 117 if ((http = cupsConnectDest(dest, CUPS_DEST_FLAGS_NONE, 30000, NULL, NULL, 0, NULL, NULL)) == NULL) 118 { 119 printf("testdest: Unable to connect to destination \"%s\": %s\n", argv[1], cupsLastErrorString()); 120 return (1); 121 } 122 123 if ((dinfo = cupsCopyDestInfo(http, dest)) == NULL) 124 { 125 printf("testdest: Unable to get information for destination \"%s\": %s\n", argv[1], cupsLastErrorString()); 126 return (1); 127 } 128 129 if (argc == 2 || (!strcmp(argv[2], "supported") && argc < 6)) 130 { 131 if (argc > 3) 132 show_supported(http, dest, dinfo, argv[3], argv[4]); 133 else if (argc > 2) 134 show_supported(http, dest, dinfo, argv[3], NULL); 135 else 136 show_supported(http, dest, dinfo, NULL, NULL); 137 } 138 else if (!strcmp(argv[2], "conflicts") && argc > 3) 139 { 140 int i, /* Looping var */ 141 num_options = 0;/* Number of options */ 142 cups_option_t *options = NULL;/* Options */ 143 144 for (i = 3; i < argc; i ++) 145 num_options = cupsParseOptions(argv[i], num_options, &options); 146 147 show_conflicts(http, dest, dinfo, num_options, options); 148 } 149 else if (!strcmp(argv[2], "default") && argc == 4) 150 { 151 show_default(http, dest, dinfo, argv[3]); 152 } 153 else if (!strcmp(argv[2], "localize") && argc < 6) 154 { 155 if (argc > 3) 156 localize(http, dest, dinfo, argv[3], argv[4]); 157 else if (argc > 2) 158 localize(http, dest, dinfo, argv[3], NULL); 159 else 160 localize(http, dest, dinfo, NULL, NULL); 161 } 162 else if (!strcmp(argv[2], "media")) 163 { 164 int i; /* Looping var */ 165 const char *name = NULL; /* Media name, if any */ 166 unsigned flags = CUPS_MEDIA_FLAGS_DEFAULT; 167 /* Media selection flags */ 168 169 for (i = 3; i < argc; i ++) 170 { 171 if (!strcmp(argv[i], "borderless")) 172 flags = CUPS_MEDIA_FLAGS_BORDERLESS; 173 else if (!strcmp(argv[i], "duplex")) 174 flags = CUPS_MEDIA_FLAGS_DUPLEX; 175 else if (!strcmp(argv[i], "exact")) 176 flags = CUPS_MEDIA_FLAGS_EXACT; 177 else if (!strcmp(argv[i], "ready")) 178 flags = CUPS_MEDIA_FLAGS_READY; 179 else if (name) 180 usage(argv[i]); 181 else 182 name = argv[i]; 183 } 184 185 show_media(http, dest, dinfo, flags, name); 186 } 187 else if (!strcmp(argv[2], "print") && argc > 3) 188 { 189 int i, /* Looping var */ 190 num_options = 0;/* Number of options */ 191 cups_option_t *options = NULL;/* Options */ 192 193 for (i = 4; i < argc; i ++) 194 num_options = cupsParseOptions(argv[i], num_options, &options); 195 196 print_file(http, dest, dinfo, argv[3], num_options, options); 197 } 198 else 199 usage(argv[2]); 200 201 return (0); 202 } 203 204 205 /* 206 * 'enum_cb()' - Print the results from the enumeration of destinations. 207 */ 208 209 static int /* O - 1 to continue */ 210 enum_cb(void *user_data, /* I - User data (unused) */ 211 unsigned flags, /* I - Flags */ 212 cups_dest_t *dest) /* I - Destination */ 213 { 214 int i; /* Looping var */ 215 216 217 (void)user_data; 218 (void)flags; 219 220 if (dest->instance) 221 printf("%s/%s:\n", dest->name, dest->instance); 222 else 223 printf("%s:\n", dest->name); 224 225 for (i = 0; i < dest->num_options; i ++) 226 printf(" %s=\"%s\"\n", dest->options[i].name, dest->options[i].value); 227 228 return (1); 229 } 230 231 232 /* 233 * 'localize()' - Localize an option and value. 234 */ 235 236 static void 237 localize(http_t *http, /* I - Connection to destination */ 238 cups_dest_t *dest, /* I - Destination */ 239 cups_dinfo_t *dinfo, /* I - Destination information */ 240 const char *option, /* I - Option */ 241 const char *value) /* I - Value, if any */ 242 { 243 ipp_attribute_t *attr; /* Attribute */ 244 int i, /* Looping var */ 245 count; /* Number of values */ 246 247 248 if (!option) 249 { 250 attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes"); 251 if (attr) 252 { 253 count = ippGetCount(attr); 254 for (i = 0; i < count; i ++) 255 localize(http, dest, dinfo, ippGetString(attr, i, NULL), NULL); 256 } 257 else 258 { 259 static const char * const options[] = 260 { /* List of standard options */ 261 CUPS_COPIES, 262 CUPS_FINISHINGS, 263 CUPS_MEDIA, 264 CUPS_NUMBER_UP, 265 CUPS_ORIENTATION, 266 CUPS_PRINT_COLOR_MODE, 267 CUPS_PRINT_QUALITY, 268 CUPS_SIDES 269 }; 270 271 puts("No job-creation-attributes-supported attribute, probing instead."); 272 273 for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++) 274 if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL)) 275 localize(http, dest, dinfo, options[i], NULL); 276 } 277 } 278 else if (!value) 279 { 280 printf("%s (%s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option)); 281 282 if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL) 283 { 284 count = ippGetCount(attr); 285 286 switch (ippGetValueTag(attr)) 287 { 288 case IPP_TAG_INTEGER : 289 for (i = 0; i < count; i ++) 290 printf(" %d\n", ippGetInteger(attr, i)); 291 break; 292 293 case IPP_TAG_ENUM : 294 for (i = 0; i < count; i ++) 295 printf(" %s\n", ippEnumString(option, ippGetInteger(attr, i))); 296 break; 297 298 case IPP_TAG_RANGE : 299 for (i = 0; i < count; i ++) 300 { 301 int upper, lower = ippGetRange(attr, i, &upper); 302 303 printf(" %d-%d\n", lower, upper); 304 } 305 break; 306 307 case IPP_TAG_RESOLUTION : 308 for (i = 0; i < count; i ++) 309 { 310 int xres, yres; 311 ipp_res_t units; 312 xres = ippGetResolution(attr, i, &yres, &units); 313 314 if (xres == yres) 315 printf(" %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm"); 316 else 317 printf(" %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm"); 318 } 319 break; 320 321 case IPP_TAG_TEXTLANG : 322 case IPP_TAG_NAMELANG : 323 case IPP_TAG_TEXT : 324 case IPP_TAG_NAME : 325 case IPP_TAG_KEYWORD : 326 case IPP_TAG_URI : 327 case IPP_TAG_URISCHEME : 328 case IPP_TAG_CHARSET : 329 case IPP_TAG_LANGUAGE : 330 case IPP_TAG_MIMETYPE : 331 for (i = 0; i < count; i ++) 332 printf(" %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL))); 333 break; 334 335 case IPP_TAG_STRING : 336 for (i = 0; i < count; i ++) 337 { 338 int j, len; 339 unsigned char *data = ippGetOctetString(attr, i, &len); 340 341 fputs(" ", stdout); 342 for (j = 0; j < len; j ++) 343 { 344 if (data[j] < ' ' || data[j] >= 0x7f) 345 printf("<%02X>", data[j]); 346 else 347 putchar(data[j]); 348 } 349 putchar('\n'); 350 } 351 break; 352 353 case IPP_TAG_BOOLEAN : 354 break; 355 356 default : 357 printf(" %s\n", ippTagString(ippGetValueTag(attr))); 358 break; 359 } 360 } 361 362 } 363 else 364 puts(cupsLocalizeDestValue(http, dest, dinfo, option, value)); 365 } 366 367 368 /* 369 * 'print_file()' - Print a file. 370 */ 371 372 static void 373 print_file(http_t *http, /* I - Connection to destination */ 374 cups_dest_t *dest, /* I - Destination */ 375 cups_dinfo_t *dinfo, /* I - Destination information */ 376 const char *filename, /* I - File to print */ 377 int num_options, /* I - Number of options */ 378 cups_option_t *options) /* I - Options */ 379 { 380 cups_file_t *fp; /* File to print */ 381 int job_id; /* Job ID */ 382 ipp_status_t status; /* Submission status */ 383 const char *title; /* Title of job */ 384 char buffer[32768]; /* File buffer */ 385 ssize_t bytes; /* Bytes read/to write */ 386 387 388 if ((fp = cupsFileOpen(filename, "r")) == NULL) 389 { 390 printf("Unable to open \"%s\": %s\n", filename, strerror(errno)); 391 return; 392 } 393 394 if ((title = strrchr(filename, '/')) != NULL) 395 title ++; 396 else 397 title = filename; 398 399 if ((status = cupsCreateDestJob(http, dest, dinfo, &job_id, title, num_options, options)) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED) 400 { 401 printf("Unable to create job: %s\n", cupsLastErrorString()); 402 cupsFileClose(fp); 403 return; 404 } 405 406 printf("Created job ID: %d\n", job_id); 407 408 if (cupsStartDestDocument(http, dest, dinfo, job_id, title, CUPS_FORMAT_AUTO, 0, NULL, 1) != HTTP_STATUS_CONTINUE) 409 { 410 printf("Unable to send document: %s\n", cupsLastErrorString()); 411 cupsFileClose(fp); 412 return; 413 } 414 415 while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0) 416 { 417 if (cupsWriteRequestData(http, buffer, (size_t)bytes) != HTTP_STATUS_CONTINUE) 418 { 419 printf("Unable to write document data: %s\n", cupsLastErrorString()); 420 break; 421 } 422 } 423 424 cupsFileClose(fp); 425 426 if ((status = cupsFinishDestDocument(http, dest, dinfo)) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED) 427 { 428 printf("Unable to send document: %s\n", cupsLastErrorString()); 429 return; 430 } 431 432 puts("Job queued."); 433 } 434 435 436 /* 437 * 'show_conflicts()' - Show conflicts for selected options. 438 */ 439 440 static void 441 show_conflicts( 442 http_t *http, /* I - Connection to destination */ 443 cups_dest_t *dest, /* I - Destination */ 444 cups_dinfo_t *dinfo, /* I - Destination information */ 445 int num_options, /* I - Number of options */ 446 cups_option_t *options) /* I - Options */ 447 { 448 (void)http; 449 (void)dest; 450 (void)dinfo; 451 (void)num_options; 452 (void)options; 453 } 454 455 456 /* 457 * 'show_default()' - Show default value for option. 458 */ 459 460 static void 461 show_default(http_t *http, /* I - Connection to destination */ 462 cups_dest_t *dest, /* I - Destination */ 463 cups_dinfo_t *dinfo, /* I - Destination information */ 464 const char *option) /* I - Option */ 465 { 466 (void)http; 467 (void)dest; 468 (void)dinfo; 469 (void)option; 470 } 471 472 473 /* 474 * 'show_media()' - Show available media. 475 */ 476 477 static void 478 show_media(http_t *http, /* I - Connection to destination */ 479 cups_dest_t *dest, /* I - Destination */ 480 cups_dinfo_t *dinfo, /* I - Destination information */ 481 unsigned flags, /* I - Media flags */ 482 const char *name) /* I - Size name */ 483 { 484 int i, /* Looping var */ 485 count; /* Number of sizes */ 486 cups_size_t size; /* Media size info */ 487 488 489 if (name) 490 { 491 double dw, dl; /* Width and length from name */ 492 char units[32]; /* Units */ 493 int width, /* Width in 100ths of millimeters */ 494 length; /* Length in 100ths of millimeters */ 495 496 497 if (sscanf(name, "%lfx%lf%31s", &dw, &dl, units) == 3) 498 { 499 if (!strcmp(units, "in")) 500 { 501 width = (int)(dw * 2540.0); 502 length = (int)(dl * 2540.0); 503 } 504 else if (!strcmp(units, "mm")) 505 { 506 width = (int)(dw * 100.0); 507 length = (int)(dl * 100.0); 508 } 509 else 510 { 511 puts(" bad units in size"); 512 return; 513 } 514 515 if (cupsGetDestMediaBySize(http, dest, dinfo, width, length, flags, &size)) 516 { 517 printf(" %s (%s) %dx%d B%d L%d R%d T%d\n", size.media, cupsLocalizeDestMedia(http, dest, dinfo, flags, &size), size.width, size.length, size.bottom, size.left, size.right, size.top); 518 } 519 else 520 { 521 puts(" not supported"); 522 } 523 } 524 else if (cupsGetDestMediaByName(http, dest, dinfo, name, flags, &size)) 525 { 526 printf(" %s (%s) %dx%d B%d L%d R%d T%d\n", size.media, cupsLocalizeDestMedia(http, dest, dinfo, flags, &size), size.width, size.length, size.bottom, size.left, size.right, size.top); 527 } 528 else 529 { 530 puts(" not supported"); 531 } 532 } 533 else 534 { 535 count = cupsGetDestMediaCount(http, dest, dinfo, flags); 536 printf("%d size%s:\n", count, count == 1 ? "" : "s"); 537 538 for (i = 0; i < count; i ++) 539 { 540 if (cupsGetDestMediaByIndex(http, dest, dinfo, i, flags, &size)) 541 printf(" %s (%s) %dx%d B%d L%d R%d T%d\n", size.media, cupsLocalizeDestMedia(http, dest, dinfo, flags, &size), size.width, size.length, size.bottom, size.left, size.right, size.top); 542 else 543 puts(" error"); 544 } 545 } 546 } 547 548 549 /* 550 * 'show_supported()' - Show supported options, values, etc. 551 */ 552 553 static void 554 show_supported(http_t *http, /* I - Connection to destination */ 555 cups_dest_t *dest, /* I - Destination */ 556 cups_dinfo_t *dinfo, /* I - Destination information */ 557 const char *option, /* I - Option, if any */ 558 const char *value) /* I - Value, if any */ 559 { 560 ipp_attribute_t *attr; /* Attribute */ 561 int i, /* Looping var */ 562 count; /* Number of values */ 563 564 565 if (!option) 566 { 567 attr = cupsFindDestSupported(http, dest, dinfo, "job-creation-attributes"); 568 if (attr) 569 { 570 count = ippGetCount(attr); 571 for (i = 0; i < count; i ++) 572 show_supported(http, dest, dinfo, ippGetString(attr, i, NULL), NULL); 573 } 574 else 575 { 576 static const char * const options[] = 577 { /* List of standard options */ 578 CUPS_COPIES, 579 CUPS_FINISHINGS, 580 CUPS_MEDIA, 581 CUPS_NUMBER_UP, 582 CUPS_ORIENTATION, 583 CUPS_PRINT_COLOR_MODE, 584 CUPS_PRINT_QUALITY, 585 CUPS_SIDES 586 }; 587 588 puts("No job-creation-attributes-supported attribute, probing instead."); 589 590 for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])); i ++) 591 if (cupsCheckDestSupported(http, dest, dinfo, options[i], NULL)) 592 show_supported(http, dest, dinfo, options[i], NULL); 593 } 594 } 595 else if (!value) 596 { 597 puts(option); 598 if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL) 599 { 600 count = ippGetCount(attr); 601 602 switch (ippGetValueTag(attr)) 603 { 604 case IPP_TAG_INTEGER : 605 for (i = 0; i < count; i ++) 606 printf(" %d\n", ippGetInteger(attr, i)); 607 break; 608 609 case IPP_TAG_ENUM : 610 for (i = 0; i < count; i ++) 611 printf(" %s\n", ippEnumString(option, ippGetInteger(attr, i))); 612 break; 613 614 case IPP_TAG_RANGE : 615 for (i = 0; i < count; i ++) 616 { 617 int upper, lower = ippGetRange(attr, i, &upper); 618 619 printf(" %d-%d\n", lower, upper); 620 } 621 break; 622 623 case IPP_TAG_RESOLUTION : 624 for (i = 0; i < count; i ++) 625 { 626 int xres, yres; 627 ipp_res_t units; 628 xres = ippGetResolution(attr, i, &yres, &units); 629 630 if (xres == yres) 631 printf(" %d%s\n", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm"); 632 else 633 printf(" %dx%d%s\n", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm"); 634 } 635 break; 636 637 case IPP_TAG_TEXTLANG : 638 case IPP_TAG_NAMELANG : 639 case IPP_TAG_TEXT : 640 case IPP_TAG_NAME : 641 case IPP_TAG_KEYWORD : 642 case IPP_TAG_URI : 643 case IPP_TAG_URISCHEME : 644 case IPP_TAG_CHARSET : 645 case IPP_TAG_LANGUAGE : 646 case IPP_TAG_MIMETYPE : 647 for (i = 0; i < count; i ++) 648 printf(" %s\n", ippGetString(attr, i, NULL)); 649 break; 650 651 case IPP_TAG_STRING : 652 for (i = 0; i < count; i ++) 653 { 654 int j, len; 655 unsigned char *data = ippGetOctetString(attr, i, &len); 656 657 fputs(" ", stdout); 658 for (j = 0; j < len; j ++) 659 { 660 if (data[j] < ' ' || data[j] >= 0x7f) 661 printf("<%02X>", data[j]); 662 else 663 putchar(data[j]); 664 } 665 putchar('\n'); 666 } 667 break; 668 669 case IPP_TAG_BOOLEAN : 670 break; 671 672 default : 673 printf(" %s\n", ippTagString(ippGetValueTag(attr))); 674 break; 675 } 676 } 677 678 } 679 else if (cupsCheckDestSupported(http, dest, dinfo, option, value)) 680 puts("YES"); 681 else 682 puts("NO"); 683 } 684 685 686 /* 687 * 'usage()' - Show program usage. 688 */ 689 690 static void 691 usage(const char *arg) /* I - Argument for usage message */ 692 { 693 if (arg) 694 printf("testdest: Unknown option \"%s\".\n", arg); 695 696 puts("Usage:"); 697 puts(" ./testdest name [operation ...]"); 698 puts(" ./testdest ipp://... [operation ...]"); 699 puts(" ./testdest ipps://... [operation ...]"); 700 puts(" ./testdest --enum [grayscale] [color] [duplex] [staple] [small]\n" 701 " [medium] [large]"); 702 puts(""); 703 puts("Operations:"); 704 puts(" conflicts options"); 705 puts(" default option"); 706 puts(" localize option [value]"); 707 puts(" media [borderless] [duplex] [exact] [ready] [name or size]"); 708 puts(" print filename [options]"); 709 puts(" supported [option [value]]"); 710 711 exit(arg != NULL); 712 } 713