Home | History | Annotate | Download | only in filter
      1 /*
      2  * Simulated client test program for CUPS.
      3  *
      4  * Copyright 2017 by Apple Inc.
      5  *
      6  * These coded instructions, statements, and computer programs are the
      7  * property of Apple Inc. and are protected by Federal copyright
      8  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
      9  * which should have been included with this file.  If this file is
     10  * missing or damaged, see the license at "http://www.cups.org/".
     11  *
     12  * This file is subject to the Apple OS-Developed Software exception.
     13  */
     14 
     15 /*
     16  * Include necessary headers...
     17  */
     18 
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <cups/cups.h>
     22 #include <cups/raster.h>
     23 #include <cups/string-private.h>
     24 #include <cups/thread-private.h>
     25 
     26 
     27 /*
     28  * Local types...
     29  */
     30 
     31 typedef struct _client_monitor_s
     32 {
     33   const char		*uri,		/* Printer URI */
     34 			*hostname,	/* Hostname */
     35 			*user,		/* Username */
     36 			*resource;	/* Resource path */
     37   int			port;		/* Port number */
     38   http_encryption_t	encryption;	/* Use encryption? */
     39   ipp_pstate_t		printer_state;	/* Current printer state */
     40   char                  printer_state_reasons[1024];
     41                                         /* Current printer-state-reasons */
     42   int			job_id; 	/* Job ID for submitted job */
     43   ipp_jstate_t		job_state;	/* Current job state */
     44   char                  job_state_reasons[1024];
     45                                         /* Current job-state-reasons */
     46 } _client_monitor_t;
     47 
     48 
     49 /*
     50  * Local functions...
     51  */
     52 
     53 static const char       *make_raster_file(ipp_t *response, int grayscale, char *tempname, size_t tempsize, const char **format);
     54 static void	        *monitor_printer(_client_monitor_t *monitor);
     55 static void             show_attributes(const char *title, int request, ipp_t *ipp);
     56 static void             show_capabilities(ipp_t *response);
     57 static void             usage(void);
     58 
     59 
     60 /*
     61  * 'main()' - Main entry.
     62  */
     63 
     64 int					/* O - Exit status */
     65 main(int  argc,				/* I - Number of command-line arguments */
     66      char *argv[])			/* I - Command-line arguments */
     67 {
     68   int                   i;              /* Looping var */
     69   const char            *opt,           /* Current option */
     70                         *uri = NULL,    /* Printer URI */
     71                         *printfile = NULL,
     72                                         /* Print file */
     73                         *printformat = NULL;
     74                                         /* Print format */
     75   int                   keepfile = 0,   /* Keep temp file? */
     76                         grayscale = 0,  /* Force grayscale? */
     77                         verbosity = 0;  /* Verbosity */
     78   char                  tempfile[1024] = "",
     79                                         /* Temporary file (if any) */
     80                         scheme[32],     /* URI scheme */
     81                         userpass[256],  /* Username:password */
     82                         hostname[256],  /* Hostname */
     83                         resource[256];  /* Resource path */
     84   int                   port;           /* Port number */
     85   http_encryption_t     encryption;     /* Encryption mode */
     86   _client_monitor_t     monitor;        /* Monitoring data */
     87   http_t                *http;          /* HTTP connection */
     88   ipp_t                 *request,       /* IPP request */
     89                         *response;      /* IPP response */
     90   ipp_attribute_t       *attr;          /* IPP attribute */
     91   static const char * const pattrs[] =  /* Printer attributes we are interested in */
     92   {
     93     "job-template",
     94     "printer-defaults",
     95     "printer-description",
     96     "media-col-database",
     97     "media-col-ready"
     98   };
     99 
    100 
    101  /*
    102   * Parse command-line options...
    103   */
    104 
    105   for (i = 1; i < argc; i ++)
    106   {
    107     if (argv[i][0] == '-')
    108     {
    109       for (opt = argv[i] + 1; *opt; opt ++)
    110       {
    111         switch (*opt)
    112         {
    113           case 'd' : /* -d document-format */
    114               if (printformat)
    115               {
    116                 puts("Document format can only be specified once.");
    117                 usage();
    118                 return (1);
    119               }
    120 
    121               i ++;
    122               if (i >= argc)
    123               {
    124                 puts("Expected document format after '-d'.");
    125                 usage();
    126                 return (1);
    127               }
    128 
    129               printformat = argv[i];
    130               break;
    131 
    132           case 'f' : /* -f print-file */
    133               if (printfile)
    134               {
    135                 puts("Print file can only be specified once.");
    136                 usage();
    137                 return (1);
    138               }
    139 
    140               i ++;
    141               if (i >= argc)
    142               {
    143                 puts("Expected print file after '-f'.");
    144                 usage();
    145                 return (1);
    146               }
    147 
    148               printfile = argv[i];
    149               break;
    150 
    151           case 'g' :
    152               grayscale = 1;
    153               break;
    154 
    155           case 'k' :
    156               keepfile = 1;
    157               break;
    158 
    159           case 'v' :
    160               verbosity ++;
    161               break;
    162 
    163           default :
    164               printf("Unknown option '-%c'.\n", *opt);
    165               usage();
    166               return (1);
    167         }
    168       }
    169     }
    170     else if (uri || (strncmp(argv[i], "ipp://", 6) && strncmp(argv[i], "ipps://", 7)))
    171     {
    172       printf("Unknown command-line argument '%s'.\n", argv[i]);
    173       usage();
    174       return (1);
    175     }
    176     else
    177       uri = argv[i];
    178   }
    179 
    180  /*
    181   * Make sure we have everything we need.
    182   */
    183 
    184   if (!uri)
    185   {
    186     puts("Expected printer URI.");
    187     usage();
    188     return (1);
    189   }
    190 
    191  /*
    192   * Connect to the printer...
    193   */
    194 
    195   if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
    196   {
    197     printf("Bad printer URI '%s'.\n", uri);
    198     return (1);
    199   }
    200 
    201   if (!port)
    202     port = IPP_PORT;
    203 
    204   if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
    205     encryption = HTTP_ENCRYPTION_ALWAYS;
    206   else
    207     encryption = HTTP_ENCRYPTION_IF_REQUESTED;
    208 
    209   if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 0, NULL)) == NULL)
    210   {
    211     printf("Unable to connect to '%s' on port %d: %s\n", hostname, port, cupsLastErrorString());
    212     return (1);
    213   }
    214 
    215  /*
    216   * Query printer status and capabilities...
    217   */
    218 
    219   request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
    220   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
    221   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
    222   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
    223 
    224   response = cupsDoRequest(http, request, resource);
    225 
    226   if (verbosity)
    227     show_capabilities(response);
    228 
    229  /*
    230   * Now figure out what we will be printing...
    231   */
    232 
    233   if (printfile)
    234   {
    235    /*
    236     * User specified a print file, figure out the format...
    237     */
    238 
    239     if ((opt = strrchr(printfile, '.')) != NULL)
    240     {
    241      /*
    242       * Guess the format from the extension...
    243       */
    244 
    245       if (!strcmp(opt, ".jpg"))
    246         printformat = "image/jpeg";
    247       else if (!strcmp(opt, ".pdf"))
    248         printformat = "application/pdf";
    249       else if (!strcmp(opt, ".ps"))
    250         printformat = "application/postscript";
    251       else if (!strcmp(opt, ".pwg"))
    252         printformat = "image/pwg-raster";
    253       else if (!strcmp(opt, ".urf"))
    254         printformat = "image/urf";
    255       else
    256         printformat = "application/octet-stream";
    257     }
    258     else
    259     {
    260      /*
    261       * Tell the printer to auto-detect...
    262       */
    263 
    264       printformat = "application/octet-stream";
    265     }
    266   }
    267   else
    268   {
    269    /*
    270     * No file specified, make something to test with...
    271     */
    272 
    273     if ((printfile = make_raster_file(response, grayscale, tempfile, sizeof(tempfile), &printformat)) == NULL)
    274       return (1);
    275   }
    276 
    277   ippDelete(response);
    278 
    279  /*
    280   * Start monitoring the printer in the background...
    281   */
    282 
    283   memset(&monitor, 0, sizeof(monitor));
    284 
    285   monitor.uri           = uri;
    286   monitor.hostname      = hostname;
    287   monitor.resource      = resource;
    288   monitor.port          = port;
    289   monitor.encryption    = encryption;
    290 
    291   _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
    292 
    293  /*
    294   * Create the job and wait for completion...
    295   */
    296 
    297   request = ippNewRequest(IPP_OP_CREATE_JOB);
    298   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
    299   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
    300 
    301   if ((opt = strrchr(printfile, '/')) != NULL)
    302     opt ++;
    303   else
    304     opt = printfile;
    305 
    306   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, opt);
    307 
    308   if (verbosity)
    309     show_attributes("Create-Job request", 1, request);
    310 
    311   response = cupsDoRequest(http, request, resource);
    312 
    313   if (verbosity)
    314     show_attributes("Create-Job response", 0, response);
    315 
    316   if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
    317   {
    318     printf("Unable to create print job: %s\n", cupsLastErrorString());
    319 
    320     monitor.job_state = IPP_JSTATE_ABORTED;
    321 
    322     goto cleanup;
    323   }
    324 
    325   if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
    326   {
    327     puts("No job-id returned in Create-Job request.");
    328 
    329     monitor.job_state = IPP_JSTATE_ABORTED;
    330 
    331     goto cleanup;
    332   }
    333 
    334   monitor.job_id = ippGetInteger(attr, 0);
    335 
    336   printf("CREATED JOB %d, sending %s of type %s\n", monitor.job_id, printfile, printformat);
    337 
    338   ippDelete(response);
    339 
    340   request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
    341   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
    342   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor.job_id);
    343   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
    344   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, printformat);
    345   ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
    346 
    347   if (verbosity)
    348     show_attributes("Send-Document request", 1, request);
    349 
    350   response = cupsDoFileRequest(http, request, resource, printfile);
    351 
    352   if (verbosity)
    353     show_attributes("Send-Document response", 0, response);
    354 
    355   if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
    356   {
    357     printf("Unable to print file: %s\n", cupsLastErrorString());
    358 
    359     monitor.job_state = IPP_JSTATE_ABORTED;
    360 
    361     goto cleanup;
    362   }
    363 
    364   puts("WAITING FOR JOB TO COMPLETE");
    365 
    366   while (monitor.job_state < IPP_JSTATE_CANCELED)
    367     sleep(1);
    368 
    369  /*
    370   * Cleanup after ourselves...
    371   */
    372 
    373   cleanup:
    374 
    375   httpClose(http);
    376 
    377   if (tempfile[0] && !keepfile)
    378     unlink(tempfile);
    379 
    380   return (monitor.job_state == IPP_JSTATE_COMPLETED);
    381 }
    382 
    383 
    384 /*
    385  * 'make_raster_file()' - Create a temporary raster file.
    386  */
    387 
    388 static const char *                     /* O - Print filename */
    389 make_raster_file(ipp_t      *response,  /* I - Printer attributes */
    390                  int        grayscale,  /* I - Force grayscale? */
    391                  char       *tempname,  /* I - Temporary filename buffer */
    392                  size_t     tempsize,   /* I - Size of temp file buffer */
    393                  const char **format)   /* O - Print format */
    394 {
    395   int                   i,              /* Looping var */
    396                         count;          /* Number of values */
    397   ipp_attribute_t       *attr;          /* Printer attribute */
    398   const char            *type = NULL;   /* Raster type (colorspace + bits) */
    399   pwg_media_t           *media = NULL;  /* Media size */
    400   int                   xdpi = 0,       /* Horizontal resolution */
    401                         ydpi = 0;       /* Vertical resolution */
    402   int                   fd;             /* Temporary file */
    403   cups_mode_t           mode;           /* Raster mode */
    404   cups_raster_t         *ras;           /* Raster stream */
    405   cups_page_header2_t   header;         /* Page header */
    406   unsigned char         *line,          /* Line of raster data */
    407                         *lineptr;       /* Pointer into line */
    408   unsigned              y,              /* Current position on page */
    409                         xcount, ycount, /* Current count for X and Y */
    410                         xrep, yrep,     /* Repeat count for X and Y */
    411                         xoff, yoff,     /* Offsets for X and Y */
    412                         yend;           /* End Y value */
    413   int                   temprow,        /* Row in template */
    414                         tempcolor;      /* Template color */
    415   const char            *template;      /* Pointer into template */
    416   const unsigned char   *color;         /* Current color */
    417   static const unsigned char colors[][3] =
    418   {                                     /* Colors for test */
    419     { 191, 191, 191 },
    420     { 127, 127, 127 },
    421     {  63,  63,  63 },
    422     {   0,   0,   0 },
    423     { 255,   0,   0 },
    424     { 255, 127,   0 },
    425     { 255, 255,   0 },
    426     { 127, 255,   0 },
    427     {   0, 255,   0 },
    428     {   0, 255, 127 },
    429     {   0, 255, 255 },
    430     {   0, 127, 255 },
    431     {   0,   0, 255 },
    432     { 127,   0, 255 },
    433     { 255,   0, 255 }
    434   };
    435   static const char * const templates[] =
    436   {                                     /* Raster template */
    437     " CCC   U   U  PPPP    SSS          TTTTT  EEEEE   SSS   TTTTT          000     1     222    333      4   55555   66    77777   888    999   ",
    438     "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  ",
    439     "C      U   U  P   P  S               T    E      S        T           0   0    1        2      3  4  4   5      6         7   8   8  9   9  ",
    440     "C      U   U  PPPP    SSS   -----    T    EEEE    SSS     T           0 0 0    1      22    333   44444   555   6666      7    888    9999  ",
    441     "C      U   U  P          S           T    E          S    T           0   0    1     2         3     4       5  6   6    7    8   8      9  ",
    442     "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  ",
    443     " CCC    UUU   P       SSS            T    EEEEE   SSS     T            000    111   22222   333      4    555    666     7     888     99   ",
    444     "                                                                                                                                            "
    445   };
    446 
    447 
    448  /*
    449   * Figure out the output format...
    450   */
    451 
    452   if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) == NULL)
    453   {
    454     puts("No supported document formats, aborting.");
    455     return (NULL);
    456   }
    457 
    458   if (*format)
    459   {
    460     if (!ippContainsString(attr, *format))
    461     {
    462       printf("Printer does not support document-format '%s'.\n", *format);
    463       return (NULL);
    464     }
    465 
    466     if (!strcmp(*format, "image/urf"))
    467       mode = CUPS_RASTER_WRITE_APPLE;
    468     else if (!strcmp(*format, "image/pwg-raster"))
    469       mode = CUPS_RASTER_WRITE_PWG;
    470     else
    471     {
    472       printf("Unable to generate document-format '%s'.\n", *format);
    473       return (NULL);
    474     }
    475   }
    476   else if (ippContainsString(attr, "image/urf"))
    477   {
    478    /*
    479     * Apple Raster format...
    480     */
    481 
    482     *format = "image/urf";
    483     mode    = CUPS_RASTER_WRITE_APPLE;
    484   }
    485   else if (ippContainsString(attr, "image/pwg-raster"))
    486   {
    487    /*
    488     * PWG Raster format...
    489     */
    490 
    491     *format = "image/pwg-raster";
    492     mode    = CUPS_RASTER_WRITE_PWG;
    493   }
    494   else
    495   {
    496    /*
    497     * No supported raster format...
    498     */
    499 
    500     puts("Printer does not support Apple or PWG raster files, aborting.");
    501     return (NULL);
    502   }
    503 
    504  /*
    505   * Figure out the the media, resolution, and color mode...
    506   */
    507 
    508   if ((attr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL)
    509   {
    510    /*
    511     * Use default media...
    512     */
    513 
    514     media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
    515   }
    516   else if ((attr = ippFindAttribute(response, "media-ready", IPP_TAG_KEYWORD)) != NULL)
    517   {
    518    /*
    519     * Use ready media...
    520     */
    521 
    522     if (ippContainsString(attr, "na_letter_8.5x11in"))
    523       media = pwgMediaForPWG("na_letter_8.5x11in");
    524     else if (ippContainsString(attr, "iso_a4_210x297mm"))
    525       media = pwgMediaForPWG("iso_a4_210x297mm");
    526     else
    527       media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
    528   }
    529   else
    530   {
    531     puts("No default or ready media reported by printer, aborting.");
    532     return (NULL);
    533   }
    534 
    535   if (mode == CUPS_RASTER_WRITE_APPLE && (attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
    536   {
    537     for (i = 0, count = ippGetCount(attr); i < count; i ++)
    538     {
    539       const char *val = ippGetString(attr, i, NULL);
    540 
    541       if (!strncmp(val, "RS", 2))
    542         xdpi = ydpi = atoi(val + 2);
    543       else if (!strncmp(val, "W8", 2) && !type)
    544         type = "sgray_8";
    545       else if (!strncmp(val, "SRGB24", 6) && !grayscale)
    546         type = "srgb_8";
    547     }
    548   }
    549   else if (mode == CUPS_RASTER_WRITE_PWG && (attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
    550   {
    551     for (i = 0, count = ippGetCount(attr); i < count; i ++)
    552     {
    553       int tempxdpi, tempydpi;
    554       ipp_res_t tempunits;
    555 
    556       tempxdpi = ippGetResolution(attr, 0, &tempydpi, &tempunits);
    557 
    558       if (i == 0 || tempxdpi < xdpi || tempydpi < ydpi)
    559       {
    560         xdpi = tempxdpi;
    561         ydpi = tempydpi;
    562       }
    563     }
    564 
    565     if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) != NULL)
    566     {
    567       if (!grayscale && ippContainsString(attr, "srgb_8"))
    568         type = "srgb_8";
    569       else if (ippContainsString(attr, "sgray_8"))
    570         type = "sgray_8";
    571     }
    572   }
    573 
    574   if (xdpi < 72 || ydpi < 72)
    575   {
    576     puts("No supported raster resolutions, aborting.");
    577     return (NULL);
    578   }
    579 
    580   if (!type)
    581   {
    582     puts("No supported color spaces or bit depths, aborting.");
    583     return (NULL);
    584   }
    585 
    586  /*
    587   * Make the raster context and details...
    588   */
    589 
    590   if (!cupsRasterInitPWGHeader(&header, media, type, xdpi, ydpi, "one-sided", NULL))
    591   {
    592     printf("Unable to initialize raster context: %s\n", cupsRasterErrorString());
    593     return (NULL);
    594   }
    595 
    596   header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount] = 1;
    597 
    598   if (header.cupsWidth > (4 * header.HWResolution[0]))
    599   {
    600     xoff = header.HWResolution[0] / 2;
    601     yoff = header.HWResolution[1] / 2;
    602   }
    603   else
    604   {
    605     xoff = 0;
    606     yoff = 0;
    607   }
    608 
    609   xrep = (header.cupsWidth - 2 * xoff) / 140;
    610   yrep = xrep * header.HWResolution[1] / header.HWResolution[0];
    611   yend = header.cupsHeight - yoff;
    612 
    613  /*
    614   * Prepare the raster file...
    615   */
    616 
    617   if ((line = malloc(header.cupsBytesPerLine)) == NULL)
    618   {
    619     printf("Unable to allocate %u bytes for raster output: %s\n", header.cupsBytesPerLine, strerror(errno));
    620     return (NULL);
    621   }
    622 
    623   if ((fd = cupsTempFd(tempname, (int)tempsize)) < 0)
    624   {
    625     printf("Unable to create temporary print file: %s\n", strerror(errno));
    626     return (NULL);
    627   }
    628 
    629   if ((ras = cupsRasterOpen(fd, mode)) == NULL)
    630   {
    631     printf("Unable to open raster stream: %s\n", cupsRasterErrorString());
    632     close(fd);
    633     return (NULL);
    634   }
    635 
    636  /*
    637   * Write a single page consisting of the template dots repeated over the page.
    638   */
    639 
    640   cupsRasterWriteHeader2(ras, &header);
    641 
    642   memset(line, 0xff, header.cupsBytesPerLine);
    643 
    644   for (y = 0; y < yoff; y ++)
    645     cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
    646 
    647   for (temprow = 0, tempcolor = 0; y < yend;)
    648   {
    649     template = templates[temprow];
    650     color    = colors[tempcolor];
    651 
    652     temprow ++;
    653     if (temprow >= (int)(sizeof(templates) / sizeof(templates[0])))
    654     {
    655       temprow = 0;
    656       tempcolor ++;
    657       if (tempcolor >= (int)(sizeof(colors) / sizeof(colors[0])))
    658         tempcolor = 0;
    659       else if (tempcolor > 3 && header.cupsColorSpace == CUPS_CSPACE_SW)
    660         tempcolor = 0;
    661     }
    662 
    663     memset(line, 0xff, header.cupsBytesPerLine);
    664 
    665     if (header.cupsColorSpace == CUPS_CSPACE_SW)
    666     {
    667      /*
    668       * Do grayscale output...
    669       */
    670 
    671       for (lineptr = line + xoff; *template; template ++)
    672       {
    673         if (*template != ' ')
    674         {
    675           for (xcount = xrep; xcount > 0; xcount --)
    676             *lineptr++ = *color;
    677         }
    678         else
    679         {
    680           lineptr += xrep;
    681         }
    682       }
    683     }
    684     else
    685     {
    686      /*
    687       * Do color output...
    688       */
    689 
    690       for (lineptr = line + 3 * xoff; *template; template ++)
    691       {
    692         if (*template != ' ')
    693         {
    694           for (xcount = xrep; xcount > 0; xcount --, lineptr += 3)
    695             memcpy(lineptr, color, 3);
    696         }
    697         else
    698         {
    699           lineptr += 3 * xrep;
    700         }
    701       }
    702     }
    703 
    704     for (ycount = yrep; ycount > 0 && y < yend; ycount --, y ++)
    705       cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
    706   }
    707 
    708   memset(line, 0xff, header.cupsBytesPerLine);
    709 
    710   for (y = 0; y < header.cupsHeight; y ++)
    711     cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
    712 
    713   cupsRasterClose(ras);
    714 
    715   close(fd);
    716 
    717   printf("PRINT FILE: %s\n", tempname);
    718 
    719   return (tempname);
    720 }
    721 
    722 
    723 /*
    724  * 'monitor_printer()' - Monitor the job and printer states.
    725  */
    726 
    727 static void *				/* O - Thread exit code */
    728 monitor_printer(
    729     _client_monitor_t *monitor)		/* I - Monitoring data */
    730 {
    731   http_t	*http;			/* Connection to printer */
    732   ipp_t		*request,		/* IPP request */
    733 		*response;		/* IPP response */
    734   ipp_attribute_t *attr;		/* Attribute in response */
    735   ipp_pstate_t	printer_state;		/* Printer state */
    736   char          printer_state_reasons[1024];
    737                                         /* Printer state reasons */
    738   ipp_jstate_t	job_state;		/* Job state */
    739   char          job_state_reasons[1024];/* Printer state reasons */
    740   static const char * const jattrs[] =  /* Job attributes we want */
    741   {
    742     "job-state",
    743     "job-state-reasons"
    744   };
    745   static const char * const pattrs[] =  /* Printer attributes we want */
    746   {
    747     "printer-state",
    748     "printer-state-reasons"
    749   };
    750 
    751 
    752  /*
    753   * Open a connection to the printer...
    754   */
    755 
    756   http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC, monitor->encryption, 1, 0, NULL);
    757 
    758  /*
    759   * Loop until the job is canceled, aborted, or completed.
    760   */
    761 
    762   printer_state            = (ipp_pstate_t)0;
    763   printer_state_reasons[0] = '\0';
    764 
    765   job_state            = (ipp_jstate_t)0;
    766   job_state_reasons[0] = '\0';
    767 
    768   while (monitor->job_state < IPP_JSTATE_CANCELED)
    769   {
    770    /*
    771     * Reconnect to the printer as needed...
    772     */
    773 
    774     if (httpGetFd(http) < 0)
    775       httpReconnect2(http, 30000, NULL);
    776 
    777     if (httpGetFd(http) >= 0)
    778     {
    779      /*
    780       * Connected, so check on the printer state...
    781       */
    782 
    783       request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
    784       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
    785       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
    786       ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
    787 
    788       response = cupsDoRequest(http, request, monitor->resource);
    789 
    790       if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
    791         printer_state = (ipp_pstate_t)ippGetInteger(attr, 0);
    792 
    793       if ((attr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL)
    794         ippAttributeString(attr, printer_state_reasons, sizeof(printer_state_reasons));
    795 
    796       if (printer_state != monitor->printer_state || strcmp(printer_state_reasons, monitor->printer_state_reasons))
    797       {
    798         printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state), printer_state_reasons);
    799 
    800         monitor->printer_state = printer_state;
    801         strlcpy(monitor->printer_state_reasons, printer_state_reasons, sizeof(monitor->printer_state_reasons));
    802       }
    803 
    804       ippDelete(response);
    805 
    806       if (monitor->job_id > 0)
    807       {
    808        /*
    809         * Check the status of the job itself...
    810         */
    811 
    812         request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
    813         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
    814         ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor->job_id);
    815         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
    816         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
    817 
    818         response = cupsDoRequest(http, request, monitor->resource);
    819 
    820         if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
    821           job_state = (ipp_jstate_t)ippGetInteger(attr, 0);
    822 
    823         if ((attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD)) != NULL)
    824           ippAttributeString(attr, job_state_reasons, sizeof(job_state_reasons));
    825 
    826         if (job_state != monitor->job_state || strcmp(job_state_reasons, monitor->job_state_reasons))
    827         {
    828           printf("JOB %d: %s (%s)\n", monitor->job_id, ippEnumString("job-state", job_state), job_state_reasons);
    829 
    830           monitor->job_state = job_state;
    831           strlcpy(monitor->job_state_reasons, job_state_reasons, sizeof(monitor->job_state_reasons));
    832         }
    833 
    834         ippDelete(response);
    835       }
    836     }
    837 
    838     if (monitor->job_state < IPP_JSTATE_CANCELED)
    839     {
    840      /*
    841       * Sleep for 5 seconds...
    842       */
    843 
    844       sleep(5);
    845     }
    846   }
    847 
    848  /*
    849   * Cleanup and return...
    850   */
    851 
    852   httpClose(http);
    853 
    854   return (NULL);
    855 }
    856 
    857 
    858 /*
    859  * 'show_attributes()' - Show attributes in a request or response.
    860  */
    861 
    862 static void
    863 show_attributes(const char *title,      /* I - Title */
    864                 int        request,     /* I - 1 for request, 0 for response */
    865                 ipp_t      *ipp)        /* I - IPP request/response */
    866 {
    867   int                   minor, major = ippGetVersion(ipp, &minor);
    868                                         /* IPP version number */
    869   ipp_tag_t             group = IPP_TAG_ZERO;
    870                                         /* Current group tag */
    871   ipp_attribute_t       *attr;          /* Current attribute */
    872   const char            *name;          /* Attribute name */
    873   char                  buffer[1024];   /* Value */
    874 
    875 
    876   printf("%s:\n", title);
    877   printf("  version=%d.%d\n", major, minor);
    878   printf("  request-id=%d\n", ippGetRequestId(ipp));
    879   if (!request)
    880     printf("  status-code=%s\n", ippErrorString(ippGetStatusCode(ipp)));
    881 
    882   for (attr = ippFirstAttribute(ipp); attr; attr = ippNextAttribute(ipp))
    883   {
    884     if (group != ippGetGroupTag(attr))
    885     {
    886       group = ippGetGroupTag(attr);
    887       if (group)
    888         printf("  %s:\n", ippTagString(group));
    889     }
    890 
    891     if ((name = ippGetName(attr)) != NULL)
    892     {
    893       ippAttributeString(attr, buffer, sizeof(buffer));
    894       printf("    %s(%s%s)=%s\n", name, ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), buffer);
    895     }
    896   }
    897 }
    898 
    899 
    900 /*
    901  * 'show_capabilities()' - Show printer capabilities.
    902  */
    903 
    904 static void
    905 show_capabilities(ipp_t *response)      /* I - Printer attributes */
    906 {
    907   int                   i;              /* Looping var */
    908   ipp_attribute_t       *attr;          /* Attribute */
    909   char                  buffer[1024];   /* Attribute value buffer */
    910   static const char * const pattrs[] =  /* Attributes we want to show */
    911   {
    912     "copies-default",
    913     "copies-supported",
    914     "finishings-default",
    915     "finishings-ready",
    916     "finishings-supported",
    917     "media-default",
    918     "media-ready",
    919     "media-supported",
    920     "output-bin-default",
    921     "output-bin-supported",
    922     "print-color-mode-default",
    923     "print-color-mode-supported",
    924     "sides-default",
    925     "sides-supported",
    926     "document-format-default",
    927     "document-format-supported",
    928     "pwg-raster-document-resolution-supported",
    929     "pwg-raster-document-type-supported",
    930     "urf-supported"
    931   };
    932 
    933 
    934   puts("CAPABILITIES:");
    935   for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++)
    936   {
    937      if ((attr = ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) != NULL)
    938      {
    939        ippAttributeString(attr, buffer, sizeof(buffer));
    940        printf("  %s=%s\n", pattrs[i], buffer);
    941      }
    942   }
    943 }
    944 
    945 
    946 /*
    947  * 'usage()' - Show program usage...
    948  */
    949 
    950 static void
    951 usage(void)
    952 {
    953   puts("Usage: ./testclient printer-uri [options]");
    954   puts("Options:");
    955   puts("  -d document-format  Generate the specified format");
    956   puts("  -f print-file       Print the named file");
    957   puts("  -g                  Force grayscale printing");
    958   puts("  -k                  Keep temporary files");
    959   puts("  -v                  Be more verbose");
    960 }
    961