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