2 * "$Id: ippserver.c 10031 2011-09-30 05:24:10Z mike $"
4 * Sample IPP/2.0 server for CUPS.
6 * Copyright 2010-2011 by Apple Inc.
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/".
14 * This file is subject to the Apple OS-Developed Software exception.
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
24 * copy_job_attrs() - Copy job attributes to the response.
25 * create_client() - Accept a new network connection and create
27 * create_job() - Create a new job object from a Print-Job or
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
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
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
53 * ipp_print_uri() - Create a job object with a referenced
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
72 * valid_job_attributes() - Determine whether the job attributes are
77 * Include necessary headers...
80 #include <cups/cups-private.h>
83 #endif /* HAVE_DNSSD */
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 */
97 #endif /* HAVE_SYS_VFS_H */
104 enum _ipp_preasons_e /* printer-state-reasons bit values */
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 */
130 typedef unsigned int _ipp_preasons_t; /* Bitfield for printer-state-reasons */
132 typedef enum _ipp_media_class_e
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;
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 */
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 */
167 static const char * const media_type_supported[] =
168 /* media-type-supported values */
175 "photographic-glossy",
176 "photographic-high-gloss",
177 "photographic-matte",
178 "photographic-satin",
179 "photographic-semi-gloss",
181 "stationery-letterhead",
190 typedef struct _ipp_job_s _ipp_job_t;
192 typedef struct _ipp_printer_s /**** Printer data ****/
194 int ipv4, /* IPv4 listener */
195 ipv6; /* IPv6 listener */
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 */
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 */
220 struct _ipp_job_s /**** Job data ****/
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 */
236 typedef struct _ipp_client_s /**** Client data ****/
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 */
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,
273 const char *docformats, int ppm,
274 int ppm_color, int duplex, int port,
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,
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);
286 static void dnssd_callback(DNSServiceRef sdRef,
287 DNSServiceFlags flags,
288 DNSServiceErrorType errorCode,
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,
297 static void html_printf(_ipp_client_t *client, const char *format,
298 ...) __attribute__((__format__(__printf__,
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);
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);
338 static int KeepFiles = 0,
343 * 'main()' - Main entry to the sample server.
346 int /* O - Exit status */
347 main(int argc, /* I - Number of command-line args */
348 char *argv[]) /* I - Command-line arguments */
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 */
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 */
372 * Parse command-line arguments...
375 for (i = 1; i < argc; i ++)
376 if (argv[i][0] == '-')
378 for (opt = argv[i] + 1; *opt; opt ++)
381 case '2' : /* -2 (enable 2-sided printing) */
385 case 'M' : /* -M manufacturer */
392 case 'd' : /* -d spool-directory */
396 strlcpy(directory, argv[i], sizeof(directory));
399 case 'f' : /* -f type/subtype[,...] */
406 case 'h' : /* -h (show help) */
410 case 'i' : /* -i icon.png */
417 case 'k' : /* -k (keep files) */
421 case 'l' : /* -l location */
428 case 'm' : /* -m model */
435 case 'n' : /* -n hostname */
439 servername = argv[i];
442 case 'p' : /* -p port */
444 if (i >= argc || !isdigit(argv[i][0] & 255))
446 port = atoi(argv[i]);
450 case 'r' : /* -r regtype */
456 #endif /* HAVE_DNSSD */
458 case 's' : /* -s speed[,color-speed] */
462 if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1)
466 case 'v' : /* -v (be verbose) */
470 default : /* Unknown */
471 fprintf(stderr, "Unknown option \"-%c\".\n", *opt);
482 fprintf(stderr, "Unexpected command-line argument \"%s\"\n", argv[i]);
490 * Apply defaults as needed...
495 snprintf(directory, sizeof(directory), "/tmp/ippserver.%d", (int)getpid());
497 if (mkdir(directory, 0777) && errno != EEXIST)
499 fprintf(stderr, "Unable to create spool directory \"%s\": %s\n",
500 directory, strerror(errno));
505 fprintf(stderr, "Using spool directory \"%s\".\n", directory);
509 * Create the printer...
512 if ((printer = create_printer(servername, name, location, make, model, icon,
513 formats, ppm, ppm_color, duplex, port,
516 #endif /* HAVE_DNSSD */
521 * Run the print service...
524 run_printer(printer);
527 * Destroy the printer and exit...
530 delete_printer(printer);
537 * 'clean_jobs()' - Clean out old (completed) jobs.
541 clean_jobs(_ipp_printer_t *printer) /* I - Printer */
543 _ipp_job_t *job; /* Current job */
544 time_t cleantime; /* Clean time */
547 if (cupsArrayCount(printer->jobs) == 0)
550 cleantime = time(NULL) - 60;
552 _cupsRWLockWrite(&(printer->rwlock));
553 for (job = (_ipp_job_t *)cupsArrayFirst(printer->jobs);
555 job = (_ipp_job_t *)cupsArrayNext(printer->jobs))
556 if (job->completed && job->completed < cleantime)
558 cupsArrayRemove(printer->jobs, job);
563 _cupsRWUnlock(&(printer->rwlock));
568 * 'compare_jobs()' - Compare two jobs.
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 */
575 return (b->id - a->id);
580 * 'copy_attribute()' - Copy a single attribute.
583 static ipp_attribute_t * /* O - New 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? */
590 int i; /* Looping var */
591 ipp_attribute_t *toattr; /* Destination attribute */
594 if (Verbosity && attr->name)
596 char buffer[2048]; /* Attribute value */
598 _ippAttrString(attr, buffer, sizeof(buffer));
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);
605 switch (attr->value_tag & ~IPP_TAG_COPY)
608 toattr = ippAddSeparator(to);
611 case IPP_TAG_INTEGER :
613 toattr = ippAddIntegers(to, group_tag, attr->value_tag,
614 attr->name, attr->num_values, NULL);
616 for (i = 0; i < attr->num_values; i ++)
617 toattr->values[i].integer = attr->values[i].integer;
620 case IPP_TAG_BOOLEAN :
621 toattr = ippAddBooleans(to, group_tag, attr->name,
622 attr->num_values, NULL);
624 for (i = 0; i < attr->num_values; i ++)
625 toattr->values[i].boolean = attr->values[i].boolean;
630 case IPP_TAG_KEYWORD :
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);
642 for (i = 0; i < attr->num_values; i ++)
643 toattr->values[i].string.text = attr->values[i].string.text;
647 for (i = 0; i < attr->num_values; i ++)
648 toattr->values[i].string.text =
649 _cupsStrAlloc(attr->values[i].string.text);
654 toattr = ippAddDate(to, group_tag, attr->name,
655 attr->values[0].date);
658 case IPP_TAG_RESOLUTION :
659 toattr = ippAddResolutions(to, group_tag, attr->name,
660 attr->num_values, IPP_RES_PER_INCH,
663 for (i = 0; i < attr->num_values; i ++)
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;
672 toattr = ippAddRanges(to, group_tag, attr->name,
673 attr->num_values, NULL, NULL);
675 for (i = 0; i < attr->num_values; i ++)
677 toattr->values[i].range.lower = attr->values[i].range.lower;
678 toattr->values[i].range.upper = attr->values[i].range.upper;
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);
690 for (i = 0; i < attr->num_values; i ++)
692 toattr->values[i].string.charset = attr->values[i].string.charset;
693 toattr->values[i].string.text = attr->values[i].string.text;
698 for (i = 0; i < attr->num_values; i ++)
701 toattr->values[i].string.charset =
702 _cupsStrAlloc(attr->values[i].string.charset);
704 toattr->values[i].string.charset =
705 toattr->values[0].string.charset;
707 toattr->values[i].string.text =
708 _cupsStrAlloc(attr->values[i].string.text);
713 case IPP_TAG_BEGIN_COLLECTION :
714 toattr = ippAddCollections(to, group_tag, attr->name,
715 attr->num_values, NULL);
717 for (i = 0; i < attr->num_values; i ++)
719 toattr->values[i].collection = attr->values[i].collection;
720 attr->values[i].collection->use ++;
724 case IPP_TAG_STRING :
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;
733 toattr = ippAddOctetString(to, attr->group_tag, attr->name,
734 attr->values[0].unknown.data,
735 attr->values[0].unknown.length);
739 toattr = ippAddIntegers(to, group_tag, attr->value_tag,
740 attr->name, attr->num_values, NULL);
742 for (i = 0; i < attr->num_values; i ++)
744 toattr->values[i].unknown.length = attr->values[i].unknown.length;
746 if (toattr->values[i].unknown.length > 0)
748 if ((toattr->values[i].unknown.data =
749 malloc(toattr->values[i].unknown.length)) == NULL)
750 toattr->values[i].unknown.length = 0;
752 memcpy(toattr->values[i].unknown.data,
753 attr->values[i].unknown.data,
754 toattr->values[i].unknown.length);
757 break; /* anti-compiler-warning-code */
765 * 'copy_attributes()' - Copy attributes from one request to another.
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? */
775 ipp_attribute_t *fromattr; /* Source attribute */
781 for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
784 * Filter attributes as needed...
787 if ((group_tag != IPP_TAG_ZERO && fromattr->group_tag != group_tag &&
788 fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
791 if (!ra || cupsArrayFind(ra, fromattr->name))
792 copy_attribute(to, fromattr, fromattr->group_tag, quickcopy);
798 * 'copy_job_attrs()' - Copy job attributes to the response.
803 _ipp_client_t *client, /* I - Client */
804 _ipp_job_t *job, /* I - Job */
805 cups_array_t *ra) /* I - requested-attributes */
807 copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
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));
813 if (!ra || cupsArrayFind(ra, "job-state"))
814 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM,
815 "job-state", job->state);
817 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
821 case IPP_JOB_PENDING :
822 ippAddString(client->response, IPP_TAG_JOB,
823 IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
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");
837 ippAddString(client->response, IPP_TAG_JOB,
838 IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
839 NULL, "job-data-insufficient");
842 case IPP_JOB_PROCESSING :
844 ippAddString(client->response, IPP_TAG_JOB,
845 IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
846 NULL, "processing-to-stop-point");
848 ippAddString(client->response, IPP_TAG_JOB,
849 IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons",
850 NULL, "job-printing");
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");
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");
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");
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");
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);
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);
892 * 'create_client()' - Accept a new network connection and create a client
896 static _ipp_client_t * /* O - Client */
897 create_client(_ipp_printer_t *printer, /* I - Printer */
898 int sock) /* I - Listen socket */
900 _ipp_client_t *client; /* Client */
901 int val; /* Parameter value */
902 socklen_t addrlen; /* Length of address */
905 if ((client = calloc(1, sizeof(_ipp_client_t))) == NULL)
907 perror("Unable to allocate memory for client");
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;
918 * Accept the client and get the remote address...
921 addrlen = sizeof(http_addr_t);
923 if ((client->http.fd = accept(sock, (struct sockaddr *)&(client->addr),
926 perror("Unable to accept client connection");
933 httpAddrString(&(client->addr), client->http.hostname,
934 sizeof(client->http.hostname));
937 fprintf(stderr, "Accepted connection from %s (%s)\n", client->http.hostname,
938 client->http.hostaddr->addr.sa_family == AF_INET ? "IPv4" : "IPv6");
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...
948 setsockopt(client->http.fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val,
956 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
960 static _ipp_job_t * /* O - Job */
961 create_job(_ipp_client_t *client) /* I - Client */
963 _ipp_job_t *job; /* Job */
964 ipp_attribute_t *attr; /* Job attribute */
965 char uri[1024]; /* job-uri value */
968 _cupsRWLockWrite(&(client->printer->rwlock));
969 if (client->printer->active_job &&
970 client->printer->active_job->state < IPP_JOB_CANCELED)
973 * Only accept a single job at a time...
976 _cupsRWLockWrite(&(client->printer->rwlock));
981 * Allocate and initialize the job object...
984 if ((job = calloc(1, sizeof(_ipp_job_t))) == NULL)
986 perror("Unable to allocate memory for job");
990 job->printer = client->printer;
991 job->attrs = client->request;
992 job->state = IPP_JOB_HELD;
994 client->request = NULL;
997 * Set all but the first two attributes to the job attributes group...
1000 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
1001 attr->group_tag = IPP_TAG_JOB;
1004 * Get the requesting-user-name, document format, and priority...
1007 if ((attr = ippFindAttribute(job->attrs, "requesting-user-name",
1008 IPP_TAG_NAME)) != NULL)
1010 _cupsStrFree(attr->name);
1011 attr->name = _cupsStrAlloc("job-originating-user-name");
1014 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME | IPP_TAG_COPY,
1015 "job-originating-user-name", NULL, "anonymous");
1018 job->username = attr->values[0].string.text;
1020 job->username = "anonymous";
1022 if ((attr = ippFindAttribute(job->attrs, "document-format",
1023 IPP_TAG_MIMETYPE)) != NULL)
1024 job->format = attr->values[0].string.text;
1026 job->format = "application/octet-stream";
1029 * Add job description attributes and add to the jobs array...
1032 job->id = client->printer->next_job_id ++;
1034 snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id);
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",
1043 cupsArrayAdd(client->printer->jobs, job);
1044 client->printer->active_job = job;
1046 _cupsRWUnlock(&(client->printer->rwlock));
1053 * 'create_listener()' - Create a listener socket.
1056 static int /* O - Listener socket or -1 on error */
1057 create_listener(int family, /* I - Address family */
1058 int *port) /* IO - Port number */
1060 int sock, /* Listener socket */
1061 val; /* Socket value */
1062 http_addr_t address; /* Listen address */
1063 socklen_t addrlen; /* Length of listen address */
1066 if ((sock = socket(family, SOCK_STREAM, 0)) < 0)
1070 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
1073 if (family == AF_INET6)
1074 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
1075 #endif /* IPV6_V6ONLY */
1080 * Get the auto-assigned port number for the IPv4 socket...
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))
1087 perror("getsockname() failed");
1091 *port = _httpAddrPort(&address);
1093 fprintf(stderr, "Listening on port %d.\n", *port);
1096 memset(&address, 0, sizeof(address));
1097 address.addr.sa_family = family;
1098 _httpAddrSetPort(&address, *port);
1100 if (bind(sock, (struct sockaddr *)&address, httpAddrLength(&address)))
1106 if (listen(sock, 5))
1117 * 'create_media_col()' - Create a media-col value.
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 */
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 */
1133 snprintf(media_key, sizeof(media_key), "%s_%s%s", media, type,
1134 margins == 0 ? "_borderless" : "");
1136 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL,
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",
1150 ippDelete(media_size);
1157 * 'create_media_size()' - Create a media-size value.
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 */
1164 ipp_t *media_size = ippNew(); /* media-size value */
1167 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension",
1169 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension",
1172 return (media_size);
1177 * 'create_printer()' - Create, register, and listen for connections to a
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 */
1194 const char *regtype, /* I - Bonjour service type */
1195 #endif /* HAVE_DNSSD */
1196 const char *directory) /* I - Spool directory */
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 */
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 */
1232 IPP_REVERSE_LANDSCAPE,
1233 IPP_REVERSE_PORTRAIT
1235 static const char * const versions[] =/* ipp-versions-supported values */
1241 static const int ops[] = /* operations-supported values */
1250 IPP_GET_JOB_ATTRIBUTES,
1252 IPP_GET_PRINTER_ATTRIBUTES
1254 static const char * const charsets[] =/* charset-supported values */
1259 static const char * const job_creation[] =
1260 { /* job-creation-attributes-supported values */
1262 "ipp-attribute-fidelity",
1267 "multiple-document-handling",
1268 "orientation-requested",
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",
1281 static const int media_xxx_margin_supported[] =
1282 { /* media-xxx-margin-supported values */
1286 static const char * const multiple_document_handling[] =
1287 { /* multiple-document-handling-supported values */
1288 "separate-documents-uncollated-copies",
1289 "separate-documents-collated-copies"
1291 static const int print_quality_supported[] =
1292 { /* print-quality-supported values */
1297 static const char * const reference_uri_schemes_supported[] =
1298 { /* reference-uri-schemes-supported */
1304 #endif /* HAVE_SSL */
1306 static const char * const sides_supported[] =
1307 { /* sides-supported values */
1309 "two-sided-long-edge",
1310 "two-sided-short-edge"
1312 static const char * const which_jobs[] =
1313 { /* which-jobs-supported values */
1322 "processing-stopped"
1327 * Allocate memory for the printer...
1330 if ((printer = calloc(1, sizeof(_ipp_printer_t))) == NULL)
1332 perror("Unable to allocate memory for printer");
1338 printer->name = _cupsStrAlloc(name);
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,
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;
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);
1357 _cupsRWInit(&(printer->rwlock));
1360 * Create the listener sockets...
1363 if ((printer->ipv4 = create_listener(AF_INET, &(printer->port))) < 0)
1365 perror("Unable to create IPv4 listener");
1369 if ((printer->ipv6 = create_listener(AF_INET6, &(printer->port))) < 0)
1371 perror("Unable to create IPv6 listener");
1376 * Prepare values for the printer attributes...
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, "/");
1386 fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
1387 fprintf(stderr, "printer-uri=\"%s\"\n", uri);
1390 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
1393 formats[0] = strdup(docformats);
1394 defformat = formats[0];
1395 for (ptr = strchr(formats[0], ','); ptr; ptr = strchr(ptr, ','))
1398 formats[num_formats++] = ptr;
1400 if (!_cups_strcasecmp(ptr, "application/octet-stream"))
1404 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
1405 ptr = device_id + strlen(device_id);
1407 for (i = 0; i < num_formats; i ++)
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,
1426 strlcat(device_id, ";", sizeof(device_id));
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.
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;
1441 k_supported = (int)spoolsize;
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;
1450 k_supported = (int)spoolsize;
1453 k_supported = INT_MAX;
1454 #endif /* HAVE_STATVFS */
1457 * Create the printer attributes. This list of attributes is sorted to improve
1458 * performance when the client provides a requested-attributes attribute...
1461 printer->attrs = ippNew();
1463 /* charset-configured */
1464 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
1465 "charset-configured", NULL, "utf-8");
1467 /* charset-supported */
1468 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
1469 "charset-supported", sizeof(charsets) / sizeof(charsets[0]),
1472 /* color-supported */
1473 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "color-supported",
1476 /* compression-supported */
1477 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1478 "compression-supported", NULL, "none");
1480 /* copies-default */
1481 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1482 "copies-default", 1);
1484 /* copies-supported */
1485 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
1487 /* document-format-default */
1488 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
1489 "document-format-default", NULL, defformat);
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);
1496 /* finishings-default */
1497 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1498 "finishings-default", IPP_FINISHINGS_NONE);
1500 /* finishings-supported */
1501 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1502 "finishings-supported", IPP_FINISHINGS_NONE);
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");
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);
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);
1519 /* job-k-octets-supported */
1520 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0,
1523 /* job-priority-default */
1524 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1525 "job-priority-default", 50);
1527 /* job-priority-supported */
1528 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1529 "job-priority-supported", 100);
1531 /* job-sheets-default */
1532 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
1533 "job-sheets-default", NULL, "none");
1535 /* job-sheets-supported */
1536 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
1537 "job-sheets-supported", NULL, "none");
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);
1546 /* media-col-database */
1547 for (num_database = 0, i = 0;
1548 i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
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 */
1556 num_database += (int)(sizeof(media_type_supported) /
1557 sizeof(media_type_supported[0])) + 6;
1558 /* All types + borderless */
1561 media_col_database = ippAddCollections(printer->attrs, IPP_TAG_PRINTER,
1562 "media-col-database", num_database,
1564 for (media_col_value = media_col_database->values, i = 0;
1565 i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1569 j < (int)(sizeof(media_type_supported) /
1570 sizeof(media_type_supported[0]));
1573 if (media_col_sizes[i][2] == _IPP_ENV_ONLY &&
1574 strcmp(media_type_supported[j], "auto") &&
1575 strcmp(media_type_supported[j], "envelope"))
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))
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]);
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)))
1593 * Add borderless version for this combination...
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]);
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]);
1612 ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-default",
1614 ippDelete(media_col_default);
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);
1624 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1625 "media-default", NULL, media_supported[0]);
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);
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);
1641 /* media-supported */
1642 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1644 (int)(sizeof(media_supported) / sizeof(media_supported[0])),
1645 NULL, media_supported);
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])),
1654 i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1656 media_size_supported->values[i].collection =
1657 create_media_size(media_col_sizes[i][0], media_col_sizes[i][1]);
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);
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);
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);
1680 /* multiple-document-jobs-supported */
1681 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER,
1682 "multiple-document-jobs-supported", 0);
1684 /* natural-language-configured */
1685 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE | IPP_TAG_COPY,
1686 "natural-language-configured", NULL, "en");
1688 /* number-up-default */
1689 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1690 "number-up-default", 1);
1692 /* number-up-supported */
1693 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1694 "number-up-supported", 1);
1696 /* operations-supported */
1697 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1698 "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1700 /* orientation-requested-default */
1701 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
1702 "orientation-requested-default", 0);
1704 /* orientation-requested-supported */
1705 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1706 "orientation-requested-supported", 4, orients);
1708 /* output-bin-default */
1709 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1710 "output-bin-default", NULL, "face-down");
1712 /* output-bin-supported */
1713 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1714 "output-bin-supported", NULL, "face-down");
1716 /* pages-per-minute */
1717 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1718 "pages-per-minute", ppm);
1720 /* pages-per-minute-color */
1722 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1723 "pages-per-minute-color", ppm_color);
1725 /* pdl-override-supported */
1726 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1727 "pdl-override-supported", NULL, "attempted");
1729 /* print-quality-default */
1730 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1731 "print-quality-default", IPP_QUALITY_NORMAL);
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);
1740 /* printer-device-id */
1741 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1742 "printer-device-id", NULL, device_id);
1745 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1746 "printer-icons", NULL, icons);
1748 /* printer-is-accepting-jobs */
1749 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
1753 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
1756 /* printer-location */
1757 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1758 "printer-location", NULL, location);
1760 /* printer-make-and-model */
1761 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1762 "printer-make-and-model", NULL, make_model);
1764 /* printer-more-info */
1765 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1766 "printer-more-info", NULL, adminurl);
1769 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name",
1772 /* printer-resolution-default */
1773 ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
1774 "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
1776 /* printer-resolution-supported */
1777 ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
1778 "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
1780 /* printer-uri-supported */
1781 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1782 "printer-uri-supported", NULL, uri);
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);
1793 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1794 "sides-default", NULL, "one-sided");
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);
1800 /* uri-authentication-supported */
1801 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1802 "uri-authentication-supported", NULL, "none");
1804 /* uri-security-supported */
1805 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1806 "uri-security-supported", NULL, "none");
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);
1815 debug_attributes("Printer", printer->attrs, 0);
1819 * Register the printer with Bonjour...
1822 if (!register_printer(printer, location, make, model, docformats, adminurl,
1823 ppm_color > 0, duplex, regtype))
1825 #endif /* HAVE_DNSSD */
1835 * If we get here we were unable to create the printer...
1840 delete_printer(printer);
1846 * 'create_requested_array()' - Create an array for requested-attributes.
1849 static cups_array_t * /* O - requested-attributes array */
1850 create_requested_array(
1851 _ipp_client_t *client) /* I - Client */
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 */
1860 * Get the requested-attributes attribute, and return NULL if we don't
1864 if ((requested = ippFindAttribute(client->request, "requested-attributes",
1865 IPP_TAG_KEYWORD)) == NULL)
1869 * If the attribute contains a single "all" keyword, return NULL...
1872 if (requested->num_values == 1 &&
1873 !strcmp(requested->values[0].string.text, "all"))
1877 * Create an array using "strcmp" as the comparison function...
1880 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
1882 for (i = 0; i < requested->num_values; i ++)
1884 value = requested->values[i].string.text;
1886 if (!strcmp(value, "job-template"))
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");
1932 else if (!strcmp(value, "job-description"))
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");
1963 else if (!strcmp(value, "printer-description"))
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");
2013 else if (!strcmp(value, "printer-defaults"))
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");
2027 else if (!strcmp(value, "subscription-template"))
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");
2040 cupsArrayAdd(ra, value);
2048 * 'debug_attributes()' - Print attributes in a request or response.
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 */
2056 ipp_tag_t group_tag; /* Current group */
2057 ipp_attribute_t *attr; /* Current attribute */
2058 char buffer[2048]; /* String buffer for value */
2064 fprintf(stderr, "%s:\n", title);
2065 fprintf(stderr, " version=%d.%d\n", ipp->request.any.version[0],
2066 ipp->request.any.version[1]);
2068 fprintf(stderr, " operation-id=%s(%04x)\n",
2069 ippOpString(ipp->request.op.operation_id),
2070 ipp->request.op.operation_id);
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);
2077 for (attr = ipp->attrs, group_tag = IPP_TAG_ZERO; attr; attr = attr->next)
2079 if (attr->group_tag != group_tag)
2081 group_tag = attr->group_tag;
2082 fprintf(stderr, " %s\n", ippTagString(group_tag));
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);
2097 * 'delete_client()' - Close the socket and free all memory used by a client
2102 delete_client(_ipp_client_t *client) /* I - Client */
2105 fprintf(stderr, "Closing connection from %s (%s)\n", client->http.hostname,
2106 client->http.hostaddr->addr.sa_family == AF_INET ? "IPv4" : "IPv6");
2109 * Flush pending writes before closing...
2112 httpFlushWrite(&(client->http));
2114 if (client->http.fd >= 0)
2115 close(client->http.fd);
2121 httpClearCookie(&(client->http));
2122 httpClearFields(&(client->http));
2124 ippDelete(client->request);
2126 ippDelete(client->response);
2133 * 'delete_job()' - Remove from the printer and free all memory used by a job
2138 delete_job(_ipp_job_t *job) /* I - Job */
2141 fprintf(stderr, "Removing job #%d from history.\n", job->id);
2143 ippDelete(job->attrs);
2148 unlink(job->filename);
2150 free(job->filename);
2158 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2159 * used by a printer object.
2163 delete_printer(_ipp_printer_t *printer) /* I - Printer */
2165 if (printer->ipv4 >= 0)
2166 close(printer->ipv4);
2168 if (printer->ipv6 >= 0)
2169 close(printer->ipv6);
2172 if (printer->printer_ref)
2173 DNSServiceRefDeallocate(printer->printer_ref);
2175 if (printer->ipp_ref)
2176 DNSServiceRefDeallocate(printer->ipp_ref);
2178 if (printer->http_ref)
2179 DNSServiceRefDeallocate(printer->http_ref);
2181 if (printer->common_ref)
2182 DNSServiceRefDeallocate(printer->common_ref);
2184 TXTRecordDeallocate(&(printer->ipp_txt));
2186 if (printer->dnssd_name)
2187 _cupsStrFree(printer->dnssd_name);
2188 #endif /* HAVE_DNSSD */
2191 _cupsStrFree(printer->name);
2193 _cupsStrFree(printer->icon);
2194 if (printer->directory)
2195 _cupsStrFree(printer->directory);
2196 if (printer->hostname)
2197 _cupsStrFree(printer->hostname);
2199 _cupsStrFree(printer->uri);
2201 ippDelete(printer->attrs);
2202 cupsArrayDelete(printer->jobs);
2210 * 'dnssd_callback()' - Handle Bonjour registration events.
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 */
2225 fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n",
2226 regtype, (int)errorCode);
2229 else if (_cups_strcasecmp(name, printer->dnssd_name))
2232 fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name);
2234 /* No lock needed since only the main thread accesses/changes this */
2235 _cupsStrFree(printer->dnssd_name);
2236 printer->dnssd_name = _cupsStrAlloc(name);
2239 #endif /* HAVE_DNSSD */
2243 * 'find_job()' - Find a job specified in a request.
2246 static _ipp_job_t * /* O - Job or NULL */
2247 find_job(_ipp_client_t *client) /* I - Client */
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 */
2256 if ((attr = ippFindAttribute(client->request, "job-uri",
2257 IPP_TAG_URI)) != NULL)
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);
2264 else if ((attr = ippFindAttribute(client->request, "job-id",
2265 IPP_TAG_INTEGER)) != NULL)
2266 key.id = attr->values[0].integer;
2268 _cupsRWLockRead(&(client->printer->rwlock));
2269 job = (_ipp_job_t *)cupsArrayFind(client->printer->jobs, &key);
2270 _cupsRWUnlock(&(client->printer->rwlock));
2277 * 'html_escape()' - Write a HTML-safe string.
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 */
2285 const char *start, /* Start of segment */
2286 *end; /* End of string */
2290 end = s + (slen > 0 ? slen : strlen(s));
2292 while (*s && s < end)
2294 if (*s == '&' || *s == '<')
2297 httpWrite2(&(client->http), start, s - start);
2300 httpWrite2(&(client->http), "&", 5);
2302 httpWrite2(&(client->http), "<", 4);
2311 httpWrite2(&(client->http), start, s - start);
2316 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2320 html_printf(_ipp_client_t *client, /* I - Client */
2321 const char *format, /* I - Printf-style format string */
2322 ...) /* I - Additional arguments as needed */
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 */
2337 * Loop through the format string, formatting as needed...
2340 va_start(ap, format);
2348 httpWrite2(&(client->http), start, format - start);
2351 *tptr++ = *format++;
2355 httpWrite2(&(client->http), "%", 1);
2359 else if (strchr(" -+#\'", *format))
2360 *tptr++ = *format++;
2365 * Get width from argument...
2369 width = va_arg(ap, int);
2371 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
2372 tptr += strlen(tptr);
2378 while (isdigit(*format & 255))
2380 if (tptr < (tformat + sizeof(tformat) - 1))
2383 width = width * 10 + *format++ - '0';
2389 if (tptr < (tformat + sizeof(tformat) - 1))
2397 * Get precision from argument...
2401 prec = va_arg(ap, int);
2403 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
2404 tptr += strlen(tptr);
2410 while (isdigit(*format & 255))
2412 if (tptr < (tformat + sizeof(tformat) - 1))
2415 prec = prec * 10 + *format++ - '0';
2420 if (*format == 'l' && format[1] == 'l')
2424 if (tptr < (tformat + sizeof(tformat) - 2))
2432 else if (*format == 'h' || *format == 'l' || *format == 'L')
2434 if (tptr < (tformat + sizeof(tformat) - 1))
2449 if (tptr < (tformat + sizeof(tformat) - 1))
2458 case 'E' : /* Floating point formats */
2463 if ((width + 2) > sizeof(temp))
2466 sprintf(temp, tformat, va_arg(ap, double));
2468 httpWrite2(&(client->http), temp, strlen(temp));
2471 case 'B' : /* Integer formats */
2479 if ((width + 2) > sizeof(temp))
2482 # ifdef HAVE_LONG_LONG
2484 sprintf(temp, tformat, va_arg(ap, long long));
2486 # endif /* HAVE_LONG_LONG */
2488 sprintf(temp, tformat, va_arg(ap, long));
2490 sprintf(temp, tformat, va_arg(ap, int));
2492 httpWrite2(&(client->http), temp, strlen(temp));
2495 case 'p' : /* Pointer value */
2496 if ((width + 2) > sizeof(temp))
2499 sprintf(temp, tformat, va_arg(ap, void *));
2501 httpWrite2(&(client->http), temp, strlen(temp));
2504 case 'c' : /* Character or character array */
2507 temp[0] = va_arg(ap, int);
2509 html_escape(client, temp, 1);
2512 html_escape(client, va_arg(ap, char *), (size_t)width);
2515 case 's' : /* String */
2516 if ((s = va_arg(ap, char *)) == NULL)
2519 html_escape(client, s, strlen(s));
2528 httpWrite2(&(client->http), start, format - start);
2535 * 'ipp_cancel_job()' - Cancel a job.
2539 ipp_cancel_job(_ipp_client_t *client) /* I - Client */
2541 _ipp_job_t *job; /* Job information */
2548 if ((job = find_job(client)) == NULL)
2550 respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
2555 * See if the job is already completed, canceled, or aborted; if so,
2556 * we can't cancel...
2561 case IPP_JOB_CANCELED :
2562 respond_ipp(client, IPP_NOT_POSSIBLE,
2563 "Job #%d is already canceled - can\'t cancel.", job->id);
2566 case IPP_JOB_ABORTED :
2567 respond_ipp(client, IPP_NOT_POSSIBLE,
2568 "Job #%d is already aborted - can\'t cancel.", job->id);
2571 case IPP_JOB_COMPLETED :
2572 respond_ipp(client, IPP_NOT_POSSIBLE,
2573 "Job #%d is already completed - can\'t cancel.", job->id);
2581 _cupsRWLockWrite(&(client->printer->rwlock));
2583 if (job->state == IPP_JOB_PROCESSING ||
2584 (job->state == IPP_JOB_HELD && job->fd >= 0))
2588 job->state = IPP_JOB_CANCELED;
2589 job->completed = time(NULL);
2592 _cupsRWUnlock(&(client->printer->rwlock));
2594 respond_ipp(client, IPP_OK, NULL);
2601 * 'ipp_create_job()' - Create a job object.
2605 ipp_create_job(_ipp_client_t *client) /* I - Client */
2607 _ipp_job_t *job; /* New job */
2608 cups_array_t *ra; /* Attributes to send in response */
2612 * Validate print job attributes...
2615 if (!valid_job_attributes(client))
2617 httpFlush(&(client->http));
2622 * Do we have a file to print?
2625 if (client->http.state == HTTP_POST_RECV)
2627 respond_ipp(client, IPP_BAD_REQUEST,
2628 "Unexpected document data following request.");
2636 if ((job = create_job(client)) == NULL)
2638 respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
2643 * Return the job info...
2646 respond_ipp(client, IPP_OK, NULL);
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");
2654 copy_job_attributes(client, job, ra);
2655 cupsArrayDelete(ra);
2660 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2664 ipp_get_job_attributes(
2665 _ipp_client_t *client) /* I - Client */
2667 _ipp_job_t *job; /* Job */
2668 cups_array_t *ra; /* requested-attributes */
2671 if ((job = find_job(client)) == NULL)
2673 respond_ipp(client, IPP_NOT_FOUND, "Job not found.");
2677 respond_ipp(client, IPP_OK, NULL);
2679 ra = create_requested_array(client);
2680 copy_job_attributes(client, job, ra);
2681 cupsArrayDelete(ra);
2686 * 'ipp_get_jobs()' - Get a list of job objects.
2690 ipp_get_jobs(_ipp_client_t *client) /* I - Client */
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 */
2704 * See if the "which-jobs" attribute have been specified...
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);
2712 if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
2714 job_comparison = -1;
2715 job_state = IPP_JOB_STOPPED;
2717 else if (!strcmp(attr->values[0].string.text, "completed"))
2720 job_state = IPP_JOB_CANCELED;
2722 else if (!strcmp(attr->values[0].string.text, "aborted"))
2725 job_state = IPP_JOB_ABORTED;
2727 else if (!strcmp(attr->values[0].string.text, "all"))
2730 job_state = IPP_JOB_PENDING;
2732 else if (!strcmp(attr->values[0].string.text, "canceled"))
2735 job_state = IPP_JOB_CANCELED;
2737 else if (!strcmp(attr->values[0].string.text, "pending"))
2740 job_state = IPP_JOB_PENDING;
2742 else if (!strcmp(attr->values[0].string.text, "pending-held"))
2745 job_state = IPP_JOB_HELD;
2747 else if (!strcmp(attr->values[0].string.text, "processing"))
2750 job_state = IPP_JOB_PROCESSING;
2752 else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
2755 job_state = IPP_JOB_STOPPED;
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);
2768 * See if they want to limit the number of jobs reported...
2771 if ((attr = ippFindAttribute(client->request, "limit",
2772 IPP_TAG_INTEGER)) != NULL)
2774 limit = attr->values[0].integer;
2776 fprintf(stderr, "%s Get-Jobs limit=%d", client->http.hostname, limit);
2781 if ((attr = ippFindAttribute(client->request, "first-job-id",
2782 IPP_TAG_INTEGER)) != NULL)
2784 first_job_id = attr->values[0].integer;
2786 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->http.hostname,
2793 * See if we only want to see jobs for a specific user...
2798 if ((attr = ippFindAttribute(client->request, "my-jobs",
2799 IPP_TAG_BOOLEAN)) != NULL)
2801 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->http.hostname,
2802 attr->values[0].boolean ? "true" : "false");
2804 if (attr->values[0].boolean)
2806 if ((attr = ippFindAttribute(client->request, "requesting-user-name",
2807 IPP_TAG_NAME)) == NULL)
2809 respond_ipp(client, IPP_BAD_REQUEST,
2810 "Need requesting-user-name with my-jobs.");
2814 username = attr->values[0].string.text;
2816 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2817 client->http.hostname, username);
2822 * OK, build a list of jobs for this printer...
2825 if ((ra = create_requested_array(client)) == NULL &&
2826 !ippFindAttribute(client->request, "requested-attributes",
2830 * IPP conformance - Get-Jobs has a default requested-attributes value of
2831 * "job-id" and "job-uri".
2834 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2835 cupsArrayAdd(ra, "job-id");
2836 cupsArrayAdd(ra, "job-uri");
2839 respond_ipp(client, IPP_OK, NULL);
2841 _cupsRWLockRead(&(client->printer->rwlock));
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))
2848 * Filter out jobs that don't match...
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)))
2859 ippAddSeparator(client->response);
2862 copy_job_attributes(client, job, ra);
2865 cupsArrayDelete(ra);
2867 _cupsRWUnlock(&(client->printer->rwlock));
2872 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2876 ipp_get_printer_attributes(
2877 _ipp_client_t *client) /* I - Client */
2879 cups_array_t *ra; /* Requested attributes array */
2880 _ipp_printer_t *printer; /* Printer */
2884 * Send the attributes...
2887 ra = create_requested_array(client);
2888 printer = client->printer;
2890 respond_ipp(client, IPP_OK, NULL);
2892 _cupsRWLockRead(&(printer->rwlock));
2894 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
2897 if (!ra || cupsArrayFind(ra, "printer-state"))
2898 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
2899 "printer-state", printer->state);
2901 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
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",
2909 int num_reasons = 0;/* Number of reasons */
2910 const char *reasons[32]; /* Reason strings */
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";
2945 ippAddStrings(client->response, IPP_TAG_PRINTER,
2946 IPP_TAG_KEYWORD | IPP_TAG_COPY, "printer-state-reasons",
2947 num_reasons, NULL, reasons);
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));
2955 if (!ra || cupsArrayFind(ra, "queued-job-count"))
2956 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2958 printer->active_job &&
2959 printer->active_job->state < IPP_JOB_CANCELED);
2961 _cupsRWUnlock(&(printer->rwlock));
2963 cupsArrayDelete(ra);
2968 * 'ipp_print_job()' - Create a job object with an attached document.
2972 ipp_print_job(_ipp_client_t *client) /* I - Client */
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 */
2982 * Validate print job attributes...
2985 if (!valid_job_attributes(client))
2987 httpFlush(&(client->http));
2992 * Do we have a file to print?
2995 if (client->http.state == HTTP_POST_SEND)
2997 respond_ipp(client, IPP_BAD_REQUEST, "No file in request.");
3005 if ((job = create_job(client)) == NULL)
3007 respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
3012 * Create a file for the request data...
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);
3028 snprintf(filename, sizeof(filename), "%s/%d.prn",
3029 client->printer->directory, job->id);
3031 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
3033 job->state = IPP_JOB_ABORTED;
3035 respond_ipp(client, IPP_INTERNAL_ERROR,
3036 "Unable to create print file: %s", strerror(errno));
3040 while ((bytes = httpRead2(&(client->http), buffer, sizeof(buffer))) > 0)
3042 if (write(job->fd, buffer, bytes) < bytes)
3044 int error = errno; /* Write error */
3046 job->state = IPP_JOB_ABORTED;
3053 respond_ipp(client, IPP_INTERNAL_ERROR,
3054 "Unable to write print file: %s", strerror(error));
3062 * Got an error while reading the print data, so abort this job.
3065 job->state = IPP_JOB_ABORTED;
3072 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to read print file.");
3078 int error = errno; /* Write error */
3080 job->state = IPP_JOB_ABORTED;
3085 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
3091 job->filename = strdup(filename);
3092 job->state = IPP_JOB_PENDING;
3095 * Process the job...
3099 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3101 job->state = IPP_JOB_ABORTED;
3102 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
3111 * Return the job info...
3114 respond_ipp(client, IPP_OK, NULL);
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");
3122 copy_job_attributes(client, job, ra);
3123 cupsArrayDelete(ra);
3128 * 'ipp_print_uri()' - Create a job object with a referenced document.
3132 ipp_print_uri(_ipp_client_t *client) /* I - Client */
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 */
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.",
3164 * Validate print job attributes...
3167 if (!valid_job_attributes(client))
3169 httpFlush(&(client->http));
3174 * Do we have a file to print?
3177 if (client->http.state == HTTP_POST_RECV)
3179 respond_ipp(client, IPP_BAD_REQUEST,
3180 "Unexpected document data following request.");
3185 * Do we have a document URI?
3188 if ((uri = ippFindAttribute(client->request, "document-uri",
3189 IPP_TAG_URI)) == NULL)
3191 respond_ipp(client, IPP_BAD_REQUEST, "Missing document-uri.");
3195 if (uri->num_values != 1)
3197 respond_ipp(client, IPP_BAD_REQUEST, "Too many document-uri values.");
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)
3207 respond_ipp(client, IPP_BAD_REQUEST, "Bad document-uri: %s",
3208 uri_status_strings[uri_status - HTTP_URI_OVERFLOW]);
3212 if (strcmp(scheme, "file") &&
3214 strcmp(scheme, "https") &&
3215 #endif /* HAVE_SSL */
3216 strcmp(scheme, "http"))
3218 respond_ipp(client, IPP_URI_SCHEME, "URI scheme \"%s\" not supported.",
3223 if (!strcmp(scheme, "file") && access(resource, R_OK))
3225 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
3234 if ((job = create_job(client)) == NULL)
3236 respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
3241 * Create a file for the request data...
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);
3257 snprintf(filename, sizeof(filename), "%s/%d.prn",
3258 client->printer->directory, job->id);
3260 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
3262 job->state = IPP_JOB_ABORTED;
3264 respond_ipp(client, IPP_INTERNAL_ERROR,
3265 "Unable to create print file: %s", strerror(errno));
3269 if (!strcmp(scheme, "file"))
3271 if ((infile = open(resource, O_RDONLY)) < 0)
3273 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
3280 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
3281 (errno == EAGAIN || errno == EINTR))
3283 else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes)
3285 int error = errno; /* Write error */
3287 job->state = IPP_JOB_ABORTED;
3295 respond_ipp(client, IPP_INTERNAL_ERROR,
3296 "Unable to write print file: %s", strerror(error));
3307 if (port == 443 || !strcmp(scheme, "https"))
3308 encryption = HTTP_ENCRYPT_ALWAYS;
3310 #endif /* HAVE_SSL */
3311 encryption = HTTP_ENCRYPT_IF_REQUESTED;
3313 if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL)
3315 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR,
3316 "Unable to connect to %s: %s", hostname,
3317 cupsLastErrorString());
3318 job->state = IPP_JOB_ABORTED;
3327 httpClearFields(http);
3328 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
3329 if (httpGet(http, resource))
3331 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s",
3334 job->state = IPP_JOB_ABORTED;
3344 while ((status = httpUpdate(http)) == HTTP_CONTINUE);
3346 if (status != HTTP_OK)
3348 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s",
3349 httpStatus(status));
3351 job->state = IPP_JOB_ABORTED;
3361 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3363 if (write(job->fd, buffer, bytes) < bytes)
3365 int error = errno; /* Write error */
3367 job->state = IPP_JOB_ABORTED;
3375 respond_ipp(client, IPP_INTERNAL_ERROR,
3376 "Unable to write print file: %s", strerror(error));
3386 int error = errno; /* Write error */
3388 job->state = IPP_JOB_ABORTED;
3393 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
3399 job->filename = strdup(filename);
3400 job->state = IPP_JOB_PENDING;
3403 * Process the job...
3407 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3409 job->state = IPP_JOB_ABORTED;
3410 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
3419 * Return the job info...
3422 respond_ipp(client, IPP_OK, NULL);
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");
3430 copy_job_attributes(client, job, ra);
3431 cupsArrayDelete(ra);
3436 * 'ipp_send_document()' - Add an attached document to a job object created with
3441 ipp_send_document(_ipp_client_t *client)/* I - Client */
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 */
3455 if ((job = find_job(client)) == NULL)
3457 respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
3458 httpFlush(&(client->http));
3463 * See if we already have a document for this job or the job has already
3464 * in a non-pending state...
3467 if (job->state > IPP_JOB_HELD)
3469 respond_ipp(client, IPP_NOT_POSSIBLE, "Job is not in a pending state.");
3470 httpFlush(&(client->http));
3473 else if (job->filename || job->fd >= 0)
3475 respond_ipp(client, IPP_MULTIPLE_JOBS_NOT_SUPPORTED,
3476 "Multiple document jobs are not supported.");
3477 httpFlush(&(client->http));
3481 if ((attr = ippFindAttribute(client->request, "last-document",
3482 IPP_TAG_ZERO)) == NULL)
3484 respond_ipp(client, IPP_BAD_REQUEST,
3485 "Missing required last-document attribute.");
3486 httpFlush(&(client->http));
3489 else if (attr->value_tag != IPP_TAG_BOOLEAN || attr->num_values != 1 ||
3490 !attr->values[0].boolean)
3492 respond_unsupported(client, attr);
3493 httpFlush(&(client->http));
3498 * Validate document attributes...
3501 if (!valid_doc_attributes(client))
3503 httpFlush(&(client->http));
3508 * Get the document format for the job...
3511 _cupsRWLockWrite(&(client->printer->rwlock));
3513 if ((attr = ippFindAttribute(job->attrs, "document-format",
3514 IPP_TAG_MIMETYPE)) != NULL)
3515 job->format = attr->values[0].string.text;
3517 job->format = "application/octet-stream";
3520 * Create a file for the request data...
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);
3536 snprintf(filename, sizeof(filename), "%s/%d.prn",
3537 client->printer->directory, job->id);
3539 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3541 _cupsRWUnlock(&(client->printer->rwlock));
3545 job->state = IPP_JOB_ABORTED;
3547 respond_ipp(client, IPP_INTERNAL_ERROR,
3548 "Unable to create print file: %s", strerror(errno));
3552 while ((bytes = httpRead2(&(client->http), buffer, sizeof(buffer))) > 0)
3554 if (write(job->fd, buffer, bytes) < bytes)
3556 int error = errno; /* Write error */
3558 job->state = IPP_JOB_ABORTED;
3565 respond_ipp(client, IPP_INTERNAL_ERROR,
3566 "Unable to write print file: %s", strerror(error));
3574 * Got an error while reading the print data, so abort this job.
3577 job->state = IPP_JOB_ABORTED;
3584 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to read print file.");
3590 int error = errno; /* Write error */
3592 job->state = IPP_JOB_ABORTED;
3597 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
3602 _cupsRWLockWrite(&(client->printer->rwlock));
3605 job->filename = strdup(filename);
3606 job->state = IPP_JOB_PENDING;
3608 _cupsRWUnlock(&(client->printer->rwlock));
3611 * Process the job...
3615 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3617 job->state = IPP_JOB_ABORTED;
3618 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
3627 * Return the job info...
3630 respond_ipp(client, IPP_OK, NULL);
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");
3638 copy_job_attributes(client, job, ra);
3639 cupsArrayDelete(ra);
3644 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3649 ipp_send_uri(_ipp_client_t *client) /* I - Client */
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 */
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.",
3685 if ((job = find_job(client)) == NULL)
3687 respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
3688 httpFlush(&(client->http));
3693 * See if we already have a document for this job or the job has already
3694 * in a non-pending state...
3697 if (job->state > IPP_JOB_HELD)
3699 respond_ipp(client, IPP_NOT_POSSIBLE, "Job is not in a pending state.");
3700 httpFlush(&(client->http));
3703 else if (job->filename || job->fd >= 0)
3705 respond_ipp(client, IPP_MULTIPLE_JOBS_NOT_SUPPORTED,
3706 "Multiple document jobs are not supported.");
3707 httpFlush(&(client->http));
3711 if ((attr = ippFindAttribute(client->request, "last-document",
3712 IPP_TAG_ZERO)) == NULL)
3714 respond_ipp(client, IPP_BAD_REQUEST,
3715 "Missing required last-document attribute.");
3716 httpFlush(&(client->http));
3719 else if (attr->value_tag != IPP_TAG_BOOLEAN || attr->num_values != 1 ||
3720 !attr->values[0].boolean)
3722 respond_unsupported(client, attr);
3723 httpFlush(&(client->http));
3728 * Validate document attributes...
3731 if (!valid_doc_attributes(client))
3733 httpFlush(&(client->http));
3738 * Do we have a file to print?
3741 if (client->http.state == HTTP_POST_RECV)
3743 respond_ipp(client, IPP_BAD_REQUEST,
3744 "Unexpected document data following request.");
3749 * Do we have a document URI?
3752 if ((uri = ippFindAttribute(client->request, "document-uri",
3753 IPP_TAG_URI)) == NULL)
3755 respond_ipp(client, IPP_BAD_REQUEST, "Missing document-uri.");
3759 if (uri->num_values != 1)
3761 respond_ipp(client, IPP_BAD_REQUEST, "Too many document-uri values.");
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)
3771 respond_ipp(client, IPP_BAD_REQUEST, "Bad document-uri: %s",
3772 uri_status_strings[uri_status - HTTP_URI_OVERFLOW]);
3776 if (strcmp(scheme, "file") &&
3778 strcmp(scheme, "https") &&
3779 #endif /* HAVE_SSL */
3780 strcmp(scheme, "http"))
3782 respond_ipp(client, IPP_URI_SCHEME, "URI scheme \"%s\" not supported.",
3787 if (!strcmp(scheme, "file") && access(resource, R_OK))
3789 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
3795 * Get the document format for the job...
3798 _cupsRWLockWrite(&(client->printer->rwlock));
3800 if ((attr = ippFindAttribute(job->attrs, "document-format",
3801 IPP_TAG_MIMETYPE)) != NULL)
3802 job->format = attr->values[0].string.text;
3804 job->format = "application/octet-stream";
3807 * Create a file for the request data...
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);
3823 snprintf(filename, sizeof(filename), "%s/%d.prn",
3824 client->printer->directory, job->id);
3826 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3828 _cupsRWUnlock(&(client->printer->rwlock));
3832 job->state = IPP_JOB_ABORTED;
3834 respond_ipp(client, IPP_INTERNAL_ERROR,
3835 "Unable to create print file: %s", strerror(errno));
3839 if (!strcmp(scheme, "file"))
3841 if ((infile = open(resource, O_RDONLY)) < 0)
3843 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
3850 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
3851 (errno == EAGAIN || errno == EINTR))
3853 else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes)
3855 int error = errno; /* Write error */
3857 job->state = IPP_JOB_ABORTED;
3865 respond_ipp(client, IPP_INTERNAL_ERROR,
3866 "Unable to write print file: %s", strerror(error));
3877 if (port == 443 || !strcmp(scheme, "https"))
3878 encryption = HTTP_ENCRYPT_ALWAYS;
3880 #endif /* HAVE_SSL */
3881 encryption = HTTP_ENCRYPT_IF_REQUESTED;
3883 if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL)
3885 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR,
3886 "Unable to connect to %s: %s", hostname,
3887 cupsLastErrorString());
3888 job->state = IPP_JOB_ABORTED;
3897 httpClearFields(http);
3898 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
3899 if (httpGet(http, resource))
3901 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s",
3904 job->state = IPP_JOB_ABORTED;
3914 while ((status = httpUpdate(http)) == HTTP_CONTINUE);
3916 if (status != HTTP_OK)
3918 respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s",
3919 httpStatus(status));
3921 job->state = IPP_JOB_ABORTED;
3931 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3933 if (write(job->fd, buffer, bytes) < bytes)
3935 int error = errno; /* Write error */
3937 job->state = IPP_JOB_ABORTED;
3945 respond_ipp(client, IPP_INTERNAL_ERROR,
3946 "Unable to write print file: %s", strerror(error));
3956 int error = errno; /* Write error */
3958 job->state = IPP_JOB_ABORTED;
3963 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
3968 _cupsRWLockWrite(&(client->printer->rwlock));
3971 job->filename = strdup(filename);
3972 job->state = IPP_JOB_PENDING;
3974 _cupsRWUnlock(&(client->printer->rwlock));
3977 * Process the job...
3981 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3983 job->state = IPP_JOB_ABORTED;
3984 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
3993 * Return the job info...
3996 respond_ipp(client, IPP_OK, NULL);
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");
4004 copy_job_attributes(client, job, ra);
4005 cupsArrayDelete(ra);
4010 * 'ipp_validate_job()' - Validate job creation attributes.
4014 ipp_validate_job(_ipp_client_t *client) /* I - Client */
4016 if (valid_job_attributes(client))
4017 respond_ipp(client, IPP_OK, NULL);
4022 * 'process_client()' - Process client requests on a thread.
4025 static void * /* O - Exit status */
4026 process_client(_ipp_client_t *client) /* I - Client */
4029 * Loop until we are out of requests or timeout (30 seconds)...
4032 while (httpWait(&(client->http), 30000))
4033 if (!process_http(client))
4037 * Close the conection to the client and return...
4040 delete_client(client);
4047 * 'process_http()' - Process a HTTP request.
4050 int /* O - 1 on success, 0 on failure */
4051 process_http(_ipp_client_t *client) /* I - Client connection */
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 */
4064 * Abort if we have an error on the connection...
4067 if (client->http.error)
4071 * Clear state variables...
4074 httpClearFields(&(client->http));
4075 ippDelete(client->request);
4076 ippDelete(client->response);
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;
4088 * Read a request from the connection...
4091 while ((ptr = httpGets(line, sizeof(line) - 1, &(client->http))) != NULL)
4099 * Parse the request line...
4102 fprintf(stderr, "%s %s\n", client->http.hostname, line);
4104 switch (sscanf(line, "%63s%1023s%63s", operation, uri, version))
4107 fprintf(stderr, "%s Bad request line.\n", client->http.hostname);
4108 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4112 client->http.version = HTTP_0_9;
4116 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
4118 fprintf(stderr, "%s Bad HTTP version.\n", client->http.hostname);
4119 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
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;
4129 client->http.keep_alive = HTTP_KEEPALIVE_OFF;
4133 respond_http(client, HTTP_NOT_SUPPORTED, NULL, 0);
4140 * Handle full URLs in the request line...
4143 if (!strncmp(client->uri, "http:", 5) || !strncmp(client->uri, "ipp:", 4))
4145 char scheme[32], /* Method/scheme */
4146 userpass[128], /* Username:password */
4147 hostname[HTTP_MAX_HOST];/* Hostname */
4148 int port; /* Port number */
4151 * Separate the URI into its components...
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)
4159 fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
4160 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4170 if (!_httpDecodeURI(client->uri, uri, sizeof(client->uri)))
4172 fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
4173 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4179 * Process the request...
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;
4192 fprintf(stderr, "%s Bad operation \"%s\".\n", client->http.hostname,
4194 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4198 client->start = time(NULL);
4199 client->operation = client->http.state;
4200 client->http.status = HTTP_OK;
4203 * Parse incoming parameters until the status changes...
4206 while ((status = httpUpdate(&(client->http))) == HTTP_CONTINUE);
4208 if (status != HTTP_OK)
4210 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4214 if (!client->http.fields[HTTP_FIELD_HOST][0] &&
4215 client->http.version >= HTTP_1_1)
4218 * HTTP/1.1 and higher require the "Host:" field...
4221 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4226 * Handle HTTP Upgrade...
4229 if (!_cups_strcasecmp(client->http.fields[HTTP_FIELD_CONNECTION], "Upgrade"))
4231 if (!respond_http(client, HTTP_NOT_IMPLEMENTED, NULL, 0))
4236 * Handle HTTP Expect...
4239 if (client->http.expect &&
4240 (client->operation == HTTP_POST || client->operation == HTTP_PUT))
4242 if (client->http.expect == HTTP_CONTINUE)
4245 * Send 100-continue header...
4248 if (!respond_http(client, HTTP_CONTINUE, NULL, 0))
4254 * Send 417-expectation-failed header...
4257 if (!respond_http(client, HTTP_EXPECTATION_FAILED, NULL, 0))
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;
4268 * Handle new transfers...
4271 switch (client->operation)
4275 * Do HEAD/OPTIONS command...
4278 return (respond_http(client, HTTP_OK, NULL, 0));
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));
4286 return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
4290 if (!strcmp(client->uri, "/icon.png"))
4293 * Send PNG icon file.
4296 int fd; /* Icon file */
4297 struct stat fileinfo; /* Icon file information */
4298 char buffer[4096]; /* Copy buffer */
4299 ssize_t bytes; /* Bytes */
4301 if (!stat(client->printer->icon, &fileinfo) &&
4302 (fd = open(client->printer->icon, O_RDONLY)) >= 0)
4304 if (!respond_http(client, HTTP_OK, "image/png", fileinfo.st_size))
4310 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
4311 httpWrite2(&(client->http), buffer, bytes);
4313 httpFlushWrite(&(client->http));
4318 return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
4320 else if (!strcmp(client->uri, "/"))
4323 * Show web status page...
4326 if (!respond_http(client, HTTP_OK, "text/html", 0))
4330 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4331 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4334 "<title>%s</title>\n"
4335 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4336 "type=\"image/png\">\n"
4341 "<p>%s, %d job(s).</p>\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);
4354 return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
4358 if (client->http.data_remaining < 0 ||
4359 (!client->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
4360 client->http.data_encoding == HTTP_ENCODE_LENGTH))
4363 * Negative content lengths are invalid...
4366 return (respond_http(client, HTTP_BAD_REQUEST, NULL, 0));
4369 if (strcmp(client->http.fields[HTTP_FIELD_CONTENT_TYPE],
4373 * Not an IPP request...
4376 return (respond_http(client, HTTP_BAD_REQUEST, NULL, 0));
4380 * Read the IPP request...
4383 client->request = ippNew();
4385 while ((state = ippRead(&(client->http), client->request)) != IPP_DATA)
4386 if (state == IPP_ERROR)
4388 fprintf(stderr, "%s IPP read error (%s).\n", client->http.hostname,
4389 cupsLastErrorString());
4390 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
4395 * Now that we have the IPP request, process the request...
4398 return (process_ipp(client));
4401 break; /* Anti-compiler-warning-code */
4409 * 'process_ipp()' - Process an IPP request.
4412 static int /* O - 1 on success, 0 on error */
4413 process_ipp(_ipp_client_t *client) /* I - Client */
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 */
4422 debug_attributes("Request", client->request, 1);
4425 * First build an empty response message for this request...
4428 client->operation_id = client->request->request.op.operation_id;
4429 client->response = ippNew();
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;
4439 * Then validate the request header and required attributes...
4442 if (client->request->request.any.version[0] < 1 ||
4443 client->request->request.any.version[0] > 2)
4446 * Return an error, since we only support IPP 1.x and 2.x.
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]);
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.");
4462 * Make sure that the attributes are provided in the correct order and
4463 * don't repeat groups...
4466 for (attr = client->request->attrs, group = attr->group_tag;
4469 if (attr->group_tag < group && attr->group_tag != IPP_TAG_ZERO)
4472 * Out of order; return an error...
4475 respond_ipp(client, IPP_BAD_REQUEST,
4476 "Attribute groups are out of order (%x < %x).",
4477 attr->group_tag, group);
4481 group = attr->group_tag;
4486 * Then make sure that the first three attributes are:
4488 * attributes-charset
4489 * attributes-natural-language
4490 * printer-uri/job-uri
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)
4504 if (attr && attr->name &&
4505 !strcmp(attr->name, "attributes-natural-language") &&
4506 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
4511 if ((attr = ippFindAttribute(client->request, "printer-uri",
4512 IPP_TAG_URI)) != NULL)
4514 else if ((attr = ippFindAttribute(client->request, "job-uri",
4515 IPP_TAG_URI)) != NULL)
4520 ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
4521 "attributes-charset", NULL,
4522 charset ? charset->values[0].string.text : "utf-8");
4524 ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
4525 "attributes-natural-language", NULL,
4526 language ? language->values[0].string.text : "en");
4529 _cups_strcasecmp(charset->values[0].string.text, "us-ascii") &&
4530 _cups_strcasecmp(charset->values[0].string.text, "utf-8"))
4533 * Bad character set...
4536 respond_ipp(client, IPP_BAD_REQUEST,
4537 "Unsupported character set \"%s\".",
4538 charset->values[0].string.text);
4540 else if (!charset || !language || !uri)
4543 * Return an error, since attributes-charset,
4544 * attributes-natural-language, and printer-uri/job-uri are required
4545 * for all operations.
4548 respond_ipp(client, IPP_BAD_REQUEST, "Missing required attributes.");
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))
4554 respond_ipp(client, IPP_NOT_FOUND, "%s %s not found.", uri->name,
4555 uri->values[0].string.text);
4560 * Try processing the operation...
4563 if (client->http.expect == HTTP_CONTINUE)
4566 * Send 100-continue header...
4569 if (!respond_http(client, HTTP_CONTINUE, NULL, 0))
4573 switch (client->request->request.op.operation_id)
4575 case IPP_PRINT_JOB :
4576 ipp_print_job(client);
4579 case IPP_PRINT_URI :
4580 ipp_print_uri(client);
4583 case IPP_VALIDATE_JOB :
4584 ipp_validate_job(client);
4587 case IPP_CREATE_JOB :
4588 ipp_create_job(client);
4591 case IPP_SEND_DOCUMENT :
4592 ipp_send_document(client);
4596 ipp_send_uri(client);
4599 case IPP_CANCEL_JOB :
4600 ipp_cancel_job(client);
4603 case IPP_GET_JOB_ATTRIBUTES :
4604 ipp_get_job_attributes(client);
4608 ipp_get_jobs(client);
4611 case IPP_GET_PRINTER_ATTRIBUTES :
4612 ipp_get_printer_attributes(client);
4616 respond_ipp(client, IPP_OPERATION_NOT_SUPPORTED,
4617 "Operation not supported.");
4625 * Send the HTTP header and return...
4628 if (client->http.state != HTTP_POST_SEND)
4629 httpFlush(&(client->http)); /* Flush trailing (junk) data */
4631 return (respond_http(client, HTTP_OK, "application/ipp",
4632 ippLength(client->response)));
4637 * 'process_job()' - Process a print job.
4640 static void * /* O - Thread exit status */
4641 process_job(_ipp_job_t *job) /* I - Job */
4643 job->state = IPP_JOB_PROCESSING;
4644 job->printer->state = IPP_PRINTER_PROCESSING;
4649 job->state = IPP_JOB_CANCELED;
4651 job->state = IPP_JOB_COMPLETED;
4653 job->completed = time(NULL);
4654 job->printer->state = IPP_PRINTER_IDLE;
4655 job->printer->active_job = NULL;
4663 * 'register_printer()' - Register a printer object via Bonjour.
4666 static int /* O - 1 on success, 0 on error */
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 */
4678 DNSServiceErrorType error; /* Error from Bonjour */
4679 char make_model[256],/* Make and model together */
4680 product[256]; /* Product string */
4684 * Build the TXT record for IPP...
4687 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4688 snprintf(product, sizeof(product), "(%s)", model);
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),
4696 TXTRecordSetValue(&(printer->ipp_txt), "adminurl", (uint8_t)strlen(adminurl),
4698 TXTRecordSetValue(&(printer->ipp_txt), "note", (uint8_t)strlen(location),
4700 TXTRecordSetValue(&(printer->ipp_txt), "priority", 1, "0");
4701 TXTRecordSetValue(&(printer->ipp_txt), "product", (uint8_t)strlen(product),
4703 TXTRecordSetValue(&(printer->ipp_txt), "pdl", (uint8_t)strlen(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),
4709 TXTRecordSetValue(&(printer->ipp_txt), "usb_MDL", (uint8_t)strlen(model),
4711 TXTRecordSetValue(&(printer->ipp_txt), "air", 4, "none");
4714 * Create a shared service reference for Bonjour...
4717 if ((error = DNSServiceCreateConnection(&(printer->common_ref)))
4718 != kDNSServiceErr_NoError)
4720 fprintf(stderr, "Unable to create mDNSResponder connection: %d\n", error);
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...
4729 printer->printer_ref = printer->common_ref;
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)
4740 fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
4741 printer->dnssd_name, error);
4746 * Then register the _ipp._tcp (IPP) service type with the real port number to
4747 * advertise our IPP printer...
4750 printer->ipp_ref = printer->common_ref;
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)
4762 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4763 printer->dnssd_name, regtype, error);
4768 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4769 * real port number to advertise our IPP printer...
4772 printer->http_ref = printer->common_ref;
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)
4783 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4784 printer->dnssd_name, regtype, error);
4790 #endif /* HAVE_DNSSD */
4794 * 'respond_http()' - Send a HTTP response.
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 */
4803 char message[1024]; /* Text message */
4806 fprintf(stderr, "%s %s\n", client->http.hostname, httpStatus(code));
4808 if (code == HTTP_CONTINUE)
4811 * 100-continue doesn't send any headers...
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);
4820 * Format an error message...
4823 if (!type && !length && code != HTTP_OK)
4825 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
4827 type = "text/plain";
4828 length = strlen(message);
4834 * Send the HTTP status header...
4837 httpFlushWrite(&(client->http));
4839 client->http.data_encoding = HTTP_ENCODE_FIELDS;
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)
4846 * Follow the header with the response fields...
4849 if (httpPrintf(&(client->http), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
4852 if (client->http.keep_alive && client->http.version >= HTTP_1_0)
4854 if (httpPrintf(&(client->http),
4855 "Connection: Keep-Alive\r\n"
4856 "Keep-Alive: timeout=10\r\n") < 0)
4860 if (code == HTTP_METHOD_NOT_ALLOWED || client->operation == HTTP_OPTIONS)
4862 if (httpPrintf(&(client->http), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
4868 if (!strcmp(type, "text/html"))
4870 if (httpPrintf(&(client->http),
4871 "Content-Type: text/html; charset=utf-8\r\n") < 0)
4874 else if (httpPrintf(&(client->http), "Content-Type: %s\r\n", type) < 0)
4878 if (length == 0 && !message[0])
4880 if (httpPrintf(&(client->http), "Transfer-Encoding: chunked\r\n\r\n") < 0)
4883 else if (httpPrintf(&(client->http), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
4884 CUPS_LLCAST length) < 0)
4887 if (httpFlushWrite(&(client->http)) < 0)
4891 * Send the response data...
4897 * Send a plain text message.
4900 if (httpPrintf(&(client->http), "%s", message) < 0)
4903 else if (client->response)
4906 * Send an IPP response...
4909 debug_attributes("Response", client->response, 2);
4911 client->http.data_encoding = HTTP_ENCODE_LENGTH;
4912 client->http.data_remaining = (off_t)ippLength(client->response);
4913 client->response->state = IPP_IDLE;
4915 if (ippWrite(&(client->http), client->response) != IPP_DATA)
4919 client->http.data_encoding = HTTP_ENCODE_CHUNKED;
4922 * Flush the data and return...
4925 return (httpFlushWrite(&(client->http)) >= 0);
4930 * 'respond_ipp()' - Send an IPP response.
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 */
4939 va_list ap; /* Pointer to additional args */
4940 char formatted[1024]; /* Formatted errror message */
4943 client->response->request.status.status_code = status;
4945 if (!client->response->attrs)
4947 ippAddString(client->response, IPP_TAG_OPERATION,
4948 IPP_TAG_CHARSET | IPP_TAG_COPY, "attributes-charset", NULL,
4950 ippAddString(client->response, IPP_TAG_OPERATION,
4951 IPP_TAG_LANGUAGE | IPP_TAG_COPY, "attributes-natural-language",
4957 va_start(ap, message);
4958 vsnprintf(formatted, sizeof(formatted), message, ap);
4961 ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
4962 "status-message", NULL, formatted);
4965 formatted[0] = '\0';
4967 fprintf(stderr, "%s %s %s (%s)\n", client->http.hostname,
4968 ippOpString(client->operation_id), ippErrorString(status), formatted);
4973 * 'respond_unsupported()' - Respond with an unsupported attribute.
4977 respond_unsupported(
4978 _ipp_client_t *client, /* I - Client */
4979 ipp_attribute_t *attr) /* I - Atribute */
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));
4986 copy_attribute(client->response, attr, IPP_TAG_UNSUPPORTED_GROUP, 0);
4991 * 'run_printer()' - Run the printer service.
4995 run_printer(_ipp_printer_t *printer) /* I - Printer */
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 */
5004 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5007 polldata[0].fd = printer->ipv4;
5008 polldata[0].events = POLLIN;
5010 polldata[1].fd = printer->ipv6;
5011 polldata[1].events = POLLIN;
5016 polldata[num_fds ].fd = DNSServiceRefSockFD(printer->common_ref);
5017 polldata[num_fds ++].events = POLLIN;
5018 #endif /* HAVE_DNSSD */
5021 * Loop until we are killed or have a hard error...
5026 if (cupsArrayCount(printer->jobs))
5031 if (poll(polldata, num_fds, timeout) < 0 && errno != EINTR)
5033 perror("poll() failed");
5037 if (polldata[0].revents & POLLIN)
5039 if ((client = create_client(printer, printer->ipv4)) != NULL)
5041 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
5043 perror("Unable to create client thread");
5044 delete_client(client);
5049 if (polldata[1].revents & POLLIN)
5051 if ((client = create_client(printer, printer->ipv6)) != NULL)
5053 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
5055 perror("Unable to create client thread");
5056 delete_client(client);
5062 if (polldata[2].revents & POLLIN)
5063 DNSServiceProcessResult(printer->common_ref);
5064 #endif /* HAVE_DNSSD */
5067 * Clean out old jobs...
5070 clean_jobs(printer);
5076 * 'usage()' - Show program usage.
5080 usage(int status) /* O - Exit status */
5084 puts(CUPS_SVERSION " - Copyright 2010 by Apple Inc. All rights reserved.");
5088 puts("Usage: ippserver [options] \"name\"");
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");
5112 * 'valid_doc_attributes()' - Determine whether the document attributes are
5115 * When one or more document attributes are invalid, this function adds a
5116 * suitable response and attributes to the unsupported group.
5119 static int /* O - 1 if valid, 0 if not */
5120 valid_doc_attributes(
5121 _ipp_client_t *client) /* I - Client */
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 */
5130 * Check operation attributes...
5133 if ((attr = ippFindAttribute(client->request, "compression",
5134 IPP_TAG_ZERO)) != NULL)
5137 * If compression is specified, only accept "none"...
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);
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);
5151 * Is it a format we support?
5154 if ((attr = ippFindAttribute(client->request, "document-format",
5155 IPP_TAG_ZERO)) != NULL)
5157 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_MIMETYPE)
5158 respond_unsupported(client, attr);
5161 format = attr->values[0].string.text;
5163 fprintf(stderr, "%s %s document-format=\"%s\"\n",
5164 client->http.hostname,
5165 ippOpString(client->request->request.op.operation_id), format);
5169 format = "application/octet-stream";
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))
5176 * Auto-type the file using the first 4 bytes of the file...
5179 unsigned char header[4]; /* First 4 bytes of file */
5181 memset(header, 0, sizeof(header));
5182 _httpPeek(&(client->http), (char *)header, sizeof(header));
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";
5195 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
5196 client->http.hostname,
5197 ippOpString(client->request->request.op.operation_id), format);
5200 attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
5201 "document-format", NULL, format);
5204 _cupsStrFree(attr->values[0].string.text);
5205 attr->values[0].string.text = _cupsStrAlloc(format);
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)
5214 for (i = 0; i < supported->num_values; i ++)
5215 if (!_cups_strcasecmp(format, supported->values[i].string.text))
5218 if (i >= supported->num_values && attr)
5219 respond_unsupported(client, attr);
5222 return (!client->response->attrs ||
5223 !client->response->attrs->next ||
5224 !client->response->attrs->next->next);
5229 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5231 * When one or more job attributes are invalid, this function adds a suitable
5232 * response and attributes to the unsupported group.
5235 static int /* O - 1 if valid, 0 if not */
5236 valid_job_attributes(
5237 _ipp_client_t *client) /* I - Client */
5239 int i; /* Looping var */
5240 ipp_attribute_t *attr, /* Current attribute */
5241 *supported; /* xxx-supported attribute */
5245 * Check operation attributes...
5248 valid_doc_attributes(client);
5251 * Check the various job template attributes...
5254 if ((attr = ippFindAttribute(client->request, "copies",
5255 IPP_TAG_ZERO)) != NULL)
5257 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_INTEGER ||
5258 attr->values[0].integer < 1 || attr->values[0].integer > 999)
5260 respond_unsupported(client, attr);
5264 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity",
5265 IPP_TAG_ZERO)) != NULL)
5267 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_BOOLEAN)
5269 respond_unsupported(client, attr);
5273 if ((attr = ippFindAttribute(client->request, "job-hold-until",
5274 IPP_TAG_ZERO)) != NULL)
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"))
5282 respond_unsupported(client, attr);
5286 if ((attr = ippFindAttribute(client->request, "job-name",
5287 IPP_TAG_ZERO)) != NULL)
5289 if (attr->num_values != 1 ||
5290 (attr->value_tag != IPP_TAG_NAME &&
5291 attr->value_tag != IPP_TAG_NAMELANG))
5293 respond_unsupported(client, attr);
5297 if ((attr = ippFindAttribute(client->request, "job-priority",
5298 IPP_TAG_ZERO)) != NULL)
5300 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_INTEGER ||
5301 attr->values[0].integer < 1 || attr->values[0].integer > 100)
5303 respond_unsupported(client, attr);
5307 if ((attr = ippFindAttribute(client->request, "job-sheets",
5308 IPP_TAG_ZERO)) != NULL)
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"))
5316 respond_unsupported(client, attr);
5320 if ((attr = ippFindAttribute(client->request, "media",
5321 IPP_TAG_ZERO)) != NULL)
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))
5328 respond_unsupported(client, attr);
5333 i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
5335 if (!strcmp(attr->values[0].string.text, media_supported[i]))
5338 if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
5340 respond_unsupported(client, attr);
5345 if ((attr = ippFindAttribute(client->request, "media-col",
5346 IPP_TAG_ZERO)) != NULL)
5348 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_BEGIN_COLLECTION)
5350 respond_unsupported(client, attr);
5352 /* TODO: check for valid media-col */
5355 if ((attr = ippFindAttribute(client->request, "multiple-document-handling",
5356 IPP_TAG_ZERO)) != NULL)
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")))
5364 respond_unsupported(client, attr);
5368 if ((attr = ippFindAttribute(client->request, "orientation-requested",
5369 IPP_TAG_ZERO)) != NULL)
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)
5375 respond_unsupported(client, attr);
5379 if ((attr = ippFindAttribute(client->request, "page-ranges",
5380 IPP_TAG_ZERO)) != NULL)
5382 respond_unsupported(client, attr);
5385 if ((attr = ippFindAttribute(client->request, "print-quality",
5386 IPP_TAG_ZERO)) != NULL)
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)
5392 respond_unsupported(client, attr);
5396 if ((attr = ippFindAttribute(client->request, "printer-resolution",
5397 IPP_TAG_ZERO)) != NULL)
5399 respond_unsupported(client, attr);
5402 if ((attr = ippFindAttribute(client->request, "sides",
5403 IPP_TAG_ZERO)) != NULL)
5405 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_KEYWORD)
5407 respond_unsupported(client, attr);
5410 if ((supported = ippFindAttribute(client->printer->attrs, "sides",
5411 IPP_TAG_KEYWORD)) != NULL)
5413 for (i = 0; i < supported->num_values; i ++)
5414 if (!strcmp(attr->values[0].string.text,
5415 supported->values[i].string.text))
5418 if (i >= supported->num_values)
5420 respond_unsupported(client, attr);
5425 respond_unsupported(client, attr);
5429 return (!client->response->attrs ||
5430 !client->response->attrs->next ||
5431 !client->response->attrs->next->next);
5436 * End of "$Id: ippserver.c 10031 2011-09-30 05:24:10Z mike $".