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