1 /*
2 * Scheduler notification tester for CUPS.
3 *
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 2006-2007 by Easy Software Products.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
8 */
9
10 /*
11 * Include necessary headers...
12 */
13
14 #include <cups/cups.h>
15 #include <cups/debug-private.h>
16 #include <cups/string-private.h>
17 #include <signal.h>
18 #include <cups/ipp-private.h> /* TODO: Update so we don't need this */
19
20
21 /*
22 * Local globals...
23 */
24
25 static int terminate = 0;
26
27
28 /*
29 * Local functions...
30 */
31
32 static void print_attributes(ipp_t *ipp, int indent);
33 static void sigterm_handler(int sig);
34 static void usage(void) _CUPS_NORETURN;
35
36
37 /*
38 * 'main()' - Subscribe to the .
39 */
40
41 int
main(int argc,char * argv[])42 main(int argc, /* I - Number of command-line arguments */
43 char *argv[]) /* I - Command-line arguments */
44 {
45 int i; /* Looping var */
46 const char *uri; /* URI to use */
47 int num_events; /* Number of events */
48 const char *events[100]; /* Events */
49 int subscription_id, /* notify-subscription-id */
50 sequence_number, /* notify-sequence-number */
51 interval; /* Interval between polls */
52 http_t *http; /* HTTP connection */
53 ipp_t *request, /* IPP request */
54 *response; /* IPP response */
55 ipp_attribute_t *attr; /* Current attribute */
56 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
57 struct sigaction action; /* Actions for POSIX signals */
58 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
59
60
61 /*
62 * Parse command-line...
63 */
64
65 num_events = 0;
66 uri = NULL;
67
68 for (i = 1; i < argc; i ++)
69 if (!strcmp(argv[i], "-E"))
70 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
71 else if (!strcmp(argv[i], "-e"))
72 {
73 i ++;
74 if (i >= argc || num_events >= 100)
75 usage();
76
77 events[num_events] = argv[i];
78 num_events ++;
79 }
80 else if (!strcmp(argv[i], "-h"))
81 {
82 i ++;
83 if (i >= argc)
84 usage();
85
86 cupsSetServer(argv[i]);
87 }
88 else if (uri || strncmp(argv[i], "ipp://", 6))
89 usage();
90 else
91 uri = argv[i];
92
93 if (!uri)
94 usage();
95
96 if (num_events == 0)
97 {
98 events[0] = "all";
99 num_events = 1;
100 }
101
102 /*
103 * Connect to the server...
104 */
105
106 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
107 cupsEncryption())) == NULL)
108 {
109 perror(cupsServer());
110 return (1);
111 }
112
113 /*
114 * Catch CTRL-C and SIGTERM...
115 */
116
117 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
118 sigset(SIGINT, sigterm_handler);
119 sigset(SIGTERM, sigterm_handler);
120 #elif defined(HAVE_SIGACTION)
121 memset(&action, 0, sizeof(action));
122
123 sigemptyset(&action.sa_mask);
124 action.sa_handler = sigterm_handler;
125 sigaction(SIGINT, &action, NULL);
126 sigaction(SIGTERM, &action, NULL);
127 #else
128 signal(SIGINT, sigterm_handler);
129 signal(SIGTERM, sigterm_handler);
130 #endif /* HAVE_SIGSET */
131
132 /*
133 * Create the subscription...
134 */
135
136 if (strstr(uri, "/jobs/"))
137 {
138 request = ippNewRequest(IPP_CREATE_JOB_SUBSCRIPTION);
139 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
140 }
141 else
142 {
143 request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
144 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
145 uri);
146 }
147
148 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
149 NULL, cupsUser());
150
151 ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events",
152 num_events, NULL, events);
153 ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
154 "notify-pull-method", NULL, "ippget");
155
156 response = cupsDoRequest(http, request, uri);
157 if (cupsLastError() >= IPP_BAD_REQUEST)
158 {
159 fprintf(stderr, "Create-%s-Subscription: %s\n",
160 strstr(uri, "/jobs") ? "Job" : "Printer", cupsLastErrorString());
161 ippDelete(response);
162 httpClose(http);
163 return (1);
164 }
165
166 if ((attr = ippFindAttribute(response, "notify-subscription-id",
167 IPP_TAG_INTEGER)) == NULL)
168 {
169 fputs("ERROR: No notify-subscription-id in response!\n", stderr);
170 ippDelete(response);
171 httpClose(http);
172 return (1);
173 }
174
175 subscription_id = attr->values[0].integer;
176
177 printf("Create-%s-Subscription: notify-subscription-id=%d\n",
178 strstr(uri, "/jobs/") ? "Job" : "Printer", subscription_id);
179
180 ippDelete(response);
181
182 /*
183 * Monitor for events...
184 */
185
186 sequence_number = 0;
187
188 while (!terminate)
189 {
190 /*
191 * Get the current events...
192 */
193
194 printf("\nGet-Notifications(%d,%d):", subscription_id, sequence_number);
195 fflush(stdout);
196
197 request = ippNewRequest(IPP_GET_NOTIFICATIONS);
198
199 if (strstr(uri, "/jobs/"))
200 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
201 else
202 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
203 uri);
204
205 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
206 "requesting-user-name", NULL, cupsUser());
207
208 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
209 "notify-subscription-ids", subscription_id);
210 if (sequence_number)
211 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
212 "notify-sequence-numbers", sequence_number + 1);
213
214 response = cupsDoRequest(http, request, uri);
215
216 printf(" %s\n", ippErrorString(cupsLastError()));
217
218 if (cupsLastError() >= IPP_BAD_REQUEST)
219 fprintf(stderr, "Get-Notifications: %s\n", cupsLastErrorString());
220 else if (response)
221 {
222 print_attributes(response, 0);
223
224 for (attr = ippFindAttribute(response, "notify-sequence-number",
225 IPP_TAG_INTEGER);
226 attr;
227 attr = ippFindNextAttribute(response, "notify-sequence-number",
228 IPP_TAG_INTEGER))
229 if (attr->values[0].integer > sequence_number)
230 sequence_number = attr->values[0].integer;
231 }
232
233 if ((attr = ippFindAttribute(response, "notify-get-interval",
234 IPP_TAG_INTEGER)) != NULL &&
235 attr->values[0].integer > 0)
236 interval = attr->values[0].integer;
237 else
238 interval = 5;
239
240 ippDelete(response);
241 sleep((unsigned)interval);
242 }
243
244 /*
245 * Cancel the subscription...
246 */
247
248 printf("\nCancel-Subscription:");
249 fflush(stdout);
250
251 request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
252
253 if (strstr(uri, "/jobs/"))
254 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
255 else
256 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
257 uri);
258
259 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
260 NULL, cupsUser());
261
262 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
263 "notify-subscription-id", subscription_id);
264
265 ippDelete(cupsDoRequest(http, request, uri));
266
267 printf(" %s\n", ippErrorString(cupsLastError()));
268
269 if (cupsLastError() >= IPP_BAD_REQUEST)
270 fprintf(stderr, "Cancel-Subscription: %s\n", cupsLastErrorString());
271
272 /*
273 * Close the connection and return...
274 */
275
276 httpClose(http);
277
278 return (0);
279 }
280
281
282 /*
283 * 'print_attributes()' - Print the attributes in a request...
284 */
285
286 static void
print_attributes(ipp_t * ipp,int indent)287 print_attributes(ipp_t *ipp, /* I - IPP request */
288 int indent) /* I - Indentation */
289 {
290 int i; /* Looping var */
291 ipp_tag_t group; /* Current group */
292 ipp_attribute_t *attr; /* Current attribute */
293 _ipp_value_t *val; /* Current value */
294 static const char * const tags[] = /* Value/group tag strings */
295 {
296 "reserved-00",
297 "operation-attributes-tag",
298 "job-attributes-tag",
299 "end-of-attributes-tag",
300 "printer-attributes-tag",
301 "unsupported-attributes-tag",
302 "subscription-attributes-tag",
303 "event-attributes-tag",
304 "reserved-08",
305 "reserved-09",
306 "reserved-0A",
307 "reserved-0B",
308 "reserved-0C",
309 "reserved-0D",
310 "reserved-0E",
311 "reserved-0F",
312 "unsupported",
313 "default",
314 "unknown",
315 "no-value",
316 "reserved-14",
317 "not-settable",
318 "delete-attr",
319 "admin-define",
320 "reserved-18",
321 "reserved-19",
322 "reserved-1A",
323 "reserved-1B",
324 "reserved-1C",
325 "reserved-1D",
326 "reserved-1E",
327 "reserved-1F",
328 "reserved-20",
329 "integer",
330 "boolean",
331 "enum",
332 "reserved-24",
333 "reserved-25",
334 "reserved-26",
335 "reserved-27",
336 "reserved-28",
337 "reserved-29",
338 "reserved-2a",
339 "reserved-2b",
340 "reserved-2c",
341 "reserved-2d",
342 "reserved-2e",
343 "reserved-2f",
344 "octetString",
345 "dateTime",
346 "resolution",
347 "rangeOfInteger",
348 "begCollection",
349 "textWithLanguage",
350 "nameWithLanguage",
351 "endCollection",
352 "reserved-38",
353 "reserved-39",
354 "reserved-3a",
355 "reserved-3b",
356 "reserved-3c",
357 "reserved-3d",
358 "reserved-3e",
359 "reserved-3f",
360 "reserved-40",
361 "textWithoutLanguage",
362 "nameWithoutLanguage",
363 "reserved-43",
364 "keyword",
365 "uri",
366 "uriScheme",
367 "charset",
368 "naturalLanguage",
369 "mimeMediaType",
370 "memberName"
371 };
372
373
374 for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
375 {
376 if ((attr->group_tag == IPP_TAG_ZERO && indent <= 8) || !attr->name)
377 {
378 group = IPP_TAG_ZERO;
379 putchar('\n');
380 continue;
381 }
382
383 if (group != attr->group_tag)
384 {
385 group = attr->group_tag;
386
387 putchar('\n');
388 for (i = 4; i < indent; i ++)
389 putchar(' ');
390
391 printf("%s:\n\n", tags[group]);
392 }
393
394 for (i = 0; i < indent; i ++)
395 putchar(' ');
396
397 printf("%s (", attr->name);
398 if (attr->num_values > 1)
399 printf("1setOf ");
400 printf("%s):", tags[attr->value_tag]);
401
402 switch (attr->value_tag)
403 {
404 case IPP_TAG_ENUM :
405 case IPP_TAG_INTEGER :
406 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
407 printf(" %d", val->integer);
408 putchar('\n');
409 break;
410
411 case IPP_TAG_BOOLEAN :
412 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
413 printf(" %s", val->boolean ? "true" : "false");
414 putchar('\n');
415 break;
416
417 case IPP_TAG_RANGE :
418 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
419 printf(" %d-%d", val->range.lower, val->range.upper);
420 putchar('\n');
421 break;
422
423 case IPP_TAG_DATE :
424 {
425 char vstring[256]; /* Formatted time */
426
427 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
428 printf(" (%s)", _cupsStrDate(vstring, sizeof(vstring), ippDateToTime(val->date)));
429 }
430 putchar('\n');
431 break;
432
433 case IPP_TAG_RESOLUTION :
434 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
435 printf(" %dx%d%s", val->resolution.xres, val->resolution.yres,
436 val->resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
437 putchar('\n');
438 break;
439
440 case IPP_TAG_STRING :
441 case IPP_TAG_TEXTLANG :
442 case IPP_TAG_NAMELANG :
443 case IPP_TAG_TEXT :
444 case IPP_TAG_NAME :
445 case IPP_TAG_KEYWORD :
446 case IPP_TAG_URI :
447 case IPP_TAG_URISCHEME :
448 case IPP_TAG_CHARSET :
449 case IPP_TAG_LANGUAGE :
450 case IPP_TAG_MIMETYPE :
451 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
452 printf(" \"%s\"", val->string.text);
453 putchar('\n');
454 break;
455
456 case IPP_TAG_BEGIN_COLLECTION :
457 putchar('\n');
458
459 for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
460 {
461 if (i)
462 putchar('\n');
463 print_attributes(val->collection, indent + 4);
464 }
465 break;
466
467 default :
468 printf("UNKNOWN (%d values)\n", attr->num_values);
469 break;
470 }
471 }
472 }
473
474
475 /*
476 * 'sigterm_handler()' - Flag when the user hits CTRL-C...
477 */
478
479 static void
sigterm_handler(int sig)480 sigterm_handler(int sig) /* I - Signal number (unused) */
481 {
482 (void)sig;
483
484 terminate = 1;
485 }
486
487
488 /*
489 * 'usage()' - Show program usage...
490 */
491
492 static void
usage(void)493 usage(void)
494 {
495 puts("Usage: testsub [-E] [-e event ... -e eventN] [-h hostname] URI");
496 exit(0);
497 }
498