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