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