1/* 2 * CUPS API test program for CUPS. 3 * 4 * Copyright 2007-2017 by Apple Inc. 5 * Copyright 2007 by Easy Software Products. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16/* 17 * Include necessary headers... 18 */ 19 20#undef _CUPS_NO_DEPRECATED 21#include "string-private.h" 22#include "cups.h" 23#include "ppd.h" 24#include <stdlib.h> 25 26 27/* 28 * Local functions... 29 */ 30 31static int dests_equal(cups_dest_t *a, cups_dest_t *b); 32static int enum_cb(void *user_data, unsigned flags, cups_dest_t *dest); 33static void show_diffs(cups_dest_t *a, cups_dest_t *b); 34 35 36/* 37 * 'main()' - Main entry. 38 */ 39 40int /* O - Exit status */ 41main(int argc, /* I - Number of command-line arguments */ 42 char *argv[]) /* I - Command-line arguments */ 43{ 44 int status = 0, /* Exit status */ 45 i, /* Looping var */ 46 num_dests; /* Number of destinations */ 47 cups_dest_t *dests, /* Destinations */ 48 *dest, /* Current destination */ 49 *named_dest; /* Current named destination */ 50 const char *dest_name, /* Destination name */ 51 *dval, /* Destination value */ 52 *ppdfile; /* PPD file */ 53 ppd_file_t *ppd; /* PPD file data */ 54 int num_jobs; /* Number of jobs for queue */ 55 cups_job_t *jobs; /* Jobs for queue */ 56 57 58 if (argc > 1) 59 { 60 if (!strcmp(argv[1], "enum")) 61 { 62 cups_ptype_t mask = CUPS_PRINTER_LOCAL, 63 /* Printer type mask */ 64 type = CUPS_PRINTER_LOCAL; 65 /* Printer type */ 66 int msec = 0; /* Timeout in milliseconds */ 67 68 69 for (i = 2; i < argc; i ++) 70 if (isdigit(argv[i][0] & 255) || argv[i][0] == '.') 71 msec = (int)(atof(argv[i]) * 1000); 72 else if (!_cups_strcasecmp(argv[i], "bw")) 73 { 74 mask |= CUPS_PRINTER_BW; 75 type |= CUPS_PRINTER_BW; 76 } 77 else if (!_cups_strcasecmp(argv[i], "color")) 78 { 79 mask |= CUPS_PRINTER_COLOR; 80 type |= CUPS_PRINTER_COLOR; 81 } 82 else if (!_cups_strcasecmp(argv[i], "mono")) 83 { 84 mask |= CUPS_PRINTER_COLOR; 85 } 86 else if (!_cups_strcasecmp(argv[i], "duplex")) 87 { 88 mask |= CUPS_PRINTER_DUPLEX; 89 type |= CUPS_PRINTER_DUPLEX; 90 } 91 else if (!_cups_strcasecmp(argv[i], "simplex")) 92 { 93 mask |= CUPS_PRINTER_DUPLEX; 94 } 95 else if (!_cups_strcasecmp(argv[i], "staple")) 96 { 97 mask |= CUPS_PRINTER_STAPLE; 98 type |= CUPS_PRINTER_STAPLE; 99 } 100 else if (!_cups_strcasecmp(argv[i], "copies")) 101 { 102 mask |= CUPS_PRINTER_COPIES; 103 type |= CUPS_PRINTER_COPIES; 104 } 105 else if (!_cups_strcasecmp(argv[i], "collate")) 106 { 107 mask |= CUPS_PRINTER_COLLATE; 108 type |= CUPS_PRINTER_COLLATE; 109 } 110 else if (!_cups_strcasecmp(argv[i], "punch")) 111 { 112 mask |= CUPS_PRINTER_PUNCH; 113 type |= CUPS_PRINTER_PUNCH; 114 } 115 else if (!_cups_strcasecmp(argv[i], "cover")) 116 { 117 mask |= CUPS_PRINTER_COVER; 118 type |= CUPS_PRINTER_COVER; 119 } 120 else if (!_cups_strcasecmp(argv[i], "bind")) 121 { 122 mask |= CUPS_PRINTER_BIND; 123 type |= CUPS_PRINTER_BIND; 124 } 125 else if (!_cups_strcasecmp(argv[i], "sort")) 126 { 127 mask |= CUPS_PRINTER_SORT; 128 type |= CUPS_PRINTER_SORT; 129 } 130 else if (!_cups_strcasecmp(argv[i], "mfp")) 131 { 132 mask |= CUPS_PRINTER_MFP; 133 type |= CUPS_PRINTER_MFP; 134 } 135 else if (!_cups_strcasecmp(argv[i], "printer")) 136 { 137 mask |= CUPS_PRINTER_MFP; 138 } 139 else if (!_cups_strcasecmp(argv[i], "large")) 140 { 141 mask |= CUPS_PRINTER_LARGE; 142 type |= CUPS_PRINTER_LARGE; 143 } 144 else if (!_cups_strcasecmp(argv[i], "medium")) 145 { 146 mask |= CUPS_PRINTER_MEDIUM; 147 type |= CUPS_PRINTER_MEDIUM; 148 } 149 else if (!_cups_strcasecmp(argv[i], "small")) 150 { 151 mask |= CUPS_PRINTER_SMALL; 152 type |= CUPS_PRINTER_SMALL; 153 } 154 else 155 fprintf(stderr, "Unknown argument \"%s\" ignored...\n", argv[i]); 156 157 cupsEnumDests(CUPS_DEST_FLAGS_NONE, msec, NULL, type, mask, enum_cb, NULL); 158 } 159 else if (!strcmp(argv[1], "password")) 160 { 161 const char *pass = cupsGetPassword("Password:"); 162 /* Password string */ 163 164 if (pass) 165 printf("Password entered: %s\n", pass); 166 else 167 puts("No password entered."); 168 } 169 else if (!strcmp(argv[1], "ppd") && argc == 3) 170 { 171 /* 172 * ./testcups ppd printer 173 */ 174 175 http_status_t http_status; /* Status */ 176 char buffer[1024]; /* PPD filename */ 177 time_t modtime = 0; /* Last modified */ 178 179 if ((http_status = cupsGetPPD3(CUPS_HTTP_DEFAULT, argv[2], &modtime, 180 buffer, sizeof(buffer))) != HTTP_STATUS_OK) 181 printf("Unable to get PPD: %d (%s)\n", (int)http_status, 182 cupsLastErrorString()); 183 else 184 puts(buffer); 185 } 186 else if (!strcmp(argv[1], "print") && argc == 5) 187 { 188 /* 189 * ./testcups print printer file interval 190 */ 191 192 int interval, /* Interval between writes */ 193 job_id; /* Job ID */ 194 cups_file_t *fp; /* Print file */ 195 char buffer[16384]; /* Read/write buffer */ 196 ssize_t bytes; /* Bytes read/written */ 197 198 if ((fp = cupsFileOpen(argv[3], "r")) == NULL) 199 { 200 printf("Unable to open \"%s\": %s\n", argv[2], strerror(errno)); 201 return (1); 202 } 203 204 if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, argv[2], "testcups", 0, 205 NULL)) <= 0) 206 { 207 printf("Unable to create print job on %s: %s\n", argv[1], 208 cupsLastErrorString()); 209 return (1); 210 } 211 212 interval = atoi(argv[4]); 213 214 if (cupsStartDocument(CUPS_HTTP_DEFAULT, argv[1], job_id, argv[2], 215 CUPS_FORMAT_AUTO, 1) != HTTP_STATUS_CONTINUE) 216 { 217 puts("Unable to start document!"); 218 return (1); 219 } 220 221 while ((bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0) 222 { 223 printf("Writing %d bytes...\n", (int)bytes); 224 225 if (cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, (size_t)bytes) != HTTP_STATUS_CONTINUE) 226 { 227 puts("Unable to write bytes!"); 228 return (1); 229 } 230 231 if (interval > 0) 232 sleep((unsigned)interval); 233 } 234 235 cupsFileClose(fp); 236 237 if (cupsFinishDocument(CUPS_HTTP_DEFAULT, 238 argv[1]) > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED) 239 { 240 puts("Unable to finish document!"); 241 return (1); 242 } 243 } 244 else 245 { 246 puts("Usage:"); 247 puts(""); 248 puts("Run basic unit tests:"); 249 puts(""); 250 puts(" ./testcups"); 251 puts(""); 252 puts("Enumerate printers (for N seconds, -1 for indefinitely):"); 253 puts(""); 254 puts(" ./testcups enum [seconds]"); 255 puts(""); 256 puts("Ask for a password:"); 257 puts(""); 258 puts(" ./testcups password"); 259 puts(""); 260 puts("Get the PPD file:"); 261 puts(""); 262 puts(" ./testcups ppd printer"); 263 puts(""); 264 puts("Print a file (interval controls delay between buffers in seconds):"); 265 puts(""); 266 puts(" ./testcups print printer file interval"); 267 return (1); 268 } 269 270 return (0); 271 } 272 273 /* 274 * cupsGetDests() 275 */ 276 277 fputs("cupsGetDests: ", stdout); 278 fflush(stdout); 279 280 num_dests = cupsGetDests(&dests); 281 282 if (num_dests == 0) 283 { 284 puts("FAIL"); 285 return (1); 286 } 287 else 288 { 289 printf("PASS (%d dests)\n", num_dests); 290 291 for (i = num_dests, dest = dests; i > 0; i --, dest ++) 292 { 293 printf(" %s", dest->name); 294 295 if (dest->instance) 296 printf(" /%s", dest->instance); 297 298 if (dest->is_default) 299 puts(" ***DEFAULT***"); 300 else 301 putchar('\n'); 302 } 303 } 304 305 /* 306 * cupsGetDest(NULL) 307 */ 308 309 fputs("cupsGetDest(NULL): ", stdout); 310 fflush(stdout); 311 312 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) 313 { 314 for (i = num_dests, dest = dests; i > 0; i --, dest ++) 315 if (dest->is_default) 316 break; 317 318 if (i) 319 { 320 status = 1; 321 puts("FAIL"); 322 } 323 else 324 puts("PASS (no default)"); 325 326 dest = NULL; 327 } 328 else 329 printf("PASS (%s)\n", dest->name); 330 331 /* 332 * cupsGetNamedDest(NULL, NULL, NULL) 333 */ 334 335 fputs("cupsGetNamedDest(NULL, NULL, NULL): ", stdout); 336 fflush(stdout); 337 338 if ((named_dest = cupsGetNamedDest(NULL, NULL, NULL)) == NULL || 339 !dests_equal(dest, named_dest)) 340 { 341 if (!dest) 342 puts("PASS (no default)"); 343 else if (named_dest) 344 { 345 puts("FAIL (different values)"); 346 show_diffs(dest, named_dest); 347 status = 1; 348 } 349 else 350 { 351 puts("FAIL (no default)"); 352 status = 1; 353 } 354 } 355 else 356 printf("PASS (%s)\n", named_dest->name); 357 358 if (named_dest) 359 cupsFreeDests(1, named_dest); 360 361 /* 362 * cupsGetDest(printer) 363 */ 364 365 for (i = 0, dest_name = NULL; i < num_dests; i ++) 366 { 367 if ((dval = cupsGetOption("printer-is-temporary", dests[i].num_options, dest[i].options)) != NULL && !strcmp(dval, "false")) 368 { 369 dest_name = dests[i].name; 370 break; 371 } 372 } 373 374 printf("cupsGetDest(\"%s\"): ", dest_name ? dest_name : "(null)"); 375 fflush(stdout); 376 377 if ((dest = cupsGetDest(dest_name, NULL, num_dests, dests)) == NULL) 378 { 379 puts("FAIL"); 380 return (1); 381 } 382 else 383 puts("PASS"); 384 385 /* 386 * cupsGetNamedDest(NULL, printer, instance) 387 */ 388 389 printf("cupsGetNamedDest(NULL, \"%s\", \"%s\"): ", dest->name, 390 dest->instance ? dest->instance : "(null)"); 391 fflush(stdout); 392 393 if ((named_dest = cupsGetNamedDest(NULL, dest->name, dest->instance)) == NULL || 394 !dests_equal(dest, named_dest)) 395 { 396 if (named_dest) 397 { 398 puts("FAIL (different values)"); 399 show_diffs(dest, named_dest); 400 } 401 else 402 puts("FAIL (no destination)"); 403 404 405 status = 1; 406 } 407 else 408 puts("PASS"); 409 410 if (named_dest) 411 cupsFreeDests(1, named_dest); 412 413 /* 414 * cupsPrintFile() 415 */ 416 417 fputs("cupsPrintFile: ", stdout); 418 fflush(stdout); 419 420 if (cupsPrintFile(dest->name, "../test/testfile.pdf", "Test Page", 421 dest->num_options, dest->options) <= 0) 422 { 423 printf("FAIL (%s)\n", cupsLastErrorString()); 424 return (1); 425 } 426 else 427 puts("PASS"); 428 429 /* 430 * cupsGetPPD(printer) 431 */ 432 433 fputs("cupsGetPPD: ", stdout); 434 fflush(stdout); 435 436 if ((ppdfile = cupsGetPPD(dest->name)) == NULL) 437 { 438 puts("FAIL"); 439 } 440 else 441 { 442 puts("PASS"); 443 444 /* 445 * ppdOpenFile() 446 */ 447 448 fputs("ppdOpenFile: ", stdout); 449 fflush(stdout); 450 451 if ((ppd = ppdOpenFile(ppdfile)) == NULL) 452 { 453 puts("FAIL"); 454 return (1); 455 } 456 else 457 puts("PASS"); 458 459 ppdClose(ppd); 460 unlink(ppdfile); 461 } 462 463 /* 464 * cupsGetJobs() 465 */ 466 467 fputs("cupsGetJobs: ", stdout); 468 fflush(stdout); 469 470 num_jobs = cupsGetJobs(&jobs, NULL, 0, -1); 471 472 if (num_jobs == 0) 473 { 474 puts("FAIL"); 475 return (1); 476 } 477 else 478 puts("PASS"); 479 480 cupsFreeJobs(num_jobs, jobs); 481 cupsFreeDests(num_dests, dests); 482 483 return (status); 484} 485 486 487/* 488 * 'dests_equal()' - Determine whether two destinations are equal. 489 */ 490 491static int /* O - 1 if equal, 0 if not equal */ 492dests_equal(cups_dest_t *a, /* I - First destination */ 493 cups_dest_t *b) /* I - Second destination */ 494{ 495 int i; /* Looping var */ 496 cups_option_t *aoption; /* Current option */ 497 const char *bval; /* Option value */ 498 499 500 if (a == b) 501 return (1); 502 503 if (!a || !b) 504 return (0); 505 506 if (_cups_strcasecmp(a->name, b->name) || 507 (a->instance && !b->instance) || 508 (!a->instance && b->instance) || 509 (a->instance && _cups_strcasecmp(a->instance, b->instance)) || 510 a->num_options != b->num_options) 511 return (0); 512 513 for (i = a->num_options, aoption = a->options; i > 0; i --, aoption ++) 514 if ((bval = cupsGetOption(aoption->name, b->num_options, 515 b->options)) == NULL || 516 strcmp(aoption->value, bval)) 517 return (0); 518 519 return (1); 520} 521 522 523/* 524 * 'enum_cb()' - Report additions and removals. 525 */ 526 527static int /* O - 1 to continue, 0 to stop */ 528enum_cb(void *user_data, /* I - User data (unused) */ 529 unsigned flags, /* I - Destination flags */ 530 cups_dest_t *dest) /* I - Destination */ 531{ 532 int i; /* Looping var */ 533 cups_option_t *option; /* Current option */ 534 535 536 (void)user_data; 537 538 if (flags & CUPS_DEST_FLAGS_REMOVED) 539 printf("Removed '%s':\n", dest->name); 540 else 541 printf("Added '%s':\n", dest->name); 542 543 for (i = dest->num_options, option = dest->options; i > 0; i --, option ++) 544 printf(" %s=\"%s\"\n", option->name, option->value); 545 546 putchar('\n'); 547 548 return (1); 549} 550 551 552/* 553 * 'show_diffs()' - Show differences between two destinations. 554 */ 555 556static void 557show_diffs(cups_dest_t *a, /* I - First destination */ 558 cups_dest_t *b) /* I - Second destination */ 559{ 560 int i; /* Looping var */ 561 cups_option_t *aoption; /* Current option */ 562 cups_option_t *boption; /* Current option */ 563 const char *bval; /* Option value */ 564 565 566 if (!a || !b) 567 return; 568 569 puts(" Item cupsGetDest cupsGetNamedDest"); 570 puts(" -------------------- ------------------------ ------------------------"); 571 572 if (_cups_strcasecmp(a->name, b->name)) 573 printf(" name %-24.24s %-24.24s\n", a->name, b->name); 574 575 if ((a->instance && !b->instance) || 576 (!a->instance && b->instance) || 577 (a->instance && _cups_strcasecmp(a->instance, b->instance))) 578 printf(" instance %-24.24s %-24.24s\n", 579 a->instance ? a->instance : "(null)", 580 b->instance ? b->instance : "(null)"); 581 582 if (a->num_options != b->num_options) 583 printf(" num_options %-24d %-24d\n", a->num_options, 584 b->num_options); 585 586 for (i = a->num_options, aoption = a->options; i > 0; i --, aoption ++) 587 if ((bval = cupsGetOption(aoption->name, b->num_options, 588 b->options)) == NULL || 589 strcmp(aoption->value, bval)) 590 printf(" %-20.20s %-24.24s %-24.24s\n", aoption->name, 591 aoption->value, bval ? bval : "(null)"); 592 593 for (i = b->num_options, boption = b->options; i > 0; i --, boption ++) 594 if (!cupsGetOption(boption->name, a->num_options, a->options)) 595 printf(" %-20.20s %-24.24s %-24.24s\n", boption->name, 596 boption->value, "(null)"); 597} 598