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