/* * Scheduler notification tester for CUPS. * * Copyright 2007-2014 by Apple Inc. * Copyright 2006-2007 by Easy Software Products. * * Licensed under Apache License v2.0. See the file "LICENSE" for more information. */ /* * Include necessary headers... */ #include #include #include #include #include /* TODO: Update so we don't need this */ /* * Local globals... */ static int terminate = 0; /* * Local functions... */ static void print_attributes(ipp_t *ipp, int indent); static void sigterm_handler(int sig); static void usage(void) _CUPS_NORETURN; /* * 'main()' - Subscribe to the . */ int main(int argc, /* I - Number of command-line arguments */ char *argv[]) /* I - Command-line arguments */ { int i; /* Looping var */ const char *uri; /* URI to use */ int num_events; /* Number of events */ const char *events[100]; /* Events */ int subscription_id, /* notify-subscription-id */ sequence_number, /* notify-sequence-number */ interval; /* Interval between polls */ http_t *http; /* HTTP connection */ ipp_t *request, /* IPP request */ *response; /* IPP response */ ipp_attribute_t *attr; /* Current attribute */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* Actions for POSIX signals */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ /* * Parse command-line... */ num_events = 0; uri = NULL; for (i = 1; i < argc; i ++) if (!strcmp(argv[i], "-E")) cupsSetEncryption(HTTP_ENCRYPT_REQUIRED); else if (!strcmp(argv[i], "-e")) { i ++; if (i >= argc || num_events >= 100) usage(); events[num_events] = argv[i]; num_events ++; } else if (!strcmp(argv[i], "-h")) { i ++; if (i >= argc) usage(); cupsSetServer(argv[i]); } else if (uri || strncmp(argv[i], "ipp://", 6)) usage(); else uri = argv[i]; if (!uri) usage(); if (num_events == 0) { events[0] = "all"; num_events = 1; } /* * Connect to the server... */ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) == NULL) { perror(cupsServer()); return (1); } /* * Catch CTRL-C and SIGTERM... */ #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ sigset(SIGINT, sigterm_handler); sigset(SIGTERM, sigterm_handler); #elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); action.sa_handler = sigterm_handler; sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); #else signal(SIGINT, sigterm_handler); signal(SIGTERM, sigterm_handler); #endif /* HAVE_SIGSET */ /* * Create the subscription... */ if (strstr(uri, "/jobs/")) { request = ippNewRequest(IPP_CREATE_JOB_SUBSCRIPTION); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); } else { request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); } ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events", num_events, NULL, events); ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-pull-method", NULL, "ippget"); response = cupsDoRequest(http, request, uri); if (cupsLastError() >= IPP_BAD_REQUEST) { fprintf(stderr, "Create-%s-Subscription: %s\n", strstr(uri, "/jobs") ? "Job" : "Printer", cupsLastErrorString()); ippDelete(response); httpClose(http); return (1); } if ((attr = ippFindAttribute(response, "notify-subscription-id", IPP_TAG_INTEGER)) == NULL) { fputs("ERROR: No notify-subscription-id in response!\n", stderr); ippDelete(response); httpClose(http); return (1); } subscription_id = attr->values[0].integer; printf("Create-%s-Subscription: notify-subscription-id=%d\n", strstr(uri, "/jobs/") ? "Job" : "Printer", subscription_id); ippDelete(response); /* * Monitor for events... */ sequence_number = 0; while (!terminate) { /* * Get the current events... */ printf("\nGet-Notifications(%d,%d):", subscription_id, sequence_number); fflush(stdout); request = ippNewRequest(IPP_GET_NOTIFICATIONS); if (strstr(uri, "/jobs/")) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); else ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-subscription-ids", subscription_id); if (sequence_number) ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-sequence-numbers", sequence_number + 1); response = cupsDoRequest(http, request, uri); printf(" %s\n", ippErrorString(cupsLastError())); if (cupsLastError() >= IPP_BAD_REQUEST) fprintf(stderr, "Get-Notifications: %s\n", cupsLastErrorString()); else if (response) { print_attributes(response, 0); for (attr = ippFindAttribute(response, "notify-sequence-number", IPP_TAG_INTEGER); attr; attr = ippFindNextAttribute(response, "notify-sequence-number", IPP_TAG_INTEGER)) if (attr->values[0].integer > sequence_number) sequence_number = attr->values[0].integer; } if ((attr = ippFindAttribute(response, "notify-get-interval", IPP_TAG_INTEGER)) != NULL && attr->values[0].integer > 0) interval = attr->values[0].integer; else interval = 5; ippDelete(response); sleep((unsigned)interval); } /* * Cancel the subscription... */ printf("\nCancel-Subscription:"); fflush(stdout); request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION); if (strstr(uri, "/jobs/")) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); else ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-subscription-id", subscription_id); ippDelete(cupsDoRequest(http, request, uri)); printf(" %s\n", ippErrorString(cupsLastError())); if (cupsLastError() >= IPP_BAD_REQUEST) fprintf(stderr, "Cancel-Subscription: %s\n", cupsLastErrorString()); /* * Close the connection and return... */ httpClose(http); return (0); } /* * 'print_attributes()' - Print the attributes in a request... */ static void print_attributes(ipp_t *ipp, /* I - IPP request */ int indent) /* I - Indentation */ { int i; /* Looping var */ ipp_tag_t group; /* Current group */ ipp_attribute_t *attr; /* Current attribute */ _ipp_value_t *val; /* Current value */ static const char * const tags[] = /* Value/group tag strings */ { "reserved-00", "operation-attributes-tag", "job-attributes-tag", "end-of-attributes-tag", "printer-attributes-tag", "unsupported-attributes-tag", "subscription-attributes-tag", "event-attributes-tag", "reserved-08", "reserved-09", "reserved-0A", "reserved-0B", "reserved-0C", "reserved-0D", "reserved-0E", "reserved-0F", "unsupported", "default", "unknown", "no-value", "reserved-14", "not-settable", "delete-attr", "admin-define", "reserved-18", "reserved-19", "reserved-1A", "reserved-1B", "reserved-1C", "reserved-1D", "reserved-1E", "reserved-1F", "reserved-20", "integer", "boolean", "enum", "reserved-24", "reserved-25", "reserved-26", "reserved-27", "reserved-28", "reserved-29", "reserved-2a", "reserved-2b", "reserved-2c", "reserved-2d", "reserved-2e", "reserved-2f", "octetString", "dateTime", "resolution", "rangeOfInteger", "begCollection", "textWithLanguage", "nameWithLanguage", "endCollection", "reserved-38", "reserved-39", "reserved-3a", "reserved-3b", "reserved-3c", "reserved-3d", "reserved-3e", "reserved-3f", "reserved-40", "textWithoutLanguage", "nameWithoutLanguage", "reserved-43", "keyword", "uri", "uriScheme", "charset", "naturalLanguage", "mimeMediaType", "memberName" }; for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next) { if ((attr->group_tag == IPP_TAG_ZERO && indent <= 8) || !attr->name) { group = IPP_TAG_ZERO; putchar('\n'); continue; } if (group != attr->group_tag) { group = attr->group_tag; putchar('\n'); for (i = 4; i < indent; i ++) putchar(' '); printf("%s:\n\n", tags[group]); } for (i = 0; i < indent; i ++) putchar(' '); printf("%s (", attr->name); if (attr->num_values > 1) printf("1setOf "); printf("%s):", tags[attr->value_tag]); switch (attr->value_tag) { case IPP_TAG_ENUM : case IPP_TAG_INTEGER : for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++) printf(" %d", val->integer); putchar('\n'); break; case IPP_TAG_BOOLEAN : for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++) printf(" %s", val->boolean ? "true" : "false"); putchar('\n'); break; case IPP_TAG_RANGE : for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++) printf(" %d-%d", val->range.lower, val->range.upper); putchar('\n'); break; case IPP_TAG_DATE : { char vstring[256]; /* Formatted time */ for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++) printf(" (%s)", _cupsStrDate(vstring, sizeof(vstring), ippDateToTime(val->date))); } putchar('\n'); break; case IPP_TAG_RESOLUTION : for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++) printf(" %dx%d%s", val->resolution.xres, val->resolution.yres, val->resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpcm"); putchar('\n'); break; case IPP_TAG_STRING : case IPP_TAG_TEXTLANG : case IPP_TAG_NAMELANG : case IPP_TAG_TEXT : case IPP_TAG_NAME : case IPP_TAG_KEYWORD : case IPP_TAG_URI : case IPP_TAG_URISCHEME : case IPP_TAG_CHARSET : case IPP_TAG_LANGUAGE : case IPP_TAG_MIMETYPE : for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++) printf(" \"%s\"", val->string.text); putchar('\n'); break; case IPP_TAG_BEGIN_COLLECTION : putchar('\n'); for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++) { if (i) putchar('\n'); print_attributes(val->collection, indent + 4); } break; default : printf("UNKNOWN (%d values)\n", attr->num_values); break; } } } /* * 'sigterm_handler()' - Flag when the user hits CTRL-C... */ static void sigterm_handler(int sig) /* I - Signal number (unused) */ { (void)sig; terminate = 1; } /* * 'usage()' - Show program usage... */ static void usage(void) { puts("Usage: testsub [-E] [-e event ... -e eventN] [-h hostname] URI"); exit(0); }