summaryrefslogtreecommitdiff
path: root/filter
diff options
context:
space:
mode:
authorMichael Sweet <michael.r.sweet@gmail.com>2017-08-29 17:27:49 -0400
committerMichael Sweet <michael.r.sweet@gmail.com>2017-08-29 17:27:49 -0400
commit4b16c717e5fe978a544bab5ce69bfd8edad31819 (patch)
treeb19f664eb91e43f120c5221cad32ae5f11f4abfc /filter
parent240a27f93b3ba35d8d0f18913f6a7570f1f4ab1d (diff)
downloadcups-4b16c717e5fe978a544bab5ce69bfd8edad31819.tar.gz
Move test client program to filter directory, finish initial implementation.
Diffstat (limited to 'filter')
-rw-r--r--filter/Makefile14
-rw-r--r--filter/testclient.c839
2 files changed, 852 insertions, 1 deletions
diff --git a/filter/Makefile b/filter/Makefile
index fc53f85c6..02aab14d8 100644
--- a/filter/Makefile
+++ b/filter/Makefile
@@ -29,6 +29,7 @@ LIBTARGETS = \
libcupsimage.a
UNITTARGETS = \
rasterbench \
+ testclient \
testraster
TARGETS = \
$(LIBTARGETS) \
@@ -38,7 +39,7 @@ IMAGEOBJS = error.o interpret.o raster.o
OBJS = $(IMAGEOBJS) \
commandtops.o gziptoany.o common.o pstops.o \
rasterbench.o rastertoepson.o rastertohp.o rastertolabel.o \
- rastertopwg.o testraster.o
+ rastertopwg.o testclient.o testraster.o
#
@@ -341,6 +342,17 @@ rastertopwg-static: rastertopwg.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
#
+# testclient (dependency on static libraries is intentional)
+#
+
+testclient: testclient.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
+ echo Linking $@...
+ $(LD_CC) $(LDFLAGS) -o $@ testclient.o \
+ libcupsimage.a ../cups/$(LIBCUPSSTATIC) \
+ $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+
+
+#
# testraster
#
diff --git a/filter/testclient.c b/filter/testclient.c
new file mode 100644
index 000000000..d6ad4249c
--- /dev/null
+++ b/filter/testclient.c
@@ -0,0 +1,839 @@
+/*
+ * Simulated client test program for CUPS.
+ *
+ * Copyright 2017 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/cups.h>
+#include <cups/raster.h>
+#include <cups/string-private.h>
+#include <cups/thread-private.h>
+
+
+/*
+ * Local types...
+ */
+
+typedef struct _client_monitor_s
+{
+ const char *uri, /* Printer URI */
+ *hostname, /* Hostname */
+ *user, /* Username */
+ *resource; /* Resource path */
+ int port; /* Port number */
+ http_encryption_t encryption; /* Use encryption? */
+ ipp_pstate_t printer_state; /* Current printer state */
+ char printer_state_reasons[1024];
+ /* Current printer-state-reasons */
+ int job_id; /* Job ID for submitted job */
+ ipp_jstate_t job_state; /* Current job state */
+ char job_state_reasons[1024];
+ /* Current job-state-reasons */
+} _client_monitor_t;
+
+
+/*
+ * Local functions...
+ */
+
+static const char *make_raster_file(ipp_t *response, char *tempname, size_t tempsize, const char **format);
+static void *monitor_printer(_client_monitor_t *monitor);
+static void show_capabilities(ipp_t *response);
+static void usage(void);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ const char *opt, /* Current option */
+ *uri = NULL, /* Printer URI */
+ *printfile = NULL,
+ /* Print file */
+ *printformat = NULL;
+ /* Print format */
+ char tempfile[1024] = "",
+ /* Temporary file (if any) */
+ scheme[32], /* URI scheme */
+ userpass[256], /* Username:password */
+ hostname[256], /* Hostname */
+ resource[256]; /* Resource path */
+ int port; /* Port number */
+ http_encryption_t encryption; /* Encryption mode */
+ _client_monitor_t monitor; /* Monitoring data */
+ http_t *http; /* HTTP connection */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* IPP attribute */
+ static const char * const pattrs[] = /* Printer attributes we are interested in */
+ {
+ "job-template",
+ "printer-defaults",
+ "printer-description",
+ "media-col-database",
+ "media-col-ready"
+ };
+
+
+ /*
+ * Parse command-line options...
+ */
+
+ for (i = 1; i < argc; i ++)
+ {
+ if (argv[i][0] == '-')
+ {
+ for (opt = argv[i] + 1; *opt; opt ++)
+ {
+ switch (*opt)
+ {
+ case 'f' : /* -f print-file */
+ if (printfile)
+ {
+ puts("Print file can only be specified once.");
+ usage();
+ return (1);
+ }
+
+ i ++;
+ if (i >= argc)
+ {
+ puts("Expected print file after '-f'.");
+ usage();
+ return (1);
+ }
+
+ printfile = argv[i];
+ break;
+
+ default :
+ printf("Unknown option '-%c'.\n", *opt);
+ usage();
+ return (1);
+ }
+ }
+ }
+ else if (uri || (strncmp(argv[i], "ipp://", 6) && strncmp(argv[i], "ipps://", 7)))
+ {
+ printf("Unknown command-line argument '%s'.\n", argv[i]);
+ usage();
+ return (1);
+ }
+ else
+ uri = argv[i];
+ }
+
+ /*
+ * Make sure we have everything we need.
+ */
+
+ if (!uri)
+ {
+ puts("Expected printer URI.");
+ usage();
+ return (1);
+ }
+
+ /*
+ * Connect to the printer...
+ */
+
+ if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
+ {
+ printf("Bad printer URI '%s'.\n", uri);
+ return (1);
+ }
+
+ if (!port)
+ port = IPP_PORT;
+
+ if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
+ encryption = HTTP_ENCRYPTION_ALWAYS;
+ else
+ encryption = HTTP_ENCRYPTION_IF_REQUESTED;
+
+ if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 0, NULL)) == NULL)
+ {
+ printf("Unable to connect to '%s' on port %d: %s\n", hostname, port, cupsLastErrorString());
+ return (1);
+ }
+
+ /*
+ * Query printer status and capabilities...
+ */
+
+ request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+ 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_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
+
+ response = cupsDoRequest(http, request, resource);
+
+ show_capabilities(response);
+
+ /*
+ * Now figure out what we will be printing...
+ */
+
+ if (printfile)
+ {
+ /*
+ * User specified a print file, figure out the format...
+ */
+
+ if ((opt = strrchr(printfile, '.')) != NULL)
+ {
+ /*
+ * Guess the format from the extension...
+ */
+
+ if (!strcmp(opt, ".jpg"))
+ printformat = "image/jpeg";
+ else if (!strcmp(opt, ".pdf"))
+ printformat = "application/pdf";
+ else if (!strcmp(opt, ".ps"))
+ printformat = "application/postscript";
+ else if (!strcmp(opt, ".pwg"))
+ printformat = "image/pwg-raster";
+ else if (!strcmp(opt, ".urf"))
+ printformat = "image/urf";
+ else
+ printformat = "application/octet-stream";
+ }
+ else
+ {
+ /*
+ * Tell the printer to auto-detect...
+ */
+
+ printformat = "application/octet-stream";
+ }
+ }
+ else
+ {
+ /*
+ * No file specified, make something to test with...
+ */
+
+ if ((printfile = make_raster_file(response, tempfile, sizeof(tempfile), &printformat)) == NULL)
+ return (1);
+ }
+
+ ippDelete(response);
+
+ /*
+ * Start monitoring the printer in the background...
+ */
+
+ memset(&monitor, 0, sizeof(monitor));
+
+ monitor.uri = uri;
+ monitor.hostname = hostname;
+ monitor.resource = resource;
+ monitor.port = port;
+ monitor.encryption = encryption;
+
+ _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
+
+ /*
+ * Create the job and wait for completion...
+ */
+
+ request = ippNewRequest(IPP_OP_CREATE_JOB);
+ 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());
+
+ if ((opt = strrchr(printfile, '/')) != NULL)
+ opt ++;
+ else
+ opt = printfile;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, opt);
+
+ response = cupsDoRequest(http, request, resource);
+
+ if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+ {
+ printf("Unable to create print job: %s\n", cupsLastErrorString());
+
+ monitor.job_state = IPP_JSTATE_ABORTED;
+
+ goto cleanup;
+ }
+
+ if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
+ {
+ puts("No job-id returned in Create-Job request.");
+
+ monitor.job_state = IPP_JSTATE_ABORTED;
+
+ goto cleanup;
+ }
+
+ monitor.job_id = ippGetInteger(attr, 0);
+
+ ippDelete(response);
+
+ request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor.job_id);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, printformat);
+ ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
+
+ response = cupsDoFileRequest(http, request, resource, printfile);
+
+ if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+ {
+ printf("Unable to print file: %s\n", cupsLastErrorString());
+
+ monitor.job_state = IPP_JSTATE_ABORTED;
+
+ goto cleanup;
+ }
+
+ while (monitor.job_state < IPP_JSTATE_CANCELED)
+ sleep(1);
+
+ /*
+ * Cleanup after ourselves...
+ */
+
+ cleanup:
+
+ httpClose(http);
+
+ if (tempfile[0])
+ unlink(tempfile);
+
+ return (monitor.job_state == IPP_JSTATE_COMPLETED);
+}
+
+
+/*
+ * 'make_raster_file()' - Create a temporary raster file.
+ */
+
+static const char * /* O - Print filename */
+make_raster_file(ipp_t *response, /* I - Printer attributes */
+ char *tempname, /* I - Temporary filename buffer */
+ size_t tempsize, /* I - Size of temp file buffer */
+ const char **format) /* O - Print format */
+{
+ int i, /* Looping var */
+ count; /* Number of values */
+ ipp_attribute_t *attr; /* Printer attribute */
+ const char *type = NULL; /* Raster type (colorspace + bits) */
+ pwg_media_t *media = NULL; /* Media size */
+ int xdpi = 0, /* Horizontal resolution */
+ ydpi = 0; /* Vertical resolution */
+ int fd; /* Temporary file */
+ cups_mode_t mode; /* Raster mode */
+ cups_raster_t *ras; /* Raster stream */
+ cups_page_header2_t header; /* Page header */
+ unsigned char *line, /* Line of raster data */
+ *lineptr; /* Pointer into line */
+ unsigned y, /* Current position on page */
+ xcount, ycount, /* Current count for X and Y */
+ xrep, yrep, /* Repeat count for X and Y */
+ xoff, yoff, /* Offsets for X and Y */
+ yend; /* End Y value */
+ int temprow, /* Row in template */
+ tempcolor; /* Template color */
+ const char *template; /* Pointer into template */
+ const unsigned char *color; /* Current color */
+ static const unsigned char colors[][3] =
+ { /* Colors for test */
+ { 191, 191, 191 },
+ { 127, 127, 127 },
+ { 63, 63, 63 },
+ { 0, 0, 0 },
+ { 255, 0, 0 },
+ { 255, 127, 0 },
+ { 255, 255, 0 },
+ { 127, 255, 0 },
+ { 0, 255, 0 },
+ { 0, 255, 127 },
+ { 0, 255, 255 },
+ { 0, 127, 255 },
+ { 0, 0, 255 },
+ { 127, 0, 255 },
+ { 255, 0, 255 }
+ };
+ static const char * const templates[] =
+ { /* Raster template */
+ " CCC U U PPPP SSS TTTTT EEEEE SSS TTTTT 000 1 222 333 4 55555 66 77777 888 999 ",
+ "C C U U P P S S T E S S T 0 0 11 2 2 3 3 4 4 5 6 7 8 8 9 9 ",
+ "C U U P P S T E S T 0 0 1 2 3 4 4 5 6 7 8 8 9 9 ",
+ "C U U PPPP SSS ----- T EEEE SSS T 0 0 0 1 22 333 44444 555 6666 7 888 9999 ",
+ "C U U P S T E S T 0 0 1 2 3 4 5 6 6 7 8 8 9 ",
+ "C C U U P S S T E S S T 0 0 1 2 3 3 4 5 5 6 6 7 8 8 9 ",
+ " CCC UUU P SSS T EEEEE SSS T 000 111 22222 333 4 555 666 7 888 99 ",
+ " "
+ };
+
+
+ /*
+ * Figure out the output format...
+ */
+
+ if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) == NULL)
+ {
+ puts("No supported document formats, aborting.");
+ return (NULL);
+ }
+
+ if (ippContainsString(attr, "image/urf"))
+ {
+ /*
+ * Apple Raster format...
+ */
+
+ *format = "image/urf";
+ mode = CUPS_RASTER_WRITE_APPLE;
+ }
+ else if (ippContainsString(attr, "image/pwg-raster"))
+ {
+ /*
+ * PWG Raster format...
+ */
+
+ *format = "image/pwg-raster";
+ mode = CUPS_RASTER_WRITE_PWG;
+ }
+ else
+ {
+ /*
+ * No supported raster format...
+ */
+
+ puts("Printer does not support Apple or PWG raster files, aborting.");
+ return (NULL);
+ }
+
+ /*
+ * Figure out the the media, resolution, and color mode...
+ */
+
+ if ((attr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL)
+ {
+ /*
+ * Use default media...
+ */
+
+ media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
+ }
+ else if ((attr = ippFindAttribute(response, "media-ready", IPP_TAG_KEYWORD)) != NULL)
+ {
+ /*
+ * Use ready media...
+ */
+
+ if (ippContainsString(attr, "na_letter_8.5x11in"))
+ media = pwgMediaForPWG("na_letter_8.5x11in");
+ else if (ippContainsString(attr, "iso_a4_210x297mm"))
+ media = pwgMediaForPWG("iso_a4_210x297mm");
+ else
+ media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
+ }
+ else
+ {
+ puts("No default or ready media reported by printer, aborting.");
+ return (NULL);
+ }
+
+ if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
+ {
+ for (i = 0, count = ippGetCount(attr); i < count; i ++)
+ {
+ const char *val = ippGetString(attr, i, NULL);
+
+ if (val[0] == 'R')
+ xdpi = ydpi = atoi(val + 1);
+ else if (!strcmp(val, "W8") && !type)
+ type = "sgray_8";
+ else if (!strcmp(val, "SRGB24"))
+ type = "srgb_8";
+ }
+ }
+ else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
+ {
+ for (i = 0, count = ippGetCount(attr); i < count; i ++)
+ {
+ int tempxdpi, tempydpi;
+ ipp_res_t tempunits;
+
+ tempxdpi = ippGetResolution(attr, 0, &tempydpi, &tempunits);
+
+ if (i == 0 || tempxdpi < xdpi || tempydpi < ydpi)
+ {
+ xdpi = tempxdpi;
+ ydpi = tempydpi;
+ }
+ }
+
+ if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) != NULL)
+ {
+ if (ippContainsString(attr, "srgb_8"))
+ type = "srgb_8";
+ else if (ippContainsString(attr, "sgray_8"))
+ type = "sgray_8";
+ }
+ }
+
+ if (xdpi < 72 || ydpi < 72)
+ {
+ puts("No supported raster resolutions, aborting.");
+ return (NULL);
+ }
+
+ if (!type)
+ {
+ puts("No supported color spaces or bit depths, aborting.");
+ return (NULL);
+ }
+
+ /*
+ * Make the raster context and details...
+ */
+
+ if (!cupsRasterInitPWGHeader(&header, media, type, xdpi, ydpi, "one-sided", NULL))
+ {
+ printf("Unable to initialize raster context: %s\n", cupsRasterErrorString());
+ return (NULL);
+ }
+
+ header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount] = 1;
+
+ if (header.cupsWidth > (4 * header.HWResolution[0]))
+ {
+ xoff = header.HWResolution[0] / 2;
+ yoff = header.HWResolution[1] / 2;
+ }
+ else
+ {
+ xoff = 0;
+ yoff = 0;
+ }
+
+ xrep = (header.cupsWidth - 2 * xoff) / 140;
+ yrep = xrep * header.HWResolution[1] / header.HWResolution[0];
+ yend = header.cupsHeight - yoff;
+
+ /*
+ * Prepare the raster file...
+ */
+
+ if ((line = malloc(header.cupsBytesPerLine)) == NULL)
+ {
+ printf("Unable to allocate %u bytes for raster output: %s\n", header.cupsBytesPerLine, strerror(errno));
+ return (NULL);
+ }
+
+ if ((fd = cupsTempFd(tempname, (int)tempsize)) < 0)
+ {
+ printf("Unable to create temporary print file: %s\n", strerror(errno));
+ return (NULL);
+ }
+
+ if ((ras = cupsRasterOpen(fd, mode)) == NULL)
+ {
+ printf("Unable to open raster stream: %s\n", cupsRasterErrorString());
+ close(fd);
+ return (NULL);
+ }
+
+ /*
+ * Write a single page consisting of the template dots repeated over the page.
+ */
+
+ cupsRasterWriteHeader2(ras, &header);
+
+ memset(line, 0xff, header.cupsBytesPerLine);
+
+ for (y = 0; y < yoff; y ++)
+ cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+
+ for (temprow = 0, tempcolor = 0; y < yend; y ++)
+ {
+ template = templates[temprow];
+ color = colors[tempcolor];
+
+ temprow ++;
+ if (temprow >= (int)(sizeof(templates) / sizeof(templates[0])))
+ {
+ temprow = 0;
+ tempcolor ++;
+ if (tempcolor >= (int)(sizeof(colors) / sizeof(colors[0])))
+ tempcolor = 0;
+ }
+
+ memset(line, 0xff, header.cupsBytesPerLine);
+
+ if (header.cupsColorSpace == CUPS_CSPACE_SW)
+ {
+ /*
+ * Do grayscale output...
+ */
+
+ for (lineptr = line + xoff; *template; template ++)
+ {
+ if (*template != ' ')
+ {
+ for (xcount = xrep; xcount > 0; xcount --)
+ *lineptr++ = *color;
+ }
+ else
+ {
+ lineptr += xrep;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Do color output...
+ */
+
+ for (lineptr = line + 3 * xoff; *template; template ++)
+ {
+ if (*template != ' ')
+ {
+ for (xcount = xrep; xcount > 0; xcount --, lineptr += 3)
+ memcpy(lineptr, color, 3);
+ }
+ else
+ {
+ lineptr += 3 * xrep;
+ }
+ }
+ }
+
+ for (ycount = yrep; ycount > 0 && y < yend; ycount --, y ++)
+ cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+ }
+
+ memset(line, 0xff, header.cupsBytesPerLine);
+
+ for (y = 0; y < header.cupsHeight; y ++)
+ cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+
+ cupsRasterClose(ras);
+
+ close(fd);
+
+ printf("PRINT FILE: %s\n", tempname);
+
+ return (tempname);
+}
+
+
+/*
+ * 'monitor_printer()' - Monitor the job and printer states.
+ */
+
+static void * /* O - Thread exit code */
+monitor_printer(
+ _client_monitor_t *monitor) /* I - Monitoring data */
+{
+ http_t *http; /* Connection to printer */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* Attribute in response */
+ ipp_pstate_t printer_state; /* Printer state */
+ char printer_state_reasons[1024];
+ /* Printer state reasons */
+ ipp_jstate_t job_state; /* Job state */
+ char job_state_reasons[1024];/* Printer state reasons */
+ static const char * const jattrs[] = /* Job attributes we want */
+ {
+ "job-state",
+ "job-state-reasons"
+ };
+ static const char * const pattrs[] = /* Printer attributes we want */
+ {
+ "printer-state",
+ "printer-state-reasons"
+ };
+
+
+ /*
+ * Open a connection to the printer...
+ */
+
+ http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC, monitor->encryption, 1, 0, NULL);
+
+ /*
+ * Loop until the job is canceled, aborted, or completed.
+ */
+
+ printer_state = (ipp_pstate_t)0;
+ printer_state_reasons[0] = '\0';
+
+ job_state = (ipp_jstate_t)0;
+ job_state_reasons[0] = '\0';
+
+ while (monitor->job_state < IPP_JSTATE_CANCELED)
+ {
+ /*
+ * Reconnect to the printer as needed...
+ */
+
+ if (httpGetFd(http) < 0)
+ httpReconnect2(http, 30000, NULL);
+
+ if (httpGetFd(http) >= 0)
+ {
+ /*
+ * Connected, so check on the printer state...
+ */
+
+ request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
+
+ response = cupsDoRequest(http, request, monitor->resource);
+
+ if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+ printer_state = (ipp_pstate_t)ippGetInteger(attr, 0);
+
+ if ((attr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL)
+ ippAttributeString(attr, printer_state_reasons, sizeof(printer_state_reasons));
+
+ if (printer_state != monitor->printer_state || strcmp(printer_state_reasons, monitor->printer_state_reasons))
+ {
+ printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state), printer_state_reasons);
+
+ monitor->printer_state = printer_state;
+ strlcpy(monitor->printer_state_reasons, printer_state_reasons, sizeof(monitor->printer_state_reasons));
+ }
+
+ ippDelete(response);
+
+ if (monitor->job_id > 0)
+ {
+ /*
+ * Check the status of the job itself...
+ */
+
+ request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor->job_id);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
+
+ response = cupsDoRequest(http, request, monitor->resource);
+
+ if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
+ job_state = (ipp_jstate_t)ippGetInteger(attr, 0);
+
+ if ((attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD)) != NULL)
+ ippAttributeString(attr, job_state_reasons, sizeof(job_state_reasons));
+
+ if (job_state != monitor->job_state || strcmp(job_state_reasons, monitor->job_state_reasons))
+ {
+ printf("JOB %d: %s (%s)\n", monitor->job_id, ippEnumString("job-state", job_state), job_state_reasons);
+
+ monitor->job_state = job_state;
+ strlcpy(monitor->job_state_reasons, job_state_reasons, sizeof(monitor->job_state_reasons));
+ }
+
+ ippDelete(response);
+ }
+ }
+
+ if (monitor->job_state < IPP_JSTATE_CANCELED)
+ {
+ /*
+ * Sleep for 5 seconds...
+ */
+
+ sleep(5);
+ }
+ }
+
+ /*
+ * Cleanup and return...
+ */
+
+ httpClose(http);
+
+ return (NULL);
+}
+
+
+/*
+ * 'show_capabilities()' - Show printer capabilities.
+ */
+
+static void
+show_capabilities(ipp_t *response) /* I - Printer attributes */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* Attribute */
+ char buffer[1024]; /* Attribute value buffer */
+ static const char * const pattrs[] = /* Attributes we want to show */
+ {
+ "copies-default",
+ "copies-supported",
+ "finishings-default",
+ "finishings-ready",
+ "finishings-supported",
+ "media-default",
+ "media-ready",
+ "media-supported",
+ "output-bin-default",
+ "output-bin-supported",
+ "print-color-mode-default",
+ "print-color-mode-supported",
+ "sides-default",
+ "sides-supported",
+ "document-format-default",
+ "document-format-supported",
+ "pwg-raster-document-resolution-supported",
+ "pwg-raster-document-type-supported",
+ "urf-supported"
+ };
+
+
+ puts("CAPABILITIES:");
+ for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++)
+ {
+ if ((attr = ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) != NULL)
+ {
+ ippAttributeString(attr, buffer, sizeof(buffer));
+ printf(" %s=%s\n", pattrs[i], buffer);
+ }
+ }
+}
+
+
+/*
+ * 'usage()' - Show program usage...
+ */
+
+static void
+usage(void)
+{
+ puts("Usage: ./testclient printer-uri [-f print-file]");
+}