Revert manifest to default one
[external/cups.git] / test / ippserver.c
1 /*
2  * "$Id: ippserver.c 10031 2011-09-30 05:24:10Z mike $"
3  *
4  *   Sample IPP/2.0 server for CUPS.
5  *
6  *   Copyright 2010-2011 by Apple Inc.
7  *
8  *   These coded instructions, statements, and computer programs are the
9  *   property of Apple Inc. and are protected by Federal copyright
10  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
11  *   which should have been included with this file.  If this file is
12  *   file is missing or damaged, see the license at "http://www.cups.org/".
13  *
14  *   This file is subject to the Apple OS-Developed Software exception.
15  *
16  * Contents:
17  *
18  *   main()                       - Main entry to the sample server.
19  *   clean_jobs()                 - Clean out old (completed) jobs.
20  *   compare_jobs()               - Compare two jobs.
21  *   copy_attribute()             - Copy a single attribute.
22  *   copy_attributes()            - Copy attributes from one request to
23  *                                  another.
24  *   copy_job_attrs()             - Copy job attributes to the response.
25  *   create_client()              - Accept a new network connection and create
26  *                                  a client object.
27  *   create_job()                 - Create a new job object from a Print-Job or
28  *                                  Create-Job request.
29  *   create_listener()            - Create a listener socket.
30  *   create_media_col()           - Create a media-col value.
31  *   create_printer()             - Create, register, and listen for
32  *                                  connections to a printer object.
33  *   create_requested_array()     - Create an array for requested-attributes.
34  *   debug_attributes()           - Print attributes in a request or response.
35  *   delete_client()              - Close the socket and free all memory used
36  *                                  by a client object.
37  *   delete_job()                 - Remove from the printer and free all memory
38  *                                  used by a job object.
39  *   delete_printer()             - Unregister, close listen sockets, and free
40  *                                  all memory used by a printer object.
41  *   dnssd_callback()             - Handle Bonjour registration events.
42  *   find_job()                   - Find a job specified in a request.
43  *   html_escape()                - Write a HTML-safe string.
44  *   html_printf()                - Send formatted text to the client, quoting
45  *                                  as needed.
46  *   ipp_cancel_job()             - Cancel a job.
47  *   ipp_create_job()             - Create a job object.
48  *   ipp_get_job_attributes()     - Get the attributes for a job object.
49  *   ipp_get_jobs()               - Get a list of job objects.
50  *   ipp_get_printer_attributes() - Get the attributes for a printer object.
51  *   ipp_print_job()              - Create a job object with an attached
52  *                                  document.
53  *   ipp_print_uri()              - Create a job object with a referenced
54  *                                  document.
55  *   ipp_send_document()          - Add an attached document to a job object
56  *                                  created with Create-Job.
57  *   ipp_send_uri()               - Add a referenced document to a job object
58  *                                  created with Create-Job.
59  *   ipp_validate_job()           - Validate job creation attributes.
60  *   process_client()             - Process client requests on a thread.
61  *   process_http()               - Process a HTTP request.
62  *   process_ipp()                - Process an IPP request.
63  *   process_job()                - Process a print job.
64  *   register_printer()           - Register a printer object via Bonjour.
65  *   respond_http()               - Send a HTTP response.
66  *   respond_ipp()                - Send an IPP response.
67  *   respond_unsupported()        - Respond with an unsupported attribute.
68  *   run_printer()                - Run the printer service.
69  *   usage()                      - Show program usage.
70  *   valid_doc_attributes()       - Determine whether the document attributes
71  *                                  are valid.
72  *   valid_job_attributes()       - Determine whether the job attributes are
73  *                                  valid.
74  */
75
76 /*
77  * Include necessary headers...
78  */
79
80 #include <cups/cups-private.h>
81 #ifdef HAVE_DNSSD
82 #  include <dns_sd.h>
83 #endif /* HAVE_DNSSD */
84 #include <sys/stat.h>
85 #include <poll.h>
86 #ifdef HAVE_SYS_MOUNT_H
87 #  include <sys/mount.h>
88 #endif /* HAVE_SYS_MOUNT_H */
89 #ifdef HAVE_SYS_STATFS_H
90 #  include <sys/statfs.h>
91 #endif /* HAVE_SYS_STATFS_H */
92 #ifdef HAVE_SYS_STATVFS_H
93 #  include <sys/statvfs.h>
94 #endif /* HAVE_SYS_STATVFS_H */
95 #ifdef HAVE_SYS_VFS_H
96 #  include <sys/vfs.h>
97 #endif /* HAVE_SYS_VFS_H */
98
99
100 /*
101  * Constants...
102  */
103
104 enum _ipp_preasons_e                    /* printer-state-reasons bit values */
105 {
106   _IPP_PRINTER_NONE = 0x0000,           /* none */
107   _IPP_PRINTER_OTHER = 0x0001,          /* other */
108   _IPP_PRINTER_COVER_OPEN = 0x0002,     /* cover-open */
109   _IPP_PRINTER_INPUT_TRAY_MISSING = 0x0004,
110                                         /* input-tray-missing */
111   _IPP_PRINTER_MARKER_SUPPLY_EMPTY = 0x0008,
112                                         /* marker-supply-empty */
113   _IPP_PRINTER_MARKER_SUPPLY_LOW = 0x0010,
114                                         /* marker-suply-low */
115   _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL = 0x0020,
116                                         /* marker-waste-almost-full */
117   _IPP_PRINTER_MARKER_WASTE_FULL = 0x0040,
118                                         /* marker-waste-full */
119   _IPP_PRINTER_MEDIA_EMPTY = 0x0080,    /* media-empty */
120   _IPP_PRINTER_MEDIA_JAM = 0x0100,      /* media-jam */
121   _IPP_PRINTER_MEDIA_LOW = 0x0200,      /* media-low */
122   _IPP_PRINTER_MEDIA_NEEDED = 0x0400,   /* media-needed */
123   _IPP_PRINTER_MOVING_TO_PAUSED = 0x0800,
124                                         /* moving-to-paused */
125   _IPP_PRINTER_PAUSED = 0x1000,         /* paused */
126   _IPP_PRINTER_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
127   _IPP_PRINTER_TONER_EMPTY = 0x4000,    /* toner-empty */
128   _IPP_PRINTER_TONER_LOW = 0x8000       /* toner-low */
129 };
130 typedef unsigned int _ipp_preasons_t;   /* Bitfield for printer-state-reasons */
131
132 typedef enum _ipp_media_class_e
133 {
134   _IPP_GENERAL,                         /* General-purpose size */
135   _IPP_PHOTO_ONLY,                      /* Photo-only size */
136   _IPP_ENV_ONLY                         /* Envelope-only size */
137 } _ipp_media_class_t;
138
139 static const char * const media_supported[] =
140 {                                       /* media-supported values */
141   "iso_a4_210x297mm",                   /* A4 */
142   "iso_a5_148x210mm",                   /* A5 */
143   "iso_a6_105x148mm",                   /* A6 */
144   "iso_dl_110x220mm",                   /* DL */
145   "na_legal_8.5x14in",                  /* Legal */
146   "na_letter_8.5x11in",                 /* Letter */
147   "na_number-10_4.125x9.5in",           /* #10 */
148   "na_index-3x5_3x5in",                 /* 3x5 */
149   "oe_photo-l_3.5x5in",                 /* L */
150   "na_index-4x6_4x6in",                 /* 4x6 */
151   "na_5x7_5x7in"                        /* 5x7 aka 2L */
152 };
153 static const int media_col_sizes[][3] =
154 {                                       /* media-col-database sizes */
155   { 21000, 29700, _IPP_GENERAL },       /* A4 */
156   { 14800, 21000, _IPP_PHOTO_ONLY },    /* A5 */
157   { 10500, 14800, _IPP_PHOTO_ONLY },    /* A6 */
158   { 11000, 22000, _IPP_ENV_ONLY },      /* DL */
159   { 21590, 35560, _IPP_GENERAL },       /* Legal */
160   { 21590, 27940, _IPP_GENERAL },       /* Letter */
161   { 10477, 24130, _IPP_ENV_ONLY },      /* #10 */
162   {  7630, 12700, _IPP_PHOTO_ONLY },    /* 3x5 */
163   {  8890, 12700, _IPP_PHOTO_ONLY },    /* L */
164   { 10160, 15240, _IPP_PHOTO_ONLY },    /* 4x6 */
165   { 12700, 17780, _IPP_PHOTO_ONLY }     /* 5x7 aka 2L */
166 };
167 static const char * const media_type_supported[] =
168                                       /* media-type-supported values */
169 {
170   "auto",
171   "cardstock",
172   "envelope",
173   "labels",
174   "other",
175   "photographic-glossy",
176   "photographic-high-gloss",
177   "photographic-matte",
178   "photographic-satin",
179   "photographic-semi-gloss",
180   "stationery",
181   "stationery-letterhead",
182   "transparency"
183 };
184
185
186 /*
187  * Structures...
188  */
189
190 typedef struct _ipp_job_s _ipp_job_t;
191
192 typedef struct _ipp_printer_s           /**** Printer data ****/
193 {
194   int                   ipv4,           /* IPv4 listener */
195                         ipv6;           /* IPv6 listener */
196 #ifdef HAVE_DNSSD
197   DNSServiceRef         common_ref,     /* Shared service connection */
198                         ipp_ref,        /* Bonjour IPP service */
199                         http_ref,       /* Bonjour HTTP service */
200                         printer_ref;    /* Bonjour LPD service */
201   TXTRecordRef          ipp_txt;        /* Bonjour IPP TXT record */
202   char                  *dnssd_name;    /* printer-dnssd-name */
203 #endif /* HAVE_DNSSD */
204   char                  *name,          /* printer-name */
205                         *icon,          /* Icon filename */
206                         *directory,     /* Spool directory */
207                         *hostname,      /* Hostname */
208                         *uri;           /* printer-uri-supported */
209   int                   port;           /* Port */
210   size_t                urilen;         /* Length of printer URI */
211   ipp_t                 *attrs;         /* Static attributes */
212   ipp_pstate_t          state;          /* printer-state value */
213   _ipp_preasons_t       state_reasons;  /* printer-state-reasons values */
214   cups_array_t          *jobs;          /* Jobs */
215   _ipp_job_t            *active_job;    /* Current active/pending job */
216   int                   next_job_id;    /* Next job-id value */
217   _cups_rwlock_t        rwlock;         /* Printer lock */
218 } _ipp_printer_t;
219
220 struct _ipp_job_s                       /**** Job data ****/
221 {
222   int                   id;             /* Job ID */
223   char                  *name,          /* job-name */
224                         *username,      /* job-originating-user-name */
225                         *format;        /* document-format */
226   ipp_jstate_t          state;          /* job-state value */
227   time_t                processing,     /* time-at-processing value */
228                         completed;      /* time-at-completed value */
229   ipp_t                 *attrs;         /* Static attributes */
230   int                   cancel;         /* Non-zero when job canceled */
231   char                  *filename;      /* Print file name */
232   int                   fd;             /* Print file descriptor */
233   _ipp_printer_t        *printer;       /* Printer */
234 };
235
236 typedef struct _ipp_client_s            /**** Client data ****/
237 {
238   http_t                http;           /* HTTP connection */
239   ipp_t                 *request,       /* IPP request */
240                         *response;      /* IPP response */
241   time_t                start;          /* Request start time */
242   http_state_t          operation;      /* Request operation */
243   ipp_op_t              operation_id;   /* IPP operation-id */
244   char                  uri[1024];      /* Request URI */
245   http_addr_t           addr;           /* Client address */
246   _ipp_printer_t        *printer;       /* Printer */
247   _ipp_job_t            *job;           /* Current job, if any */
248 } _ipp_client_t;
249
250
251 /*
252  * Local functions...
253  */
254
255 static void             clean_jobs(_ipp_printer_t *printer);
256 static int              compare_jobs(_ipp_job_t *a, _ipp_job_t *b);
257 static ipp_attribute_t  *copy_attribute(ipp_t *to, ipp_attribute_t *attr,
258                                         ipp_tag_t group_tag, int quickcopy);
259 static void             copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra,
260                                         ipp_tag_t group_tag, int quickcopy);
261 static void             copy_job_attributes(_ipp_client_t *client,
262                                             _ipp_job_t *job, cups_array_t *ra);
263 static _ipp_client_t    *create_client(_ipp_printer_t *printer, int sock);
264 static _ipp_job_t       *create_job(_ipp_client_t *client);
265 static int              create_listener(int family, int *port);
266 static ipp_t            *create_media_col(const char *media, const char *type,
267                                           int width, int length, int margins);
268 static ipp_t            *create_media_size(int width, int length);
269 static _ipp_printer_t   *create_printer(const char *servername,
270                                         const char *name, const char *location,
271                                         const char *make, const char *model,
272                                         const char *icon,
273                                         const char *docformats, int ppm,
274                                         int ppm_color, int duplex, int port,
275 #ifdef HAVE_DNSSD
276                                         const char *regtype,
277 #endif /* HAVE_DNSSD */
278                                         const char *directory);
279 static cups_array_t     *create_requested_array(_ipp_client_t *client);
280 static void             debug_attributes(const char *title, ipp_t *ipp,
281                                          int response);
282 static void             delete_client(_ipp_client_t *client);
283 static void             delete_job(_ipp_job_t *job);
284 static void             delete_printer(_ipp_printer_t *printer);
285 #ifdef HAVE_DNSSD
286 static void             dnssd_callback(DNSServiceRef sdRef,
287                                        DNSServiceFlags flags,
288                                        DNSServiceErrorType errorCode,
289                                        const char *name,
290                                        const char *regtype,
291                                        const char *domain,
292                                        _ipp_printer_t *printer);
293 #endif /* HAVE_DNSSD */
294 static _ipp_job_t       *find_job(_ipp_client_t *client);
295 static void             html_escape(_ipp_client_t *client, const char *s,
296                                     size_t slen);
297 static void             html_printf(_ipp_client_t *client, const char *format,
298                                     ...) __attribute__((__format__(__printf__,
299                                                                    2, 3)));
300 static void             ipp_cancel_job(_ipp_client_t *client);
301 static void             ipp_create_job(_ipp_client_t *client);
302 static void             ipp_get_job_attributes(_ipp_client_t *client);
303 static void             ipp_get_jobs(_ipp_client_t *client);
304 static void             ipp_get_printer_attributes(_ipp_client_t *client);
305 static void             ipp_print_job(_ipp_client_t *client);
306 static void             ipp_print_uri(_ipp_client_t *client);
307 static void             ipp_send_document(_ipp_client_t *client);
308 static void             ipp_send_uri(_ipp_client_t *client);
309 static void             ipp_validate_job(_ipp_client_t *client);
310 static void             *process_client(_ipp_client_t *client);
311 static int              process_http(_ipp_client_t *client);
312 static int              process_ipp(_ipp_client_t *client);
313 static void             *process_job(_ipp_job_t *job);
314 #ifdef HAVE_DNSSD
315 static int              register_printer(_ipp_printer_t *printer,
316                                          const char *location, const char *make,
317                                          const char *model, const char *formats,
318                                          const char *adminurl, int color,
319                                          int duplex, const char *regtype);
320 #endif /* HAVE_DNSSD */
321 static int              respond_http(_ipp_client_t *client, http_status_t code,
322                                      const char *type, size_t length);
323 static void             respond_ipp(_ipp_client_t *client, ipp_status_t status,
324                                     const char *message, ...)
325                         __attribute__ ((__format__ (__printf__, 3, 4)));
326 static void             respond_unsupported(_ipp_client_t *client,
327                                             ipp_attribute_t *attr);
328 static void             run_printer(_ipp_printer_t *printer);
329 static void             usage(int status) __attribute__((noreturn));
330 static int              valid_doc_attributes(_ipp_client_t *client);
331 static int              valid_job_attributes(_ipp_client_t *client);
332
333
334 /*
335  * Globals...
336  */
337
338 static int              KeepFiles = 0,
339                         Verbosity = 0;
340
341
342 /*
343  * 'main()' - Main entry to the sample server.
344  */
345
346 int                                     /* O - Exit status */
347 main(int  argc,                         /* I - Number of command-line args */
348      char *argv[])                      /* I - Command-line arguments */
349 {
350   int           i;                      /* Looping var */
351   const char    *opt,                   /* Current option character */
352                 *servername = NULL,     /* Server host name */
353                 *name = NULL,           /* Printer name */
354                 *location = "",         /* Location of printer */
355                 *make = "Test",         /* Manufacturer */
356                 *model = "Printer",     /* Model */
357                 *icon = "printer.png",  /* Icon file */
358                 *formats = "application/pdf,image/jpeg";
359                                         /* Supported formats */
360 #ifdef HAVE_DNSSD
361   const char    *regtype = "_ipp._tcp"; /* Bonjour service type */
362 #endif /* HAVE_DNSSD */
363   int           port = 8631,            /* Port number (0 = auto) TODO: FIX */
364                 duplex = 0,             /* Duplex mode */
365                 ppm = 10,               /* Pages per minute for mono */
366                 ppm_color = 0;          /* Pages per minute for color */
367   char          directory[1024] = "";   /* Spool directory */
368   _ipp_printer_t *printer;              /* Printer object */
369
370
371  /*
372   * Parse command-line arguments...
373   */
374
375   for (i = 1; i < argc; i ++)
376     if (argv[i][0] == '-')
377     {
378       for (opt = argv[i] + 1; *opt; opt ++)
379         switch (*opt)
380         {
381           case '2' : /* -2 (enable 2-sided printing) */
382               duplex = 1;
383               break;
384
385           case 'M' : /* -M manufacturer */
386               i ++;
387               if (i >= argc)
388                 usage(1);
389               make = argv[i];
390               break;
391
392           case 'd' : /* -d spool-directory */
393               i ++;
394               if (i >= argc)
395                 usage(1);
396               strlcpy(directory, argv[i], sizeof(directory));
397               break;
398
399           case 'f' : /* -f type/subtype[,...] */
400               i ++;
401               if (i >= argc)
402                 usage(1);
403               formats = argv[i];
404               break;
405
406           case 'h' : /* -h (show help) */
407               usage(0);
408               break;
409
410           case 'i' : /* -i icon.png */
411               i ++;
412               if (i >= argc)
413                 usage(1);
414               icon = argv[i];
415               break;
416
417           case 'k' : /* -k (keep files) */
418               KeepFiles = 1;
419               break;
420
421           case 'l' : /* -l location */
422               i ++;
423               if (i >= argc)
424                 usage(1);
425               location = argv[i];
426               break;
427
428           case 'm' : /* -m model */
429               i ++;
430               if (i >= argc)
431                 usage(1);
432               model = argv[i];
433               break;
434
435           case 'n' : /* -n hostname */
436               i ++;
437               if (i >= argc)
438                 usage(1);
439               servername = argv[i];
440               break;
441
442           case 'p' : /* -p port */
443               i ++;
444               if (i >= argc || !isdigit(argv[i][0] & 255))
445                 usage(1);
446               port = atoi(argv[i]);
447               break;
448
449 #ifdef HAVE_DNSSD
450           case 'r' : /* -r regtype */
451               i ++;
452               if (i >= argc)
453                 usage(1);
454               regtype = argv[i];
455               break;
456 #endif /* HAVE_DNSSD */
457
458           case 's' : /* -s speed[,color-speed] */
459               i ++;
460               if (i >= argc)
461                 usage(1);
462               if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1)
463                 usage(1);
464               break;
465
466           case 'v' : /* -v (be verbose) */
467               Verbosity ++;
468               break;
469
470           default : /* Unknown */
471               fprintf(stderr, "Unknown option \"-%c\".\n", *opt);
472               usage(1);
473               break;
474         }
475     }
476     else if (!name)
477     {
478       name = argv[i];
479     }
480     else
481     {
482       fprintf(stderr, "Unexpected command-line argument \"%s\"\n", argv[i]);
483       usage(1);
484     }
485
486   if (!name)
487     usage(1);
488
489  /*
490   * Apply defaults as needed...
491   */
492
493   if (!directory[0])
494   {
495     snprintf(directory, sizeof(directory), "/tmp/ippserver.%d", (int)getpid());
496
497     if (mkdir(directory, 0777) && errno != EEXIST)
498     {
499       fprintf(stderr, "Unable to create spool directory \"%s\": %s\n",
500               directory, strerror(errno));
501       usage(1);
502     }
503
504     if (Verbosity)
505       fprintf(stderr, "Using spool directory \"%s\".\n", directory);
506   }
507
508  /*
509   * Create the printer...
510   */
511
512   if ((printer = create_printer(servername, name, location, make, model, icon,
513                                 formats, ppm, ppm_color, duplex, port,
514 #ifdef HAVE_DNSSD
515                                 regtype,
516 #endif /* HAVE_DNSSD */
517                                 directory)) == NULL)
518     return (1);
519
520  /*
521   * Run the print service...
522   */
523
524   run_printer(printer);
525
526  /*
527   * Destroy the printer and exit...
528   */
529
530   delete_printer(printer);
531
532   return (0);
533 }
534
535
536 /*
537  * 'clean_jobs()' - Clean out old (completed) jobs.
538  */
539
540 static void
541 clean_jobs(_ipp_printer_t *printer)     /* I - Printer */
542 {
543   _ipp_job_t    *job;                   /* Current job */
544   time_t        cleantime;              /* Clean time */
545
546
547   if (cupsArrayCount(printer->jobs) == 0)
548     return;
549
550   cleantime = time(NULL) - 60;
551
552   _cupsRWLockWrite(&(printer->rwlock));
553   for (job = (_ipp_job_t *)cupsArrayFirst(printer->jobs);
554        job;
555        job = (_ipp_job_t *)cupsArrayNext(printer->jobs))
556     if (job->completed && job->completed < cleantime)
557     {
558       cupsArrayRemove(printer->jobs, job);
559       delete_job(job);
560     }
561     else
562       break;
563   _cupsRWUnlock(&(printer->rwlock));
564 }
565
566
567 /*
568  * 'compare_jobs()' - Compare two jobs.
569  */
570
571 static int                              /* O - Result of comparison */
572 compare_jobs(_ipp_job_t *a,             /* I - First job */
573              _ipp_job_t *b)             /* I - Second job */
574 {
575   return (b->id - a->id);
576 }
577
578
579 /*
580  * 'copy_attribute()' - Copy a single attribute.
581  */
582
583 static ipp_attribute_t *                /* O - New attribute */
584 copy_attribute(
585     ipp_t           *to,                /* O - Destination request/response */
586     ipp_attribute_t *attr,              /* I - Attribute to copy */
587     ipp_tag_t       group_tag,          /* I - Group to put the copy in */
588     int             quickcopy)          /* I - Do a quick copy? */
589 {
590   int                   i;              /* Looping var */
591   ipp_attribute_t       *toattr;        /* Destination attribute */
592
593
594   if (Verbosity && attr->name)
595   {
596     char        buffer[2048];           /* Attribute value */
597
598     _ippAttrString(attr, buffer, sizeof(buffer));
599
600     fprintf(stderr, "Copying %s (%s%s) %s\n", attr->name,
601             attr->num_values > 1 ? "1setOf " : "",
602             ippTagString(attr->value_tag & ~IPP_TAG_COPY), buffer);
603   }
604
605   switch (attr->value_tag & ~IPP_TAG_COPY)
606   {
607     case IPP_TAG_ZERO :
608         toattr = ippAddSeparator(to);
609         break;
610
611     case IPP_TAG_INTEGER :
612     case IPP_TAG_ENUM :
613         toattr = ippAddIntegers(to, group_tag, attr->value_tag,
614                                 attr->name, attr->num_values, NULL);
615
616         for (i = 0; i < attr->num_values; i ++)
617           toattr->values[i].integer = attr->values[i].integer;
618         break;
619
620     case IPP_TAG_BOOLEAN :
621         toattr = ippAddBooleans(to, group_tag, attr->name,
622                                 attr->num_values, NULL);
623
624         for (i = 0; i < attr->num_values; i ++)
625           toattr->values[i].boolean = attr->values[i].boolean;
626         break;
627
628     case IPP_TAG_TEXT :
629     case IPP_TAG_NAME :
630     case IPP_TAG_KEYWORD :
631     case IPP_TAG_URI :
632     case IPP_TAG_URISCHEME :
633     case IPP_TAG_CHARSET :
634     case IPP_TAG_LANGUAGE :
635     case IPP_TAG_MIMETYPE :
636         toattr = ippAddStrings(to, group_tag,
637                                (ipp_tag_t)(attr->value_tag | quickcopy),
638                                attr->name, attr->num_values, NULL, NULL);
639
640         if (quickcopy)
641         {
642           for (i = 0; i < attr->num_values; i ++)
643             toattr->values[i].string.text = attr->values[i].string.text;
644         }
645         else
646         {
647           for (i = 0; i < attr->num_values; i ++)
648             toattr->values[i].string.text =
649                 _cupsStrAlloc(attr->values[i].string.text);
650         }
651         break;
652
653     case IPP_TAG_DATE :
654         toattr = ippAddDate(to, group_tag, attr->name,
655                             attr->values[0].date);
656         break;
657
658     case IPP_TAG_RESOLUTION :
659         toattr = ippAddResolutions(to, group_tag, attr->name,
660                                    attr->num_values, IPP_RES_PER_INCH,
661                                    NULL, NULL);
662
663         for (i = 0; i < attr->num_values; i ++)
664         {
665           toattr->values[i].resolution.xres  = attr->values[i].resolution.xres;
666           toattr->values[i].resolution.yres  = attr->values[i].resolution.yres;
667           toattr->values[i].resolution.units = attr->values[i].resolution.units;
668         }
669         break;
670
671     case IPP_TAG_RANGE :
672         toattr = ippAddRanges(to, group_tag, attr->name,
673                               attr->num_values, NULL, NULL);
674
675         for (i = 0; i < attr->num_values; i ++)
676         {
677           toattr->values[i].range.lower = attr->values[i].range.lower;
678           toattr->values[i].range.upper = attr->values[i].range.upper;
679         }
680         break;
681
682     case IPP_TAG_TEXTLANG :
683     case IPP_TAG_NAMELANG :
684         toattr = ippAddStrings(to, group_tag,
685                                (ipp_tag_t)(attr->value_tag | quickcopy),
686                                attr->name, attr->num_values, NULL, NULL);
687
688         if (quickcopy)
689         {
690           for (i = 0; i < attr->num_values; i ++)
691           {
692             toattr->values[i].string.charset = attr->values[i].string.charset;
693             toattr->values[i].string.text    = attr->values[i].string.text;
694           }
695         }
696         else
697         {
698           for (i = 0; i < attr->num_values; i ++)
699           {
700             if (!i)
701               toattr->values[i].string.charset =
702                   _cupsStrAlloc(attr->values[i].string.charset);
703             else
704               toattr->values[i].string.charset =
705                   toattr->values[0].string.charset;
706
707             toattr->values[i].string.text =
708                 _cupsStrAlloc(attr->values[i].string.text);
709           }
710         }
711         break;
712
713     case IPP_TAG_BEGIN_COLLECTION :
714         toattr = ippAddCollections(to, group_tag, attr->name,
715                                    attr->num_values, NULL);
716
717         for (i = 0; i < attr->num_values; i ++)
718         {
719           toattr->values[i].collection = attr->values[i].collection;
720           attr->values[i].collection->use ++;
721         }
722         break;
723
724     case IPP_TAG_STRING :
725         if (quickcopy)
726         {
727           toattr = ippAddOctetString(to, group_tag, attr->name, NULL, 0);
728           toattr->value_tag |= quickcopy;
729           toattr->values[0].unknown.data   = attr->values[0].unknown.data;
730           toattr->values[0].unknown.length = attr->values[0].unknown.length;
731         }
732         else
733           toattr = ippAddOctetString(to, attr->group_tag, attr->name,
734                                      attr->values[0].unknown.data,
735                                      attr->values[0].unknown.length);
736         break;
737
738     default :
739         toattr = ippAddIntegers(to, group_tag, attr->value_tag,
740                                 attr->name, attr->num_values, NULL);
741
742         for (i = 0; i < attr->num_values; i ++)
743         {
744           toattr->values[i].unknown.length = attr->values[i].unknown.length;
745
746           if (toattr->values[i].unknown.length > 0)
747           {
748             if ((toattr->values[i].unknown.data =
749                      malloc(toattr->values[i].unknown.length)) == NULL)
750               toattr->values[i].unknown.length = 0;
751             else
752               memcpy(toattr->values[i].unknown.data,
753                      attr->values[i].unknown.data,
754                      toattr->values[i].unknown.length);
755           }
756         }
757         break; /* anti-compiler-warning-code */
758   }
759
760   return (toattr);
761 }
762
763
764 /*
765  * 'copy_attributes()' - Copy attributes from one request to another.
766  */
767
768 static void
769 copy_attributes(ipp_t        *to,       /* I - Destination request */
770                 ipp_t        *from,     /* I - Source request */
771                 cups_array_t *ra,       /* I - Requested attributes */
772                 ipp_tag_t    group_tag, /* I - Group to copy */
773                 int          quickcopy) /* I - Do a quick copy? */
774 {
775   ipp_attribute_t       *fromattr;      /* Source attribute */
776
777
778   if (!to || !from)
779     return;
780
781   for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
782   {
783    /*
784     * Filter attributes as needed...
785     */
786
787     if ((group_tag != IPP_TAG_ZERO && fromattr->group_tag != group_tag &&
788          fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
789       continue;
790
791     if (!ra || cupsArrayFind(ra, fromattr->name))
792       copy_attribute(to, fromattr, fromattr->group_tag, quickcopy);
793   }
794 }
795
796
797 /*
798  * 'copy_job_attrs()' - Copy job attributes to the response.
799  */
800
801 static void
802 copy_job_attributes(
803     _ipp_client_t *client,              /* I - Client */
804     _ipp_job_t    *job,                 /* I - Job */
805     cups_array_t  *ra)                  /* I - requested-attributes */
806 {
807   copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
808
809   if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
810     ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
811                   "job-printer-up-time", (int)time(NULL));
812
813   if (!ra || cupsArrayFind(ra, "job-state"))
814     ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM,
815                   "job-state", job->state);
816
817   if (!ra || cupsArrayFind(ra, "job-state-reasons"))
818   {
819     switch (job->state)
820     {
821       case IPP_JOB_PENDING :
822           ippAddString(client->response, IPP_TAG_JOB,
823                        IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
824                        NULL, "none");
825           break;
826
827       case IPP_JOB_HELD :
828           if (job->fd >= 0)
829             ippAddString(client->response, IPP_TAG_JOB,
830                          IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
831                          NULL, "job-incoming");
832           else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
833             ippAddString(client->response, IPP_TAG_JOB,
834                          IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
835                          NULL, "job-hold-until-specified");
836           else
837             ippAddString(client->response, IPP_TAG_JOB,
838                          IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
839                          NULL, "job-data-insufficient");
840           break;
841
842       case IPP_JOB_PROCESSING :
843           if (job->cancel)
844             ippAddString(client->response, IPP_TAG_JOB,
845                          IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
846                          NULL, "processing-to-stop-point");
847           else
848             ippAddString(client->response, IPP_TAG_JOB,
849                          IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
850                          NULL, "job-printing");
851           break;
852
853       case IPP_JOB_STOPPED :
854           ippAddString(client->response, IPP_TAG_JOB,
855                        IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
856                        NULL, "job-stopped");
857           break;
858
859       case IPP_JOB_CANCELED :
860           ippAddString(client->response, IPP_TAG_JOB,
861                        IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
862                        NULL, "job-canceled-by-user");
863           break;
864
865       case IPP_JOB_ABORTED :
866           ippAddString(client->response, IPP_TAG_JOB,
867                        IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
868                        NULL, "aborted-by-system");
869           break;
870
871       case IPP_JOB_COMPLETED :
872           ippAddString(client->response, IPP_TAG_JOB,
873                        IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
874                        NULL, "job-completed-successfully");
875           break;
876     }
877   }
878
879   if (!ra || cupsArrayFind(ra, "time-at-completed"))
880     ippAddInteger(client->response, IPP_TAG_JOB,
881                   job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
882                   "time-at-completed", job->completed);
883
884   if (!ra || cupsArrayFind(ra, "time-at-processing"))
885     ippAddInteger(client->response, IPP_TAG_JOB,
886                   job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
887                   "time-at-processing", job->processing);
888 }
889
890
891 /*
892  * 'create_client()' - Accept a new network connection and create a client
893  *                     object.
894  */
895
896 static _ipp_client_t *                  /* O - Client */
897 create_client(_ipp_printer_t *printer,  /* I - Printer */
898               int            sock)      /* I - Listen socket */
899 {
900   _ipp_client_t *client;                /* Client */
901   int           val;                    /* Parameter value */
902   socklen_t     addrlen;                /* Length of address */
903
904
905   if ((client = calloc(1, sizeof(_ipp_client_t))) == NULL)
906   {
907     perror("Unable to allocate memory for client");
908     return (NULL);
909   }
910
911   client->printer         = printer;
912   client->http.activity   = time(NULL);
913   client->http.hostaddr   = &(client->addr);
914   client->http.blocking   = 1;
915   client->http.wait_value = 60000;
916
917  /*
918   * Accept the client and get the remote address...
919   */
920
921   addrlen = sizeof(http_addr_t);
922
923   if ((client->http.fd = accept(sock, (struct sockaddr *)&(client->addr),
924                                 &addrlen)) < 0)
925   {
926     perror("Unable to accept client connection");
927
928     free(client);
929
930     return (NULL);
931   }
932
933   httpAddrString(&(client->addr), client->http.hostname,
934                  sizeof(client->http.hostname));
935
936   if (Verbosity)
937     fprintf(stderr, "Accepted connection from %s (%s)\n", client->http.hostname,
938             client->http.hostaddr->addr.sa_family == AF_INET ? "IPv4" : "IPv6");
939
940  /*
941   * Using TCP_NODELAY improves responsiveness, especially on systems
942   * with a slow loopback interface.  Since we write large buffers
943   * when sending print files and requests, there shouldn't be any
944   * performance penalty for this...
945   */
946
947   val = 1;
948   setsockopt(client->http.fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val,
949              sizeof(val));
950
951   return (client);
952 }
953
954
955 /*
956  * 'create_job()' - Create a new job object from a Print-Job or Create-Job
957  *                  request.
958  */
959
960 static _ipp_job_t *                     /* O - Job */
961 create_job(_ipp_client_t *client)       /* I - Client */
962 {
963   _ipp_job_t            *job;           /* Job */
964   ipp_attribute_t       *attr;          /* Job attribute */
965   char                  uri[1024];      /* job-uri value */
966
967
968   _cupsRWLockWrite(&(client->printer->rwlock));
969   if (client->printer->active_job &&
970       client->printer->active_job->state < IPP_JOB_CANCELED)
971   {
972    /*
973     * Only accept a single job at a time...
974     */
975
976     _cupsRWLockWrite(&(client->printer->rwlock));
977     return (NULL);
978   }
979
980  /*
981   * Allocate and initialize the job object...
982   */
983
984   if ((job = calloc(1, sizeof(_ipp_job_t))) == NULL)
985   {
986     perror("Unable to allocate memory for job");
987     return (NULL);
988   }
989
990   job->printer    = client->printer;
991   job->attrs      = client->request;
992   job->state      = IPP_JOB_HELD;
993   job->fd         = -1;
994   client->request = NULL;
995
996  /*
997   * Set all but the first two attributes to the job attributes group...
998   */
999
1000   for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
1001     attr->group_tag = IPP_TAG_JOB;
1002
1003  /*
1004   * Get the requesting-user-name, document format, and priority...
1005   */
1006
1007   if ((attr = ippFindAttribute(job->attrs, "requesting-user-name",
1008                                IPP_TAG_NAME)) != NULL)
1009   {
1010     _cupsStrFree(attr->name);
1011     attr->name = _cupsStrAlloc("job-originating-user-name");
1012   }
1013   else
1014     attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME | IPP_TAG_COPY,
1015                         "job-originating-user-name", NULL, "anonymous");
1016
1017   if (attr)
1018     job->username = attr->values[0].string.text;
1019   else
1020     job->username = "anonymous";
1021
1022   if ((attr = ippFindAttribute(job->attrs, "document-format",
1023                                IPP_TAG_MIMETYPE)) != NULL)
1024     job->format = attr->values[0].string.text;
1025   else
1026     job->format = "application/octet-stream";
1027
1028  /*
1029   * Add job description attributes and add to the jobs array...
1030   */
1031
1032   job->id = client->printer->next_job_id ++;
1033
1034   snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id);
1035
1036   ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1037   ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
1038   ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
1039                client->printer->uri);
1040   ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
1041                 (int)time(NULL));
1042
1043   cupsArrayAdd(client->printer->jobs, job);
1044   client->printer->active_job = job;
1045
1046   _cupsRWUnlock(&(client->printer->rwlock));
1047
1048   return (job);
1049 }
1050
1051
1052 /*
1053  * 'create_listener()' - Create a listener socket.
1054  */
1055
1056 static int                              /* O  - Listener socket or -1 on error */
1057 create_listener(int family,             /* I  - Address family */
1058                 int *port)              /* IO - Port number */
1059 {
1060   int           sock,                   /* Listener socket */
1061                 val;                    /* Socket value */
1062   http_addr_t   address;                /* Listen address */
1063   socklen_t     addrlen;                /* Length of listen address */
1064
1065
1066   if ((sock = socket(family, SOCK_STREAM, 0)) < 0)
1067     return (-1);
1068
1069   val = 1;
1070   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
1071
1072 #ifdef IPV6_V6ONLY
1073   if (family == AF_INET6)
1074     setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
1075 #endif /* IPV6_V6ONLY */
1076
1077   if (!*port)
1078   {
1079    /*
1080     * Get the auto-assigned port number for the IPv4 socket...
1081     */
1082
1083     /* TODO: This code does not appear to work - port is always 0... */
1084     addrlen = sizeof(address);
1085     if (getsockname(sock, (struct sockaddr *)&address, &addrlen))
1086     {
1087       perror("getsockname() failed");
1088       *port = 8631;
1089     }
1090     else
1091       *port = _httpAddrPort(&address);
1092
1093     fprintf(stderr, "Listening on port %d.\n", *port);
1094   }
1095
1096   memset(&address, 0, sizeof(address));
1097   address.addr.sa_family = family;
1098   _httpAddrSetPort(&address, *port);
1099
1100   if (bind(sock, (struct sockaddr *)&address, httpAddrLength(&address)))
1101   {
1102     close(sock);
1103     return (-1);
1104   }
1105
1106   if (listen(sock, 5))
1107   {
1108     close(sock);
1109     return (-1);
1110   }
1111
1112   return (sock);
1113 }
1114
1115
1116 /*
1117  * 'create_media_col()' - Create a media-col value.
1118  */
1119
1120 static ipp_t *                          /* O - media-col collection */
1121 create_media_col(const char *media,     /* I - Media name */
1122                  const char *type,      /* I - Nedua type */
1123                  int        width,      /* I - x-dimension in 2540ths */
1124                  int        length,     /* I - y-dimension in 2540ths */
1125                  int        margins)    /* I - Value for margins */
1126 {
1127   ipp_t *media_col = ippNew(),          /* media-col value */
1128         *media_size = create_media_size(width, length);
1129                                         /* media-size value */
1130   char  media_key[256];                 /* media-key value */
1131
1132
1133   snprintf(media_key, sizeof(media_key), "%s_%s%s", media, type,
1134            margins == 0 ? "_borderless" : "");
1135
1136   ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL,
1137                media_key);
1138   ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
1139   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1140                 "media-bottom-margin", margins);
1141   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1142                 "media-left-margin", margins);
1143   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1144                 "media-right-margin", margins);
1145   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1146                 "media-top-margin", margins);
1147   ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type",
1148                NULL, type);
1149
1150   ippDelete(media_size);
1151
1152   return (media_col);
1153 }
1154
1155
1156 /*
1157  * 'create_media_size()' - Create a media-size value.
1158  */
1159
1160 static ipp_t *                          /* O - media-col collection */
1161 create_media_size(int width,            /* I - x-dimension in 2540ths */
1162                   int length)           /* I - y-dimension in 2540ths */
1163 {
1164   ipp_t *media_size = ippNew();         /* media-size value */
1165
1166
1167   ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension",
1168                 width);
1169   ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension",
1170                 length);
1171
1172   return (media_size);
1173 }
1174
1175
1176 /*
1177  * 'create_printer()' - Create, register, and listen for connections to a
1178  *                      printer object.
1179  */
1180
1181 static _ipp_printer_t *                 /* O - Printer */
1182 create_printer(const char *servername,  /* I - Server hostname (NULL for default) */
1183                const char *name,        /* I - printer-name */
1184                const char *location,    /* I - printer-location */
1185                const char *make,        /* I - printer-make-and-model */
1186                const char *model,       /* I - printer-make-and-model */
1187                const char *icon,        /* I - printer-icons */
1188                const char *docformats,  /* I - document-format-supported */
1189                int        ppm,          /* I - Pages per minute in grayscale */
1190                int        ppm_color,    /* I - Pages per minute in color (0 for gray) */
1191                int        duplex,       /* I - 1 = duplex, 0 = simplex */
1192                int        port,         /* I - Port for listeners or 0 for auto */
1193 #ifdef HAVE_DNSSD
1194                const char *regtype,     /* I - Bonjour service type */
1195 #endif /* HAVE_DNSSD */
1196                const char *directory)   /* I - Spool directory */
1197 {
1198   int                   i, j;           /* Looping vars */
1199   _ipp_printer_t        *printer;       /* Printer */
1200   char                  hostname[256],  /* Hostname */
1201                         uri[1024],      /* Printer URI */
1202                         icons[1024],    /* printer-icons URI */
1203                         adminurl[1024], /* printer-more-info URI */
1204                         device_id[1024],/* printer-device-id */
1205                         make_model[128];/* printer-make-and-model */
1206   int                   num_formats;    /* Number of document-format-supported values */
1207   char                  *defformat,     /* document-format-default value */
1208                         *formats[100],  /* document-format-supported values */
1209                         *ptr;           /* Pointer into string */
1210   const char            *prefix;        /* Prefix string */
1211   int                   num_database;   /* Number of database values */
1212   ipp_attribute_t       *media_col_database,
1213                                         /* media-col-database value */
1214                         *media_size_supported;
1215                                         /* media-size-supported value */
1216   ipp_t                 *media_col_default;
1217                                         /* media-col-default value */
1218   ipp_value_t           *media_col_value;
1219                                         /* Current media-col-database value */
1220   int                   k_supported;    /* Maximum file size supported */
1221 #ifdef HAVE_STATVFS
1222   struct statvfs        spoolinfo;      /* FS info for spool directory */
1223   double                spoolsize;      /* FS size */
1224 #elif defined(HAVE_STATFS)
1225   struct statfs         spoolinfo;      /* FS info for spool directory */
1226   double                spoolsize;      /* FS size */
1227 #endif /* HAVE_STATVFS */
1228   static const int      orients[4] =    /* orientation-requested-supported values */
1229   {
1230     IPP_PORTRAIT,
1231     IPP_LANDSCAPE,
1232     IPP_REVERSE_LANDSCAPE,
1233     IPP_REVERSE_PORTRAIT
1234   };
1235   static const char * const versions[] =/* ipp-versions-supported values */
1236   {
1237     "1.0",
1238     "1.1",
1239     "2.0"
1240   };
1241   static const int      ops[] =         /* operations-supported values */
1242   {
1243     IPP_PRINT_JOB,
1244     IPP_PRINT_URI,
1245     IPP_VALIDATE_JOB,
1246     IPP_CREATE_JOB,
1247     IPP_SEND_DOCUMENT,
1248     IPP_SEND_URI,
1249     IPP_CANCEL_JOB,
1250     IPP_GET_JOB_ATTRIBUTES,
1251     IPP_GET_JOBS,
1252     IPP_GET_PRINTER_ATTRIBUTES
1253   };
1254   static const char * const charsets[] =/* charset-supported values */
1255   {
1256     "us-ascii",
1257     "utf-8"
1258   };
1259   static const char * const job_creation[] =
1260   {                                     /* job-creation-attributes-supported values */
1261     "copies",
1262     "ipp-attribute-fidelity",
1263     "job-name",
1264     "job-priority",
1265     "media",
1266     "media-col",
1267     "multiple-document-handling",
1268     "orientation-requested",
1269     "print-quality",
1270     "sides"
1271   };
1272   static const char * const media_col_supported[] =
1273   {                                     /* media-col-supported values */
1274     "media-bottom-margin",
1275     "media-left-margin",
1276     "media-right-margin",
1277     "media-size",
1278     "media-top-margin",
1279     "media-type"
1280   };
1281   static const int      media_xxx_margin_supported[] =
1282   {                                     /* media-xxx-margin-supported values */
1283     0,
1284     635
1285   };
1286   static const char * const multiple_document_handling[] =
1287   {                                     /* multiple-document-handling-supported values */
1288     "separate-documents-uncollated-copies",
1289     "separate-documents-collated-copies"
1290   };
1291   static const int      print_quality_supported[] =
1292   {                                     /* print-quality-supported values */
1293     IPP_QUALITY_DRAFT,
1294     IPP_QUALITY_NORMAL,
1295     IPP_QUALITY_HIGH
1296   };
1297   static const char * const reference_uri_schemes_supported[] =
1298   {                                     /* reference-uri-schemes-supported */
1299     "file",
1300     "ftp",
1301     "http"
1302 #ifdef HAVE_SSL
1303     , "https"
1304 #endif /* HAVE_SSL */
1305   };
1306   static const char * const sides_supported[] =
1307   {                                     /* sides-supported values */
1308     "one-sided",
1309     "two-sided-long-edge",
1310     "two-sided-short-edge"
1311   };
1312   static const char * const which_jobs[] =
1313   {                                     /* which-jobs-supported values */
1314     "completed",
1315     "not-completed",
1316     "aborted",
1317     "all",
1318     "canceled",
1319     "pending",
1320     "pending-held",
1321     "processing",
1322     "processing-stopped"
1323   };
1324
1325
1326  /*
1327   * Allocate memory for the printer...
1328   */
1329
1330   if ((printer = calloc(1, sizeof(_ipp_printer_t))) == NULL)
1331   {
1332     perror("Unable to allocate memory for printer");
1333     return (NULL);
1334   }
1335
1336   printer->ipv4          = -1;
1337   printer->ipv6          = -1;
1338   printer->name          = _cupsStrAlloc(name);
1339 #ifdef HAVE_DNSSD
1340   printer->dnssd_name    = _cupsStrRetain(printer->name);
1341 #endif /* HAVE_DNSSD */
1342   printer->directory     = _cupsStrAlloc(directory);
1343   printer->hostname      = _cupsStrAlloc(servername ? servername :
1344                                              httpGetHostname(NULL, hostname,
1345                                                              sizeof(hostname)));
1346   printer->port          = port;
1347   printer->state         = IPP_PRINTER_IDLE;
1348   printer->state_reasons = _IPP_PRINTER_NONE;
1349   printer->jobs          = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
1350   printer->next_job_id   = 1;
1351
1352   httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1353                   printer->hostname, printer->port, "/ipp");
1354   printer->uri    = _cupsStrAlloc(uri);
1355   printer->urilen = strlen(uri);
1356
1357   _cupsRWInit(&(printer->rwlock));
1358
1359  /*
1360   * Create the listener sockets...
1361   */
1362
1363   if ((printer->ipv4 = create_listener(AF_INET, &(printer->port))) < 0)
1364   {
1365     perror("Unable to create IPv4 listener");
1366     goto bad_printer;
1367   }
1368
1369   if ((printer->ipv6 = create_listener(AF_INET6, &(printer->port))) < 0)
1370   {
1371     perror("Unable to create IPv6 listener");
1372     goto bad_printer;
1373   }
1374
1375  /*
1376   * Prepare values for the printer attributes...
1377   */
1378
1379   httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), "http", NULL,
1380                   printer->hostname, printer->port, "/icon.png");
1381   httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), "http", NULL,
1382                   printer->hostname, printer->port, "/");
1383
1384   if (Verbosity)
1385   {
1386     fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
1387     fprintf(stderr, "printer-uri=\"%s\"\n", uri);
1388   }
1389
1390   snprintf(make_model, sizeof(make_model), "%s %s", make, model);
1391
1392   num_formats = 1;
1393   formats[0]  = strdup(docformats);
1394   defformat   = formats[0];
1395   for (ptr = strchr(formats[0], ','); ptr; ptr = strchr(ptr, ','))
1396   {
1397     *ptr++ = '\0';
1398     formats[num_formats++] = ptr;
1399
1400     if (!_cups_strcasecmp(ptr, "application/octet-stream"))
1401       defformat = ptr;
1402   }
1403
1404   snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
1405   ptr    = device_id + strlen(device_id);
1406   prefix = "CMD:";
1407   for (i = 0; i < num_formats; i ++)
1408   {
1409     if (!_cups_strcasecmp(formats[i], "application/pdf"))
1410       snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPDF", prefix);
1411     else if (!_cups_strcasecmp(formats[i], "application/postscript"))
1412       snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPS", prefix);
1413     else if (!_cups_strcasecmp(formats[i], "application/vnd.hp-PCL"))
1414       snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPCL", prefix);
1415     else if (!_cups_strcasecmp(formats[i], "image/jpeg"))
1416       snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sJPEG", prefix);
1417     else if (!_cups_strcasecmp(formats[i], "image/png"))
1418       snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPNG", prefix);
1419     else if (_cups_strcasecmp(formats[i], "application/octet-stream"))
1420       snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%s%s", prefix,
1421                formats[i]);
1422
1423     ptr += strlen(ptr);
1424     prefix = ",";
1425   }
1426   strlcat(device_id, ";", sizeof(device_id));
1427
1428  /*
1429   * Get the maximum spool size based on the size of the filesystem used for
1430   * the spool directory.  If the host OS doesn't support the statfs call
1431   * or the filesystem is larger than 2TiB, always report INT_MAX.
1432   */
1433
1434 #ifdef HAVE_STATVFS
1435   if (statvfs(printer->directory, &spoolinfo))
1436     k_supported = INT_MAX;
1437   else if ((spoolsize = (double)spoolinfo.f_frsize *
1438                         spoolinfo.f_blocks / 1024) > INT_MAX)
1439     k_supported = INT_MAX;
1440   else
1441     k_supported = (int)spoolsize;
1442
1443 #elif defined(HAVE_STATFS)
1444   if (statfs(printer->directory, &spoolinfo))
1445     k_supported = INT_MAX;
1446   else if ((spoolsize = (double)spoolinfo.f_bsize *
1447                         spoolinfo.f_blocks / 1024) > INT_MAX)
1448     k_supported = INT_MAX;
1449   else
1450     k_supported = (int)spoolsize;
1451
1452 #else
1453   k_supported = INT_MAX;
1454 #endif /* HAVE_STATVFS */
1455
1456  /*
1457   * Create the printer attributes.  This list of attributes is sorted to improve
1458   * performance when the client provides a requested-attributes attribute...
1459   */
1460
1461   printer->attrs = ippNew();
1462
1463   /* charset-configured */
1464   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
1465                "charset-configured", NULL, "utf-8");
1466
1467   /* charset-supported */
1468   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
1469                 "charset-supported", sizeof(charsets) / sizeof(charsets[0]),
1470                 NULL, charsets);
1471
1472   /* color-supported */
1473   ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "color-supported",
1474                 ppm_color > 0);
1475
1476   /* compression-supported */
1477   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1478                "compression-supported", NULL, "none");
1479
1480   /* copies-default */
1481   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1482                 "copies-default", 1);
1483
1484   /* copies-supported */
1485   ippAddRange(printer->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
1486
1487   /* document-format-default */
1488   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
1489                "document-format-default", NULL, defformat);
1490
1491   /* document-format-supported */
1492   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
1493                 "document-format-supported", num_formats, NULL,
1494                 (const char * const *)formats);
1495
1496   /* finishings-default */
1497   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1498                 "finishings-default", IPP_FINISHINGS_NONE);
1499
1500   /* finishings-supported */
1501   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1502                 "finishings-supported", IPP_FINISHINGS_NONE);
1503
1504   /* generated-natural-language-supported */
1505   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE | IPP_TAG_COPY,
1506                "generated-natural-language-supported", NULL, "en");
1507
1508   /* ipp-versions-supported */
1509   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1510                 "ipp-versions-supported",
1511                 sizeof(versions) / sizeof(versions[0]), NULL, versions);
1512
1513   /* job-creation-attributes-supported */
1514   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1515                 "job-creation-attributes-supported",
1516                 sizeof(job_creation) / sizeof(job_creation[0]),
1517                 NULL, job_creation);
1518
1519   /* job-k-octets-supported */
1520   ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0,
1521               k_supported);
1522
1523   /* job-priority-default */
1524   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1525                 "job-priority-default", 50);
1526
1527   /* job-priority-supported */
1528   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1529                 "job-priority-supported", 100);
1530
1531   /* job-sheets-default */
1532   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
1533                "job-sheets-default", NULL, "none");
1534
1535   /* job-sheets-supported */
1536   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
1537                "job-sheets-supported", NULL, "none");
1538
1539   /* media-bottom-margin-supported */
1540   ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1541                  "media-bottom-margin-supported",
1542                  (int)(sizeof(media_xxx_margin_supported) /
1543                        sizeof(media_xxx_margin_supported[0])),
1544                  media_xxx_margin_supported);
1545
1546   /* media-col-database */
1547   for (num_database = 0, i = 0;
1548        i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1549        i ++)
1550   {
1551     if (media_col_sizes[i][2] == _IPP_ENV_ONLY)
1552       num_database += 2;                /* auto + envelope */
1553     else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY)
1554       num_database += 12;               /* auto + photographic-* + borderless */
1555     else
1556       num_database += (int)(sizeof(media_type_supported) /
1557                             sizeof(media_type_supported[0])) + 6;
1558                                         /* All types + borderless */
1559   }
1560
1561   media_col_database = ippAddCollections(printer->attrs, IPP_TAG_PRINTER,
1562                                          "media-col-database", num_database,
1563                                          NULL);
1564   for (media_col_value = media_col_database->values, i = 0;
1565        i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1566        i ++)
1567   {
1568     for (j = 0;
1569          j < (int)(sizeof(media_type_supported) /
1570                    sizeof(media_type_supported[0]));
1571          j ++)
1572     {
1573       if (media_col_sizes[i][2] == _IPP_ENV_ONLY &&
1574           strcmp(media_type_supported[j], "auto") &&
1575           strcmp(media_type_supported[j], "envelope"))
1576         continue;
1577       else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY &&
1578                strcmp(media_type_supported[j], "auto") &&
1579                strncmp(media_type_supported[j], "photographic-", 13))
1580         continue;
1581
1582       media_col_value->collection =
1583           create_media_col(media_supported[i], media_type_supported[j],
1584                            media_col_sizes[i][0], media_col_sizes[i][1],
1585                            media_xxx_margin_supported[1]);
1586       media_col_value ++;
1587
1588       if (media_col_sizes[i][2] != _IPP_ENV_ONLY &&
1589           (!strcmp(media_type_supported[j], "auto") ||
1590            !strncmp(media_type_supported[j], "photographic-", 13)))
1591       {
1592        /*
1593         * Add borderless version for this combination...
1594         */
1595
1596         media_col_value->collection =
1597             create_media_col(media_supported[i], media_type_supported[j],
1598                              media_col_sizes[i][0], media_col_sizes[i][1],
1599                              media_xxx_margin_supported[0]);
1600         media_col_value ++;
1601       }
1602     }
1603   }
1604
1605   /* media-col-default */
1606   media_col_default = create_media_col(media_supported[0],
1607                                        media_type_supported[0],
1608                                        media_col_sizes[0][0],
1609                                        media_col_sizes[0][1],
1610                                        media_xxx_margin_supported[1]);
1611
1612   ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-default",
1613                    media_col_default);
1614   ippDelete(media_col_default);
1615
1616   /* media-col-supported */
1617   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1618                 "media-col-supported",
1619                 (int)(sizeof(media_col_supported) /
1620                       sizeof(media_col_supported[0])), NULL,
1621                 media_col_supported);
1622
1623   /* media-default */
1624   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1625                "media-default", NULL, media_supported[0]);
1626
1627   /* media-left-margin-supported */
1628   ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1629                  "media-left-margin-supported",
1630                  (int)(sizeof(media_xxx_margin_supported) /
1631                        sizeof(media_xxx_margin_supported[0])),
1632                  media_xxx_margin_supported);
1633
1634   /* media-right-margin-supported */
1635   ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1636                  "media-right-margin-supported",
1637                  (int)(sizeof(media_xxx_margin_supported) /
1638                        sizeof(media_xxx_margin_supported[0])),
1639                  media_xxx_margin_supported);
1640
1641   /* media-supported */
1642   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1643                 "media-supported",
1644                 (int)(sizeof(media_supported) / sizeof(media_supported[0])),
1645                 NULL, media_supported);
1646
1647   /* media-size-supported */
1648   media_size_supported = ippAddCollections(printer->attrs, IPP_TAG_PRINTER,
1649                                            "media-size-supported",
1650                                            (int)(sizeof(media_col_sizes) /
1651                                                  sizeof(media_col_sizes[0])),
1652                                            NULL);
1653   for (i = 0;
1654        i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1655        i ++)
1656     media_size_supported->values[i].collection =
1657         create_media_size(media_col_sizes[i][0], media_col_sizes[i][1]);
1658
1659   /* media-top-margin-supported */
1660   ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1661                  "media-top-margin-supported",
1662                  (int)(sizeof(media_xxx_margin_supported) /
1663                        sizeof(media_xxx_margin_supported[0])),
1664                  media_xxx_margin_supported);
1665
1666   /* media-type-supported */
1667   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1668                 "media-type-supported",
1669                 (int)(sizeof(media_type_supported) /
1670                       sizeof(media_type_supported[0])),
1671                 NULL, media_type_supported);
1672
1673   /* multiple-document-handling-supported */
1674   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1675                 "multiple-document-handling-supported",
1676                 sizeof(multiple_document_handling) /
1677                     sizeof(multiple_document_handling[0]), NULL,
1678                 multiple_document_handling);
1679
1680   /* multiple-document-jobs-supported */
1681   ippAddBoolean(printer->attrs, IPP_TAG_PRINTER,
1682                 "multiple-document-jobs-supported", 0);
1683
1684   /* natural-language-configured */
1685   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE | IPP_TAG_COPY,
1686                "natural-language-configured", NULL, "en");
1687
1688   /* number-up-default */
1689   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1690                 "number-up-default", 1);
1691
1692   /* number-up-supported */
1693   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1694                 "number-up-supported", 1);
1695
1696   /* operations-supported */
1697   ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1698                  "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1699
1700   /* orientation-requested-default */
1701   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
1702                 "orientation-requested-default", 0);
1703
1704   /* orientation-requested-supported */
1705   ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1706                  "orientation-requested-supported", 4, orients);
1707
1708   /* output-bin-default */
1709   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1710                "output-bin-default", NULL, "face-down");
1711
1712   /* output-bin-supported */
1713   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1714                "output-bin-supported", NULL, "face-down");
1715
1716   /* pages-per-minute */
1717   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1718                 "pages-per-minute", ppm);
1719
1720   /* pages-per-minute-color */
1721   if (ppm_color > 0)
1722     ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1723                   "pages-per-minute-color", ppm_color);
1724
1725   /* pdl-override-supported */
1726   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1727                "pdl-override-supported", NULL, "attempted");
1728
1729   /* print-quality-default */
1730   ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1731                 "print-quality-default", IPP_QUALITY_NORMAL);
1732
1733   /* print-quality-supported */
1734   ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1735                  "print-quality-supported",
1736                  (int)(sizeof(print_quality_supported) /
1737                        sizeof(print_quality_supported[0])),
1738                  print_quality_supported);
1739
1740   /* printer-device-id */
1741   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1742                "printer-device-id", NULL, device_id);
1743
1744   /* printer-icons */
1745   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1746                "printer-icons", NULL, icons);
1747
1748   /* printer-is-accepting-jobs */
1749   ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
1750                 1);
1751
1752   /* printer-info */
1753   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
1754                NULL, name);
1755
1756   /* printer-location */
1757   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1758                "printer-location", NULL, location);
1759
1760   /* printer-make-and-model */
1761   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1762                "printer-make-and-model", NULL, make_model);
1763
1764   /* printer-more-info */
1765   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1766                "printer-more-info", NULL, adminurl);
1767
1768   /* printer-name */
1769   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name",
1770                NULL, name);
1771
1772   /* printer-resolution-default */
1773   ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
1774                    "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
1775
1776   /* printer-resolution-supported */
1777   ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
1778                    "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
1779
1780   /* printer-uri-supported */
1781   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1782                "printer-uri-supported", NULL, uri);
1783
1784   /* reference-uri-scheme-supported */
1785   ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1786                 IPP_TAG_URISCHEME | IPP_TAG_COPY,
1787                 "reference-uri-schemes-supported",
1788                 (int)(sizeof(reference_uri_schemes_supported) /
1789                       sizeof(reference_uri_schemes_supported[0])),
1790                 NULL, reference_uri_schemes_supported);
1791
1792   /* sides-default */
1793   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1794                "sides-default", NULL, "one-sided");
1795
1796   /* sides-supported */
1797   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1798                 "sides-supported", duplex ? 3 : 1, NULL, sides_supported);
1799
1800   /* uri-authentication-supported */
1801   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1802                "uri-authentication-supported", NULL, "none");
1803
1804   /* uri-security-supported */
1805   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1806                "uri-security-supported", NULL, "none");
1807
1808   /* which-jobs-supported */
1809   ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1810                 "which-jobs-supported",
1811                 sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
1812
1813   free(formats[0]);
1814
1815   debug_attributes("Printer", printer->attrs, 0);
1816
1817 #ifdef HAVE_DNSSD
1818  /*
1819   * Register the printer with Bonjour...
1820   */
1821
1822   if (!register_printer(printer, location, make, model, docformats, adminurl,
1823                         ppm_color > 0, duplex, regtype))
1824     goto bad_printer;
1825 #endif /* HAVE_DNSSD */
1826
1827  /*
1828   * Return it!
1829   */
1830
1831   return (printer);
1832
1833
1834  /*
1835   * If we get here we were unable to create the printer...
1836   */
1837
1838   bad_printer:
1839
1840   delete_printer(printer);
1841   return (NULL);
1842 }
1843
1844
1845 /*
1846  * 'create_requested_array()' - Create an array for requested-attributes.
1847  */
1848
1849 static cups_array_t *                   /* O - requested-attributes array */
1850 create_requested_array(
1851     _ipp_client_t *client)              /* I - Client */
1852 {
1853   int                   i;              /* Looping var */
1854   ipp_attribute_t       *requested;     /* requested-attributes attribute */
1855   cups_array_t          *ra;            /* Requested attributes array */
1856   char                  *value;         /* Current value */
1857
1858
1859  /*
1860   * Get the requested-attributes attribute, and return NULL if we don't
1861   * have one...
1862   */
1863
1864   if ((requested = ippFindAttribute(client->request, "requested-attributes",
1865                                     IPP_TAG_KEYWORD)) == NULL)
1866     return (NULL);
1867
1868  /*
1869   * If the attribute contains a single "all" keyword, return NULL...
1870   */
1871
1872   if (requested->num_values == 1 &&
1873       !strcmp(requested->values[0].string.text, "all"))
1874     return (NULL);
1875
1876  /*
1877   * Create an array using "strcmp" as the comparison function...
1878   */
1879
1880   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
1881
1882   for (i = 0; i < requested->num_values; i ++)
1883   {
1884     value = requested->values[i].string.text;
1885
1886     if (!strcmp(value, "job-template"))
1887     {
1888       cupsArrayAdd(ra, "copies");
1889       cupsArrayAdd(ra, "copies-default");
1890       cupsArrayAdd(ra, "copies-supported");
1891       cupsArrayAdd(ra, "finishings");
1892       cupsArrayAdd(ra, "finishings-default");
1893       cupsArrayAdd(ra, "finishings-supported");
1894       cupsArrayAdd(ra, "job-hold-until");
1895       cupsArrayAdd(ra, "job-hold-until-default");
1896       cupsArrayAdd(ra, "job-hold-until-supported");
1897       cupsArrayAdd(ra, "job-priority");
1898       cupsArrayAdd(ra, "job-priority-default");
1899       cupsArrayAdd(ra, "job-priority-supported");
1900       cupsArrayAdd(ra, "job-sheets");
1901       cupsArrayAdd(ra, "job-sheets-default");
1902       cupsArrayAdd(ra, "job-sheets-supported");
1903       cupsArrayAdd(ra, "media");
1904       cupsArrayAdd(ra, "media-col");
1905       cupsArrayAdd(ra, "media-col-default");
1906       cupsArrayAdd(ra, "media-col-supported");
1907       cupsArrayAdd(ra, "media-default");
1908       cupsArrayAdd(ra, "media-source-supported");
1909       cupsArrayAdd(ra, "media-supported");
1910       cupsArrayAdd(ra, "media-type-supported");
1911       cupsArrayAdd(ra, "multiple-document-handling");
1912       cupsArrayAdd(ra, "multiple-document-handling-default");
1913       cupsArrayAdd(ra, "multiple-document-handling-supported");
1914       cupsArrayAdd(ra, "number-up");
1915       cupsArrayAdd(ra, "number-up-default");
1916       cupsArrayAdd(ra, "number-up-supported");
1917       cupsArrayAdd(ra, "orientation-requested");
1918       cupsArrayAdd(ra, "orientation-requested-default");
1919       cupsArrayAdd(ra, "orientation-requested-supported");
1920       cupsArrayAdd(ra, "page-ranges");
1921       cupsArrayAdd(ra, "page-ranges-supported");
1922       cupsArrayAdd(ra, "printer-resolution");
1923       cupsArrayAdd(ra, "printer-resolution-default");
1924       cupsArrayAdd(ra, "printer-resolution-supported");
1925       cupsArrayAdd(ra, "print-quality");
1926       cupsArrayAdd(ra, "print-quality-default");
1927       cupsArrayAdd(ra, "print-quality-supported");
1928       cupsArrayAdd(ra, "sides");
1929       cupsArrayAdd(ra, "sides-default");
1930       cupsArrayAdd(ra, "sides-supported");
1931     }
1932     else if (!strcmp(value, "job-description"))
1933     {
1934       cupsArrayAdd(ra, "date-time-at-completed");
1935       cupsArrayAdd(ra, "date-time-at-creation");
1936       cupsArrayAdd(ra, "date-time-at-processing");
1937       cupsArrayAdd(ra, "job-detailed-status-message");
1938       cupsArrayAdd(ra, "job-document-access-errors");
1939       cupsArrayAdd(ra, "job-id");
1940       cupsArrayAdd(ra, "job-impressions");
1941       cupsArrayAdd(ra, "job-impressions-completed");
1942       cupsArrayAdd(ra, "job-k-octets");
1943       cupsArrayAdd(ra, "job-k-octets-processed");
1944       cupsArrayAdd(ra, "job-media-sheets");
1945       cupsArrayAdd(ra, "job-media-sheets-completed");
1946       cupsArrayAdd(ra, "job-message-from-operator");
1947       cupsArrayAdd(ra, "job-more-info");
1948       cupsArrayAdd(ra, "job-name");
1949       cupsArrayAdd(ra, "job-originating-user-name");
1950       cupsArrayAdd(ra, "job-printer-up-time");
1951       cupsArrayAdd(ra, "job-printer-uri");
1952       cupsArrayAdd(ra, "job-state");
1953       cupsArrayAdd(ra, "job-state-message");
1954       cupsArrayAdd(ra, "job-state-reasons");
1955       cupsArrayAdd(ra, "job-uri");
1956       cupsArrayAdd(ra, "number-of-documents");
1957       cupsArrayAdd(ra, "number-of-intervening-jobs");
1958       cupsArrayAdd(ra, "output-device-assigned");
1959       cupsArrayAdd(ra, "time-at-completed");
1960       cupsArrayAdd(ra, "time-at-creation");
1961       cupsArrayAdd(ra, "time-at-processing");
1962     }
1963     else if (!strcmp(value, "printer-description"))
1964     {
1965       cupsArrayAdd(ra, "charset-configured");
1966       cupsArrayAdd(ra, "charset-supported");
1967       cupsArrayAdd(ra, "color-supported");
1968       cupsArrayAdd(ra, "compression-supported");
1969       cupsArrayAdd(ra, "document-format-default");
1970       cupsArrayAdd(ra, "document-format-supported");
1971       cupsArrayAdd(ra, "generated-natural-language-supported");
1972       cupsArrayAdd(ra, "ipp-versions-supported");
1973       cupsArrayAdd(ra, "job-impressions-supported");
1974       cupsArrayAdd(ra, "job-k-octets-supported");
1975       cupsArrayAdd(ra, "job-media-sheets-supported");
1976       cupsArrayAdd(ra, "multiple-document-jobs-supported");
1977       cupsArrayAdd(ra, "multiple-operation-time-out");
1978       cupsArrayAdd(ra, "natural-language-configured");
1979       cupsArrayAdd(ra, "notify-attributes-supported");
1980       cupsArrayAdd(ra, "notify-lease-duration-default");
1981       cupsArrayAdd(ra, "notify-lease-duration-supported");
1982       cupsArrayAdd(ra, "notify-max-events-supported");
1983       cupsArrayAdd(ra, "notify-events-default");
1984       cupsArrayAdd(ra, "notify-events-supported");
1985       cupsArrayAdd(ra, "notify-pull-method-supported");
1986       cupsArrayAdd(ra, "notify-schemes-supported");
1987       cupsArrayAdd(ra, "operations-supported");
1988       cupsArrayAdd(ra, "pages-per-minute");
1989       cupsArrayAdd(ra, "pages-per-minute-color");
1990       cupsArrayAdd(ra, "pdl-override-supported");
1991       cupsArrayAdd(ra, "printer-alert");
1992       cupsArrayAdd(ra, "printer-alert-description");
1993       cupsArrayAdd(ra, "printer-current-time");
1994       cupsArrayAdd(ra, "printer-driver-installer");
1995       cupsArrayAdd(ra, "printer-info");
1996       cupsArrayAdd(ra, "printer-is-accepting-jobs");
1997       cupsArrayAdd(ra, "printer-location");
1998       cupsArrayAdd(ra, "printer-make-and-model");
1999       cupsArrayAdd(ra, "printer-message-from-operator");
2000       cupsArrayAdd(ra, "printer-more-info");
2001       cupsArrayAdd(ra, "printer-more-info-manufacturer");
2002       cupsArrayAdd(ra, "printer-name");
2003       cupsArrayAdd(ra, "printer-state");
2004       cupsArrayAdd(ra, "printer-state-message");
2005       cupsArrayAdd(ra, "printer-state-reasons");
2006       cupsArrayAdd(ra, "printer-up-time");
2007       cupsArrayAdd(ra, "printer-uri-supported");
2008       cupsArrayAdd(ra, "queued-job-count");
2009       cupsArrayAdd(ra, "reference-uri-schemes-supported");
2010       cupsArrayAdd(ra, "uri-authentication-supported");
2011       cupsArrayAdd(ra, "uri-security-supported");
2012     }
2013     else if (!strcmp(value, "printer-defaults"))
2014     {
2015       cupsArrayAdd(ra, "copies-default");
2016       cupsArrayAdd(ra, "document-format-default");
2017       cupsArrayAdd(ra, "finishings-default");
2018       cupsArrayAdd(ra, "job-hold-until-default");
2019       cupsArrayAdd(ra, "job-priority-default");
2020       cupsArrayAdd(ra, "job-sheets-default");
2021       cupsArrayAdd(ra, "media-default");
2022       cupsArrayAdd(ra, "media-col-default");
2023       cupsArrayAdd(ra, "number-up-default");
2024       cupsArrayAdd(ra, "orientation-requested-default");
2025       cupsArrayAdd(ra, "sides-default");
2026     }
2027     else if (!strcmp(value, "subscription-template"))
2028     {
2029       cupsArrayAdd(ra, "notify-attributes");
2030       cupsArrayAdd(ra, "notify-charset");
2031       cupsArrayAdd(ra, "notify-events");
2032       cupsArrayAdd(ra, "notify-lease-duration");
2033       cupsArrayAdd(ra, "notify-natural-language");
2034       cupsArrayAdd(ra, "notify-pull-method");
2035       cupsArrayAdd(ra, "notify-recipient-uri");
2036       cupsArrayAdd(ra, "notify-time-interval");
2037       cupsArrayAdd(ra, "notify-user-data");
2038     }
2039     else
2040       cupsArrayAdd(ra, value);
2041   }
2042
2043   return (ra);
2044 }
2045
2046
2047 /*
2048  * 'debug_attributes()' - Print attributes in a request or response.
2049  */
2050
2051 static void
2052 debug_attributes(const char *title,     /* I - Title */
2053                  ipp_t      *ipp,       /* I - Request/response */
2054                  int        type)       /* I - 0 = object, 1 = request, 2 = response */
2055 {
2056   ipp_tag_t             group_tag;      /* Current group */
2057   ipp_attribute_t       *attr;          /* Current attribute */
2058   char                  buffer[2048];   /* String buffer for value */
2059
2060
2061   if (Verbosity <= 1)
2062     return;
2063
2064   fprintf(stderr, "%s:\n", title);
2065   fprintf(stderr, "  version=%d.%d\n", ipp->request.any.version[0],
2066           ipp->request.any.version[1]);
2067   if (type == 1)
2068     fprintf(stderr, "  operation-id=%s(%04x)\n",
2069             ippOpString(ipp->request.op.operation_id),
2070             ipp->request.op.operation_id);
2071   else if (type == 2)
2072     fprintf(stderr, "  status-code=%s(%04x)\n",
2073             ippErrorString(ipp->request.status.status_code),
2074             ipp->request.status.status_code);
2075   fprintf(stderr, "  request-id=%d\n\n", ipp->request.any.request_id);
2076
2077   for (attr = ipp->attrs, group_tag = IPP_TAG_ZERO; attr; attr = attr->next)
2078   {
2079     if (attr->group_tag != group_tag)
2080     {
2081       group_tag = attr->group_tag;
2082       fprintf(stderr, "  %s\n", ippTagString(group_tag));
2083     }
2084
2085     if (attr->name)
2086     {
2087       _ippAttrString(attr, buffer, sizeof(buffer));
2088       fprintf(stderr, "    %s (%s%s) %s\n", attr->name,
2089               attr->num_values > 1 ? "1setOf " : "",
2090               ippTagString(attr->value_tag), buffer);
2091     }
2092   }
2093 }
2094
2095
2096 /*
2097  * 'delete_client()' - Close the socket and free all memory used by a client
2098  *                     object.
2099  */
2100
2101 static void
2102 delete_client(_ipp_client_t *client)    /* I - Client */
2103 {
2104   if (Verbosity)
2105     fprintf(stderr, "Closing connection from %s (%s)\n", client->http.hostname,
2106             client->http.hostaddr->addr.sa_family == AF_INET ? "IPv4" : "IPv6");
2107
2108  /*
2109   * Flush pending writes before closing...
2110   */
2111
2112   httpFlushWrite(&(client->http));
2113
2114   if (client->http.fd >= 0)
2115     close(client->http.fd);
2116
2117  /*
2118   * Free memory...
2119   */
2120
2121   httpClearCookie(&(client->http));
2122   httpClearFields(&(client->http));
2123
2124   ippDelete(client->request);
2125
2126   ippDelete(client->response);
2127
2128   free(client);
2129 }
2130
2131
2132 /*
2133  * 'delete_job()' - Remove from the printer and free all memory used by a job
2134  *                  object.
2135  */
2136
2137 static void
2138 delete_job(_ipp_job_t *job)             /* I - Job */
2139 {
2140   if (Verbosity)
2141     fprintf(stderr, "Removing job #%d from history.\n", job->id);
2142
2143   ippDelete(job->attrs);
2144
2145   if (job->filename)
2146   {
2147     if (!KeepFiles)
2148       unlink(job->filename);
2149
2150     free(job->filename);
2151   }
2152
2153   free(job);
2154 }
2155
2156
2157 /*
2158  * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2159  *                      used by a printer object.
2160  */
2161
2162 static void
2163 delete_printer(_ipp_printer_t *printer) /* I - Printer */
2164 {
2165   if (printer->ipv4 >= 0)
2166     close(printer->ipv4);
2167
2168   if (printer->ipv6 >= 0)
2169     close(printer->ipv6);
2170
2171 #if HAVE_DNSSD
2172   if (printer->printer_ref)
2173     DNSServiceRefDeallocate(printer->printer_ref);
2174
2175   if (printer->ipp_ref)
2176     DNSServiceRefDeallocate(printer->ipp_ref);
2177
2178   if (printer->http_ref)
2179     DNSServiceRefDeallocate(printer->http_ref);
2180
2181   if (printer->common_ref)
2182     DNSServiceRefDeallocate(printer->common_ref);
2183
2184   TXTRecordDeallocate(&(printer->ipp_txt));
2185
2186   if (printer->dnssd_name)
2187     _cupsStrFree(printer->dnssd_name);
2188 #endif /* HAVE_DNSSD */
2189
2190   if (printer->name)
2191     _cupsStrFree(printer->name);
2192   if (printer->icon)
2193     _cupsStrFree(printer->icon);
2194   if (printer->directory)
2195     _cupsStrFree(printer->directory);
2196   if (printer->hostname)
2197     _cupsStrFree(printer->hostname);
2198   if (printer->uri)
2199     _cupsStrFree(printer->uri);
2200
2201   ippDelete(printer->attrs);
2202   cupsArrayDelete(printer->jobs);
2203
2204   free(printer);
2205 }
2206
2207
2208 #ifdef HAVE_DNSSD
2209 /*
2210  * 'dnssd_callback()' - Handle Bonjour registration events.
2211  */
2212
2213 static void
2214 dnssd_callback(
2215     DNSServiceRef       sdRef,          /* I - Service reference */
2216     DNSServiceFlags     flags,          /* I - Status flags */
2217     DNSServiceErrorType errorCode,      /* I - Error, if any */
2218     const char          *name,          /* I - Service name */
2219     const char          *regtype,       /* I - Service type */
2220     const char          *domain,        /* I - Domain for service */
2221     _ipp_printer_t      *printer)       /* I - Printer */
2222 {
2223   if (errorCode)
2224   {
2225     fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n",
2226             regtype, (int)errorCode);
2227     return;
2228   }
2229   else if (_cups_strcasecmp(name, printer->dnssd_name))
2230   {
2231     if (Verbosity)
2232       fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name);
2233
2234     /* No lock needed since only the main thread accesses/changes this */
2235     _cupsStrFree(printer->dnssd_name);
2236     printer->dnssd_name = _cupsStrAlloc(name);
2237   }
2238 }
2239 #endif /* HAVE_DNSSD */
2240
2241
2242 /*
2243  * 'find_job()' - Find a job specified in a request.
2244  */
2245
2246 static _ipp_job_t *                     /* O - Job or NULL */
2247 find_job(_ipp_client_t *client)         /* I - Client */
2248 {
2249   ipp_attribute_t       *attr;          /* job-id or job-uri attribute */
2250   _ipp_job_t            key,            /* Job search key */
2251                         *job;           /* Matching job, if any */
2252
2253
2254   key.id = 0;
2255
2256   if ((attr = ippFindAttribute(client->request, "job-uri",
2257                                IPP_TAG_URI)) != NULL)
2258   {
2259     if (!strncmp(attr->values[0].string.text, client->printer->uri,
2260                  client->printer->urilen) &&
2261         attr->values[0].string.text[client->printer->urilen] == '/')
2262       key.id = atoi(attr->values[0].string.text + client->printer->urilen + 1);
2263   }
2264   else if ((attr = ippFindAttribute(client->request, "job-id",
2265                                     IPP_TAG_INTEGER)) != NULL)
2266     key.id = attr->values[0].integer;
2267
2268   _cupsRWLockRead(&(client->printer->rwlock));
2269   job = (_ipp_job_t *)cupsArrayFind(client->printer->jobs, &key);
2270   _cupsRWUnlock(&(client->printer->rwlock));
2271
2272   return (job);
2273 }
2274
2275
2276 /*
2277  * 'html_escape()' - Write a HTML-safe string.
2278  */
2279
2280 static void
2281 html_escape(_ipp_client_t *client,      /* I - Client */
2282             const char    *s,           /* I - String to write */
2283             size_t        slen)         /* I - Number of characters to write */
2284 {
2285   const char    *start,                 /* Start of segment */
2286                 *end;                   /* End of string */
2287
2288
2289   start = s;
2290   end   = s + (slen > 0 ? slen : strlen(s));
2291
2292   while (*s && s < end)
2293   {
2294     if (*s == '&' || *s == '<')
2295     {
2296       if (s > start)
2297         httpWrite2(&(client->http), start, s - start);
2298
2299       if (*s == '&')
2300         httpWrite2(&(client->http), "&amp;", 5);
2301       else
2302         httpWrite2(&(client->http), "&lt;", 4);
2303
2304       start = s + 1;
2305     }
2306
2307     s ++;
2308   }
2309
2310   if (s > start)
2311     httpWrite2(&(client->http), start, s - start);
2312 }
2313
2314
2315 /*
2316  * 'html_printf()' - Send formatted text to the client, quoting as needed.
2317  */
2318
2319 static void
2320 html_printf(_ipp_client_t *client,      /* I - Client */
2321             const char    *format,      /* I - Printf-style format string */
2322             ...)                        /* I - Additional arguments as needed */
2323 {
2324   va_list       ap;                     /* Pointer to arguments */
2325   const char    *start;                 /* Start of string */
2326   char          size,                   /* Size character (h, l, L) */
2327                 type;                   /* Format type character */
2328   int           width,                  /* Width of field */
2329                 prec;                   /* Number of characters of precision */
2330   char          tformat[100],           /* Temporary format string for sprintf() */
2331                 *tptr,                  /* Pointer into temporary format */
2332                 temp[1024];             /* Buffer for formatted numbers */
2333   char          *s;                     /* Pointer to string */
2334
2335
2336  /*
2337   * Loop through the format string, formatting as needed...
2338   */
2339
2340   va_start(ap, format);
2341   start = format;
2342
2343   while (*format)
2344   {
2345     if (*format == '%')
2346     {
2347       if (format > start)
2348         httpWrite2(&(client->http), start, format - start);
2349
2350       tptr    = tformat;
2351       *tptr++ = *format++;
2352
2353       if (*format == '%')
2354       {
2355         httpWrite2(&(client->http), "%", 1);
2356         format ++;
2357         continue;
2358       }
2359       else if (strchr(" -+#\'", *format))
2360         *tptr++ = *format++;
2361
2362       if (*format == '*')
2363       {
2364        /*
2365         * Get width from argument...
2366         */
2367
2368         format ++;
2369         width = va_arg(ap, int);
2370
2371         snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
2372         tptr += strlen(tptr);
2373       }
2374       else
2375       {
2376         width = 0;
2377
2378         while (isdigit(*format & 255))
2379         {
2380           if (tptr < (tformat + sizeof(tformat) - 1))
2381             *tptr++ = *format;
2382
2383           width = width * 10 + *format++ - '0';
2384         }
2385       }
2386
2387       if (*format == '.')
2388       {
2389         if (tptr < (tformat + sizeof(tformat) - 1))
2390           *tptr++ = *format;
2391
2392         format ++;
2393
2394         if (*format == '*')
2395         {
2396          /*
2397           * Get precision from argument...
2398           */
2399
2400           format ++;
2401           prec = va_arg(ap, int);
2402
2403           snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
2404           tptr += strlen(tptr);
2405         }
2406         else
2407         {
2408           prec = 0;
2409
2410           while (isdigit(*format & 255))
2411           {
2412             if (tptr < (tformat + sizeof(tformat) - 1))
2413               *tptr++ = *format;
2414
2415             prec = prec * 10 + *format++ - '0';
2416           }
2417         }
2418       }
2419
2420       if (*format == 'l' && format[1] == 'l')
2421       {
2422         size = 'L';
2423
2424         if (tptr < (tformat + sizeof(tformat) - 2))
2425         {
2426           *tptr++ = 'l';
2427           *tptr++ = 'l';
2428         }
2429
2430         format += 2;
2431       }
2432       else if (*format == 'h' || *format == 'l' || *format == 'L')
2433       {
2434         if (tptr < (tformat + sizeof(tformat) - 1))
2435           *tptr++ = *format;
2436
2437         size = *format++;
2438       }
2439       else
2440         size = 0;
2441
2442
2443       if (!*format)
2444       {
2445         start = format;
2446         break;
2447       }
2448
2449       if (tptr < (tformat + sizeof(tformat) - 1))
2450         *tptr++ = *format;
2451
2452       type  = *format++;
2453       *tptr = '\0';
2454       start = format;
2455
2456       switch (type)
2457       {
2458         case 'E' : /* Floating point formats */
2459         case 'G' :
2460         case 'e' :
2461         case 'f' :
2462         case 'g' :
2463             if ((width + 2) > sizeof(temp))
2464               break;
2465
2466             sprintf(temp, tformat, va_arg(ap, double));
2467
2468             httpWrite2(&(client->http), temp, strlen(temp));
2469             break;
2470
2471         case 'B' : /* Integer formats */
2472         case 'X' :
2473         case 'b' :
2474         case 'd' :
2475         case 'i' :
2476         case 'o' :
2477         case 'u' :
2478         case 'x' :
2479             if ((width + 2) > sizeof(temp))
2480               break;
2481
2482 #  ifdef HAVE_LONG_LONG
2483             if (size == 'L')
2484               sprintf(temp, tformat, va_arg(ap, long long));
2485             else
2486 #  endif /* HAVE_LONG_LONG */
2487             if (size == 'l')
2488               sprintf(temp, tformat, va_arg(ap, long));
2489             else
2490               sprintf(temp, tformat, va_arg(ap, int));
2491
2492             httpWrite2(&(client->http), temp, strlen(temp));
2493             break;
2494
2495         case 'p' : /* Pointer value */
2496             if ((width + 2) > sizeof(temp))
2497               break;
2498
2499             sprintf(temp, tformat, va_arg(ap, void *));
2500
2501             httpWrite2(&(client->http), temp, strlen(temp));
2502             break;
2503
2504         case 'c' : /* Character or character array */
2505             if (width <= 1)
2506             {
2507               temp[0] = va_arg(ap, int);
2508               temp[1] = '\0';
2509               html_escape(client, temp, 1);
2510             }
2511             else
2512               html_escape(client, va_arg(ap, char *), (size_t)width);
2513             break;
2514
2515         case 's' : /* String */
2516             if ((s = va_arg(ap, char *)) == NULL)
2517               s = "(null)";
2518
2519             html_escape(client, s, strlen(s));
2520             break;
2521       }
2522     }
2523     else
2524       format ++;
2525   }
2526
2527   if (format > start)
2528     httpWrite2(&(client->http), start, format - start);
2529
2530   va_end(ap);
2531 }
2532
2533
2534 /*
2535  * 'ipp_cancel_job()' - Cancel a job.
2536  */
2537
2538 static void
2539 ipp_cancel_job(_ipp_client_t *client)   /* I - Client */
2540 {
2541   _ipp_job_t            *job;           /* Job information */
2542
2543
2544  /*
2545   * Get the job...
2546   */
2547
2548   if ((job = find_job(client)) == NULL)
2549   {
2550     respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
2551     return;
2552   }
2553
2554  /*
2555   * See if the job is already completed, canceled, or aborted; if so,
2556   * we can't cancel...
2557   */
2558
2559   switch (job->state)
2560   {
2561     case IPP_JOB_CANCELED :
2562         respond_ipp(client, IPP_NOT_POSSIBLE,
2563                     "Job #%d is already canceled - can\'t cancel.", job->id);
2564         break;
2565
2566     case IPP_JOB_ABORTED :
2567         respond_ipp(client, IPP_NOT_POSSIBLE,
2568                     "Job #%d is already aborted - can\'t cancel.", job->id);
2569         break;
2570
2571     case IPP_JOB_COMPLETED :
2572         respond_ipp(client, IPP_NOT_POSSIBLE,
2573                     "Job #%d is already completed - can\'t cancel.", job->id);
2574         break;
2575
2576     default :
2577        /*
2578         * Cancel the job...
2579         */
2580
2581         _cupsRWLockWrite(&(client->printer->rwlock));
2582
2583         if (job->state == IPP_JOB_PROCESSING ||
2584             (job->state == IPP_JOB_HELD && job->fd >= 0))
2585           job->cancel = 1;
2586         else
2587         {
2588           job->state     = IPP_JOB_CANCELED;
2589           job->completed = time(NULL);
2590         }
2591
2592         _cupsRWUnlock(&(client->printer->rwlock));
2593
2594         respond_ipp(client, IPP_OK, NULL);
2595         break;
2596   }
2597 }
2598
2599
2600 /*
2601  * 'ipp_create_job()' - Create a job object.
2602  */
2603
2604 static void
2605 ipp_create_job(_ipp_client_t *client)   /* I - Client */
2606 {
2607   _ipp_job_t            *job;           /* New job */
2608   cups_array_t          *ra;            /* Attributes to send in response */
2609
2610
2611  /*
2612   * Validate print job attributes...
2613   */
2614
2615   if (!valid_job_attributes(client))
2616   {
2617     httpFlush(&(client->http));
2618     return;
2619   }
2620
2621  /*
2622   * Do we have a file to print?
2623   */
2624
2625   if (client->http.state == HTTP_POST_RECV)
2626   {
2627     respond_ipp(client, IPP_BAD_REQUEST,
2628                 "Unexpected document data following request.");
2629     return;
2630   }
2631
2632  /*
2633   * Create the job...
2634   */
2635
2636   if ((job = create_job(client)) == NULL)
2637   {
2638     respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
2639     return;
2640   }
2641
2642  /*
2643   * Return the job info...
2644   */
2645
2646   respond_ipp(client, IPP_OK, NULL);
2647
2648   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2649   cupsArrayAdd(ra, "job-id");
2650   cupsArrayAdd(ra, "job-state");
2651   cupsArrayAdd(ra, "job-state-reasons");
2652   cupsArrayAdd(ra, "job-uri");
2653
2654   copy_job_attributes(client, job, ra);
2655   cupsArrayDelete(ra);
2656 }
2657
2658
2659 /*
2660  * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2661  */
2662
2663 static void
2664 ipp_get_job_attributes(
2665     _ipp_client_t *client)              /* I - Client */
2666 {
2667   _ipp_job_t    *job;                   /* Job */
2668   cups_array_t  *ra;                    /* requested-attributes */
2669
2670
2671   if ((job = find_job(client)) == NULL)
2672   {
2673     respond_ipp(client, IPP_NOT_FOUND, "Job not found.");
2674     return;
2675   }
2676
2677   respond_ipp(client, IPP_OK, NULL);
2678
2679   ra = create_requested_array(client);
2680   copy_job_attributes(client, job, ra);
2681   cupsArrayDelete(ra);
2682 }
2683
2684
2685 /*
2686  * 'ipp_get_jobs()' - Get a list of job objects.
2687  */
2688
2689 static void
2690 ipp_get_jobs(_ipp_client_t *client)     /* I - Client */
2691 {
2692   ipp_attribute_t       *attr;          /* Current attribute */
2693   int                   job_comparison; /* Job comparison */
2694   ipp_jstate_t          job_state;      /* job-state value */
2695   int                   first_job_id,   /* First job ID */
2696                         limit,          /* Maximum number of jobs to return */
2697                         count;          /* Number of jobs that match */
2698   const char            *username;      /* Username */
2699   _ipp_job_t            *job;           /* Current job pointer */
2700   cups_array_t          *ra;            /* Requested attributes array */
2701
2702
2703  /*
2704   * See if the "which-jobs" attribute have been specified...
2705   */
2706
2707   if ((attr = ippFindAttribute(client->request, "which-jobs",
2708                                IPP_TAG_KEYWORD)) != NULL)
2709     fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->http.hostname,
2710             attr->values[0].string.text);
2711
2712   if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
2713   {
2714     job_comparison = -1;
2715     job_state      = IPP_JOB_STOPPED;
2716   }
2717   else if (!strcmp(attr->values[0].string.text, "completed"))
2718   {
2719     job_comparison = 1;
2720     job_state      = IPP_JOB_CANCELED;
2721   }
2722   else if (!strcmp(attr->values[0].string.text, "aborted"))
2723   {
2724     job_comparison = 0;
2725     job_state      = IPP_JOB_ABORTED;
2726   }
2727   else if (!strcmp(attr->values[0].string.text, "all"))
2728   {
2729     job_comparison = 1;
2730     job_state      = IPP_JOB_PENDING;
2731   }
2732   else if (!strcmp(attr->values[0].string.text, "canceled"))
2733   {
2734     job_comparison = 0;
2735     job_state      = IPP_JOB_CANCELED;
2736   }
2737   else if (!strcmp(attr->values[0].string.text, "pending"))
2738   {
2739     job_comparison = 0;
2740     job_state      = IPP_JOB_PENDING;
2741   }
2742   else if (!strcmp(attr->values[0].string.text, "pending-held"))
2743   {
2744     job_comparison = 0;
2745     job_state      = IPP_JOB_HELD;
2746   }
2747   else if (!strcmp(attr->values[0].string.text, "processing"))
2748   {
2749     job_comparison = 0;
2750     job_state      = IPP_JOB_PROCESSING;
2751   }
2752   else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
2753   {
2754     job_comparison = 0;
2755     job_state      = IPP_JOB_STOPPED;
2756   }
2757   else
2758   {
2759     respond_ipp(client, IPP_ATTRIBUTES,
2760                 "The which-jobs value \"%s\" is not supported.",
2761                 attr->values[0].string.text);
2762     ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
2763                  "which-jobs", NULL, attr->values[0].string.text);
2764     return;
2765   }
2766
2767  /*
2768   * See if they want to limit the number of jobs reported...
2769   */
2770
2771   if ((attr = ippFindAttribute(client->request, "limit",
2772                                IPP_TAG_INTEGER)) != NULL)
2773   {
2774     limit = attr->values[0].integer;
2775
2776     fprintf(stderr, "%s Get-Jobs limit=%d", client->http.hostname, limit);
2777   }
2778   else
2779     limit = 0;
2780
2781   if ((attr = ippFindAttribute(client->request, "first-job-id",
2782                                IPP_TAG_INTEGER)) != NULL)
2783   {
2784     first_job_id = attr->values[0].integer;
2785
2786     fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->http.hostname,
2787             first_job_id);
2788   }
2789   else
2790     first_job_id = 1;
2791
2792  /*
2793   * See if we only want to see jobs for a specific user...
2794   */
2795
2796   username = NULL;
2797
2798   if ((attr = ippFindAttribute(client->request, "my-jobs",
2799                                IPP_TAG_BOOLEAN)) != NULL)
2800   {
2801     fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->http.hostname,
2802             attr->values[0].boolean ? "true" : "false");
2803
2804     if (attr->values[0].boolean)
2805     {
2806       if ((attr = ippFindAttribute(client->request, "requesting-user-name",
2807                                         IPP_TAG_NAME)) == NULL)
2808       {
2809         respond_ipp(client, IPP_BAD_REQUEST,
2810                     "Need requesting-user-name with my-jobs.");
2811         return;
2812       }
2813
2814       username = attr->values[0].string.text;
2815
2816       fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2817               client->http.hostname, username);
2818     }
2819   }
2820
2821  /*
2822   * OK, build a list of jobs for this printer...
2823   */
2824
2825   if ((ra = create_requested_array(client)) == NULL &&
2826       !ippFindAttribute(client->request, "requested-attributes",
2827                         IPP_TAG_KEYWORD))
2828   {
2829    /*
2830     * IPP conformance - Get-Jobs has a default requested-attributes value of
2831     * "job-id" and "job-uri".
2832     */
2833
2834     ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2835     cupsArrayAdd(ra, "job-id");
2836     cupsArrayAdd(ra, "job-uri");
2837   }
2838
2839   respond_ipp(client, IPP_OK, NULL);
2840
2841   _cupsRWLockRead(&(client->printer->rwlock));
2842
2843   for (count = 0, job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs);
2844        (limit <= 0 || count < limit) && job;
2845        job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
2846   {
2847    /*
2848     * Filter out jobs that don't match...
2849     */
2850
2851     if ((job_comparison < 0 && job->state > job_state) ||
2852         (job_comparison == 0 && job->state != job_state) ||
2853         (job_comparison > 0 && job->state < job_state) ||
2854         job->id < first_job_id ||
2855         (username && job->username && _cups_strcasecmp(username, job->username)))
2856       continue;
2857
2858     if (count > 0)
2859       ippAddSeparator(client->response);
2860
2861     count ++;
2862     copy_job_attributes(client, job, ra);
2863   }
2864
2865   cupsArrayDelete(ra);
2866
2867   _cupsRWUnlock(&(client->printer->rwlock));
2868 }
2869
2870
2871 /*
2872  * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2873  */
2874
2875 static void
2876 ipp_get_printer_attributes(
2877     _ipp_client_t *client)              /* I - Client */
2878 {
2879   cups_array_t          *ra;            /* Requested attributes array */
2880   _ipp_printer_t        *printer;       /* Printer */
2881
2882
2883  /*
2884   * Send the attributes...
2885   */
2886
2887   ra      = create_requested_array(client);
2888   printer = client->printer;
2889
2890   respond_ipp(client, IPP_OK, NULL);
2891
2892   _cupsRWLockRead(&(printer->rwlock));
2893
2894   copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
2895                   IPP_TAG_COPY);
2896
2897   if (!ra || cupsArrayFind(ra, "printer-state"))
2898     ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
2899                   "printer-state", printer->state);
2900
2901   if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
2902   {
2903     if (printer->state_reasons == _IPP_PRINTER_NONE)
2904       ippAddString(client->response, IPP_TAG_PRINTER,
2905                    IPP_TAG_KEYWORD | IPP_TAG_COPY, "printer-state-reasons",
2906                    NULL, "none");
2907     else
2908     {
2909       int                       num_reasons = 0;/* Number of reasons */
2910       const char                *reasons[32];   /* Reason strings */
2911
2912       if (printer->state_reasons & _IPP_PRINTER_OTHER)
2913         reasons[num_reasons ++] = "other";
2914       if (printer->state_reasons & _IPP_PRINTER_COVER_OPEN)
2915         reasons[num_reasons ++] = "cover-open";
2916       if (printer->state_reasons & _IPP_PRINTER_INPUT_TRAY_MISSING)
2917         reasons[num_reasons ++] = "input-tray-missing";
2918       if (printer->state_reasons & _IPP_PRINTER_MARKER_SUPPLY_EMPTY)
2919         reasons[num_reasons ++] = "marker-supply-empty-warning";
2920       if (printer->state_reasons & _IPP_PRINTER_MARKER_SUPPLY_LOW)
2921         reasons[num_reasons ++] = "marker-supply-low-report";
2922       if (printer->state_reasons & _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL)
2923         reasons[num_reasons ++] = "marker-waste-almost-full-report";
2924       if (printer->state_reasons & _IPP_PRINTER_MARKER_WASTE_FULL)
2925         reasons[num_reasons ++] = "marker-waste-full-warning";
2926       if (printer->state_reasons & _IPP_PRINTER_MEDIA_EMPTY)
2927         reasons[num_reasons ++] = "media-empty-warning";
2928       if (printer->state_reasons & _IPP_PRINTER_MEDIA_JAM)
2929         reasons[num_reasons ++] = "media-jam-warning";
2930       if (printer->state_reasons & _IPP_PRINTER_MEDIA_LOW)
2931         reasons[num_reasons ++] = "media-low-report";
2932       if (printer->state_reasons & _IPP_PRINTER_MEDIA_NEEDED)
2933         reasons[num_reasons ++] = "media-needed-report";
2934       if (printer->state_reasons & _IPP_PRINTER_MOVING_TO_PAUSED)
2935         reasons[num_reasons ++] = "moving-to-paused";
2936       if (printer->state_reasons & _IPP_PRINTER_PAUSED)
2937         reasons[num_reasons ++] = "paused";
2938       if (printer->state_reasons & _IPP_PRINTER_SPOOL_AREA_FULL)
2939         reasons[num_reasons ++] = "spool-area-full";
2940       if (printer->state_reasons & _IPP_PRINTER_TONER_EMPTY)
2941         reasons[num_reasons ++] = "toner-empty-warning";
2942       if (printer->state_reasons & _IPP_PRINTER_TONER_LOW)
2943         reasons[num_reasons ++] = "toner-low-report";
2944
2945       ippAddStrings(client->response, IPP_TAG_PRINTER,
2946                     IPP_TAG_KEYWORD | IPP_TAG_COPY,  "printer-state-reasons",
2947                     num_reasons, NULL, reasons);
2948     }
2949   }
2950
2951   if (!ra || cupsArrayFind(ra, "printer-up-time"))
2952     ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2953                   "printer-up-time", (int)time(NULL));
2954
2955   if (!ra || cupsArrayFind(ra, "queued-job-count"))
2956     ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2957                   "queued-job-count",
2958                   printer->active_job &&
2959                       printer->active_job->state < IPP_JOB_CANCELED);
2960
2961   _cupsRWUnlock(&(printer->rwlock));
2962
2963   cupsArrayDelete(ra);
2964 }
2965
2966
2967 /*
2968  * 'ipp_print_job()' - Create a job object with an attached document.
2969  */
2970
2971 static void
2972 ipp_print_job(_ipp_client_t *client)    /* I - Client */
2973 {
2974   _ipp_job_t            *job;           /* New job */
2975   char                  filename[1024], /* Filename buffer */
2976                         buffer[4096];   /* Copy buffer */
2977   ssize_t               bytes;          /* Bytes read */
2978   cups_array_t          *ra;            /* Attributes to send in response */
2979
2980
2981  /*
2982   * Validate print job attributes...
2983   */
2984
2985   if (!valid_job_attributes(client))
2986   {
2987     httpFlush(&(client->http));
2988     return;
2989   }
2990
2991  /*
2992   * Do we have a file to print?
2993   */
2994
2995   if (client->http.state == HTTP_POST_SEND)
2996   {
2997     respond_ipp(client, IPP_BAD_REQUEST, "No file in request.");
2998     return;
2999   }
3000
3001  /*
3002   * Print the job...
3003   */
3004
3005   if ((job = create_job(client)) == NULL)
3006   {
3007     respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
3008     return;
3009   }
3010
3011  /*
3012   * Create a file for the request data...
3013   */
3014
3015   if (!_cups_strcasecmp(job->format, "image/jpeg"))
3016     snprintf(filename, sizeof(filename), "%s/%d.jpg",
3017              client->printer->directory, job->id);
3018   else if (!_cups_strcasecmp(job->format, "image/png"))
3019     snprintf(filename, sizeof(filename), "%s/%d.png",
3020              client->printer->directory, job->id);
3021   else if (!_cups_strcasecmp(job->format, "application/pdf"))
3022     snprintf(filename, sizeof(filename), "%s/%d.pdf",
3023              client->printer->directory, job->id);
3024   else if (!_cups_strcasecmp(job->format, "application/postscript"))
3025     snprintf(filename, sizeof(filename), "%s/%d.ps",
3026              client->printer->directory, job->id);
3027   else
3028     snprintf(filename, sizeof(filename), "%s/%d.prn",
3029              client->printer->directory, job->id);
3030
3031   if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
3032   {
3033     job->state = IPP_JOB_ABORTED;
3034
3035     respond_ipp(client, IPP_INTERNAL_ERROR,
3036                 "Unable to create print file: %s", strerror(errno));
3037     return;
3038   }
3039
3040   while ((bytes = httpRead2(&(client->http), buffer, sizeof(buffer))) > 0)
3041   {
3042     if (write(job->fd, buffer, bytes) < bytes)
3043     {
3044       int error = errno;                /* Write error */
3045
3046       job->state = IPP_JOB_ABORTED;
3047
3048       close(job->fd);
3049       job->fd = -1;
3050
3051       unlink(filename);
3052
3053       respond_ipp(client, IPP_INTERNAL_ERROR,
3054                   "Unable to write print file: %s", strerror(error));
3055       return;
3056     }
3057   }
3058
3059   if (bytes < 0)
3060   {
3061    /*
3062     * Got an error while reading the print data, so abort this job.
3063     */
3064
3065     job->state = IPP_JOB_ABORTED;
3066
3067     close(job->fd);
3068     job->fd = -1;
3069
3070     unlink(filename);
3071
3072     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to read print file.");
3073     return;
3074   }
3075
3076   if (close(job->fd))
3077   {
3078     int error = errno;          /* Write error */
3079
3080     job->state = IPP_JOB_ABORTED;
3081     job->fd    = -1;
3082
3083     unlink(filename);
3084
3085     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
3086                 strerror(error));
3087     return;
3088   }
3089
3090   job->fd       = -1;
3091   job->filename = strdup(filename);
3092   job->state    = IPP_JOB_PENDING;
3093
3094  /*
3095   * Process the job...
3096   */
3097
3098 #if 0
3099   if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3100   {
3101     job->state = IPP_JOB_ABORTED;
3102     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
3103     return;
3104   }
3105
3106 #else
3107   process_job(job);
3108 #endif /* 0 */
3109
3110  /*
3111   * Return the job info...
3112   */
3113
3114   respond_ipp(client, IPP_OK, NULL);
3115
3116   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3117   cupsArrayAdd(ra, "job-id");
3118   cupsArrayAdd(ra, "job-state");
3119   cupsArrayAdd(ra, "job-state-reasons");
3120   cupsArrayAdd(ra, "job-uri");
3121
3122   copy_job_attributes(client, job, ra);
3123   cupsArrayDelete(ra);
3124 }
3125
3126
3127 /*
3128  * 'ipp_print_uri()' - Create a job object with a referenced document.
3129  */
3130
3131 static void
3132 ipp_print_uri(_ipp_client_t *client)    /* I - Client */
3133 {
3134   _ipp_job_t            *job;           /* New job */
3135   ipp_attribute_t       *uri;           /* document-uri */
3136   char                  scheme[256],    /* URI scheme */
3137                         userpass[256],  /* Username and password info */
3138                         hostname[256],  /* Hostname */
3139                         resource[1024]; /* Resource path */
3140   int                   port;           /* Port number */
3141   http_uri_status_t     uri_status;     /* URI decode status */
3142   http_encryption_t     encryption;     /* Encryption to use, if any */
3143   http_t                *http;          /* Connection for http/https URIs */
3144   http_status_t         status;         /* Access status for http/https URIs */
3145   int                   infile;         /* Input file for local file URIs */
3146   char                  filename[1024], /* Filename buffer */
3147                         buffer[4096];   /* Copy buffer */
3148   ssize_t               bytes;          /* Bytes read */
3149   cups_array_t          *ra;            /* Attributes to send in response */
3150   static const char * const uri_status_strings[] =
3151   {                                     /* URI decode errors */
3152     "URI too large.",
3153     "Bad arguments to function.",
3154     "Bad resource in URI.",
3155     "Bad port number in URI.",
3156     "Bad hostname in URI.",
3157     "Bad username in URI.",
3158     "Bad scheme in URI.",
3159     "Bad/empty URI."
3160   };
3161
3162
3163  /*
3164   * Validate print job attributes...
3165   */
3166
3167   if (!valid_job_attributes(client))
3168   {
3169     httpFlush(&(client->http));
3170     return;
3171   }
3172
3173  /*
3174   * Do we have a file to print?
3175   */
3176
3177   if (client->http.state == HTTP_POST_RECV)
3178   {
3179     respond_ipp(client, IPP_BAD_REQUEST,
3180                 "Unexpected document data following request.");
3181     return;
3182   }
3183
3184  /*
3185   * Do we have a document URI?
3186   */
3187
3188   if ((uri = ippFindAttribute(client->request, "document-uri",
3189                               IPP_TAG_URI)) == NULL)
3190   {
3191     respond_ipp(client, IPP_BAD_REQUEST, "Missing document-uri.");
3192     return;
3193   }
3194
3195   if (uri->num_values != 1)
3196   {
3197     respond_ipp(client, IPP_BAD_REQUEST, "Too many document-uri values.");
3198     return;
3199   }
3200
3201   uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
3202                                scheme, sizeof(scheme), userpass,
3203                                sizeof(userpass), hostname, sizeof(hostname),
3204                                &port, resource, sizeof(resource));
3205   if (uri_status < HTTP_URI_OK)
3206   {
3207     respond_ipp(client, IPP_BAD_REQUEST, "Bad document-uri: %s",
3208                 uri_status_strings[uri_status - HTTP_URI_OVERFLOW]);
3209     return;
3210   }
3211
3212   if (strcmp(scheme, "file") &&
3213 #ifdef HAVE_SSL
3214       strcmp(scheme, "https") &&
3215 #endif /* HAVE_SSL */
3216       strcmp(scheme, "http"))
3217   {
3218     respond_ipp(client, IPP_URI_SCHEME, "URI scheme \"%s\" not supported.",
3219                 scheme);
3220     return;
3221   }
3222
3223   if (!strcmp(scheme, "file") && access(resource, R_OK))
3224   {
3225     respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
3226                 strerror(errno));
3227     return;
3228   }
3229
3230  /*
3231   * Print the job...
3232   */
3233
3234   if ((job = create_job(client)) == NULL)
3235   {
3236     respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
3237     return;
3238   }
3239
3240  /*
3241   * Create a file for the request data...
3242   */
3243
3244   if (!_cups_strcasecmp(job->format, "image/jpeg"))
3245     snprintf(filename, sizeof(filename), "%s/%d.jpg",
3246              client->printer->directory, job->id);
3247   else if (!_cups_strcasecmp(job->format, "image/png"))
3248     snprintf(filename, sizeof(filename), "%s/%d.png",
3249              client->printer->directory, job->id);
3250   else if (!_cups_strcasecmp(job->format, "application/pdf"))
3251     snprintf(filename, sizeof(filename), "%s/%d.pdf",
3252              client->printer->directory, job->id);
3253   else if (!_cups_strcasecmp(job->format, "application/postscript"))
3254     snprintf(filename, sizeof(filename), "%s/%d.ps",
3255              client->printer->directory, job->id);
3256   else
3257     snprintf(filename, sizeof(filename), "%s/%d.prn",
3258              client->printer->directory, job->id);
3259
3260   if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
3261   {
3262     job->state = IPP_JOB_ABORTED;
3263
3264     respond_ipp(client, IPP_INTERNAL_ERROR,
3265                 "Unable to create print file: %s", strerror(errno));
3266     return;
3267   }
3268
3269   if (!strcmp(scheme, "file"))
3270   {
3271     if ((infile = open(resource, O_RDONLY)) < 0)
3272     {
3273       respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
3274                   strerror(errno));
3275       return;
3276     }
3277
3278     do
3279     {
3280       if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
3281           (errno == EAGAIN || errno == EINTR))
3282         bytes = 1;
3283       else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes)
3284       {
3285         int error = errno;              /* Write error */
3286
3287         job->state = IPP_JOB_ABORTED;
3288
3289         close(job->fd);
3290         job->fd = -1;
3291
3292         unlink(filename);
3293         close(infile);
3294
3295         respond_ipp(client, IPP_INTERNAL_ERROR,
3296                     "Unable to write print file: %s", strerror(error));
3297         return;
3298       }
3299     }
3300     while (bytes > 0);
3301
3302     close(infile);
3303   }
3304   else
3305   {
3306 #ifdef HAVE_SSL
3307     if (port == 443 || !strcmp(scheme, "https"))
3308       encryption = HTTP_ENCRYPT_ALWAYS;
3309     else
3310 #endif /* HAVE_SSL */
3311     encryption = HTTP_ENCRYPT_IF_REQUESTED;
3312
3313     if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL)
3314     {
3315       respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR,
3316                   "Unable to connect to %s: %s", hostname,
3317                   cupsLastErrorString());
3318       job->state = IPP_JOB_ABORTED;
3319
3320       close(job->fd);
3321       job->fd = -1;
3322
3323       unlink(filename);
3324       return;
3325     }
3326
3327     httpClearFields(http);
3328     httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
3329     if (httpGet(http, resource))
3330     {
3331       respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s",
3332                   strerror(errno));
3333
3334       job->state = IPP_JOB_ABORTED;
3335
3336       close(job->fd);
3337       job->fd = -1;
3338
3339       unlink(filename);
3340       httpClose(http);
3341       return;
3342     }
3343
3344     while ((status = httpUpdate(http)) == HTTP_CONTINUE);
3345
3346     if (status != HTTP_OK)
3347     {
3348       respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s",
3349                   httpStatus(status));
3350
3351       job->state = IPP_JOB_ABORTED;
3352
3353       close(job->fd);
3354       job->fd = -1;
3355
3356       unlink(filename);
3357       httpClose(http);
3358       return;
3359     }
3360
3361     while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3362     {
3363       if (write(job->fd, buffer, bytes) < bytes)
3364       {
3365         int error = errno;              /* Write error */
3366
3367         job->state = IPP_JOB_ABORTED;
3368
3369         close(job->fd);
3370         job->fd = -1;
3371
3372         unlink(filename);
3373         httpClose(http);
3374
3375         respond_ipp(client, IPP_INTERNAL_ERROR,
3376                     "Unable to write print file: %s", strerror(error));
3377         return;
3378       }
3379     }
3380
3381     httpClose(http);
3382   }
3383
3384   if (close(job->fd))
3385   {
3386     int error = errno;          /* Write error */
3387
3388     job->state = IPP_JOB_ABORTED;
3389     job->fd    = -1;
3390
3391     unlink(filename);
3392
3393     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
3394                 strerror(error));
3395     return;
3396   }
3397
3398   job->fd       = -1;
3399   job->filename = strdup(filename);
3400   job->state    = IPP_JOB_PENDING;
3401
3402  /*
3403   * Process the job...
3404   */
3405
3406 #if 0
3407   if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3408   {
3409     job->state = IPP_JOB_ABORTED;
3410     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
3411     return;
3412   }
3413
3414 #else
3415   process_job(job);
3416 #endif /* 0 */
3417
3418  /*
3419   * Return the job info...
3420   */
3421
3422   respond_ipp(client, IPP_OK, NULL);
3423
3424   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3425   cupsArrayAdd(ra, "job-id");
3426   cupsArrayAdd(ra, "job-state");
3427   cupsArrayAdd(ra, "job-state-reasons");
3428   cupsArrayAdd(ra, "job-uri");
3429
3430   copy_job_attributes(client, job, ra);
3431   cupsArrayDelete(ra);
3432 }
3433
3434
3435 /*
3436  * 'ipp_send_document()' - Add an attached document to a job object created with
3437  *                         Create-Job.
3438  */
3439
3440 static void
3441 ipp_send_document(_ipp_client_t *client)/* I - Client */
3442 {
3443   _ipp_job_t            *job;           /* Job information */
3444   char                  filename[1024], /* Filename buffer */
3445                         buffer[4096];   /* Copy buffer */
3446   ssize_t               bytes;          /* Bytes read */
3447   ipp_attribute_t       *attr;          /* Current attribute */
3448   cups_array_t          *ra;            /* Attributes to send in response */
3449
3450
3451  /*
3452   * Get the job...
3453   */
3454
3455   if ((job = find_job(client)) == NULL)
3456   {
3457     respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
3458     httpFlush(&(client->http));
3459     return;
3460   }
3461
3462  /*
3463   * See if we already have a document for this job or the job has already
3464   * in a non-pending state...
3465   */
3466
3467   if (job->state > IPP_JOB_HELD)
3468   {
3469     respond_ipp(client, IPP_NOT_POSSIBLE, "Job is not in a pending state.");
3470     httpFlush(&(client->http));
3471     return;
3472   }
3473   else if (job->filename || job->fd >= 0)
3474   {
3475     respond_ipp(client, IPP_MULTIPLE_JOBS_NOT_SUPPORTED,
3476                 "Multiple document jobs are not supported.");
3477     httpFlush(&(client->http));
3478     return;
3479   }
3480
3481   if ((attr = ippFindAttribute(client->request, "last-document",
3482                                IPP_TAG_ZERO)) == NULL)
3483   {
3484     respond_ipp(client, IPP_BAD_REQUEST,
3485                 "Missing required last-document attribute.");
3486     httpFlush(&(client->http));
3487     return;
3488   }
3489   else if (attr->value_tag != IPP_TAG_BOOLEAN || attr->num_values != 1 ||
3490            !attr->values[0].boolean)
3491   {
3492     respond_unsupported(client, attr);
3493     httpFlush(&(client->http));
3494     return;
3495   }
3496
3497  /*
3498   * Validate document attributes...
3499   */
3500
3501   if (!valid_doc_attributes(client))
3502   {
3503     httpFlush(&(client->http));
3504     return;
3505   }
3506
3507  /*
3508   * Get the document format for the job...
3509   */
3510
3511   _cupsRWLockWrite(&(client->printer->rwlock));
3512
3513   if ((attr = ippFindAttribute(job->attrs, "document-format",
3514                                IPP_TAG_MIMETYPE)) != NULL)
3515     job->format = attr->values[0].string.text;
3516   else
3517     job->format = "application/octet-stream";
3518
3519  /*
3520   * Create a file for the request data...
3521   */
3522
3523   if (!_cups_strcasecmp(job->format, "image/jpeg"))
3524     snprintf(filename, sizeof(filename), "%s/%d.jpg",
3525              client->printer->directory, job->id);
3526   else if (!_cups_strcasecmp(job->format, "image/png"))
3527     snprintf(filename, sizeof(filename), "%s/%d.png",
3528              client->printer->directory, job->id);
3529   else if (!_cups_strcasecmp(job->format, "application/pdf"))
3530     snprintf(filename, sizeof(filename), "%s/%d.pdf",
3531              client->printer->directory, job->id);
3532   else if (!_cups_strcasecmp(job->format, "application/postscript"))
3533     snprintf(filename, sizeof(filename), "%s/%d.ps",
3534              client->printer->directory, job->id);
3535   else
3536     snprintf(filename, sizeof(filename), "%s/%d.prn",
3537              client->printer->directory, job->id);
3538
3539   job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3540
3541   _cupsRWUnlock(&(client->printer->rwlock));
3542
3543   if (job->fd < 0)
3544   {
3545     job->state = IPP_JOB_ABORTED;
3546
3547     respond_ipp(client, IPP_INTERNAL_ERROR,
3548                 "Unable to create print file: %s", strerror(errno));
3549     return;
3550   }
3551
3552   while ((bytes = httpRead2(&(client->http), buffer, sizeof(buffer))) > 0)
3553   {
3554     if (write(job->fd, buffer, bytes) < bytes)
3555     {
3556       int error = errno;                /* Write error */
3557
3558       job->state = IPP_JOB_ABORTED;
3559
3560       close(job->fd);
3561       job->fd = -1;
3562
3563       unlink(filename);
3564
3565       respond_ipp(client, IPP_INTERNAL_ERROR,
3566                   "Unable to write print file: %s", strerror(error));
3567       return;
3568     }
3569   }
3570
3571   if (bytes < 0)
3572   {
3573    /*
3574     * Got an error while reading the print data, so abort this job.
3575     */
3576
3577     job->state = IPP_JOB_ABORTED;
3578
3579     close(job->fd);
3580     job->fd = -1;
3581
3582     unlink(filename);
3583
3584     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to read print file.");
3585     return;
3586   }
3587
3588   if (close(job->fd))
3589   {
3590     int error = errno;          /* Write error */
3591
3592     job->state = IPP_JOB_ABORTED;
3593     job->fd    = -1;
3594
3595     unlink(filename);
3596
3597     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
3598                 strerror(error));
3599     return;
3600   }
3601
3602   _cupsRWLockWrite(&(client->printer->rwlock));
3603
3604   job->fd       = -1;
3605   job->filename = strdup(filename);
3606   job->state    = IPP_JOB_PENDING;
3607
3608   _cupsRWUnlock(&(client->printer->rwlock));
3609
3610  /*
3611   * Process the job...
3612   */
3613
3614 #if 0
3615   if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3616   {
3617     job->state = IPP_JOB_ABORTED;
3618     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
3619     return;
3620   }
3621
3622 #else
3623   process_job(job);
3624 #endif /* 0 */
3625
3626  /*
3627   * Return the job info...
3628   */
3629
3630   respond_ipp(client, IPP_OK, NULL);
3631
3632   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3633   cupsArrayAdd(ra, "job-id");
3634   cupsArrayAdd(ra, "job-state");
3635   cupsArrayAdd(ra, "job-state-reasons");
3636   cupsArrayAdd(ra, "job-uri");
3637
3638   copy_job_attributes(client, job, ra);
3639   cupsArrayDelete(ra);
3640 }
3641
3642
3643 /*
3644  * 'ipp_send_uri()' - Add a referenced document to a job object created with
3645  *                    Create-Job.
3646  */
3647
3648 static void
3649 ipp_send_uri(_ipp_client_t *client)     /* I - Client */
3650 {
3651   _ipp_job_t            *job;           /* Job information */
3652   ipp_attribute_t       *uri;           /* document-uri */
3653   char                  scheme[256],    /* URI scheme */
3654                         userpass[256],  /* Username and password info */
3655                         hostname[256],  /* Hostname */
3656                         resource[1024]; /* Resource path */
3657   int                   port;           /* Port number */
3658   http_uri_status_t     uri_status;     /* URI decode status */
3659   http_encryption_t     encryption;     /* Encryption to use, if any */
3660   http_t                *http;          /* Connection for http/https URIs */
3661   http_status_t         status;         /* Access status for http/https URIs */
3662   int                   infile;         /* Input file for local file URIs */
3663   char                  filename[1024], /* Filename buffer */
3664                         buffer[4096];   /* Copy buffer */
3665   ssize_t               bytes;          /* Bytes read */
3666   ipp_attribute_t       *attr;          /* Current attribute */
3667   cups_array_t          *ra;            /* Attributes to send in response */
3668   static const char * const uri_status_strings[] =
3669   {                                     /* URI decode errors */
3670     "URI too large.",
3671     "Bad arguments to function.",
3672     "Bad resource in URI.",
3673     "Bad port number in URI.",
3674     "Bad hostname in URI.",
3675     "Bad username in URI.",
3676     "Bad scheme in URI.",
3677     "Bad/empty URI."
3678   };
3679
3680
3681  /*
3682   * Get the job...
3683   */
3684
3685   if ((job = find_job(client)) == NULL)
3686   {
3687     respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
3688     httpFlush(&(client->http));
3689     return;
3690   }
3691
3692  /*
3693   * See if we already have a document for this job or the job has already
3694   * in a non-pending state...
3695   */
3696
3697   if (job->state > IPP_JOB_HELD)
3698   {
3699     respond_ipp(client, IPP_NOT_POSSIBLE, "Job is not in a pending state.");
3700     httpFlush(&(client->http));
3701     return;
3702   }
3703   else if (job->filename || job->fd >= 0)
3704   {
3705     respond_ipp(client, IPP_MULTIPLE_JOBS_NOT_SUPPORTED,
3706                 "Multiple document jobs are not supported.");
3707     httpFlush(&(client->http));
3708     return;
3709   }
3710
3711   if ((attr = ippFindAttribute(client->request, "last-document",
3712                                IPP_TAG_ZERO)) == NULL)
3713   {
3714     respond_ipp(client, IPP_BAD_REQUEST,
3715                 "Missing required last-document attribute.");
3716     httpFlush(&(client->http));
3717     return;
3718   }
3719   else if (attr->value_tag != IPP_TAG_BOOLEAN || attr->num_values != 1 ||
3720            !attr->values[0].boolean)
3721   {
3722     respond_unsupported(client, attr);
3723     httpFlush(&(client->http));
3724     return;
3725   }
3726
3727  /*
3728   * Validate document attributes...
3729   */
3730
3731   if (!valid_doc_attributes(client))
3732   {
3733     httpFlush(&(client->http));
3734     return;
3735   }
3736
3737  /*
3738   * Do we have a file to print?
3739   */
3740
3741   if (client->http.state == HTTP_POST_RECV)
3742   {
3743     respond_ipp(client, IPP_BAD_REQUEST,
3744                 "Unexpected document data following request.");
3745     return;
3746   }
3747
3748  /*
3749   * Do we have a document URI?
3750   */
3751
3752   if ((uri = ippFindAttribute(client->request, "document-uri",
3753                               IPP_TAG_URI)) == NULL)
3754   {
3755     respond_ipp(client, IPP_BAD_REQUEST, "Missing document-uri.");
3756     return;
3757   }
3758
3759   if (uri->num_values != 1)
3760   {
3761     respond_ipp(client, IPP_BAD_REQUEST, "Too many document-uri values.");
3762     return;
3763   }
3764
3765   uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
3766                                scheme, sizeof(scheme), userpass,
3767                                sizeof(userpass), hostname, sizeof(hostname),
3768                                &port, resource, sizeof(resource));
3769   if (uri_status < HTTP_URI_OK)
3770   {
3771     respond_ipp(client, IPP_BAD_REQUEST, "Bad document-uri: %s",
3772                 uri_status_strings[uri_status - HTTP_URI_OVERFLOW]);
3773     return;
3774   }
3775
3776   if (strcmp(scheme, "file") &&
3777 #ifdef HAVE_SSL
3778       strcmp(scheme, "https") &&
3779 #endif /* HAVE_SSL */
3780       strcmp(scheme, "http"))
3781   {
3782     respond_ipp(client, IPP_URI_SCHEME, "URI scheme \"%s\" not supported.",
3783                 scheme);
3784     return;
3785   }
3786
3787   if (!strcmp(scheme, "file") && access(resource, R_OK))
3788   {
3789     respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
3790                 strerror(errno));
3791     return;
3792   }
3793
3794  /*
3795   * Get the document format for the job...
3796   */
3797
3798   _cupsRWLockWrite(&(client->printer->rwlock));
3799
3800   if ((attr = ippFindAttribute(job->attrs, "document-format",
3801                                IPP_TAG_MIMETYPE)) != NULL)
3802     job->format = attr->values[0].string.text;
3803   else
3804     job->format = "application/octet-stream";
3805
3806  /*
3807   * Create a file for the request data...
3808   */
3809
3810   if (!_cups_strcasecmp(job->format, "image/jpeg"))
3811     snprintf(filename, sizeof(filename), "%s/%d.jpg",
3812              client->printer->directory, job->id);
3813   else if (!_cups_strcasecmp(job->format, "image/png"))
3814     snprintf(filename, sizeof(filename), "%s/%d.png",
3815              client->printer->directory, job->id);
3816   else if (!_cups_strcasecmp(job->format, "application/pdf"))
3817     snprintf(filename, sizeof(filename), "%s/%d.pdf",
3818              client->printer->directory, job->id);
3819   else if (!_cups_strcasecmp(job->format, "application/postscript"))
3820     snprintf(filename, sizeof(filename), "%s/%d.ps",
3821              client->printer->directory, job->id);
3822   else
3823     snprintf(filename, sizeof(filename), "%s/%d.prn",
3824              client->printer->directory, job->id);
3825
3826   job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3827
3828   _cupsRWUnlock(&(client->printer->rwlock));
3829
3830   if (job->fd < 0)
3831   {
3832     job->state = IPP_JOB_ABORTED;
3833
3834     respond_ipp(client, IPP_INTERNAL_ERROR,
3835                 "Unable to create print file: %s", strerror(errno));
3836     return;
3837   }
3838
3839   if (!strcmp(scheme, "file"))
3840   {
3841     if ((infile = open(resource, O_RDONLY)) < 0)
3842     {
3843       respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
3844                   strerror(errno));
3845       return;
3846     }
3847
3848     do
3849     {
3850       if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
3851           (errno == EAGAIN || errno == EINTR))
3852         bytes = 1;
3853       else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes)
3854       {
3855         int error = errno;              /* Write error */
3856
3857         job->state = IPP_JOB_ABORTED;
3858
3859         close(job->fd);
3860         job->fd = -1;
3861
3862         unlink(filename);
3863         close(infile);
3864
3865         respond_ipp(client, IPP_INTERNAL_ERROR,
3866                     "Unable to write print file: %s", strerror(error));
3867         return;
3868       }
3869     }
3870     while (bytes > 0);
3871
3872     close(infile);
3873   }
3874   else
3875   {
3876 #ifdef HAVE_SSL
3877     if (port == 443 || !strcmp(scheme, "https"))
3878       encryption = HTTP_ENCRYPT_ALWAYS;
3879     else
3880 #endif /* HAVE_SSL */
3881     encryption = HTTP_ENCRYPT_IF_REQUESTED;
3882
3883     if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL)
3884     {
3885       respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR,
3886                   "Unable to connect to %s: %s", hostname,
3887                   cupsLastErrorString());
3888       job->state = IPP_JOB_ABORTED;
3889
3890       close(job->fd);
3891       job->fd = -1;
3892
3893       unlink(filename);
3894       return;
3895     }
3896
3897     httpClearFields(http);
3898     httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
3899     if (httpGet(http, resource))
3900     {
3901       respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s",
3902                   strerror(errno));
3903
3904       job->state = IPP_JOB_ABORTED;
3905
3906       close(job->fd);
3907       job->fd = -1;
3908
3909       unlink(filename);
3910       httpClose(http);
3911       return;
3912     }
3913
3914     while ((status = httpUpdate(http)) == HTTP_CONTINUE);
3915
3916     if (status != HTTP_OK)
3917     {
3918       respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s",
3919                   httpStatus(status));
3920
3921       job->state = IPP_JOB_ABORTED;
3922
3923       close(job->fd);
3924       job->fd = -1;
3925
3926       unlink(filename);
3927       httpClose(http);
3928       return;
3929     }
3930
3931     while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3932     {
3933       if (write(job->fd, buffer, bytes) < bytes)
3934       {
3935         int error = errno;              /* Write error */
3936
3937         job->state = IPP_JOB_ABORTED;
3938
3939         close(job->fd);
3940         job->fd = -1;
3941
3942         unlink(filename);
3943         httpClose(http);
3944
3945         respond_ipp(client, IPP_INTERNAL_ERROR,
3946                     "Unable to write print file: %s", strerror(error));
3947         return;
3948       }
3949     }
3950
3951     httpClose(http);
3952   }
3953
3954   if (close(job->fd))
3955   {
3956     int error = errno;          /* Write error */
3957
3958     job->state = IPP_JOB_ABORTED;
3959     job->fd    = -1;
3960
3961     unlink(filename);
3962
3963     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
3964                 strerror(error));
3965     return;
3966   }
3967
3968   _cupsRWLockWrite(&(client->printer->rwlock));
3969
3970   job->fd       = -1;
3971   job->filename = strdup(filename);
3972   job->state    = IPP_JOB_PENDING;
3973
3974   _cupsRWUnlock(&(client->printer->rwlock));
3975
3976  /*
3977   * Process the job...
3978   */
3979
3980 #if 0
3981   if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3982   {
3983     job->state = IPP_JOB_ABORTED;
3984     respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
3985     return;
3986   }
3987
3988 #else
3989   process_job(job);
3990 #endif /* 0 */
3991
3992  /*
3993   * Return the job info...
3994   */
3995
3996   respond_ipp(client, IPP_OK, NULL);
3997
3998   ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3999   cupsArrayAdd(ra, "job-id");
4000   cupsArrayAdd(ra, "job-state");
4001   cupsArrayAdd(ra, "job-state-reasons");
4002   cupsArrayAdd(ra, "job-uri");
4003
4004   copy_job_attributes(client, job, ra);
4005   cupsArrayDelete(ra);
4006 }
4007
4008
4009 /*
4010  * 'ipp_validate_job()' - Validate job creation attributes.
4011  */
4012
4013 static void
4014 ipp_validate_job(_ipp_client_t *client) /* I - Client */
4015 {
4016   if (valid_job_attributes(client))
4017     respond_ipp(client, IPP_OK, NULL);
4018 }
4019
4020
4021 /*
4022  * 'process_client()' - Process client requests on a thread.
4023  */
4024
4025 static void *                           /* O - Exit status */
4026 process_client(_ipp_client_t *client)   /* I - Client */
4027 {
4028  /*
4029   * Loop until we are out of requests or timeout (30 seconds)...
4030   */
4031
4032   while (httpWait(&(client->http), 30000))
4033     if (!process_http(client))
4034       break;
4035
4036  /*
4037   * Close the conection to the client and return...
4038   */
4039
4040   delete_client(client);
4041
4042   return (NULL);
4043 }
4044
4045
4046 /*
4047  * 'process_http()' - Process a HTTP request.
4048  */
4049
4050 int                                     /* O - 1 on success, 0 on failure */
4051 process_http(_ipp_client_t *client)     /* I - Client connection */
4052 {
4053   char                  line[4096],     /* Line from client... */
4054                         operation[64],  /* Operation code from socket */
4055                         uri[1024],      /* URI */
4056                         version[64],    /* HTTP version number string */
4057                         *ptr;           /* Pointer into strings */
4058   int                   major, minor;   /* HTTP version numbers */
4059   http_status_t         status;         /* Transfer status */
4060   ipp_state_t           state;          /* State of IPP transfer */
4061
4062
4063  /*
4064   * Abort if we have an error on the connection...
4065   */
4066
4067   if (client->http.error)
4068     return (0);
4069
4070  /*
4071   * Clear state variables...
4072   */
4073
4074   httpClearFields(&(client->http));
4075   ippDelete(client->request);
4076   ippDelete(client->response);
4077
4078   client->http.activity       = time(NULL);
4079   client->http.version        = HTTP_1_1;
4080   client->http.keep_alive     = HTTP_KEEPALIVE_OFF;
4081   client->http.data_encoding  = HTTP_ENCODE_LENGTH;
4082   client->http.data_remaining = 0;
4083   client->request             = NULL;
4084   client->response            = NULL;
4085   client->operation           = HTTP_WAITING;
4086
4087  /*
4088   * Read a request from the connection...
4089   */
4090
4091   while ((ptr = httpGets(line, sizeof(line) - 1, &(client->http))) != NULL)
4092     if (*ptr)
4093       break;
4094
4095   if (!ptr)
4096     return (0);
4097
4098  /*
4099   * Parse the request line...
4100   */
4101
4102   fprintf(stderr, "%s %s\n", client->http.hostname, line);
4103
4104   switch (sscanf(line, "%63s%1023s%63s", operation, uri, version))
4105   {
4106     case 1 :
4107         fprintf(stderr, "%s Bad request line.\n", client->http.hostname);
4108         respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4109         return (0);
4110
4111     case 2 :
4112         client->http.version = HTTP_0_9;
4113         break;
4114
4115     case 3 :
4116         if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
4117         {
4118           fprintf(stderr, "%s Bad HTTP version.\n", client->http.hostname);
4119           respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4120           return (0);
4121         }
4122
4123         if (major < 2)
4124         {
4125           client->http.version = (http_version_t)(major * 100 + minor);
4126           if (client->http.version == HTTP_1_1)
4127             client->http.keep_alive = HTTP_KEEPALIVE_ON;
4128           else
4129             client->http.keep_alive = HTTP_KEEPALIVE_OFF;
4130         }
4131         else
4132         {
4133           respond_http(client, HTTP_NOT_SUPPORTED, NULL, 0);
4134           return (0);
4135         }
4136         break;
4137   }
4138
4139  /*
4140   * Handle full URLs in the request line...
4141   */
4142
4143   if (!strncmp(client->uri, "http:", 5) || !strncmp(client->uri, "ipp:", 4))
4144   {
4145     char        scheme[32],             /* Method/scheme */
4146                 userpass[128],          /* Username:password */
4147                 hostname[HTTP_MAX_HOST];/* Hostname */
4148     int         port;                   /* Port number */
4149
4150    /*
4151     * Separate the URI into its components...
4152     */
4153
4154     if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
4155                         userpass, sizeof(userpass),
4156                         hostname, sizeof(hostname), &port,
4157                         client->uri, sizeof(client->uri)) < HTTP_URI_OK)
4158     {
4159       fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
4160       respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4161       return (0);
4162     }
4163   }
4164   else
4165   {
4166    /*
4167     * Decode URI
4168     */
4169
4170     if (!_httpDecodeURI(client->uri, uri, sizeof(client->uri)))
4171     {
4172       fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
4173       respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4174       return (0);
4175     }
4176   }
4177
4178  /*
4179   * Process the request...
4180   */
4181
4182   if (!strcmp(operation, "GET"))
4183     client->http.state = HTTP_GET;
4184   else if (!strcmp(operation, "POST"))
4185     client->http.state = HTTP_POST;
4186   else if (!strcmp(operation, "OPTIONS"))
4187     client->http.state = HTTP_OPTIONS;
4188   else if (!strcmp(operation, "HEAD"))
4189     client->http.state = HTTP_HEAD;
4190   else
4191   {
4192     fprintf(stderr, "%s Bad operation \"%s\".\n", client->http.hostname,
4193             operation);
4194     respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4195     return (0);
4196   }
4197
4198   client->start       = time(NULL);
4199   client->operation   = client->http.state;
4200   client->http.status = HTTP_OK;
4201
4202  /*
4203   * Parse incoming parameters until the status changes...
4204   */
4205
4206   while ((status = httpUpdate(&(client->http))) == HTTP_CONTINUE);
4207
4208   if (status != HTTP_OK)
4209   {
4210     respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4211     return (0);
4212   }
4213
4214   if (!client->http.fields[HTTP_FIELD_HOST][0] &&
4215       client->http.version >= HTTP_1_1)
4216   {
4217    /*
4218     * HTTP/1.1 and higher require the "Host:" field...
4219     */
4220
4221     respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4222     return (0);
4223   }
4224
4225  /*
4226   * Handle HTTP Upgrade...
4227   */
4228
4229   if (!_cups_strcasecmp(client->http.fields[HTTP_FIELD_CONNECTION], "Upgrade"))
4230   {
4231     if (!respond_http(client, HTTP_NOT_IMPLEMENTED, NULL, 0))
4232       return (0);
4233   }
4234
4235  /*
4236   * Handle HTTP Expect...
4237   */
4238
4239   if (client->http.expect &&
4240       (client->operation == HTTP_POST || client->operation == HTTP_PUT))
4241   {
4242     if (client->http.expect == HTTP_CONTINUE)
4243     {
4244      /*
4245       * Send 100-continue header...
4246       */
4247
4248       if (!respond_http(client, HTTP_CONTINUE, NULL, 0))
4249         return (0);
4250     }
4251     else
4252     {
4253      /*
4254       * Send 417-expectation-failed header...
4255       */
4256
4257       if (!respond_http(client, HTTP_EXPECTATION_FAILED, NULL, 0))
4258         return (0);
4259
4260       httpPrintf(&(client->http), "Content-Length: 0\r\n");
4261       httpPrintf(&(client->http), "\r\n");
4262       httpFlushWrite(&(client->http));
4263       client->http.data_encoding = HTTP_ENCODE_LENGTH;
4264     }
4265   }
4266
4267  /*
4268   * Handle new transfers...
4269   */
4270
4271   switch (client->operation)
4272   {
4273     case HTTP_OPTIONS :
4274        /*
4275         * Do HEAD/OPTIONS command...
4276         */
4277
4278         return (respond_http(client, HTTP_OK, NULL, 0));
4279
4280     case HTTP_HEAD :
4281         if (!strcmp(client->uri, "/icon.png"))
4282           return (respond_http(client, HTTP_OK, "image/png", 0));
4283         else if (!strcmp(client->uri, "/"))
4284           return (respond_http(client, HTTP_OK, "text/html", 0));
4285         else
4286           return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
4287         break;
4288
4289     case HTTP_GET :
4290         if (!strcmp(client->uri, "/icon.png"))
4291         {
4292          /*
4293           * Send PNG icon file.
4294           */
4295
4296           int           fd;             /* Icon file */
4297           struct stat   fileinfo;       /* Icon file information */
4298           char          buffer[4096];   /* Copy buffer */
4299           ssize_t       bytes;          /* Bytes */
4300
4301           if (!stat(client->printer->icon, &fileinfo) &&
4302               (fd = open(client->printer->icon, O_RDONLY)) >= 0)
4303           {
4304             if (!respond_http(client, HTTP_OK, "image/png", fileinfo.st_size))
4305             {
4306               close(fd);
4307               return (0);
4308             }
4309
4310             while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
4311               httpWrite2(&(client->http), buffer, bytes);
4312
4313             httpFlushWrite(&(client->http));
4314
4315             close(fd);
4316           }
4317           else
4318             return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
4319         }
4320         else if (!strcmp(client->uri, "/"))
4321         {
4322          /*
4323           * Show web status page...
4324           */
4325
4326           if (!respond_http(client, HTTP_OK, "text/html", 0))
4327             return (0);
4328
4329           html_printf(client,
4330                       "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4331                       "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4332                       "<html>\n"
4333                       "<head>\n"
4334                       "<title>%s</title>\n"
4335                       "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4336                       "type=\"image/png\">\n"
4337                       "</head>\n"
4338                       "<body>\n"
4339                       "</body>\n"
4340                       "<h1>%s</h1>\n"
4341                       "<p>%s, %d job(s).</p>\n"
4342                       "</body>\n"
4343                       "</html>\n",
4344                       client->printer->name, client->printer->name,
4345                       client->printer->state == IPP_PRINTER_IDLE ? "Idle" :
4346                           client->printer->state == IPP_PRINTER_PROCESSING ?
4347                           "Printing" : "Stopped",
4348                       cupsArrayCount(client->printer->jobs));
4349           httpWrite2(&(client->http), "", 0);
4350
4351           return (1);
4352         }
4353         else
4354           return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
4355         break;
4356
4357     case HTTP_POST :
4358         if (client->http.data_remaining < 0 ||
4359             (!client->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
4360              client->http.data_encoding == HTTP_ENCODE_LENGTH))
4361         {
4362          /*
4363           * Negative content lengths are invalid...
4364           */
4365
4366           return (respond_http(client, HTTP_BAD_REQUEST, NULL, 0));
4367         }
4368
4369         if (strcmp(client->http.fields[HTTP_FIELD_CONTENT_TYPE],
4370                    "application/ipp"))
4371         {
4372          /*
4373           * Not an IPP request...
4374           */
4375
4376           return (respond_http(client, HTTP_BAD_REQUEST, NULL, 0));
4377         }
4378
4379        /*
4380         * Read the IPP request...
4381         */
4382
4383         client->request = ippNew();
4384
4385         while ((state = ippRead(&(client->http), client->request)) != IPP_DATA)
4386           if (state == IPP_ERROR)
4387           {
4388             fprintf(stderr, "%s IPP read error (%s).\n", client->http.hostname,
4389                     cupsLastErrorString());
4390             respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4391             return (0);
4392           }
4393
4394        /*
4395         * Now that we have the IPP request, process the request...
4396         */
4397
4398         return (process_ipp(client));
4399
4400     default :
4401         break; /* Anti-compiler-warning-code */
4402   }
4403
4404   return (1);
4405 }
4406
4407
4408 /*
4409  * 'process_ipp()' - Process an IPP request.
4410  */
4411
4412 static int                              /* O - 1 on success, 0 on error */
4413 process_ipp(_ipp_client_t *client)      /* I - Client */
4414 {
4415   ipp_tag_t             group;          /* Current group tag */
4416   ipp_attribute_t       *attr;          /* Current attribute */
4417   ipp_attribute_t       *charset;       /* Character set attribute */
4418   ipp_attribute_t       *language;      /* Language attribute */
4419   ipp_attribute_t       *uri;           /* Printer URI attribute */
4420
4421
4422   debug_attributes("Request", client->request, 1);
4423
4424  /*
4425   * First build an empty response message for this request...
4426   */
4427
4428   client->operation_id = client->request->request.op.operation_id;
4429   client->response     = ippNew();
4430
4431   client->response->request.status.version[0] =
4432       client->request->request.op.version[0];
4433   client->response->request.status.version[1] =
4434       client->request->request.op.version[1];
4435   client->response->request.status.request_id =
4436       client->request->request.op.request_id;
4437
4438  /*
4439   * Then validate the request header and required attributes...
4440   */
4441
4442   if (client->request->request.any.version[0] < 1 ||
4443       client->request->request.any.version[0] > 2)
4444   {
4445    /*
4446     * Return an error, since we only support IPP 1.x and 2.x.
4447     */
4448
4449     respond_ipp(client, IPP_VERSION_NOT_SUPPORTED,
4450                 "Bad request version number %d.%d.",
4451                 client->request->request.any.version[0],
4452                 client->request->request.any.version[1]);
4453   }
4454   else if (client->request->request.any.request_id <= 0)
4455     respond_ipp(client, IPP_BAD_REQUEST, "Bad request-id %d.",
4456                 client->request->request.any.request_id);
4457   else if (!client->request->attrs)
4458     respond_ipp(client, IPP_BAD_REQUEST, "No attributes in request.");
4459   else
4460   {
4461    /*
4462     * Make sure that the attributes are provided in the correct order and
4463     * don't repeat groups...
4464     */
4465
4466     for (attr = client->request->attrs, group = attr->group_tag;
4467          attr;
4468          attr = attr->next)
4469       if (attr->group_tag < group && attr->group_tag != IPP_TAG_ZERO)
4470       {
4471        /*
4472         * Out of order; return an error...
4473         */
4474
4475         respond_ipp(client, IPP_BAD_REQUEST,
4476                        "Attribute groups are out of order (%x < %x).",
4477                        attr->group_tag, group);
4478         break;
4479       }
4480       else
4481         group = attr->group_tag;
4482
4483     if (!attr)
4484     {
4485      /*
4486       * Then make sure that the first three attributes are:
4487       *
4488       *     attributes-charset
4489       *     attributes-natural-language
4490       *     printer-uri/job-uri
4491       */
4492
4493       attr = client->request->attrs;
4494       if (attr && attr->name &&
4495           !strcmp(attr->name, "attributes-charset") &&
4496           (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET)
4497         charset = attr;
4498       else
4499         charset = NULL;
4500
4501       if (attr)
4502         attr = attr->next;
4503
4504       if (attr && attr->name &&
4505           !strcmp(attr->name, "attributes-natural-language") &&
4506           (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
4507         language = attr;
4508       else
4509         language = NULL;
4510
4511       if ((attr = ippFindAttribute(client->request, "printer-uri",
4512                                    IPP_TAG_URI)) != NULL)
4513         uri = attr;
4514       else if ((attr = ippFindAttribute(client->request, "job-uri",
4515                                         IPP_TAG_URI)) != NULL)
4516         uri = attr;
4517       else
4518         uri = NULL;
4519
4520       ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
4521                    "attributes-charset", NULL,
4522                    charset ? charset->values[0].string.text : "utf-8");
4523
4524       ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
4525                    "attributes-natural-language", NULL,
4526                    language ? language->values[0].string.text : "en");
4527
4528       if (charset &&
4529           _cups_strcasecmp(charset->values[0].string.text, "us-ascii") &&
4530           _cups_strcasecmp(charset->values[0].string.text, "utf-8"))
4531       {
4532        /*
4533         * Bad character set...
4534         */
4535
4536         respond_ipp(client, IPP_BAD_REQUEST,
4537                     "Unsupported character set \"%s\".",
4538                     charset->values[0].string.text);
4539       }
4540       else if (!charset || !language || !uri)
4541       {
4542        /*
4543         * Return an error, since attributes-charset,
4544         * attributes-natural-language, and printer-uri/job-uri are required
4545         * for all operations.
4546         */
4547
4548         respond_ipp(client, IPP_BAD_REQUEST, "Missing required attributes.");
4549       }
4550       else if (strcmp(uri->values[0].string.text, client->printer->uri) &&
4551                strncmp(uri->values[0].string.text, client->printer->uri,
4552                        client->printer->urilen))
4553       {
4554         respond_ipp(client, IPP_NOT_FOUND, "%s %s not found.", uri->name,
4555                     uri->values[0].string.text);
4556       }
4557       else
4558       {
4559        /*
4560         * Try processing the operation...
4561         */
4562
4563         if (client->http.expect == HTTP_CONTINUE)
4564         {
4565          /*
4566           * Send 100-continue header...
4567           */
4568
4569           if (!respond_http(client, HTTP_CONTINUE, NULL, 0))
4570             return (0);
4571         }
4572
4573         switch (client->request->request.op.operation_id)
4574         {
4575           case IPP_PRINT_JOB :
4576               ipp_print_job(client);
4577               break;
4578
4579           case IPP_PRINT_URI :
4580               ipp_print_uri(client);
4581               break;
4582
4583           case IPP_VALIDATE_JOB :
4584               ipp_validate_job(client);
4585               break;
4586
4587           case IPP_CREATE_JOB :
4588               ipp_create_job(client);
4589               break;
4590
4591           case IPP_SEND_DOCUMENT :
4592               ipp_send_document(client);
4593               break;
4594
4595           case IPP_SEND_URI :
4596               ipp_send_uri(client);
4597               break;
4598
4599           case IPP_CANCEL_JOB :
4600               ipp_cancel_job(client);
4601               break;
4602
4603           case IPP_GET_JOB_ATTRIBUTES :
4604               ipp_get_job_attributes(client);
4605               break;
4606
4607           case IPP_GET_JOBS :
4608               ipp_get_jobs(client);
4609               break;
4610
4611           case IPP_GET_PRINTER_ATTRIBUTES :
4612               ipp_get_printer_attributes(client);
4613               break;
4614
4615           default :
4616               respond_ipp(client, IPP_OPERATION_NOT_SUPPORTED,
4617                           "Operation not supported.");
4618               break;
4619         }
4620       }
4621     }
4622   }
4623
4624  /*
4625   * Send the HTTP header and return...
4626   */
4627
4628   if (client->http.state != HTTP_POST_SEND)
4629     httpFlush(&(client->http));         /* Flush trailing (junk) data */
4630
4631   return (respond_http(client, HTTP_OK, "application/ipp",
4632                        ippLength(client->response)));
4633 }
4634
4635
4636 /*
4637  * 'process_job()' - Process a print job.
4638  */
4639
4640 static void *                           /* O - Thread exit status */
4641 process_job(_ipp_job_t *job)            /* I - Job */
4642 {
4643   job->state          = IPP_JOB_PROCESSING;
4644   job->printer->state = IPP_PRINTER_PROCESSING;
4645
4646   sleep(5);
4647
4648   if (job->cancel)
4649     job->state = IPP_JOB_CANCELED;
4650   else
4651     job->state = IPP_JOB_COMPLETED;
4652
4653   job->completed           = time(NULL);
4654   job->printer->state      = IPP_PRINTER_IDLE;
4655   job->printer->active_job = NULL;
4656
4657   return (NULL);
4658 }
4659
4660
4661 #ifdef HAVE_DNSSD
4662 /*
4663  * 'register_printer()' - Register a printer object via Bonjour.
4664  */
4665
4666 static int                              /* O - 1 on success, 0 on error */
4667 register_printer(
4668     _ipp_printer_t *printer,            /* I - Printer */
4669     const char     *location,           /* I - Location */
4670     const char     *make,               /* I - Manufacturer */
4671     const char     *model,              /* I - Model name */
4672     const char     *formats,            /* I - Supported formats */
4673     const char     *adminurl,           /* I - Web interface URL */
4674     int            color,               /* I - 1 = color, 0 = monochrome */
4675     int            duplex,              /* I - 1 = duplex, 0 = simplex */
4676     const char     *regtype)            /* I - Service type */
4677 {
4678   DNSServiceErrorType   error;          /* Error from Bonjour */
4679   char                  make_model[256],/* Make and model together */
4680                         product[256];   /* Product string */
4681
4682
4683  /*
4684   * Build the TXT record for IPP...
4685   */
4686
4687   snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4688   snprintf(product, sizeof(product), "(%s)", model);
4689
4690   TXTRecordCreate(&(printer->ipp_txt), 1024, NULL);
4691   TXTRecordSetValue(&(printer->ipp_txt), "txtvers", 1, "1");
4692   TXTRecordSetValue(&(printer->ipp_txt), "qtotal", 1, "1");
4693   TXTRecordSetValue(&(printer->ipp_txt), "rp", 3, "ipp");
4694   TXTRecordSetValue(&(printer->ipp_txt), "ty", (uint8_t)strlen(make_model),
4695                     make_model);
4696   TXTRecordSetValue(&(printer->ipp_txt), "adminurl", (uint8_t)strlen(adminurl),
4697                     adminurl);
4698   TXTRecordSetValue(&(printer->ipp_txt), "note", (uint8_t)strlen(location),
4699                     location);
4700   TXTRecordSetValue(&(printer->ipp_txt), "priority", 1, "0");
4701   TXTRecordSetValue(&(printer->ipp_txt), "product", (uint8_t)strlen(product),
4702                     product);
4703   TXTRecordSetValue(&(printer->ipp_txt), "pdl", (uint8_t)strlen(formats),
4704                     formats);
4705   TXTRecordSetValue(&(printer->ipp_txt), "Color", 1, color ? "T" : "F");
4706   TXTRecordSetValue(&(printer->ipp_txt), "Duplex", 1, duplex ? "T" : "F");
4707   TXTRecordSetValue(&(printer->ipp_txt), "usb_MFG", (uint8_t)strlen(make),
4708                     make);
4709   TXTRecordSetValue(&(printer->ipp_txt), "usb_MDL", (uint8_t)strlen(model),
4710                     model);
4711   TXTRecordSetValue(&(printer->ipp_txt), "air", 4, "none");
4712
4713  /*
4714   * Create a shared service reference for Bonjour...
4715   */
4716
4717   if ((error = DNSServiceCreateConnection(&(printer->common_ref)))
4718           != kDNSServiceErr_NoError)
4719   {
4720     fprintf(stderr, "Unable to create mDNSResponder connection: %d\n", error);
4721     return (0);
4722   }
4723
4724  /*
4725   * Register the _printer._tcp (LPD) service type with a port number of 0 to
4726   * defend our service name but not actually support LPD...
4727   */
4728
4729   printer->printer_ref = printer->common_ref;
4730
4731   if ((error = DNSServiceRegister(&(printer->printer_ref),
4732                                   kDNSServiceFlagsShareConnection,
4733                                   0 /* interfaceIndex */, printer->dnssd_name,
4734                                   "_printer._tcp", NULL /* domain */,
4735                                   NULL /* host */, 0 /* port */, 0 /* txtLen */,
4736                                   NULL /* txtRecord */,
4737                                   (DNSServiceRegisterReply)dnssd_callback,
4738                                   printer)) != kDNSServiceErr_NoError)
4739   {
4740     fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
4741             printer->dnssd_name, error);
4742     return (0);
4743   }
4744
4745  /*
4746   * Then register the _ipp._tcp (IPP) service type with the real port number to
4747   * advertise our IPP printer...
4748   */
4749
4750   printer->ipp_ref = printer->common_ref;
4751
4752   if ((error = DNSServiceRegister(&(printer->ipp_ref),
4753                                   kDNSServiceFlagsShareConnection,
4754                                   0 /* interfaceIndex */, printer->dnssd_name,
4755                                   regtype, NULL /* domain */,
4756                                   NULL /* host */, htons(printer->port),
4757                                   TXTRecordGetLength(&(printer->ipp_txt)),
4758                                   TXTRecordGetBytesPtr(&(printer->ipp_txt)),
4759                                   (DNSServiceRegisterReply)dnssd_callback,
4760                                   printer)) != kDNSServiceErr_NoError)
4761   {
4762     fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4763             printer->dnssd_name, regtype, error);
4764     return (0);
4765   }
4766
4767  /*
4768   * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4769   * real port number to advertise our IPP printer...
4770   */
4771
4772   printer->http_ref = printer->common_ref;
4773
4774   if ((error = DNSServiceRegister(&(printer->http_ref),
4775                                   kDNSServiceFlagsShareConnection,
4776                                   0 /* interfaceIndex */, printer->dnssd_name,
4777                                   "_http._tcp,_printer", NULL /* domain */,
4778                                   NULL /* host */, htons(printer->port),
4779                                   0 /* txtLen */, NULL, /* txtRecord */
4780                                   (DNSServiceRegisterReply)dnssd_callback,
4781                                   printer)) != kDNSServiceErr_NoError)
4782   {
4783     fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4784             printer->dnssd_name, regtype, error);
4785     return (0);
4786   }
4787
4788   return (1);
4789 }
4790 #endif /* HAVE_DNSSD */
4791
4792
4793 /*
4794  * 'respond_http()' - Send a HTTP response.
4795  */
4796
4797 int                                     /* O - 1 on success, 0 on failure */
4798 respond_http(_ipp_client_t *client,     /* I - Client */
4799              http_status_t code,        /* I - HTTP status of response */
4800              const char    *type,       /* I - MIME type of response */
4801              size_t        length)      /* I - Length of response */
4802 {
4803   char  message[1024];                  /* Text message */
4804
4805
4806   fprintf(stderr, "%s %s\n", client->http.hostname, httpStatus(code));
4807
4808   if (code == HTTP_CONTINUE)
4809   {
4810    /*
4811     * 100-continue doesn't send any headers...
4812     */
4813
4814     return (httpPrintf(&(client->http), "HTTP/%d.%d 100 Continue\r\n\r\n",
4815                        client->http.version / 100,
4816                        client->http.version % 100) > 0);
4817   }
4818
4819  /*
4820   * Format an error message...
4821   */
4822
4823   if (!type && !length && code != HTTP_OK)
4824   {
4825     snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
4826
4827     type   = "text/plain";
4828     length = strlen(message);
4829   }
4830   else
4831     message[0] = '\0';
4832
4833  /*
4834   * Send the HTTP status header...
4835   */
4836
4837   httpFlushWrite(&(client->http));
4838
4839   client->http.data_encoding = HTTP_ENCODE_FIELDS;
4840
4841   if (httpPrintf(&(client->http), "HTTP/%d.%d %d %s\r\n", client->http.version / 100,
4842                  client->http.version % 100, code, httpStatus(code)) < 0)
4843     return (0);
4844
4845  /*
4846   * Follow the header with the response fields...
4847   */
4848
4849   if (httpPrintf(&(client->http), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
4850     return (0);
4851
4852   if (client->http.keep_alive && client->http.version >= HTTP_1_0)
4853   {
4854     if (httpPrintf(&(client->http),
4855                    "Connection: Keep-Alive\r\n"
4856                    "Keep-Alive: timeout=10\r\n") < 0)
4857       return (0);
4858   }
4859
4860   if (code == HTTP_METHOD_NOT_ALLOWED || client->operation == HTTP_OPTIONS)
4861   {
4862     if (httpPrintf(&(client->http), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
4863       return (0);
4864   }
4865
4866   if (type)
4867   {
4868     if (!strcmp(type, "text/html"))
4869     {
4870       if (httpPrintf(&(client->http),
4871                      "Content-Type: text/html; charset=utf-8\r\n") < 0)
4872         return (0);
4873     }
4874     else if (httpPrintf(&(client->http), "Content-Type: %s\r\n", type) < 0)
4875       return (0);
4876   }
4877
4878   if (length == 0 && !message[0])
4879   {
4880     if (httpPrintf(&(client->http), "Transfer-Encoding: chunked\r\n\r\n") < 0)
4881       return (0);
4882   }
4883   else if (httpPrintf(&(client->http), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
4884                       CUPS_LLCAST length) < 0)
4885     return (0);
4886
4887   if (httpFlushWrite(&(client->http)) < 0)
4888     return (0);
4889
4890  /*
4891   * Send the response data...
4892   */
4893
4894   if (message[0])
4895   {
4896    /*
4897     * Send a plain text message.
4898     */
4899
4900     if (httpPrintf(&(client->http), "%s", message) < 0)
4901       return (0);
4902   }
4903   else if (client->response)
4904   {
4905    /*
4906     * Send an IPP response...
4907     */
4908
4909     debug_attributes("Response", client->response, 2);
4910
4911     client->http.data_encoding  = HTTP_ENCODE_LENGTH;
4912     client->http.data_remaining = (off_t)ippLength(client->response);
4913     client->response->state     = IPP_IDLE;
4914
4915     if (ippWrite(&(client->http), client->response) != IPP_DATA)
4916       return (0);
4917   }
4918   else
4919     client->http.data_encoding = HTTP_ENCODE_CHUNKED;
4920
4921  /*
4922   * Flush the data and return...
4923   */
4924
4925   return (httpFlushWrite(&(client->http)) >= 0);
4926 }
4927
4928
4929 /*
4930  * 'respond_ipp()' - Send an IPP response.
4931  */
4932
4933 static void
4934 respond_ipp(_ipp_client_t *client,      /* I - Client */
4935             ipp_status_t  status,       /* I - status-code */
4936             const char    *message,     /* I - printf-style status-message */
4937             ...)                        /* I - Additional args as needed */
4938 {
4939   va_list       ap;                     /* Pointer to additional args */
4940   char          formatted[1024];        /* Formatted errror message */
4941
4942
4943   client->response->request.status.status_code = status;
4944
4945   if (!client->response->attrs)
4946   {
4947     ippAddString(client->response, IPP_TAG_OPERATION,
4948                  IPP_TAG_CHARSET | IPP_TAG_COPY, "attributes-charset", NULL,
4949                  "utf-8");
4950     ippAddString(client->response, IPP_TAG_OPERATION,
4951                  IPP_TAG_LANGUAGE | IPP_TAG_COPY, "attributes-natural-language",
4952                  NULL, "en-us");
4953   }
4954
4955   if (message)
4956   {
4957     va_start(ap, message);
4958     vsnprintf(formatted, sizeof(formatted), message, ap);
4959     va_end(ap);
4960
4961     ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
4962                  "status-message", NULL, formatted);
4963   }
4964   else
4965     formatted[0] = '\0';
4966
4967   fprintf(stderr, "%s %s %s (%s)\n", client->http.hostname,
4968           ippOpString(client->operation_id), ippErrorString(status), formatted);
4969 }
4970
4971
4972 /*
4973  * 'respond_unsupported()' - Respond with an unsupported attribute.
4974  */
4975
4976 static void
4977 respond_unsupported(
4978     _ipp_client_t   *client,            /* I - Client */
4979     ipp_attribute_t *attr)              /* I - Atribute */
4980 {
4981   if (!client->response->attrs)
4982     respond_ipp(client, IPP_ATTRIBUTES, "Unsupported %s %s%s value.",
4983                 attr->name, attr->num_values > 1 ? "1setOf " : "",
4984                 ippTagString(attr->value_tag));
4985
4986   copy_attribute(client->response, attr, IPP_TAG_UNSUPPORTED_GROUP, 0);
4987 }
4988
4989
4990 /*
4991  * 'run_printer()' - Run the printer service.
4992  */
4993
4994 static void
4995 run_printer(_ipp_printer_t *printer)    /* I - Printer */
4996 {
4997   int           num_fds;                /* Number of file descriptors */
4998   struct pollfd polldata[3];            /* poll() data */
4999   int           timeout;                /* Timeout for poll() */
5000   _ipp_client_t *client;                /* New client */
5001
5002
5003  /*
5004   * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5005   */
5006
5007   polldata[0].fd     = printer->ipv4;
5008   polldata[0].events = POLLIN;
5009
5010   polldata[1].fd     = printer->ipv6;
5011   polldata[1].events = POLLIN;
5012
5013   num_fds = 2;
5014
5015 #ifdef HAVE_DNSSD
5016   polldata[num_fds   ].fd     = DNSServiceRefSockFD(printer->common_ref);
5017   polldata[num_fds ++].events = POLLIN;
5018 #endif /* HAVE_DNSSD */
5019
5020  /*
5021   * Loop until we are killed or have a hard error...
5022   */
5023
5024   for (;;)
5025   {
5026     if (cupsArrayCount(printer->jobs))
5027       timeout = 10;
5028     else
5029       timeout = -1;
5030
5031     if (poll(polldata, num_fds, timeout) < 0 && errno != EINTR)
5032     {
5033       perror("poll() failed");
5034       break;
5035     }
5036
5037     if (polldata[0].revents & POLLIN)
5038     {
5039       if ((client = create_client(printer, printer->ipv4)) != NULL)
5040       {
5041         if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
5042         {
5043           perror("Unable to create client thread");
5044           delete_client(client);
5045         }
5046       }
5047     }
5048
5049     if (polldata[1].revents & POLLIN)
5050     {
5051       if ((client = create_client(printer, printer->ipv6)) != NULL)
5052       {
5053         if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
5054         {
5055           perror("Unable to create client thread");
5056           delete_client(client);
5057         }
5058       }
5059     }
5060
5061 #ifdef HAVE_DNSSD
5062     if (polldata[2].revents & POLLIN)
5063       DNSServiceProcessResult(printer->common_ref);
5064 #endif /* HAVE_DNSSD */
5065
5066    /*
5067     * Clean out old jobs...
5068     */
5069
5070     clean_jobs(printer);
5071   }
5072 }
5073
5074
5075 /*
5076  * 'usage()' - Show program usage.
5077  */
5078
5079 static void
5080 usage(int status)                       /* O - Exit status */
5081 {
5082   if (!status)
5083   {
5084     puts(CUPS_SVERSION " - Copyright 2010 by Apple Inc. All rights reserved.");
5085     puts("");
5086   }
5087
5088   puts("Usage: ippserver [options] \"name\"");
5089   puts("");
5090   puts("Options:");
5091   puts("-2                      Supports 2-sided printing (default=1-sided)");
5092   puts("-M manufacturer         Manufacturer name (default=Test)");
5093   printf("-d spool-directory      Spool directory "
5094          "(default=/tmp/ippserver.%d)\n", (int)getpid());
5095   puts("-f type/subtype[,...]   List of supported types "
5096        "(default=application/pdf,image/jpeg)");
5097   puts("-h                      Show program help");
5098   puts("-i iconfile.png         PNG icon file (default=printer.png)");
5099   puts("-l location             Location of printer (default=empty string)");
5100   puts("-m model                Model name (default=Printer)");
5101   puts("-n hostname             Hostname for printer");
5102   puts("-p port                 Port number (default=auto)");
5103   puts("-r regtype              Bonjour service type (default=_ipp._tcp)");
5104   puts("-s speed[,color-speed]  Speed in pages per minute (default=10,0)");
5105   puts("-v[vvv]                 Be (very) verbose");
5106
5107   exit(status);
5108 }
5109
5110
5111 /*
5112  * 'valid_doc_attributes()' - Determine whether the document attributes are
5113  *                            valid.
5114  *
5115  * When one or more document attributes are invalid, this function adds a
5116  * suitable response and attributes to the unsupported group.
5117  */
5118
5119 static int                              /* O - 1 if valid, 0 if not */
5120 valid_doc_attributes(
5121     _ipp_client_t *client)              /* I - Client */
5122 {
5123   int                   i;              /* Looping var */
5124   ipp_attribute_t       *attr,          /* Current attribute */
5125                         *supported;     /* document-format-supported */
5126   const char            *format = NULL; /* document-format value */
5127
5128
5129  /*
5130   * Check operation attributes...
5131   */
5132
5133   if ((attr = ippFindAttribute(client->request, "compression",
5134                                IPP_TAG_ZERO)) != NULL)
5135   {
5136    /*
5137     * If compression is specified, only accept "none"...
5138     */
5139
5140     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_KEYWORD ||
5141         strcmp(attr->values[0].string.text, "none"))
5142       respond_unsupported(client, attr);
5143     else
5144       fprintf(stderr, "%s %s compression=\"%s\"\n",
5145               client->http.hostname,
5146               ippOpString(client->request->request.op.operation_id),
5147               attr->values[0].string.text);
5148   }
5149
5150  /*
5151   * Is it a format we support?
5152   */
5153
5154   if ((attr = ippFindAttribute(client->request, "document-format",
5155                                IPP_TAG_ZERO)) != NULL)
5156   {
5157     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_MIMETYPE)
5158       respond_unsupported(client, attr);
5159     else
5160     {
5161       format = attr->values[0].string.text;
5162
5163       fprintf(stderr, "%s %s document-format=\"%s\"\n",
5164               client->http.hostname,
5165               ippOpString(client->request->request.op.operation_id), format);
5166     }
5167   }
5168   else
5169     format = "application/octet-stream";
5170
5171   if (!strcmp(format, "application/octet-stream") &&
5172       (client->request->request.op.operation_id == IPP_PRINT_JOB ||
5173        client->request->request.op.operation_id == IPP_SEND_DOCUMENT))
5174   {
5175    /*
5176     * Auto-type the file using the first 4 bytes of the file...
5177     */
5178
5179     unsigned char       header[4];      /* First 4 bytes of file */
5180
5181     memset(header, 0, sizeof(header));
5182     _httpPeek(&(client->http), (char *)header, sizeof(header));
5183
5184     if (!memcmp(header, "%PDF", 4))
5185       format = "application/pdf";
5186     else if (!memcmp(header, "%!", 2))
5187       format = "application/postscript";
5188     else if (!memcmp(header, "\377\330\377", 3) &&
5189              header[3] >= 0xe0 && header[3] <= 0xef)
5190       format = "image/jpeg";
5191     else if (!memcmp(header, "\211PNG", 4))
5192       format = "image/png";
5193
5194     if (format)
5195       fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
5196               client->http.hostname,
5197               ippOpString(client->request->request.op.operation_id), format);
5198
5199     if (!attr)
5200       attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
5201                           "document-format", NULL, format);
5202     else
5203     {
5204       _cupsStrFree(attr->values[0].string.text);
5205       attr->values[0].string.text = _cupsStrAlloc(format);
5206     }
5207   }
5208
5209   if (client->request->request.op.operation_id != IPP_CREATE_JOB &&
5210       (supported = ippFindAttribute(client->printer->attrs,
5211                                     "document-format-supported",
5212                                     IPP_TAG_MIMETYPE)) != NULL)
5213   {
5214     for (i = 0; i < supported->num_values; i ++)
5215       if (!_cups_strcasecmp(format, supported->values[i].string.text))
5216         break;
5217
5218     if (i >= supported->num_values && attr)
5219       respond_unsupported(client, attr);
5220   }
5221
5222   return (!client->response->attrs ||
5223           !client->response->attrs->next ||
5224           !client->response->attrs->next->next);
5225 }
5226
5227
5228 /*
5229  * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5230  *
5231  * When one or more job attributes are invalid, this function adds a suitable
5232  * response and attributes to the unsupported group.
5233  */
5234
5235 static int                              /* O - 1 if valid, 0 if not */
5236 valid_job_attributes(
5237     _ipp_client_t *client)              /* I - Client */
5238 {
5239   int                   i;              /* Looping var */
5240   ipp_attribute_t       *attr,          /* Current attribute */
5241                         *supported;     /* xxx-supported attribute */
5242
5243
5244  /*
5245   * Check operation attributes...
5246   */
5247
5248   valid_doc_attributes(client);
5249
5250  /*
5251   * Check the various job template attributes...
5252   */
5253
5254   if ((attr = ippFindAttribute(client->request, "copies",
5255                                IPP_TAG_ZERO)) != NULL)
5256   {
5257     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_INTEGER ||
5258         attr->values[0].integer < 1 || attr->values[0].integer > 999)
5259     {
5260       respond_unsupported(client, attr);
5261     }
5262   }
5263
5264   if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity",
5265                                IPP_TAG_ZERO)) != NULL)
5266   {
5267     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_BOOLEAN)
5268     {
5269       respond_unsupported(client, attr);
5270     }
5271   }
5272
5273   if ((attr = ippFindAttribute(client->request, "job-hold-until",
5274                                IPP_TAG_ZERO)) != NULL)
5275   {
5276     if (attr->num_values != 1 ||
5277         (attr->value_tag != IPP_TAG_NAME &&
5278          attr->value_tag != IPP_TAG_NAMELANG &&
5279          attr->value_tag != IPP_TAG_KEYWORD) ||
5280         strcmp(attr->values[0].string.text, "no-hold"))
5281     {
5282       respond_unsupported(client, attr);
5283     }
5284   }
5285
5286   if ((attr = ippFindAttribute(client->request, "job-name",
5287                                IPP_TAG_ZERO)) != NULL)
5288   {
5289     if (attr->num_values != 1 ||
5290         (attr->value_tag != IPP_TAG_NAME &&
5291          attr->value_tag != IPP_TAG_NAMELANG))
5292     {
5293       respond_unsupported(client, attr);
5294     }
5295   }
5296
5297   if ((attr = ippFindAttribute(client->request, "job-priority",
5298                                IPP_TAG_ZERO)) != NULL)
5299   {
5300     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_INTEGER ||
5301         attr->values[0].integer < 1 || attr->values[0].integer > 100)
5302     {
5303       respond_unsupported(client, attr);
5304     }
5305   }
5306
5307   if ((attr = ippFindAttribute(client->request, "job-sheets",
5308                                IPP_TAG_ZERO)) != NULL)
5309   {
5310     if (attr->num_values != 1 ||
5311         (attr->value_tag != IPP_TAG_NAME &&
5312          attr->value_tag != IPP_TAG_NAMELANG &&
5313          attr->value_tag != IPP_TAG_KEYWORD) ||
5314         strcmp(attr->values[0].string.text, "none"))
5315     {
5316       respond_unsupported(client, attr);
5317     }
5318   }
5319
5320   if ((attr = ippFindAttribute(client->request, "media",
5321                                IPP_TAG_ZERO)) != NULL)
5322   {
5323     if (attr->num_values != 1 ||
5324         (attr->value_tag != IPP_TAG_NAME &&
5325          attr->value_tag != IPP_TAG_NAMELANG &&
5326          attr->value_tag != IPP_TAG_KEYWORD))
5327     {
5328       respond_unsupported(client, attr);
5329     }
5330     else
5331     {
5332       for (i = 0;
5333            i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
5334            i ++)
5335         if (!strcmp(attr->values[0].string.text, media_supported[i]))
5336           break;
5337
5338       if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
5339       {
5340         respond_unsupported(client, attr);
5341       }
5342     }
5343   }
5344
5345   if ((attr = ippFindAttribute(client->request, "media-col",
5346                                IPP_TAG_ZERO)) != NULL)
5347   {
5348     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_BEGIN_COLLECTION)
5349     {
5350       respond_unsupported(client, attr);
5351     }
5352     /* TODO: check for valid media-col */
5353   }
5354
5355   if ((attr = ippFindAttribute(client->request, "multiple-document-handling",
5356                                IPP_TAG_ZERO)) != NULL)
5357   {
5358     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_KEYWORD ||
5359         (strcmp(attr->values[0].string.text,
5360                 "separate-documents-uncollated-copies") &&
5361          strcmp(attr->values[0].string.text,
5362                 "separate-documents-collated-copies")))
5363     {
5364       respond_unsupported(client, attr);
5365     }
5366   }
5367
5368   if ((attr = ippFindAttribute(client->request, "orientation-requested",
5369                                IPP_TAG_ZERO)) != NULL)
5370   {
5371     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_ENUM ||
5372         attr->values[0].integer < IPP_PORTRAIT ||
5373         attr->values[0].integer > IPP_REVERSE_PORTRAIT)
5374     {
5375       respond_unsupported(client, attr);
5376     }
5377   }
5378
5379   if ((attr = ippFindAttribute(client->request, "page-ranges",
5380                                IPP_TAG_ZERO)) != NULL)
5381   {
5382     respond_unsupported(client, attr);
5383   }
5384
5385   if ((attr = ippFindAttribute(client->request, "print-quality",
5386                                IPP_TAG_ZERO)) != NULL)
5387   {
5388     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_ENUM ||
5389         attr->values[0].integer < IPP_QUALITY_DRAFT ||
5390         attr->values[0].integer > IPP_QUALITY_HIGH)
5391     {
5392       respond_unsupported(client, attr);
5393     }
5394   }
5395
5396   if ((attr = ippFindAttribute(client->request, "printer-resolution",
5397                                IPP_TAG_ZERO)) != NULL)
5398   {
5399     respond_unsupported(client, attr);
5400   }
5401
5402   if ((attr = ippFindAttribute(client->request, "sides",
5403                                IPP_TAG_ZERO)) != NULL)
5404   {
5405     if (attr->num_values != 1 || attr->value_tag != IPP_TAG_KEYWORD)
5406     {
5407       respond_unsupported(client, attr);
5408     }
5409
5410     if ((supported = ippFindAttribute(client->printer->attrs, "sides",
5411                                       IPP_TAG_KEYWORD)) != NULL)
5412     {
5413       for (i = 0; i < supported->num_values; i ++)
5414         if (!strcmp(attr->values[0].string.text,
5415                     supported->values[i].string.text))
5416           break;
5417
5418       if (i >= supported->num_values)
5419       {
5420         respond_unsupported(client, attr);
5421       }
5422     }
5423     else
5424     {
5425       respond_unsupported(client, attr);
5426     }
5427   }
5428
5429   return (!client->response->attrs ||
5430           !client->response->attrs->next ||
5431           !client->response->attrs->next->next);
5432 }
5433
5434
5435 /*
5436  * End of "$Id: ippserver.c 10031 2011-09-30 05:24:10Z mike $".
5437  */