]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ipptest.c
Merge changes from CUPS 1.4svn-r7961.
[thirdparty/cups.git] / test / ipptest.c
1 /*
2 * "$Id: ipptest.c 7847 2008-08-19 04:22:14Z mike $"
3 *
4 * IPP test command for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2007 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 * Contents:
16 *
17 * main() - Parse options and do tests.
18 * do_tests() - Do tests as specified in the test file.
19 * ippTagValue() - Get an IPP value or group tag from a name...
20 * get_token() - Get a token from a file.
21 * print_attr() - Print an attribute on the screen.
22 * usage() - Show program usage.
23 */
24
25 /*
26 * Include necessary headers...
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <cups/string.h>
32 #include <errno.h>
33 #include <ctype.h>
34
35 #include <cups/cups.h>
36 #include <cups/language.h>
37
38
39 /*
40 * Globals...
41 */
42
43 int Verbosity = 0; /* Show all attributes? */
44
45
46 /*
47 * Local functions...
48 */
49
50 int do_tests(const char *, const char *);
51 ipp_op_t ippOpValue(const char *);
52 ipp_status_t ippErrorValue(const char *);
53 char *get_token(FILE *, char *, int, int *linenum);
54 void print_attr(ipp_attribute_t *);
55 void usage(const char *option);
56
57
58 /*
59 * 'main()' - Parse options and do tests.
60 */
61
62 int /* O - Exit status */
63 main(int argc, /* I - Number of command-line arguments */
64 char *argv[]) /* I - Command-line arguments */
65 {
66 int i; /* Looping var */
67 int status; /* Status of tests... */
68 const char *uri; /* URI to use */
69 const char *testfile; /* Test file to use */
70 int interval; /* Test interval */
71
72
73 /*
74 * We need at least:
75 *
76 * testipp URL testfile
77 */
78
79 uri = NULL;
80 testfile = NULL;
81 status = 0;
82 interval = 0;
83
84 for (i = 1; i < argc; i ++)
85 {
86 if (argv[i][0] == '-')
87 {
88 if (!strcmp(argv[i], "-v"))
89 Verbosity ++;
90 else if (!strcmp(argv[i], "-d"))
91 {
92 i ++;
93
94 if (i >= argc)
95 usage(NULL);
96 else
97 putenv(argv[i]);
98 }
99 else if (!strcmp(argv[i], "-i"))
100 {
101 i++;
102
103 if (i >= argc)
104 usage(NULL);
105 else
106 interval = atoi(argv[i]);
107 }
108 else
109 usage(argv[i]);
110 }
111 else if (!strncmp(argv[i], "ipp://", 6) ||
112 !strncmp(argv[i], "http://", 7) ||
113 !strncmp(argv[i], "https://", 8))
114 {
115 /*
116 * Set URI...
117 */
118
119 if (!testfile && uri)
120 usage(NULL);
121
122 uri = argv[i];
123 testfile = NULL;
124 }
125 else
126 {
127 /*
128 * Run test...
129 */
130
131 testfile = argv[i];
132
133 if (!do_tests(uri, testfile))
134 status ++;
135 }
136 }
137
138 if (!uri || !testfile)
139 usage(NULL);
140
141 /*
142 * Loop if the interval is set...
143 */
144
145 if (interval)
146 {
147 for (;;)
148 {
149 sleep(interval);
150 do_tests(uri, testfile);
151 }
152 }
153
154 /*
155 * Exit...
156 */
157
158 return (status);
159 }
160
161
162 /*
163 * 'do_tests()' - Do tests as specified in the test file.
164 */
165
166 int /* 1 = success, 0 = failure */
167 do_tests(const char *uri, /* I - URI to connect on */
168 const char *testfile) /* I - Test file to use */
169 {
170 int i; /* Looping var */
171 int linenum; /* Current line number */
172 int version; /* IPP version number to use */
173 http_t *http; /* HTTP connection to server */
174 char method[HTTP_MAX_URI], /* URI method */
175 userpass[HTTP_MAX_URI], /* username:password */
176 server[HTTP_MAX_URI], /* Server */
177 resource[HTTP_MAX_URI]; /* Resource path */
178 int port; /* Port number */
179 FILE *fp; /* Test file */
180 char token[1024], /* Token from file */
181 *tokenptr, /* Pointer into token */
182 temp[1024], /* Temporary string */
183 *tempptr; /* Pointer into temp string */
184 ipp_t *request; /* IPP request */
185 ipp_t *response; /* IPP response */
186 ipp_op_t op; /* Operation */
187 ipp_tag_t group; /* Current group */
188 ipp_tag_t value; /* Current value type */
189 ipp_attribute_t *attrptr; /* Attribute pointer */
190 char attr[128]; /* Attribute name */
191 int num_statuses; /* Number of valid status codes */
192 ipp_status_t statuses[100]; /* Valid status codes */
193 int num_expects; /* Number of expected attributes */
194 char *expects[100]; /* Expected attributes */
195 int num_displayed; /* Number of displayed attributes */
196 char *displayed[100]; /* Displayed attributes */
197 char name[1024]; /* Name of test */
198 char filename[1024]; /* Filename */
199 int pass; /* Did we pass the test? */
200 int job_id; /* Job ID from last operation */
201 int subscription_id; /* Subscription ID from last operation */
202
203
204 /*
205 * Open the test file...
206 */
207
208 if ((fp = fopen(testfile, "r")) == NULL)
209 {
210 printf("Unable to open test file %s - %s\n", testfile, strerror(errno));
211 return (0);
212 }
213
214 /*
215 * Connect to the server...
216 */
217
218 httpSeparateURI(HTTP_URI_CODING_ALL, uri, method, sizeof(method), userpass,
219 sizeof(userpass), server, sizeof(server), &port, resource,
220 sizeof(resource));
221 if ((http = httpConnect(server, port)) == NULL)
222 {
223 printf("Unable to connect to %s on port %d - %s\n", server, port,
224 strerror(errno));
225 fclose(fp);
226 return (0);
227 }
228
229 /*
230 * Loop on tests...
231 */
232
233 printf("\"%s\":\n", testfile);
234 pass = 1;
235 job_id = 0;
236 subscription_id = 0;
237 version = 1;
238 linenum = 1;
239
240 while (get_token(fp, token, sizeof(token), &linenum) != NULL)
241 {
242 /*
243 * Expect an open brace...
244 */
245
246 if (strcmp(token, "{"))
247 {
248 printf("Unexpected token %s seen on line %d - aborting test!\n", token,
249 linenum);
250 httpClose(http);
251 return (0);
252 }
253
254 /*
255 * Initialize things...
256 */
257
258 httpSeparateURI(HTTP_URI_CODING_ALL, uri, method, sizeof(method), userpass,
259 sizeof(userpass), server, sizeof(server), &port, resource,
260 sizeof(resource));
261
262 request = ippNew();
263 op = (ipp_op_t)0;
264 group = IPP_TAG_ZERO;
265 num_statuses = 0;
266 num_expects = 0;
267 num_displayed = 0;
268 filename[0] = '\0';
269
270 strcpy(name, testfile);
271 if (strrchr(name, '.') != NULL)
272 *strrchr(name, '.') = '\0';
273
274 /*
275 * Parse until we see a close brace...
276 */
277
278 while (get_token(fp, token, sizeof(token), &linenum) != NULL)
279 {
280 if (!strcmp(token, "}"))
281 break;
282 else if (!strcasecmp(token, "NAME"))
283 {
284 /*
285 * Name of test...
286 */
287
288 get_token(fp, name, sizeof(name), &linenum);
289 }
290 else if (!strcasecmp(token, "VERSION"))
291 {
292 /*
293 * IPP version number for test...
294 */
295
296 get_token(fp, temp, sizeof(temp), &linenum);
297 sscanf(temp, "%*d.%d", &version);
298 }
299 else if (!strcasecmp(token, "RESOURCE"))
300 {
301 /*
302 * Resource name...
303 */
304
305 get_token(fp, resource, sizeof(resource), &linenum);
306 }
307 else if (!strcasecmp(token, "OPERATION"))
308 {
309 /*
310 * Operation...
311 */
312
313 get_token(fp, token, sizeof(token), &linenum);
314 op = ippOpValue(token);
315 }
316 else if (!strcasecmp(token, "GROUP"))
317 {
318 /*
319 * Attribute group...
320 */
321
322 get_token(fp, token, sizeof(token), &linenum);
323 value = ippTagValue(token);
324
325 if (value == group)
326 ippAddSeparator(request);
327
328 group = value;
329 }
330 else if (!strcasecmp(token, "DELAY"))
331 {
332 /*
333 * Delay before operation...
334 */
335
336 int delay;
337
338 get_token(fp, token, sizeof(token), &linenum);
339 if ((delay = atoi(token)) > 0)
340 sleep(delay);
341 }
342 else if (!strcasecmp(token, "ATTR"))
343 {
344 /*
345 * Attribute...
346 */
347
348 get_token(fp, token, sizeof(token), &linenum);
349 value = ippTagValue(token);
350 get_token(fp, attr, sizeof(attr), &linenum);
351 get_token(fp, temp, sizeof(temp), &linenum);
352
353 token[sizeof(token) - 1] = '\0';
354
355 for (tempptr = temp, tokenptr = token;
356 *tempptr && tokenptr < (token + sizeof(token) - 1);)
357 if (*tempptr == '$')
358 {
359 /*
360 * Substitute a string/number...
361 */
362
363 if (!strncasecmp(tempptr + 1, "uri", 3))
364 {
365 strlcpy(tokenptr, uri, sizeof(token) - (tokenptr - token));
366 tempptr += 4;
367 }
368 else if (!strncasecmp(tempptr + 1, "method", 6))
369 {
370 strlcpy(tokenptr, method, sizeof(token) - (tokenptr - token));
371 tempptr += 7;
372 }
373 else if (!strncasecmp(tempptr + 1, "username", 8))
374 {
375 strlcpy(tokenptr, userpass, sizeof(token) - (tokenptr - token));
376 tempptr += 9;
377 }
378 else if (!strncasecmp(tempptr + 1, "hostname", 8))
379 {
380 strlcpy(tokenptr, server, sizeof(token) - (tokenptr - token));
381 tempptr += 9;
382 }
383 else if (!strncasecmp(tempptr + 1, "port", 4))
384 {
385 snprintf(tokenptr, sizeof(token) - (tokenptr - token),
386 "%d", port);
387 tempptr += 5;
388 }
389 else if (!strncasecmp(tempptr + 1, "resource", 8))
390 {
391 strlcpy(tokenptr, resource, sizeof(token) - (tokenptr - token));
392 tempptr += 9;
393 }
394 else if (!strncasecmp(tempptr + 1, "job-id", 6))
395 {
396 snprintf(tokenptr, sizeof(token) - (tokenptr - token),
397 "%d", job_id);
398 tempptr += 7;
399 }
400 else if (!strncasecmp(tempptr + 1, "notify-subscription-id", 22))
401 {
402 snprintf(tokenptr, sizeof(token) - (tokenptr - token),
403 "%d", subscription_id);
404 tempptr += 23;
405 }
406 else if (!strncasecmp(tempptr + 1, "user", 4))
407 {
408 strlcpy(tokenptr, cupsUser(), sizeof(token) - (tokenptr - token));
409 tempptr += 5;
410 }
411 else if (!strncasecmp(tempptr + 1, "ENV[", 4))
412 {
413 char *end; /* End of $ENV[name] */
414
415
416 if ((end = strchr(tempptr + 5, ']')) != NULL)
417 {
418 *end++ = '\0';
419 strlcpy(tokenptr,
420 getenv(tempptr + 5) ? getenv(tempptr + 5) : tempptr + 5,
421 sizeof(token) - (tokenptr - token));
422 tempptr = end;
423 }
424 else
425 {
426 *tokenptr++ = *tempptr++;
427 *tokenptr = '\0';
428 }
429 }
430 else
431 {
432 *tokenptr++ = *tempptr++;
433 *tokenptr = '\0';
434 }
435
436 tokenptr += strlen(tokenptr);
437 }
438 else
439 {
440 *tokenptr++ = *tempptr++;
441 *tokenptr = '\0';
442 }
443
444 switch (value)
445 {
446 case IPP_TAG_BOOLEAN :
447 if (!strcasecmp(token, "true"))
448 ippAddBoolean(request, group, attr, 1);
449 else
450 ippAddBoolean(request, group, attr, atoi(token));
451 break;
452
453 case IPP_TAG_INTEGER :
454 case IPP_TAG_ENUM :
455 ippAddInteger(request, group, value, attr, atoi(token));
456 break;
457
458 case IPP_TAG_RESOLUTION :
459 puts(" ERROR: resolution tag not yet supported!");
460 break;
461
462 case IPP_TAG_RANGE :
463 puts(" ERROR: range tag not yet supported!");
464 break;
465
466 default :
467 if (!strchr(token, ','))
468 ippAddString(request, group, value, attr, NULL, token);
469 else
470 {
471 /*
472 * Multiple string values...
473 */
474
475 int num_values; /* Number of values */
476 char *values[100], /* Values */
477 *ptr; /* Pointer to next value */
478
479
480 values[0] = token;
481 num_values = 1;
482
483 for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
484 {
485 *ptr++ = '\0';
486 values[num_values] = ptr;
487 num_values ++;
488 }
489
490 ippAddStrings(request, group, value, attr, num_values,
491 NULL, (const char **)values);
492 }
493 break;
494 }
495 }
496 else if (!strcasecmp(token, "FILE"))
497 {
498 /*
499 * File...
500 */
501
502 get_token(fp, filename, sizeof(filename), &linenum);
503 }
504 else if (!strcasecmp(token, "STATUS") &&
505 num_statuses < (int)(sizeof(statuses) / sizeof(statuses[0])))
506 {
507 /*
508 * Status...
509 */
510
511 get_token(fp, token, sizeof(token), &linenum);
512 statuses[num_statuses] = ippErrorValue(token);
513 num_statuses ++;
514 }
515 else if (!strcasecmp(token, "EXPECT") &&
516 num_expects < (int)(sizeof(expects) / sizeof(expects[0])))
517 {
518 /*
519 * Expected attributes...
520 */
521
522 get_token(fp, token, sizeof(token), &linenum);
523 expects[num_expects] = strdup(token);
524 num_expects ++;
525 }
526 else if (!strcasecmp(token, "DISPLAY") &&
527 num_displayed < (int)(sizeof(displayed) / sizeof(displayed[0])))
528 {
529 /*
530 * Display attributes...
531 */
532
533 get_token(fp, token, sizeof(token), &linenum);
534 displayed[num_displayed] = strdup(token);
535 num_displayed ++;
536 }
537 else
538 {
539 printf("Unexpected token %s seen on line %d - aborting test!\n", token,
540 linenum);
541 httpClose(http);
542 ippDelete(request);
543 return (0);
544 }
545 }
546
547 /*
548 * Submit the IPP request...
549 */
550
551 request->request.op.version[1] = version;
552 request->request.op.operation_id = op;
553 request->request.op.request_id = 1;
554
555 if (Verbosity)
556 {
557 printf("%s:\n", ippOpString(op));
558
559 for (attrptr = request->attrs; attrptr; attrptr = attrptr->next)
560 print_attr(attrptr);
561 }
562
563 printf(" %-60.60s [", name);
564 fflush(stdout);
565
566 if (filename[0])
567 response = cupsDoFileRequest(http, request, resource, filename);
568 else
569 response = cupsDoIORequest(http, request, resource, -1,
570 Verbosity ? 1 : -1);
571
572 if (response == NULL)
573 {
574 time_t curtime;
575
576 curtime = time(NULL);
577
578 puts("FAIL]");
579 printf(" ERROR %04x (%s) @ %s\n", cupsLastError(),
580 cupsLastErrorString(), ctime(&curtime));
581 pass = 0;
582 }
583 else
584 {
585 if ((attrptr = ippFindAttribute(response, "job-id",
586 IPP_TAG_INTEGER)) != NULL)
587 job_id = attrptr->values[0].integer;
588
589 if ((attrptr = ippFindAttribute(response, "notify-subscription-id",
590 IPP_TAG_INTEGER)) != NULL)
591 subscription_id = attrptr->values[0].integer;
592
593 for (i = 0; i < num_statuses; i ++)
594 if (response->request.status.status_code == statuses[i])
595 break;
596
597 if (i == num_statuses && num_statuses > 0)
598 pass = 0;
599 else
600 {
601 for (i = 0; i < num_expects; i ++)
602 if (ippFindAttribute(response, expects[i], IPP_TAG_ZERO) == NULL)
603 {
604 pass = 0;
605 break;
606 }
607 }
608
609 if (pass)
610 {
611 puts("PASS]");
612 printf(" RECEIVED: %lu bytes in response\n",
613 (unsigned long)ippLength(response));
614
615 if (Verbosity)
616 {
617 for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next)
618 print_attr(attrptr);
619 }
620 else if (num_displayed > 0)
621 {
622 for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next)
623 if (attrptr->name)
624 {
625 for (i = 0; i < num_displayed; i ++)
626 if (!strcmp(displayed[i], attrptr->name))
627 {
628 print_attr(attrptr);
629 break;
630 }
631 }
632 }
633 }
634 else
635 {
636 puts("FAIL]");
637 printf(" RECEIVED: %lu bytes in response\n",
638 (unsigned long)ippLength(response));
639
640 for (i = 0; i < num_statuses; i ++)
641 if (response->request.status.status_code == statuses[i])
642 break;
643
644 if (i == num_statuses && num_statuses > 0)
645 puts(" BAD STATUS");
646
647 printf(" status-code = %04x (%s)\n",
648 cupsLastError(), ippErrorString(cupsLastError()));
649
650 for (i = 0; i < num_expects; i ++)
651 if (ippFindAttribute(response, expects[i], IPP_TAG_ZERO) == NULL)
652 printf(" EXPECTED: %s\n", expects[i]);
653
654 for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next)
655 print_attr(attrptr);
656 }
657
658 ippDelete(response);
659 }
660
661 for (i = 0; i < num_expects; i ++)
662 free(expects[i]);
663
664 if (!pass)
665 break;
666 }
667
668 fclose(fp);
669 httpClose(http);
670
671 return (pass);
672 }
673
674
675 /*
676 * 'get_token()' - Get a token from a file.
677 */
678
679 char * /* O - Token from file or NULL on EOF */
680 get_token(FILE *fp, /* I - File to read from */
681 char *buf, /* I - Buffer to read into */
682 int buflen, /* I - Length of buffer */
683 int *linenum) /* IO - Current line number */
684 {
685 int ch, /* Character from file */
686 quote; /* Quoting character */
687 char *bufptr, /* Pointer into buffer */
688 *bufend; /* End of buffer */
689
690
691 for (;;)
692 {
693 /*
694 * Skip whitespace...
695 */
696
697 while (isspace(ch = getc(fp)))
698 {
699 if (ch == '\n')
700 (*linenum) ++;
701 }
702
703 /*
704 * Read a token...
705 */
706
707 if (ch == EOF)
708 return (NULL);
709 else if (ch == '\'' || ch == '\"')
710 {
711 /*
712 * Quoted text...
713 */
714
715 quote = ch;
716 bufptr = buf;
717 bufend = buf + buflen - 1;
718
719 while ((ch = getc(fp)) != EOF)
720 if (ch == quote)
721 break;
722 else if (bufptr < bufend)
723 *bufptr++ = ch;
724
725 *bufptr = '\0';
726 return (buf);
727 }
728 else if (ch == '#')
729 {
730 /*
731 * Comment...
732 */
733
734 while ((ch = getc(fp)) != EOF)
735 if (ch == '\n')
736 break;
737
738 (*linenum) ++;
739 }
740 else
741 {
742 /*
743 * Whitespace delimited text...
744 */
745
746 ungetc(ch, fp);
747
748 bufptr = buf;
749 bufend = buf + buflen - 1;
750
751 while ((ch = getc(fp)) != EOF)
752 if (isspace(ch) || ch == '#')
753 break;
754 else if (bufptr < bufend)
755 *bufptr++ = ch;
756
757 if (ch == '#')
758 ungetc(ch, fp);
759
760 *bufptr = '\0';
761 return (buf);
762 }
763 }
764 }
765
766
767 /*
768 * 'print_attr()' - Print an attribute on the screen.
769 */
770
771 void
772 print_attr(ipp_attribute_t *attr) /* I - Attribute to print */
773 {
774 int i; /* Looping var */
775
776
777 if (attr->name == NULL)
778 {
779 puts(" -- separator --");
780 return;
781 }
782
783 printf(" %s (%s%s) = ", attr->name,
784 attr->num_values > 1 ? "1setOf " : "",
785 ippTagString(attr->value_tag));
786
787 switch (attr->value_tag)
788 {
789 case IPP_TAG_INTEGER :
790 case IPP_TAG_ENUM :
791 for (i = 0; i < attr->num_values; i ++)
792 printf("%d ", attr->values[i].integer);
793 break;
794
795 case IPP_TAG_BOOLEAN :
796 for (i = 0; i < attr->num_values; i ++)
797 if (attr->values[i].boolean)
798 printf("true ");
799 else
800 printf("false ");
801 break;
802
803 case IPP_TAG_NOVALUE :
804 printf("novalue");
805 break;
806
807 case IPP_TAG_RANGE :
808 for (i = 0; i < attr->num_values; i ++)
809 printf("%d-%d ", attr->values[i].range.lower,
810 attr->values[i].range.upper);
811 break;
812
813 case IPP_TAG_RESOLUTION :
814 for (i = 0; i < attr->num_values; i ++)
815 printf("%dx%d%s ", attr->values[i].resolution.xres,
816 attr->values[i].resolution.yres,
817 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
818 "dpi" : "dpc");
819 break;
820
821 case IPP_TAG_STRING :
822 case IPP_TAG_TEXT :
823 case IPP_TAG_NAME :
824 case IPP_TAG_KEYWORD :
825 case IPP_TAG_CHARSET :
826 case IPP_TAG_URI :
827 case IPP_TAG_MIMETYPE :
828 case IPP_TAG_LANGUAGE :
829 for (i = 0; i < attr->num_values; i ++)
830 printf("\"%s\" ", attr->values[i].string.text);
831 break;
832
833 case IPP_TAG_TEXTLANG :
834 case IPP_TAG_NAMELANG :
835 for (i = 0; i < attr->num_values; i ++)
836 printf("\"%s\",%s ", attr->values[i].string.text,
837 attr->values[i].string.charset);
838 break;
839
840 default :
841 break; /* anti-compiler-warning-code */
842 }
843
844 putchar('\n');
845 }
846
847
848 /*
849 * 'usage()' - Show program usage.
850 */
851
852 void
853 usage(const char *option) /* I - Option string or NULL */
854 {
855 if (option)
856 fprintf(stderr, "ipptest: Unknown option \"%s\"!\n", option);
857
858 fputs("Usage: ipptest [options] URL testfile [ ... testfileN ]\n", stderr);
859 fputs("Options:\n", stderr);
860 fputs("\n", stderr);
861 fputs("-i N Repeat the last test file once every N seconds.\n", stderr);
862 fputs("-v Show all attributes in response, even on success.\n", stderr);
863
864 exit(1);
865 }
866
867
868 /*
869 * End of "$Id: ipptest.c 7847 2008-08-19 04:22:14Z mike $".
870 */