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