]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ippserver.c
Merge changes from CUPS 1.5b1-r9798.
[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 address.addr.sa_family = family;
1088 _httpAddrSetPort(&address, *port);
1089
1090 if (bind(sock, (struct sockaddr *)&address, httpAddrLength(&address)))
1091 {
1092 close(sock);
1093 return (-1);
1094 }
1095
1096 if (listen(sock, 5))
1097 {
1098 close(sock);
1099 return (-1);
1100 }
1101
1102 return (sock);
1103 }
1104
1105
1106 /*
1107 * 'create_media_col()' - Create a media-col value.
1108 */
1109
1110 static ipp_t * /* O - media-col collection */
1111 create_media_col(const char *media, /* I - Media name */
1112 const char *type, /* I - Nedua type */
1113 int width, /* I - x-dimension in 2540ths */
1114 int length, /* I - y-dimension in 2540ths */
1115 int margins) /* I - Value for margins */
1116 {
1117 ipp_t *media_col = ippNew(), /* media-col value */
1118 *media_size = ippNew(); /* media-size value */
1119 char media_key[256]; /* media-key value */
1120
1121
1122 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension",
1123 width);
1124 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension",
1125 length);
1126
1127 snprintf(media_key, sizeof(media_key), "%s_%s%s", media, type,
1128 margins == 0 ? "_borderless" : "");
1129
1130 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL,
1131 media_key);
1132 ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
1133 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1134 "media-bottom-margin", margins);
1135 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1136 "media-left-margin", margins);
1137 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1138 "media-right-margin", margins);
1139 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1140 "media-top-margin", margins);
1141 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type",
1142 NULL, type);
1143
1144 ippDelete(media_size);
1145
1146 return (media_col);
1147 }
1148
1149
1150 /*
1151 * 'create_printer()' - Create, register, and listen for connections to a
1152 * printer object.
1153 */
1154
1155 static _ipp_printer_t * /* O - Printer */
1156 create_printer(const char *servername, /* I - Server hostname (NULL for default) */
1157 const char *name, /* I - printer-name */
1158 const char *location, /* I - printer-location */
1159 const char *make, /* I - printer-make-and-model */
1160 const char *model, /* I - printer-make-and-model */
1161 const char *icon, /* I - printer-icons */
1162 const char *docformats, /* I - document-format-supported */
1163 int ppm, /* I - Pages per minute in grayscale */
1164 int ppm_color, /* I - Pages per minute in color (0 for gray) */
1165 int duplex, /* I - 1 = duplex, 0 = simplex */
1166 int port, /* I - Port for listeners or 0 for auto */
1167 #ifdef HAVE_DNSSD
1168 const char *regtype, /* I - Bonjour service type */
1169 #endif /* HAVE_DNSSD */
1170 const char *directory) /* I - Spool directory */
1171 {
1172 int i, j; /* Looping vars */
1173 _ipp_printer_t *printer; /* Printer */
1174 char hostname[256], /* Hostname */
1175 uri[1024], /* Printer URI */
1176 icons[1024], /* printer-icons URI */
1177 adminurl[1024], /* printer-more-info URI */
1178 device_id[1024],/* printer-device-id */
1179 make_model[128];/* printer-make-and-model */
1180 int num_formats; /* Number of document-format-supported values */
1181 char *defformat, /* document-format-default value */
1182 *formats[100], /* document-format-supported values */
1183 *ptr; /* Pointer into string */
1184 const char *prefix; /* Prefix string */
1185 int num_database; /* Number of database values */
1186 ipp_attribute_t *media_col_database;
1187 /* media-col-database value */
1188 ipp_t *media_col_default;
1189 /* media-col-default value */
1190 ipp_value_t *media_col_value;
1191 /* Current media-col-database value */
1192 int k_supported; /* Maximum file size supported */
1193 #ifdef HAVE_STATVFS
1194 struct statvfs spoolinfo; /* FS info for spool directory */
1195 double spoolsize; /* FS size */
1196 #elif defined(HAVE_STATFS)
1197 struct statfs spoolinfo; /* FS info for spool directory */
1198 double spoolsize; /* FS size */
1199 #endif /* HAVE_STATVFS */
1200 static const int orients[4] = /* orientation-requested-supported values */
1201 {
1202 IPP_PORTRAIT,
1203 IPP_LANDSCAPE,
1204 IPP_REVERSE_LANDSCAPE,
1205 IPP_REVERSE_PORTRAIT
1206 };
1207 static const char * const versions[] =/* ipp-versions-supported values */
1208 {
1209 "1.0",
1210 "1.1",
1211 "2.0"
1212 };
1213 static const int ops[] = /* operations-supported values */
1214 {
1215 IPP_PRINT_JOB,
1216 IPP_VALIDATE_JOB,
1217 IPP_CREATE_JOB,
1218 IPP_SEND_DOCUMENT,
1219 IPP_CANCEL_JOB,
1220 IPP_GET_JOB_ATTRIBUTES,
1221 IPP_GET_JOBS,
1222 IPP_GET_PRINTER_ATTRIBUTES
1223 };
1224 static const char * const charsets[] =/* charset-supported values */
1225 {
1226 "us-ascii",
1227 "utf-8"
1228 };
1229 static const char * const job_creation[] =
1230 { /* job-creation-attributes-supported values */
1231 "copies",
1232 "ipp-attribute-fidelity",
1233 "job-name",
1234 "job-priority",
1235 "media",
1236 "media-col",
1237 "multiple-document-handling",
1238 "orientation-requested",
1239 "print-quality",
1240 "sides"
1241 };
1242 static const char * const media_col_supported[] =
1243 { /* media-col-supported values */
1244 "media-bottom-margin",
1245 "media-left-margin",
1246 "media-right-margin",
1247 "media-size",
1248 "media-top-margin",
1249 "media-type"
1250 };
1251 static const int media_xxx_margin_supported[] =
1252 { /* media-xxx-margin-supported values */
1253 0,
1254 635
1255 };
1256 static const char * const multiple_document_handling[] =
1257 { /* multiple-document-handling-supported values */
1258 "separate-documents-uncollated-copies",
1259 "separate-documents-collated-copies"
1260 };
1261 static const int print_quality_supported[] =
1262 { /* print-quality-supported values */
1263 IPP_QUALITY_DRAFT,
1264 IPP_QUALITY_NORMAL,
1265 IPP_QUALITY_HIGH
1266 };
1267 static const char * const sides_supported[] =
1268 { /* sides-supported values */
1269 "one-sided",
1270 "two-sided-long-edge",
1271 "two-sided-short-edge"
1272 };
1273 static const char * const which_jobs[] =
1274 { /* which-jobs-supported values */
1275 "completed",
1276 "not-completed",
1277 "aborted",
1278 "all",
1279 "canceled",
1280 "pending",
1281 "pending-held",
1282 "processing",
1283 "processing-stopped"
1284 };
1285
1286
1287 /*
1288 * Allocate memory for the printer...
1289 */
1290
1291 if ((printer = calloc(1, sizeof(_ipp_printer_t))) == NULL)
1292 {
1293 perror("Unable to allocate memory for printer");
1294 return (NULL);
1295 }
1296
1297 printer->ipv4 = -1;
1298 printer->ipv6 = -1;
1299 printer->name = _cupsStrAlloc(name);
1300 #ifdef HAVE_DNSSD
1301 printer->dnssd_name = _cupsStrRetain(printer->name);
1302 #endif /* HAVE_DNSSD */
1303 printer->directory = _cupsStrAlloc(directory);
1304 printer->hostname = _cupsStrAlloc(servername ? servername :
1305 httpGetHostname(NULL, hostname,
1306 sizeof(hostname)));
1307 printer->port = port;
1308 printer->state = IPP_PRINTER_IDLE;
1309 printer->state_reasons = _IPP_PRINTER_NONE;
1310 printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
1311 printer->next_job_id = 1;
1312
1313 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1314 printer->hostname, printer->port, "/ipp");
1315 printer->uri = _cupsStrAlloc(uri);
1316 printer->urilen = strlen(uri);
1317
1318 _cupsRWInit(&(printer->rwlock));
1319
1320 /*
1321 * Create the listener sockets...
1322 */
1323
1324 if ((printer->ipv4 = create_listener(AF_INET, &(printer->port))) < 0)
1325 {
1326 perror("Unable to create IPv4 listener");
1327 goto bad_printer;
1328 }
1329
1330 if ((printer->ipv6 = create_listener(AF_INET6, &(printer->port))) < 0)
1331 {
1332 perror("Unable to create IPv6 listener");
1333 goto bad_printer;
1334 }
1335
1336 /*
1337 * Prepare values for the printer attributes...
1338 */
1339
1340 httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), "http", NULL,
1341 printer->hostname, printer->port, "/icon.png");
1342 httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), "http", NULL,
1343 printer->hostname, printer->port, "/");
1344
1345 if (Verbosity)
1346 {
1347 fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
1348 fprintf(stderr, "printer-uri=\"%s\"\n", uri);
1349 }
1350
1351 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
1352
1353 num_formats = 1;
1354 formats[0] = strdup(docformats);
1355 defformat = formats[0];
1356 for (ptr = strchr(formats[0], ','); ptr; ptr = strchr(ptr, ','))
1357 {
1358 *ptr++ = '\0';
1359 formats[num_formats++] = ptr;
1360
1361 if (!_cups_strcasecmp(ptr, "application/octet-stream"))
1362 defformat = ptr;
1363 }
1364
1365 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
1366 ptr = device_id + strlen(device_id);
1367 prefix = "CMD:";
1368 for (i = 0; i < num_formats; i ++)
1369 {
1370 if (!_cups_strcasecmp(formats[i], "application/pdf"))
1371 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPDF", prefix);
1372 else if (!_cups_strcasecmp(formats[i], "application/postscript"))
1373 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPS", prefix);
1374 else if (!_cups_strcasecmp(formats[i], "application/vnd.hp-PCL"))
1375 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPCL", prefix);
1376 else if (!_cups_strcasecmp(formats[i], "image/jpeg"))
1377 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sJPEG", prefix);
1378 else if (!_cups_strcasecmp(formats[i], "image/png"))
1379 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPNG", prefix);
1380 else if (_cups_strcasecmp(formats[i], "application/octet-stream"))
1381 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%s%s", prefix,
1382 formats[i]);
1383
1384 ptr += strlen(ptr);
1385 prefix = ",";
1386 }
1387 strlcat(device_id, ";", sizeof(device_id));
1388
1389 /*
1390 * Get the maximum spool size based on the size of the filesystem used for
1391 * the spool directory. If the host OS doesn't support the statfs call
1392 * or the filesystem is larger than 2TiB, always report INT_MAX.
1393 */
1394
1395 #ifdef HAVE_STATVFS
1396 if (statvfs(printer->directory, &spoolinfo))
1397 k_supported = INT_MAX;
1398 else if ((spoolsize = (double)spoolinfo.f_frsize *
1399 spoolinfo.f_blocks / 1024) > INT_MAX)
1400 k_supported = INT_MAX;
1401 else
1402 k_supported = (int)spoolsize;
1403
1404 #elif defined(HAVE_STATFS)
1405 if (statfs(printer->directory, &spoolinfo))
1406 k_supported = INT_MAX;
1407 else if ((spoolsize = (double)spoolinfo.f_bsize *
1408 spoolinfo.f_blocks / 1024) > INT_MAX)
1409 k_supported = INT_MAX;
1410 else
1411 k_supported = (int)spoolsize;
1412
1413 #else
1414 k_supported = INT_MAX;
1415 #endif /* HAVE_STATVFS */
1416
1417 /*
1418 * Create the printer attributes. This list of attributes is sorted to improve
1419 * performance when the client provides a requested-attributes attribute...
1420 */
1421
1422 printer->attrs = ippNew();
1423
1424 /* charset-configured */
1425 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
1426 "charset-configured", NULL, "utf-8");
1427
1428 /* charset-supported */
1429 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET | IPP_TAG_COPY,
1430 "charset-supported", sizeof(charsets) / sizeof(charsets[0]),
1431 NULL, charsets);
1432
1433 /* color-supported */
1434 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "color-supported",
1435 ppm_color > 0);
1436
1437 /* compression-supported */
1438 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1439 "compression-supported", NULL, "none");
1440
1441 /* copies-default */
1442 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1443 "copies-default", 1);
1444
1445 /* copies-supported */
1446 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
1447
1448 /* document-format-default */
1449 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
1450 "document-format-default", NULL, defformat);
1451
1452 /* document-format-supported */
1453 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
1454 "document-format-supported", num_formats, NULL,
1455 (const char * const *)formats);
1456
1457 /* finishings-default */
1458 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1459 "finishings-default", IPP_FINISHINGS_NONE);
1460
1461 /* finishings-supported */
1462 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1463 "finishings-supported", IPP_FINISHINGS_NONE);
1464
1465 /* generated-natural-language-supported */
1466 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE | IPP_TAG_COPY,
1467 "generated-natural-language-supported", NULL, "en");
1468
1469 /* ipp-versions-supported */
1470 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1471 "ipp-versions-supported",
1472 sizeof(versions) / sizeof(versions[0]), NULL, versions);
1473
1474 /* job-creation-attributes-supported */
1475 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1476 "job-creation-attributes-supported",
1477 sizeof(job_creation) / sizeof(job_creation[0]),
1478 NULL, job_creation);
1479
1480 /* job-k-octets-supported */
1481 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0,
1482 k_supported);
1483
1484 /* job-priority-default */
1485 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1486 "job-priority-default", 50);
1487
1488 /* job-priority-supported */
1489 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1490 "job-priority-supported", 100);
1491
1492 /* job-sheets-default */
1493 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
1494 "job-sheets-default", NULL, "none");
1495
1496 /* job-sheets-supported */
1497 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
1498 "job-sheets-supported", NULL, "none");
1499
1500 /* media-bottom-margin-supported */
1501 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1502 "media-bottom-margin-supported",
1503 (int)(sizeof(media_xxx_margin_supported) /
1504 sizeof(media_xxx_margin_supported[0])),
1505 media_xxx_margin_supported);
1506
1507 /* media-col-database */
1508 for (num_database = 0, i = 0;
1509 i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1510 i ++)
1511 {
1512 if (media_col_sizes[i][2] == _IPP_ENV_ONLY)
1513 num_database += 2; /* auto + envelope */
1514 else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY)
1515 num_database += 12; /* auto + photographic-* + borderless */
1516 else
1517 num_database += (int)(sizeof(media_type_supported) /
1518 sizeof(media_type_supported[0])) + 6;
1519 /* All types + borderless */
1520 }
1521
1522 media_col_database = ippAddCollections(printer->attrs, IPP_TAG_PRINTER,
1523 "media-col-database", num_database,
1524 NULL);
1525 for (media_col_value = media_col_database->values, i = 0;
1526 i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1527 i ++)
1528 {
1529 for (j = 0;
1530 j < (int)(sizeof(media_type_supported) /
1531 sizeof(media_type_supported[0]));
1532 j ++)
1533 {
1534 if (media_col_sizes[i][2] == _IPP_ENV_ONLY &&
1535 strcmp(media_type_supported[j], "auto") &&
1536 strcmp(media_type_supported[j], "envelope"))
1537 continue;
1538 else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY &&
1539 strcmp(media_type_supported[j], "auto") &&
1540 strncmp(media_type_supported[j], "photographic-", 13))
1541 continue;
1542
1543 media_col_value->collection =
1544 create_media_col(media_supported[i], media_type_supported[j],
1545 media_col_sizes[i][0], media_col_sizes[i][1],
1546 media_xxx_margin_supported[1]);
1547 media_col_value ++;
1548
1549 if (media_col_sizes[i][2] != _IPP_ENV_ONLY &&
1550 (!strcmp(media_type_supported[j], "auto") ||
1551 !strncmp(media_type_supported[j], "photographic-", 13)))
1552 {
1553 /*
1554 * Add borderless version for this combination...
1555 */
1556
1557 media_col_value->collection =
1558 create_media_col(media_supported[i], media_type_supported[j],
1559 media_col_sizes[i][0], media_col_sizes[i][1],
1560 media_xxx_margin_supported[0]);
1561 media_col_value ++;
1562 }
1563 }
1564 }
1565
1566 /* media-col-default */
1567 media_col_default = create_media_col(media_supported[0],
1568 media_type_supported[0],
1569 media_col_sizes[0][0],
1570 media_col_sizes[0][1],
1571 media_xxx_margin_supported[1]);
1572
1573 ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-default",
1574 media_col_default);
1575 ippDelete(media_col_default);
1576
1577 /* media-col-supported */
1578 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1579 "media-col-supported",
1580 (int)(sizeof(media_col_supported) /
1581 sizeof(media_col_supported[0])), NULL,
1582 media_col_supported);
1583
1584 /* media-default */
1585 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1586 "media-default", NULL, media_supported[0]);
1587
1588 /* media-left-margin-supported */
1589 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1590 "media-left-margin-supported",
1591 (int)(sizeof(media_xxx_margin_supported) /
1592 sizeof(media_xxx_margin_supported[0])),
1593 media_xxx_margin_supported);
1594
1595 /* media-right-margin-supported */
1596 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1597 "media-right-margin-supported",
1598 (int)(sizeof(media_xxx_margin_supported) /
1599 sizeof(media_xxx_margin_supported[0])),
1600 media_xxx_margin_supported);
1601
1602 /* media-supported */
1603 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1604 "media-supported",
1605 (int)(sizeof(media_supported) / sizeof(media_supported[0])),
1606 NULL, media_supported);
1607
1608 /* media-top-margin-supported */
1609 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1610 "media-top-margin-supported",
1611 (int)(sizeof(media_xxx_margin_supported) /
1612 sizeof(media_xxx_margin_supported[0])),
1613 media_xxx_margin_supported);
1614
1615 /* multiple-document-handling-supported */
1616 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1617 "multiple-document-handling-supported",
1618 sizeof(multiple_document_handling) /
1619 sizeof(multiple_document_handling[0]), NULL,
1620 multiple_document_handling);
1621
1622 /* multiple-document-jobs-supported */
1623 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER,
1624 "multiple-document-jobs-supported", 0);
1625
1626 /* natural-language-configured */
1627 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE | IPP_TAG_COPY,
1628 "natural-language-configured", NULL, "en");
1629
1630 /* number-up-default */
1631 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1632 "number-up-default", 1);
1633
1634 /* number-up-supported */
1635 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1636 "number-up-supported", 1);
1637
1638 /* operations-supported */
1639 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1640 "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1641
1642 /* orientation-requested-default */
1643 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
1644 "orientation-requested-default", 0);
1645
1646 /* orientation-requested-supported */
1647 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1648 "orientation-requested-supported", 4, orients);
1649
1650 /* output-bin-default */
1651 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1652 "output-bin-default", NULL, "face-down");
1653
1654 /* output-bin-supported */
1655 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1656 "output-bin-supported", NULL, "face-down");
1657
1658 /* pages-per-minute */
1659 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1660 "pages-per-minute", ppm);
1661
1662 /* pages-per-minute-color */
1663 if (ppm_color > 0)
1664 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1665 "pages-per-minute-color", ppm_color);
1666
1667 /* pdl-override-supported */
1668 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1669 "pdl-override-supported", NULL, "attempted");
1670
1671 /* print-quality-default */
1672 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1673 "print-quality-default", IPP_QUALITY_NORMAL);
1674
1675 /* print-quality-supported */
1676 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1677 "print-quality-supported",
1678 (int)(sizeof(print_quality_supported) /
1679 sizeof(print_quality_supported[0])),
1680 print_quality_supported);
1681
1682 /* printer-device-id */
1683 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1684 "printer-device-id", NULL, device_id);
1685
1686 /* printer-icons */
1687 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1688 "printer-icons", NULL, icons);
1689
1690 /* printer-is-accepting-jobs */
1691 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
1692 1);
1693
1694 /* printer-info */
1695 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
1696 NULL, name);
1697
1698 /* printer-location */
1699 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1700 "printer-location", NULL, location);
1701
1702 /* printer-make-and-model */
1703 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1704 "printer-make-and-model", NULL, make_model);
1705
1706 /* printer-more-info */
1707 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1708 "printer-more-info", NULL, adminurl);
1709
1710 /* printer-name */
1711 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name",
1712 NULL, name);
1713
1714 /* printer-resolution-default */
1715 ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
1716 "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
1717
1718 /* printer-resolution-supported */
1719 ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
1720 "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
1721
1722 /* printer-uri-supported */
1723 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1724 "printer-uri-supported", NULL, uri);
1725
1726 /* sides-default */
1727 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1728 "sides-default", NULL, "one-sided");
1729
1730 /* sides-supported */
1731 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1732 "sides-supported", duplex ? 3 : 1, NULL, sides_supported);
1733
1734 /* uri-authentication-supported */
1735 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1736 "uri-authentication-supported", NULL, "none");
1737
1738 /* uri-security-supported */
1739 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1740 "uri-security-supported", NULL, "none");
1741
1742 /* which-jobs-supported */
1743 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
1744 "which-jobs-supported",
1745 sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
1746
1747 free(formats[0]);
1748
1749 debug_attributes("Printer", printer->attrs);
1750
1751 #ifdef HAVE_DNSSD
1752 /*
1753 * Register the printer with Bonjour...
1754 */
1755
1756 if (!register_printer(printer, location, make, model, docformats, adminurl,
1757 ppm_color > 0, duplex, regtype))
1758 goto bad_printer;
1759 #endif /* HAVE_DNSSD */
1760
1761 /*
1762 * Return it!
1763 */
1764
1765 return (printer);
1766
1767
1768 /*
1769 * If we get here we were unable to create the printer...
1770 */
1771
1772 bad_printer:
1773
1774 delete_printer(printer);
1775 return (NULL);
1776 }
1777
1778
1779 /*
1780 * 'create_requested_array()' - Create an array for requested-attributes.
1781 */
1782
1783 static cups_array_t * /* O - requested-attributes array */
1784 create_requested_array(
1785 _ipp_client_t *client) /* I - Client */
1786 {
1787 int i; /* Looping var */
1788 ipp_attribute_t *requested; /* requested-attributes attribute */
1789 cups_array_t *ra; /* Requested attributes array */
1790 char *value; /* Current value */
1791
1792
1793 /*
1794 * Get the requested-attributes attribute, and return NULL if we don't
1795 * have one...
1796 */
1797
1798 if ((requested = ippFindAttribute(client->request, "requested-attributes",
1799 IPP_TAG_KEYWORD)) == NULL)
1800 return (NULL);
1801
1802 /*
1803 * If the attribute contains a single "all" keyword, return NULL...
1804 */
1805
1806 if (requested->num_values == 1 &&
1807 !strcmp(requested->values[0].string.text, "all"))
1808 return (NULL);
1809
1810 /*
1811 * Create an array using "strcmp" as the comparison function...
1812 */
1813
1814 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
1815
1816 for (i = 0; i < requested->num_values; i ++)
1817 {
1818 value = requested->values[i].string.text;
1819
1820 if (!strcmp(value, "job-template"))
1821 {
1822 cupsArrayAdd(ra, "copies");
1823 cupsArrayAdd(ra, "copies-default");
1824 cupsArrayAdd(ra, "copies-supported");
1825 cupsArrayAdd(ra, "finishings");
1826 cupsArrayAdd(ra, "finishings-default");
1827 cupsArrayAdd(ra, "finishings-supported");
1828 cupsArrayAdd(ra, "job-hold-until");
1829 cupsArrayAdd(ra, "job-hold-until-default");
1830 cupsArrayAdd(ra, "job-hold-until-supported");
1831 cupsArrayAdd(ra, "job-priority");
1832 cupsArrayAdd(ra, "job-priority-default");
1833 cupsArrayAdd(ra, "job-priority-supported");
1834 cupsArrayAdd(ra, "job-sheets");
1835 cupsArrayAdd(ra, "job-sheets-default");
1836 cupsArrayAdd(ra, "job-sheets-supported");
1837 cupsArrayAdd(ra, "media");
1838 cupsArrayAdd(ra, "media-col");
1839 cupsArrayAdd(ra, "media-col-default");
1840 cupsArrayAdd(ra, "media-col-supported");
1841 cupsArrayAdd(ra, "media-default");
1842 cupsArrayAdd(ra, "media-source-supported");
1843 cupsArrayAdd(ra, "media-supported");
1844 cupsArrayAdd(ra, "media-type-supported");
1845 cupsArrayAdd(ra, "multiple-document-handling");
1846 cupsArrayAdd(ra, "multiple-document-handling-default");
1847 cupsArrayAdd(ra, "multiple-document-handling-supported");
1848 cupsArrayAdd(ra, "number-up");
1849 cupsArrayAdd(ra, "number-up-default");
1850 cupsArrayAdd(ra, "number-up-supported");
1851 cupsArrayAdd(ra, "orientation-requested");
1852 cupsArrayAdd(ra, "orientation-requested-default");
1853 cupsArrayAdd(ra, "orientation-requested-supported");
1854 cupsArrayAdd(ra, "page-ranges");
1855 cupsArrayAdd(ra, "page-ranges-supported");
1856 cupsArrayAdd(ra, "printer-resolution");
1857 cupsArrayAdd(ra, "printer-resolution-default");
1858 cupsArrayAdd(ra, "printer-resolution-supported");
1859 cupsArrayAdd(ra, "print-quality");
1860 cupsArrayAdd(ra, "print-quality-default");
1861 cupsArrayAdd(ra, "print-quality-supported");
1862 cupsArrayAdd(ra, "sides");
1863 cupsArrayAdd(ra, "sides-default");
1864 cupsArrayAdd(ra, "sides-supported");
1865 }
1866 else if (!strcmp(value, "job-description"))
1867 {
1868 cupsArrayAdd(ra, "date-time-at-completed");
1869 cupsArrayAdd(ra, "date-time-at-creation");
1870 cupsArrayAdd(ra, "date-time-at-processing");
1871 cupsArrayAdd(ra, "job-detailed-status-message");
1872 cupsArrayAdd(ra, "job-document-access-errors");
1873 cupsArrayAdd(ra, "job-id");
1874 cupsArrayAdd(ra, "job-impressions");
1875 cupsArrayAdd(ra, "job-impressions-completed");
1876 cupsArrayAdd(ra, "job-k-octets");
1877 cupsArrayAdd(ra, "job-k-octets-processed");
1878 cupsArrayAdd(ra, "job-media-sheets");
1879 cupsArrayAdd(ra, "job-media-sheets-completed");
1880 cupsArrayAdd(ra, "job-message-from-operator");
1881 cupsArrayAdd(ra, "job-more-info");
1882 cupsArrayAdd(ra, "job-name");
1883 cupsArrayAdd(ra, "job-originating-user-name");
1884 cupsArrayAdd(ra, "job-printer-up-time");
1885 cupsArrayAdd(ra, "job-printer-uri");
1886 cupsArrayAdd(ra, "job-state");
1887 cupsArrayAdd(ra, "job-state-message");
1888 cupsArrayAdd(ra, "job-state-reasons");
1889 cupsArrayAdd(ra, "job-uri");
1890 cupsArrayAdd(ra, "number-of-documents");
1891 cupsArrayAdd(ra, "number-of-intervening-jobs");
1892 cupsArrayAdd(ra, "output-device-assigned");
1893 cupsArrayAdd(ra, "time-at-completed");
1894 cupsArrayAdd(ra, "time-at-creation");
1895 cupsArrayAdd(ra, "time-at-processing");
1896 }
1897 else if (!strcmp(value, "printer-description"))
1898 {
1899 cupsArrayAdd(ra, "charset-configured");
1900 cupsArrayAdd(ra, "charset-supported");
1901 cupsArrayAdd(ra, "color-supported");
1902 cupsArrayAdd(ra, "compression-supported");
1903 cupsArrayAdd(ra, "document-format-default");
1904 cupsArrayAdd(ra, "document-format-supported");
1905 cupsArrayAdd(ra, "generated-natural-language-supported");
1906 cupsArrayAdd(ra, "ipp-versions-supported");
1907 cupsArrayAdd(ra, "job-impressions-supported");
1908 cupsArrayAdd(ra, "job-k-octets-supported");
1909 cupsArrayAdd(ra, "job-media-sheets-supported");
1910 cupsArrayAdd(ra, "multiple-document-jobs-supported");
1911 cupsArrayAdd(ra, "multiple-operation-time-out");
1912 cupsArrayAdd(ra, "natural-language-configured");
1913 cupsArrayAdd(ra, "notify-attributes-supported");
1914 cupsArrayAdd(ra, "notify-lease-duration-default");
1915 cupsArrayAdd(ra, "notify-lease-duration-supported");
1916 cupsArrayAdd(ra, "notify-max-events-supported");
1917 cupsArrayAdd(ra, "notify-events-default");
1918 cupsArrayAdd(ra, "notify-events-supported");
1919 cupsArrayAdd(ra, "notify-pull-method-supported");
1920 cupsArrayAdd(ra, "notify-schemes-supported");
1921 cupsArrayAdd(ra, "operations-supported");
1922 cupsArrayAdd(ra, "pages-per-minute");
1923 cupsArrayAdd(ra, "pages-per-minute-color");
1924 cupsArrayAdd(ra, "pdl-override-supported");
1925 cupsArrayAdd(ra, "printer-alert");
1926 cupsArrayAdd(ra, "printer-alert-description");
1927 cupsArrayAdd(ra, "printer-current-time");
1928 cupsArrayAdd(ra, "printer-driver-installer");
1929 cupsArrayAdd(ra, "printer-info");
1930 cupsArrayAdd(ra, "printer-is-accepting-jobs");
1931 cupsArrayAdd(ra, "printer-location");
1932 cupsArrayAdd(ra, "printer-make-and-model");
1933 cupsArrayAdd(ra, "printer-message-from-operator");
1934 cupsArrayAdd(ra, "printer-more-info");
1935 cupsArrayAdd(ra, "printer-more-info-manufacturer");
1936 cupsArrayAdd(ra, "printer-name");
1937 cupsArrayAdd(ra, "printer-state");
1938 cupsArrayAdd(ra, "printer-state-message");
1939 cupsArrayAdd(ra, "printer-state-reasons");
1940 cupsArrayAdd(ra, "printer-up-time");
1941 cupsArrayAdd(ra, "printer-uri-supported");
1942 cupsArrayAdd(ra, "queued-job-count");
1943 cupsArrayAdd(ra, "reference-uri-schemes-supported");
1944 cupsArrayAdd(ra, "uri-authentication-supported");
1945 cupsArrayAdd(ra, "uri-security-supported");
1946 }
1947 else if (!strcmp(value, "printer-defaults"))
1948 {
1949 cupsArrayAdd(ra, "copies-default");
1950 cupsArrayAdd(ra, "document-format-default");
1951 cupsArrayAdd(ra, "finishings-default");
1952 cupsArrayAdd(ra, "job-hold-until-default");
1953 cupsArrayAdd(ra, "job-priority-default");
1954 cupsArrayAdd(ra, "job-sheets-default");
1955 cupsArrayAdd(ra, "media-default");
1956 cupsArrayAdd(ra, "media-col-default");
1957 cupsArrayAdd(ra, "number-up-default");
1958 cupsArrayAdd(ra, "orientation-requested-default");
1959 cupsArrayAdd(ra, "sides-default");
1960 }
1961 else if (!strcmp(value, "subscription-template"))
1962 {
1963 cupsArrayAdd(ra, "notify-attributes");
1964 cupsArrayAdd(ra, "notify-charset");
1965 cupsArrayAdd(ra, "notify-events");
1966 cupsArrayAdd(ra, "notify-lease-duration");
1967 cupsArrayAdd(ra, "notify-natural-language");
1968 cupsArrayAdd(ra, "notify-pull-method");
1969 cupsArrayAdd(ra, "notify-recipient-uri");
1970 cupsArrayAdd(ra, "notify-time-interval");
1971 cupsArrayAdd(ra, "notify-user-data");
1972 }
1973 else
1974 cupsArrayAdd(ra, value);
1975 }
1976
1977 return (ra);
1978 }
1979
1980
1981 /*
1982 * 'debug_attributes()' - Print attributes in a request or response.
1983 */
1984
1985 static void
1986 debug_attributes(const char *title, /* I - Title */
1987 ipp_t *ipp) /* I - Request/response */
1988 {
1989 ipp_tag_t group_tag; /* Current group */
1990 ipp_attribute_t *attr; /* Current attribute */
1991 char buffer[2048]; /* String buffer for value */
1992
1993
1994 if (Verbosity <= 1)
1995 return;
1996
1997 fprintf(stderr, "%s:\n", title);
1998 for (attr = ipp->attrs, group_tag = IPP_TAG_ZERO; attr; attr = attr->next)
1999 {
2000 if (attr->group_tag != group_tag)
2001 {
2002 group_tag = attr->group_tag;
2003 fprintf(stderr, " %s\n", ippTagString(group_tag));
2004 }
2005
2006 if (attr->name)
2007 {
2008 _ippAttrString(attr, buffer, sizeof(buffer));
2009 fprintf(stderr, " %s (%s%s) %s\n", attr->name,
2010 attr->num_values > 1 ? "1setOf " : "",
2011 ippTagString(attr->value_tag), buffer);
2012 }
2013 }
2014 }
2015
2016
2017 /*
2018 * 'delete_client()' - Close the socket and free all memory used by a client
2019 * object.
2020 */
2021
2022 static void
2023 delete_client(_ipp_client_t *client) /* I - Client */
2024 {
2025 if (Verbosity)
2026 fprintf(stderr, "Closing connection from %s (%s)\n", client->http.hostname,
2027 client->http.hostaddr->addr.sa_family == AF_INET ? "IPv4" : "IPv6");
2028
2029 /*
2030 * Flush pending writes before closing...
2031 */
2032
2033 httpFlushWrite(&(client->http));
2034
2035 if (client->http.fd >= 0)
2036 close(client->http.fd);
2037
2038 /*
2039 * Free memory...
2040 */
2041
2042 httpClearCookie(&(client->http));
2043 httpClearFields(&(client->http));
2044
2045 ippDelete(client->request);
2046
2047 ippDelete(client->response);
2048
2049 free(client);
2050 }
2051
2052
2053 /*
2054 * 'delete_job()' - Remove from the printer and free all memory used by a job
2055 * object.
2056 */
2057
2058 static void
2059 delete_job(_ipp_job_t *job) /* I - Job */
2060 {
2061 if (Verbosity)
2062 fprintf(stderr, "Removing job #%d from history.\n", job->id);
2063
2064 ippDelete(job->attrs);
2065
2066 if (job->filename)
2067 {
2068 if (!KeepFiles)
2069 unlink(job->filename);
2070
2071 free(job->filename);
2072 }
2073
2074 free(job);
2075 }
2076
2077
2078 /*
2079 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2080 * used by a printer object.
2081 */
2082
2083 static void
2084 delete_printer(_ipp_printer_t *printer) /* I - Printer */
2085 {
2086 if (printer->ipv4 >= 0)
2087 close(printer->ipv4);
2088
2089 if (printer->ipv6 >= 0)
2090 close(printer->ipv6);
2091
2092 #if HAVE_DNSSD
2093 if (printer->printer_ref)
2094 DNSServiceRefDeallocate(printer->printer_ref);
2095
2096 if (printer->ipp_ref)
2097 DNSServiceRefDeallocate(printer->ipp_ref);
2098
2099 if (printer->http_ref)
2100 DNSServiceRefDeallocate(printer->http_ref);
2101
2102 if (printer->common_ref)
2103 DNSServiceRefDeallocate(printer->common_ref);
2104
2105 TXTRecordDeallocate(&(printer->ipp_txt));
2106
2107 if (printer->dnssd_name)
2108 _cupsStrFree(printer->dnssd_name);
2109 #endif /* HAVE_DNSSD */
2110
2111 if (printer->name)
2112 _cupsStrFree(printer->name);
2113 if (printer->icon)
2114 _cupsStrFree(printer->icon);
2115 if (printer->directory)
2116 _cupsStrFree(printer->directory);
2117 if (printer->hostname)
2118 _cupsStrFree(printer->hostname);
2119 if (printer->uri)
2120 _cupsStrFree(printer->uri);
2121
2122 ippDelete(printer->attrs);
2123 cupsArrayDelete(printer->jobs);
2124
2125 free(printer);
2126 }
2127
2128
2129 #ifdef HAVE_DNSSD
2130 /*
2131 * 'dnssd_callback()' - Handle Bonjour registration events.
2132 */
2133
2134 static void
2135 dnssd_callback(
2136 DNSServiceRef sdRef, /* I - Service reference */
2137 DNSServiceFlags flags, /* I - Status flags */
2138 DNSServiceErrorType errorCode, /* I - Error, if any */
2139 const char *name, /* I - Service name */
2140 const char *regtype, /* I - Service type */
2141 const char *domain, /* I - Domain for service */
2142 _ipp_printer_t *printer) /* I - Printer */
2143 {
2144 if (errorCode)
2145 {
2146 fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n",
2147 regtype, (int)errorCode);
2148 return;
2149 }
2150 else if (_cups_strcasecmp(name, printer->dnssd_name))
2151 {
2152 if (Verbosity)
2153 fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name);
2154
2155 /* No lock needed since only the main thread accesses/changes this */
2156 _cupsStrFree(printer->dnssd_name);
2157 printer->dnssd_name = _cupsStrAlloc(name);
2158 }
2159 }
2160 #endif /* HAVE_DNSSD */
2161
2162
2163 /*
2164 * 'find_job()' - Find a job specified in a request.
2165 */
2166
2167 static _ipp_job_t * /* O - Job or NULL */
2168 find_job(_ipp_client_t *client) /* I - Client */
2169 {
2170 ipp_attribute_t *attr; /* job-id or job-uri attribute */
2171 _ipp_job_t key, /* Job search key */
2172 *job; /* Matching job, if any */
2173
2174
2175 key.id = 0;
2176
2177 if ((attr = ippFindAttribute(client->request, "job-uri",
2178 IPP_TAG_URI)) != NULL)
2179 {
2180 if (!strncmp(attr->values[0].string.text, client->printer->uri,
2181 client->printer->urilen) &&
2182 attr->values[0].string.text[client->printer->urilen] == '/')
2183 key.id = atoi(attr->values[0].string.text + client->printer->urilen + 1);
2184 }
2185 else if ((attr = ippFindAttribute(client->request, "job-id",
2186 IPP_TAG_INTEGER)) != NULL)
2187 key.id = attr->values[0].integer;
2188
2189 _cupsRWLockRead(&(client->printer->rwlock));
2190 job = (_ipp_job_t *)cupsArrayFind(client->printer->jobs, &key);
2191 _cupsRWUnlock(&(client->printer->rwlock));
2192
2193 return (job);
2194 }
2195
2196
2197 /*
2198 * 'html_escape()' - Write a HTML-safe string.
2199 */
2200
2201 static void
2202 html_escape(_ipp_client_t *client, /* I - Client */
2203 const char *s, /* I - String to write */
2204 size_t slen) /* I - Number of characters to write */
2205 {
2206 const char *start, /* Start of segment */
2207 *end; /* End of string */
2208
2209
2210 start = s;
2211 end = s + (slen > 0 ? slen : strlen(s));
2212
2213 while (*s && s < end)
2214 {
2215 if (*s == '&' || *s == '<')
2216 {
2217 if (s > start)
2218 httpWrite2(&(client->http), start, s - start);
2219
2220 if (*s == '&')
2221 httpWrite2(&(client->http), "&amp;", 5);
2222 else
2223 httpWrite2(&(client->http), "&lt;", 4);
2224
2225 start = s + 1;
2226 }
2227
2228 s ++;
2229 }
2230
2231 if (s > start)
2232 httpWrite2(&(client->http), start, s - start);
2233 }
2234
2235
2236 /*
2237 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2238 */
2239
2240 static void
2241 html_printf(_ipp_client_t *client, /* I - Client */
2242 const char *format, /* I - Printf-style format string */
2243 ...) /* I - Additional arguments as needed */
2244 {
2245 va_list ap; /* Pointer to arguments */
2246 const char *start; /* Start of string */
2247 char size, /* Size character (h, l, L) */
2248 type; /* Format type character */
2249 int width, /* Width of field */
2250 prec; /* Number of characters of precision */
2251 char tformat[100], /* Temporary format string for sprintf() */
2252 *tptr, /* Pointer into temporary format */
2253 temp[1024]; /* Buffer for formatted numbers */
2254 char *s; /* Pointer to string */
2255
2256
2257 /*
2258 * Loop through the format string, formatting as needed...
2259 */
2260
2261 va_start(ap, format);
2262 start = format;
2263
2264 while (*format)
2265 {
2266 if (*format == '%')
2267 {
2268 if (format > start)
2269 httpWrite2(&(client->http), start, format - start);
2270
2271 tptr = tformat;
2272 *tptr++ = *format++;
2273
2274 if (*format == '%')
2275 {
2276 httpWrite2(&(client->http), "%", 1);
2277 format ++;
2278 continue;
2279 }
2280 else if (strchr(" -+#\'", *format))
2281 *tptr++ = *format++;
2282
2283 if (*format == '*')
2284 {
2285 /*
2286 * Get width from argument...
2287 */
2288
2289 format ++;
2290 width = va_arg(ap, int);
2291
2292 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
2293 tptr += strlen(tptr);
2294 }
2295 else
2296 {
2297 width = 0;
2298
2299 while (isdigit(*format & 255))
2300 {
2301 if (tptr < (tformat + sizeof(tformat) - 1))
2302 *tptr++ = *format;
2303
2304 width = width * 10 + *format++ - '0';
2305 }
2306 }
2307
2308 if (*format == '.')
2309 {
2310 if (tptr < (tformat + sizeof(tformat) - 1))
2311 *tptr++ = *format;
2312
2313 format ++;
2314
2315 if (*format == '*')
2316 {
2317 /*
2318 * Get precision from argument...
2319 */
2320
2321 format ++;
2322 prec = va_arg(ap, int);
2323
2324 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
2325 tptr += strlen(tptr);
2326 }
2327 else
2328 {
2329 prec = 0;
2330
2331 while (isdigit(*format & 255))
2332 {
2333 if (tptr < (tformat + sizeof(tformat) - 1))
2334 *tptr++ = *format;
2335
2336 prec = prec * 10 + *format++ - '0';
2337 }
2338 }
2339 }
2340
2341 if (*format == 'l' && format[1] == 'l')
2342 {
2343 size = 'L';
2344
2345 if (tptr < (tformat + sizeof(tformat) - 2))
2346 {
2347 *tptr++ = 'l';
2348 *tptr++ = 'l';
2349 }
2350
2351 format += 2;
2352 }
2353 else if (*format == 'h' || *format == 'l' || *format == 'L')
2354 {
2355 if (tptr < (tformat + sizeof(tformat) - 1))
2356 *tptr++ = *format;
2357
2358 size = *format++;
2359 }
2360 else
2361 size = 0;
2362
2363
2364 if (!*format)
2365 {
2366 start = format;
2367 break;
2368 }
2369
2370 if (tptr < (tformat + sizeof(tformat) - 1))
2371 *tptr++ = *format;
2372
2373 type = *format++;
2374 *tptr = '\0';
2375 start = format;
2376
2377 switch (type)
2378 {
2379 case 'E' : /* Floating point formats */
2380 case 'G' :
2381 case 'e' :
2382 case 'f' :
2383 case 'g' :
2384 if ((width + 2) > sizeof(temp))
2385 break;
2386
2387 sprintf(temp, tformat, va_arg(ap, double));
2388
2389 httpWrite2(&(client->http), temp, strlen(temp));
2390 break;
2391
2392 case 'B' : /* Integer formats */
2393 case 'X' :
2394 case 'b' :
2395 case 'd' :
2396 case 'i' :
2397 case 'o' :
2398 case 'u' :
2399 case 'x' :
2400 if ((width + 2) > sizeof(temp))
2401 break;
2402
2403 # ifdef HAVE_LONG_LONG
2404 if (size == 'L')
2405 sprintf(temp, tformat, va_arg(ap, long long));
2406 else
2407 # endif /* HAVE_LONG_LONG */
2408 if (size == 'l')
2409 sprintf(temp, tformat, va_arg(ap, long));
2410 else
2411 sprintf(temp, tformat, va_arg(ap, int));
2412
2413 httpWrite2(&(client->http), temp, strlen(temp));
2414 break;
2415
2416 case 'p' : /* Pointer value */
2417 if ((width + 2) > sizeof(temp))
2418 break;
2419
2420 sprintf(temp, tformat, va_arg(ap, void *));
2421
2422 httpWrite2(&(client->http), temp, strlen(temp));
2423 break;
2424
2425 case 'c' : /* Character or character array */
2426 if (width <= 1)
2427 {
2428 temp[0] = va_arg(ap, int);
2429 temp[1] = '\0';
2430 html_escape(client, temp, 1);
2431 }
2432 else
2433 html_escape(client, va_arg(ap, char *), (size_t)width);
2434 break;
2435
2436 case 's' : /* String */
2437 if ((s = va_arg(ap, char *)) == NULL)
2438 s = "(null)";
2439
2440 html_escape(client, s, strlen(s));
2441 break;
2442 }
2443 }
2444 else
2445 format ++;
2446 }
2447
2448 if (format > start)
2449 httpWrite2(&(client->http), start, format - start);
2450
2451 va_end(ap);
2452 }
2453
2454
2455 /*
2456 * 'ipp_cancel_job()' - Cancel a job.
2457 */
2458
2459 static void
2460 ipp_cancel_job(_ipp_client_t *client) /* I - Client */
2461 {
2462 _ipp_job_t *job; /* Job information */
2463
2464
2465 /*
2466 * Get the job...
2467 */
2468
2469 if ((job = find_job(client)) == NULL)
2470 return;
2471
2472 /*
2473 * See if the job is already completed, canceled, or aborted; if so,
2474 * we can't cancel...
2475 */
2476
2477 switch (job->state)
2478 {
2479 case IPP_JOB_CANCELED :
2480 respond_ipp(client, IPP_NOT_POSSIBLE,
2481 "Job #%d is already canceled - can\'t cancel.", job->id);
2482 break;
2483
2484 case IPP_JOB_ABORTED :
2485 respond_ipp(client, IPP_NOT_POSSIBLE,
2486 "Job #%d is already aborted - can\'t cancel.", job->id);
2487 break;
2488
2489 case IPP_JOB_COMPLETED :
2490 respond_ipp(client, IPP_NOT_POSSIBLE,
2491 "Job #%d is already completed - can\'t cancel.", job->id);
2492 break;
2493
2494 default :
2495 /*
2496 * Cancel the job...
2497 */
2498
2499 _cupsRWLockWrite(&(client->printer->rwlock));
2500
2501 if (job->state == IPP_JOB_PROCESSING ||
2502 (job->state == IPP_JOB_HELD && job->fd >= 0))
2503 job->cancel = 1;
2504 else
2505 {
2506 job->state = IPP_JOB_CANCELED;
2507 job->completed = time(NULL);
2508 }
2509
2510 _cupsRWUnlock(&(client->printer->rwlock));
2511
2512 respond_ipp(client, IPP_OK, NULL);
2513 break;
2514 }
2515 }
2516
2517
2518 #if 0
2519 /*
2520 * 'ipp_create_job()' - Create a job object.
2521 */
2522
2523 static void
2524 ipp_create_job(_ipp_client_t *client) /* I - Client */
2525 {
2526 }
2527 #endif /* 0 */
2528
2529
2530 /*
2531 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2532 */
2533
2534 static void
2535 ipp_get_job_attributes(
2536 _ipp_client_t *client) /* I - Client */
2537 {
2538 _ipp_job_t *job; /* Job */
2539 cups_array_t *ra; /* requested-attributes */
2540
2541
2542 if ((job = find_job(client)) == NULL)
2543 {
2544 respond_ipp(client, IPP_NOT_FOUND, "Job not found.");
2545 return;
2546 }
2547
2548 respond_ipp(client, IPP_OK, NULL);
2549
2550 ra = create_requested_array(client);
2551 copy_job_attributes(client, job, ra);
2552 cupsArrayDelete(ra);
2553 }
2554
2555
2556 /*
2557 * 'ipp_get_jobs()' - Get a list of job objects.
2558 */
2559
2560 static void
2561 ipp_get_jobs(_ipp_client_t *client) /* I - Client */
2562 {
2563 ipp_attribute_t *attr; /* Current attribute */
2564 int job_comparison; /* Job comparison */
2565 ipp_jstate_t job_state; /* job-state value */
2566 int first_job_id, /* First job ID */
2567 limit, /* Maximum number of jobs to return */
2568 count; /* Number of jobs that match */
2569 const char *username; /* Username */
2570 _ipp_job_t *job; /* Current job pointer */
2571 cups_array_t *ra; /* Requested attributes array */
2572
2573
2574 /*
2575 * See if the "which-jobs" attribute have been specified...
2576 */
2577
2578 if ((attr = ippFindAttribute(client->request, "which-jobs",
2579 IPP_TAG_KEYWORD)) != NULL)
2580 fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->http.hostname,
2581 attr->values[0].string.text);
2582
2583 if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
2584 {
2585 job_comparison = -1;
2586 job_state = IPP_JOB_STOPPED;
2587 }
2588 else if (!strcmp(attr->values[0].string.text, "completed"))
2589 {
2590 job_comparison = 1;
2591 job_state = IPP_JOB_CANCELED;
2592 }
2593 else if (!strcmp(attr->values[0].string.text, "aborted"))
2594 {
2595 job_comparison = 0;
2596 job_state = IPP_JOB_ABORTED;
2597 }
2598 else if (!strcmp(attr->values[0].string.text, "all"))
2599 {
2600 job_comparison = 1;
2601 job_state = IPP_JOB_PENDING;
2602 }
2603 else if (!strcmp(attr->values[0].string.text, "canceled"))
2604 {
2605 job_comparison = 0;
2606 job_state = IPP_JOB_CANCELED;
2607 }
2608 else if (!strcmp(attr->values[0].string.text, "pending"))
2609 {
2610 job_comparison = 0;
2611 job_state = IPP_JOB_PENDING;
2612 }
2613 else if (!strcmp(attr->values[0].string.text, "pending-held"))
2614 {
2615 job_comparison = 0;
2616 job_state = IPP_JOB_HELD;
2617 }
2618 else if (!strcmp(attr->values[0].string.text, "processing"))
2619 {
2620 job_comparison = 0;
2621 job_state = IPP_JOB_PROCESSING;
2622 }
2623 else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
2624 {
2625 job_comparison = 0;
2626 job_state = IPP_JOB_STOPPED;
2627 }
2628 else
2629 {
2630 respond_ipp(client, IPP_ATTRIBUTES,
2631 "The which-jobs value \"%s\" is not supported.",
2632 attr->values[0].string.text);
2633 ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
2634 "which-jobs", NULL, attr->values[0].string.text);
2635 return;
2636 }
2637
2638 /*
2639 * See if they want to limit the number of jobs reported...
2640 */
2641
2642 if ((attr = ippFindAttribute(client->request, "limit",
2643 IPP_TAG_INTEGER)) != NULL)
2644 {
2645 limit = attr->values[0].integer;
2646
2647 fprintf(stderr, "%s Get-Jobs limit=%d", client->http.hostname, limit);
2648 }
2649 else
2650 limit = 0;
2651
2652 if ((attr = ippFindAttribute(client->request, "first-job-id",
2653 IPP_TAG_INTEGER)) != NULL)
2654 {
2655 first_job_id = attr->values[0].integer;
2656
2657 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->http.hostname,
2658 first_job_id);
2659 }
2660 else
2661 first_job_id = 1;
2662
2663 /*
2664 * See if we only want to see jobs for a specific user...
2665 */
2666
2667 username = NULL;
2668
2669 if ((attr = ippFindAttribute(client->request, "my-jobs",
2670 IPP_TAG_BOOLEAN)) != NULL)
2671 {
2672 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->http.hostname,
2673 attr->values[0].boolean ? "true" : "false");
2674
2675 if (attr->values[0].boolean)
2676 {
2677 if ((attr = ippFindAttribute(client->request, "requesting-user-name",
2678 IPP_TAG_NAME)) == NULL)
2679 {
2680 respond_ipp(client, IPP_BAD_REQUEST,
2681 "Need requesting-user-name with my-jobs.");
2682 return;
2683 }
2684
2685 username = attr->values[0].string.text;
2686
2687 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2688 client->http.hostname, username);
2689 }
2690 }
2691
2692 /*
2693 * OK, build a list of jobs for this printer...
2694 */
2695
2696 if ((ra = create_requested_array(client)) == NULL &&
2697 !ippFindAttribute(client->request, "requested-attributes",
2698 IPP_TAG_KEYWORD))
2699 {
2700 /*
2701 * IPP conformance - Get-Jobs has a default requested-attributes value of
2702 * "job-id" and "job-uri".
2703 */
2704
2705 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2706 cupsArrayAdd(ra, "job-id");
2707 cupsArrayAdd(ra, "job-uri");
2708 }
2709
2710 respond_ipp(client, IPP_OK, NULL);
2711
2712 _cupsRWLockRead(&(client->printer->rwlock));
2713
2714 for (count = 0, job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs);
2715 (limit <= 0 || count < limit) && job;
2716 job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
2717 {
2718 /*
2719 * Filter out jobs that don't match...
2720 */
2721
2722 if ((job_comparison < 0 && job->state > job_state) ||
2723 (job_comparison == 0 && job->state != job_state) ||
2724 (job_comparison > 0 && job->state < job_state) ||
2725 job->id < first_job_id ||
2726 (username && job->username && _cups_strcasecmp(username, job->username)))
2727 continue;
2728
2729 if (count > 0)
2730 ippAddSeparator(client->response);
2731
2732 count ++;
2733 copy_job_attributes(client, job, ra);
2734 }
2735
2736 cupsArrayDelete(ra);
2737
2738 _cupsRWUnlock(&(client->printer->rwlock));
2739 }
2740
2741
2742 /*
2743 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2744 */
2745
2746 static void
2747 ipp_get_printer_attributes(
2748 _ipp_client_t *client) /* I - Client */
2749 {
2750 cups_array_t *ra; /* Requested attributes array */
2751 _ipp_printer_t *printer; /* Printer */
2752
2753
2754 /*
2755 * Send the attributes...
2756 */
2757
2758 ra = create_requested_array(client);
2759 printer = client->printer;
2760
2761 respond_ipp(client, IPP_OK, NULL);
2762
2763 _cupsRWLockRead(&(printer->rwlock));
2764
2765 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
2766 IPP_TAG_COPY);
2767
2768 if (!ra || cupsArrayFind(ra, "printer-state"))
2769 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
2770 "printer-state", printer->state);
2771
2772 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
2773 {
2774 if (printer->state_reasons == _IPP_PRINTER_NONE)
2775 ippAddString(client->response, IPP_TAG_PRINTER,
2776 IPP_TAG_KEYWORD | IPP_TAG_COPY, "printer-state-reasons",
2777 NULL, "none");
2778 else
2779 {
2780 int num_reasons = 0;/* Number of reasons */
2781 const char *reasons[32]; /* Reason strings */
2782
2783 if (printer->state_reasons & _IPP_PRINTER_OTHER)
2784 reasons[num_reasons ++] = "other";
2785 if (printer->state_reasons & _IPP_PRINTER_COVER_OPEN)
2786 reasons[num_reasons ++] = "cover-open";
2787 if (printer->state_reasons & _IPP_PRINTER_INPUT_TRAY_MISSING)
2788 reasons[num_reasons ++] = "input-tray-missing";
2789 if (printer->state_reasons & _IPP_PRINTER_MARKER_SUPPLY_EMPTY)
2790 reasons[num_reasons ++] = "marker-supply-empty-warning";
2791 if (printer->state_reasons & _IPP_PRINTER_MARKER_SUPPLY_LOW)
2792 reasons[num_reasons ++] = "marker-supply-low-report";
2793 if (printer->state_reasons & _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL)
2794 reasons[num_reasons ++] = "marker-waste-almost-full-report";
2795 if (printer->state_reasons & _IPP_PRINTER_MARKER_WASTE_FULL)
2796 reasons[num_reasons ++] = "marker-waste-full-warning";
2797 if (printer->state_reasons & _IPP_PRINTER_MEDIA_EMPTY)
2798 reasons[num_reasons ++] = "media-empty-warning";
2799 if (printer->state_reasons & _IPP_PRINTER_MEDIA_JAM)
2800 reasons[num_reasons ++] = "media-jam-warning";
2801 if (printer->state_reasons & _IPP_PRINTER_MEDIA_LOW)
2802 reasons[num_reasons ++] = "media-low-report";
2803 if (printer->state_reasons & _IPP_PRINTER_MEDIA_NEEDED)
2804 reasons[num_reasons ++] = "media-needed-report";
2805 if (printer->state_reasons & _IPP_PRINTER_MOVING_TO_PAUSED)
2806 reasons[num_reasons ++] = "moving-to-paused";
2807 if (printer->state_reasons & _IPP_PRINTER_PAUSED)
2808 reasons[num_reasons ++] = "paused";
2809 if (printer->state_reasons & _IPP_PRINTER_SPOOL_AREA_FULL)
2810 reasons[num_reasons ++] = "spool-area-full";
2811 if (printer->state_reasons & _IPP_PRINTER_TONER_EMPTY)
2812 reasons[num_reasons ++] = "toner-empty-warning";
2813 if (printer->state_reasons & _IPP_PRINTER_TONER_LOW)
2814 reasons[num_reasons ++] = "toner-low-report";
2815
2816 ippAddStrings(client->response, IPP_TAG_PRINTER,
2817 IPP_TAG_KEYWORD | IPP_TAG_COPY, "printer-state-reasons",
2818 num_reasons, NULL, reasons);
2819 }
2820 }
2821
2822 if (!ra || cupsArrayFind(ra, "printer-up-time"))
2823 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2824 "printer-up-time", (int)time(NULL));
2825
2826 if (!ra || cupsArrayFind(ra, "queued-job-count"))
2827 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2828 "queued-job-count",
2829 printer->active_job &&
2830 printer->active_job->state < IPP_JOB_CANCELED);
2831
2832 _cupsRWUnlock(&(printer->rwlock));
2833
2834 cupsArrayDelete(ra);
2835 }
2836
2837
2838 /*
2839 * 'ipp_print_job()' - Create a job object with an attached document.
2840 */
2841
2842 static void
2843 ipp_print_job(_ipp_client_t *client) /* I - Client */
2844 {
2845 _ipp_job_t *job; /* New job */
2846 char filename[1024], /* Filename buffer */
2847 buffer[4096]; /* Copy buffer */
2848 ssize_t bytes; /* Bytes read */
2849 cups_array_t *ra; /* Attributes to send in response */
2850
2851
2852 /*
2853 * Validate print job attributes...
2854 */
2855
2856 if (!valid_job_attributes(client))
2857 {
2858 httpFlush(&(client->http));
2859 return;
2860 }
2861
2862 /*
2863 * Do we have a file to print?
2864 */
2865
2866 if (client->http.state == HTTP_POST_SEND)
2867 {
2868 respond_ipp(client, IPP_BAD_REQUEST, "No file in request.");
2869 return;
2870 }
2871
2872 /*
2873 * Print the job...
2874 */
2875
2876 if ((job = create_job(client)) == NULL)
2877 {
2878 respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
2879 return;
2880 }
2881
2882 /*
2883 * Create a file for the request data...
2884 */
2885
2886 if (!_cups_strcasecmp(job->format, "image/jpeg"))
2887 snprintf(filename, sizeof(filename), "%s/%d.jpg",
2888 client->printer->directory, job->id);
2889 else if (!_cups_strcasecmp(job->format, "image/png"))
2890 snprintf(filename, sizeof(filename), "%s/%d.png",
2891 client->printer->directory, job->id);
2892 else if (!_cups_strcasecmp(job->format, "application/pdf"))
2893 snprintf(filename, sizeof(filename), "%s/%d.pdf",
2894 client->printer->directory, job->id);
2895 else if (!_cups_strcasecmp(job->format, "application/postscript"))
2896 snprintf(filename, sizeof(filename), "%s/%d.ps",
2897 client->printer->directory, job->id);
2898 else
2899 snprintf(filename, sizeof(filename), "%s/%d.prn",
2900 client->printer->directory, job->id);
2901
2902 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
2903 {
2904 job->state = IPP_JOB_ABORTED;
2905
2906 respond_ipp(client, IPP_INTERNAL_ERROR,
2907 "Unable to create print file: %s", strerror(errno));
2908 return;
2909 }
2910
2911 while ((bytes = httpRead2(&(client->http), buffer, sizeof(buffer))) > 0)
2912 {
2913 if (write(job->fd, buffer, bytes) < bytes)
2914 {
2915 int error = errno; /* Write error */
2916
2917 job->state = IPP_JOB_ABORTED;
2918
2919 close(job->fd);
2920 job->fd = -1;
2921
2922 unlink(filename);
2923
2924 respond_ipp(client, IPP_INTERNAL_ERROR,
2925 "Unable to write print file: %s", strerror(error));
2926 return;
2927 }
2928 }
2929
2930 if (bytes < 0)
2931 {
2932 /*
2933 * Got an error while reading the print data, so abort this job.
2934 */
2935
2936 job->state = IPP_JOB_ABORTED;
2937
2938 close(job->fd);
2939 job->fd = -1;
2940
2941 unlink(filename);
2942
2943 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to read print file.");
2944 return;
2945 }
2946
2947 if (close(job->fd))
2948 {
2949 int error = errno; /* Write error */
2950
2951 job->state = IPP_JOB_ABORTED;
2952 job->fd = -1;
2953
2954 unlink(filename);
2955
2956 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
2957 strerror(error));
2958 return;
2959 }
2960
2961 job->fd = -1;
2962 job->filename = strdup(filename);
2963 job->state = IPP_JOB_PENDING;
2964
2965 /*
2966 * Process the job...
2967 */
2968
2969 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
2970 {
2971 job->state = IPP_JOB_ABORTED;
2972 respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
2973 return;
2974 }
2975
2976 /*
2977 * Return the job info...
2978 */
2979
2980 respond_ipp(client, IPP_OK, NULL);
2981
2982 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2983 cupsArrayAdd(ra, "job-id");
2984 cupsArrayAdd(ra, "job-state");
2985 cupsArrayAdd(ra, "job-state-reasons");
2986 cupsArrayAdd(ra, "job-uri");
2987
2988 copy_job_attributes(client, job, ra);
2989 cupsArrayDelete(ra);
2990 }
2991
2992
2993 #if 0
2994 /*
2995 * 'ipp_send_document()' - Add an attached document to a job object created with
2996 * Create-Job.
2997 */
2998
2999 static void
3000 ipp_send_document(_ipp_client_t *client)/* I - Client */
3001 {
3002 }
3003 #endif /* 0 */
3004
3005
3006 /*
3007 * 'ipp_validate_job()' - Validate job creation attributes.
3008 */
3009
3010 static void
3011 ipp_validate_job(_ipp_client_t *client) /* I - Client */
3012 {
3013 }
3014
3015
3016 /*
3017 * 'process_client()' - Process client requests on a thread.
3018 */
3019
3020 static void * /* O - Exit status */
3021 process_client(_ipp_client_t *client) /* I - Client */
3022 {
3023 /*
3024 * Loop until we are out of requests or timeout (30 seconds)...
3025 */
3026
3027 while (httpWait(&(client->http), 30000))
3028 if (!process_http(client))
3029 break;
3030
3031 /*
3032 * Close the conection to the client and return...
3033 */
3034
3035 delete_client(client);
3036
3037 return (NULL);
3038 }
3039
3040
3041 /*
3042 * 'process_http()' - Process a HTTP request.
3043 */
3044
3045 int /* O - 1 on success, 0 on failure */
3046 process_http(_ipp_client_t *client) /* I - Client connection */
3047 {
3048 char line[4096], /* Line from client... */
3049 operation[64], /* Operation code from socket */
3050 uri[1024], /* URI */
3051 version[64], /* HTTP version number string */
3052 *ptr; /* Pointer into strings */
3053 int major, minor; /* HTTP version numbers */
3054 http_status_t status; /* Transfer status */
3055 ipp_state_t state; /* State of IPP transfer */
3056
3057
3058 /*
3059 * Abort if we have an error on the connection...
3060 */
3061
3062 if (client->http.error)
3063 return (0);
3064
3065 /*
3066 * Clear state variables...
3067 */
3068
3069 httpClearFields(&(client->http));
3070 ippDelete(client->request);
3071 ippDelete(client->response);
3072
3073 client->http.activity = time(NULL);
3074 client->http.version = HTTP_1_1;
3075 client->http.keep_alive = HTTP_KEEPALIVE_OFF;
3076 client->http.data_encoding = HTTP_ENCODE_LENGTH;
3077 client->http.data_remaining = 0;
3078 client->request = NULL;
3079 client->response = NULL;
3080 client->operation = HTTP_WAITING;
3081
3082 /*
3083 * Read a request from the connection...
3084 */
3085
3086 while ((ptr = httpGets(line, sizeof(line) - 1, &(client->http))) != NULL)
3087 if (*ptr)
3088 break;
3089
3090 if (!ptr)
3091 return (0);
3092
3093 /*
3094 * Parse the request line...
3095 */
3096
3097 fprintf(stderr, "%s %s\n", client->http.hostname, line);
3098
3099 switch (sscanf(line, "%63s%1023s%63s", operation, uri, version))
3100 {
3101 case 1 :
3102 fprintf(stderr, "%s Bad request line.\n", client->http.hostname);
3103 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
3104 return (0);
3105
3106 case 2 :
3107 client->http.version = HTTP_0_9;
3108 break;
3109
3110 case 3 :
3111 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
3112 {
3113 fprintf(stderr, "%s Bad HTTP version.\n", client->http.hostname);
3114 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
3115 return (0);
3116 }
3117
3118 if (major < 2)
3119 {
3120 client->http.version = (http_version_t)(major * 100 + minor);
3121 if (client->http.version == HTTP_1_1)
3122 client->http.keep_alive = HTTP_KEEPALIVE_ON;
3123 else
3124 client->http.keep_alive = HTTP_KEEPALIVE_OFF;
3125 }
3126 else
3127 {
3128 respond_http(client, HTTP_NOT_SUPPORTED, NULL, 0);
3129 return (0);
3130 }
3131 break;
3132 }
3133
3134 /*
3135 * Handle full URLs in the request line...
3136 */
3137
3138 if (!strncmp(client->uri, "http:", 5) || !strncmp(client->uri, "ipp:", 4))
3139 {
3140 char scheme[32], /* Method/scheme */
3141 userpass[128], /* Username:password */
3142 hostname[HTTP_MAX_HOST];/* Hostname */
3143 int port; /* Port number */
3144
3145 /*
3146 * Separate the URI into its components...
3147 */
3148
3149 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
3150 userpass, sizeof(userpass),
3151 hostname, sizeof(hostname), &port,
3152 client->uri, sizeof(client->uri)) < HTTP_URI_OK)
3153 {
3154 fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
3155 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
3156 return (0);
3157 }
3158 }
3159 else
3160 {
3161 /*
3162 * Decode URI
3163 */
3164
3165 if (!_httpDecodeURI(client->uri, uri, sizeof(client->uri)))
3166 {
3167 fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
3168 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
3169 return (0);
3170 }
3171 }
3172
3173 /*
3174 * Process the request...
3175 */
3176
3177 if (!strcmp(operation, "GET"))
3178 client->http.state = HTTP_GET;
3179 else if (!strcmp(operation, "POST"))
3180 client->http.state = HTTP_POST;
3181 else if (!strcmp(operation, "OPTIONS"))
3182 client->http.state = HTTP_OPTIONS;
3183 else if (!strcmp(operation, "HEAD"))
3184 client->http.state = HTTP_HEAD;
3185 else
3186 {
3187 fprintf(stderr, "%s Bad operation \"%s\".\n", client->http.hostname,
3188 operation);
3189 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
3190 return (0);
3191 }
3192
3193 client->start = time(NULL);
3194 client->operation = client->http.state;
3195 client->http.status = HTTP_OK;
3196
3197 /*
3198 * Parse incoming parameters until the status changes...
3199 */
3200
3201 while ((status = httpUpdate(&(client->http))) == HTTP_CONTINUE);
3202
3203 if (status != HTTP_OK)
3204 {
3205 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
3206 return (0);
3207 }
3208
3209 if (!client->http.fields[HTTP_FIELD_HOST][0] &&
3210 client->http.version >= HTTP_1_1)
3211 {
3212 /*
3213 * HTTP/1.1 and higher require the "Host:" field...
3214 */
3215
3216 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
3217 return (0);
3218 }
3219
3220 /*
3221 * Handle HTTP Upgrade...
3222 */
3223
3224 if (!_cups_strcasecmp(client->http.fields[HTTP_FIELD_CONNECTION], "Upgrade"))
3225 {
3226 if (!respond_http(client, HTTP_NOT_IMPLEMENTED, NULL, 0))
3227 return (0);
3228 }
3229
3230 /*
3231 * Handle HTTP Expect...
3232 */
3233
3234 if (client->http.expect &&
3235 (client->operation == HTTP_POST || client->operation == HTTP_PUT))
3236 {
3237 if (client->http.expect == HTTP_CONTINUE)
3238 {
3239 /*
3240 * Send 100-continue header...
3241 */
3242
3243 if (!respond_http(client, HTTP_CONTINUE, NULL, 0))
3244 return (0);
3245 }
3246 else
3247 {
3248 /*
3249 * Send 417-expectation-failed header...
3250 */
3251
3252 if (!respond_http(client, HTTP_EXPECTATION_FAILED, NULL, 0))
3253 return (0);
3254
3255 httpPrintf(&(client->http), "Content-Length: 0\r\n");
3256 httpPrintf(&(client->http), "\r\n");
3257 httpFlushWrite(&(client->http));
3258 client->http.data_encoding = HTTP_ENCODE_LENGTH;
3259 }
3260 }
3261
3262 /*
3263 * Handle new transfers...
3264 */
3265
3266 switch (client->operation)
3267 {
3268 case HTTP_OPTIONS :
3269 /*
3270 * Do HEAD/OPTIONS command...
3271 */
3272
3273 return (respond_http(client, HTTP_OK, NULL, 0));
3274
3275 case HTTP_HEAD :
3276 if (!strcmp(client->uri, "/icon.png"))
3277 return (respond_http(client, HTTP_OK, "image/png", 0));
3278 else if (!strcmp(client->uri, "/"))
3279 return (respond_http(client, HTTP_OK, "text/html", 0));
3280 else
3281 return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
3282 break;
3283
3284 case HTTP_GET :
3285 if (!strcmp(client->uri, "/icon.png"))
3286 {
3287 /*
3288 * Send PNG icon file.
3289 */
3290
3291 int fd; /* Icon file */
3292 struct stat fileinfo; /* Icon file information */
3293 char buffer[4096]; /* Copy buffer */
3294 ssize_t bytes; /* Bytes */
3295
3296 if (!stat(client->printer->icon, &fileinfo) &&
3297 (fd = open(client->printer->icon, O_RDONLY)) >= 0)
3298 {
3299 if (!respond_http(client, HTTP_OK, "image/png", fileinfo.st_size))
3300 {
3301 close(fd);
3302 return (0);
3303 }
3304
3305 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
3306 httpWrite2(&(client->http), buffer, bytes);
3307
3308 httpFlushWrite(&(client->http));
3309
3310 close(fd);
3311 }
3312 else
3313 return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
3314 }
3315 else if (!strcmp(client->uri, "/"))
3316 {
3317 /*
3318 * Show web status page...
3319 */
3320
3321 if (!respond_http(client, HTTP_OK, "text/html", 0))
3322 return (0);
3323
3324 html_printf(client,
3325 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
3326 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
3327 "<html>\n"
3328 "<head>\n"
3329 "<title>%s</title>\n"
3330 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
3331 "type=\"image/png\">\n"
3332 "</head>\n"
3333 "<body>\n"
3334 "</body>\n"
3335 "<h1>%s</h1>\n"
3336 "<p>%s, %d job(s).</p>\n"
3337 "</body>\n"
3338 "</html>\n",
3339 client->printer->name, client->printer->name,
3340 client->printer->state == IPP_PRINTER_IDLE ? "Idle" :
3341 client->printer->state == IPP_PRINTER_PROCESSING ?
3342 "Printing" : "Stopped",
3343 cupsArrayCount(client->printer->jobs));
3344 httpWrite2(&(client->http), "", 0);
3345
3346 return (1);
3347 }
3348 else
3349 return (respond_http(client, HTTP_NOT_FOUND, NULL, 0));
3350 break;
3351
3352 case HTTP_POST :
3353 if (client->http.data_remaining < 0 ||
3354 (!client->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
3355 client->http.data_encoding == HTTP_ENCODE_LENGTH))
3356 {
3357 /*
3358 * Negative content lengths are invalid...
3359 */
3360
3361 return (respond_http(client, HTTP_BAD_REQUEST, NULL, 0));
3362 }
3363
3364 if (strcmp(client->http.fields[HTTP_FIELD_CONTENT_TYPE],
3365 "application/ipp"))
3366 {
3367 /*
3368 * Not an IPP request...
3369 */
3370
3371 return (respond_http(client, HTTP_BAD_REQUEST, NULL, 0));
3372 }
3373
3374 /*
3375 * Read the IPP request...
3376 */
3377
3378 client->request = ippNew();
3379
3380 while ((state = ippRead(&(client->http), client->request)) != IPP_DATA)
3381 if (state == IPP_ERROR)
3382 {
3383 fprintf(stderr, "%s IPP read error (%s).\n", client->http.hostname,
3384 ippOpString(client->request->request.op.operation_id));
3385 respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
3386 return (0);
3387 }
3388
3389 /*
3390 * Now that we have the IPP request, process the request...
3391 */
3392
3393 return (process_ipp(client));
3394
3395 default :
3396 break; /* Anti-compiler-warning-code */
3397 }
3398
3399 return (1);
3400 }
3401
3402
3403 /*
3404 * 'process_ipp()' - Process an IPP request.
3405 */
3406
3407 static int /* O - 1 on success, 0 on error */
3408 process_ipp(_ipp_client_t *client) /* I - Client */
3409 {
3410 ipp_tag_t group; /* Current group tag */
3411 ipp_attribute_t *attr; /* Current attribute */
3412 ipp_attribute_t *charset; /* Character set attribute */
3413 ipp_attribute_t *language; /* Language attribute */
3414 ipp_attribute_t *uri; /* Printer URI attribute */
3415
3416
3417 debug_attributes("Request", client->request);
3418
3419 /*
3420 * First build an empty response message for this request...
3421 */
3422
3423 client->operation_id = client->request->request.op.operation_id;
3424 client->response = ippNew();
3425
3426 client->response->request.status.version[0] =
3427 client->request->request.op.version[0];
3428 client->response->request.status.version[1] =
3429 client->request->request.op.version[1];
3430 client->response->request.status.request_id =
3431 client->request->request.op.request_id;
3432
3433 /*
3434 * Then validate the request header and required attributes...
3435 */
3436
3437 if (client->request->request.any.version[0] < 1 ||
3438 client->request->request.any.version[0] > 2)
3439 {
3440 /*
3441 * Return an error, since we only support IPP 1.x and 2.x.
3442 */
3443
3444 respond_ipp(client, IPP_VERSION_NOT_SUPPORTED,
3445 "Bad request version number %d.%d.",
3446 client->request->request.any.version[0],
3447 client->request->request.any.version[1]);
3448 }
3449 else if (client->request->request.any.request_id <= 0)
3450 respond_ipp(client, IPP_BAD_REQUEST, "Bad request-id %d.",
3451 client->request->request.any.request_id);
3452 else if (!client->request->attrs)
3453 respond_ipp(client, IPP_BAD_REQUEST, "No attributes in request.");
3454 else
3455 {
3456 /*
3457 * Make sure that the attributes are provided in the correct order and
3458 * don't repeat groups...
3459 */
3460
3461 for (attr = client->request->attrs, group = attr->group_tag;
3462 attr;
3463 attr = attr->next)
3464 if (attr->group_tag < group && attr->group_tag != IPP_TAG_ZERO)
3465 {
3466 /*
3467 * Out of order; return an error...
3468 */
3469
3470 respond_ipp(client, IPP_BAD_REQUEST,
3471 "Attribute groups are out of order (%x < %x).",
3472 attr->group_tag, group);
3473 break;
3474 }
3475 else
3476 group = attr->group_tag;
3477
3478 if (!attr)
3479 {
3480 /*
3481 * Then make sure that the first three attributes are:
3482 *
3483 * attributes-charset
3484 * attributes-natural-language
3485 * printer-uri/job-uri
3486 */
3487
3488 attr = client->request->attrs;
3489 if (attr && attr->name &&
3490 !strcmp(attr->name, "attributes-charset") &&
3491 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET)
3492 charset = attr;
3493 else
3494 charset = NULL;
3495
3496 if (attr)
3497 attr = attr->next;
3498
3499 if (attr && attr->name &&
3500 !strcmp(attr->name, "attributes-natural-language") &&
3501 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
3502 language = attr;
3503 else
3504 language = NULL;
3505
3506 if ((attr = ippFindAttribute(client->request, "printer-uri",
3507 IPP_TAG_URI)) != NULL)
3508 uri = attr;
3509 else if ((attr = ippFindAttribute(client->request, "job-uri",
3510 IPP_TAG_URI)) != NULL)
3511 uri = attr;
3512 else
3513 uri = NULL;
3514
3515 ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
3516 "attributes-charset", NULL,
3517 charset ? charset->values[0].string.text : "utf-8");
3518
3519 ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
3520 "attributes-natural-language", NULL,
3521 language ? language->values[0].string.text : "en");
3522
3523 if (charset &&
3524 _cups_strcasecmp(charset->values[0].string.text, "us-ascii") &&
3525 _cups_strcasecmp(charset->values[0].string.text, "utf-8"))
3526 {
3527 /*
3528 * Bad character set...
3529 */
3530
3531 respond_ipp(client, IPP_BAD_REQUEST,
3532 "Unsupported character set \"%s\".",
3533 charset->values[0].string.text);
3534 }
3535 else if (!charset || !language || !uri)
3536 {
3537 /*
3538 * Return an error, since attributes-charset,
3539 * attributes-natural-language, and printer-uri/job-uri are required
3540 * for all operations.
3541 */
3542
3543 respond_ipp(client, IPP_BAD_REQUEST, "Missing required attributes.");
3544 }
3545 else if (strcmp(uri->values[0].string.text, client->printer->uri) &&
3546 strncmp(uri->values[0].string.text, client->printer->uri,
3547 client->printer->urilen))
3548 {
3549 respond_ipp(client, IPP_NOT_FOUND, "%s %s not found.", uri->name,
3550 uri->values[0].string.text);
3551 }
3552 else
3553 {
3554 /*
3555 * Try processing the operation...
3556 */
3557
3558 if (client->http.expect == HTTP_CONTINUE)
3559 {
3560 /*
3561 * Send 100-continue header...
3562 */
3563
3564 if (!respond_http(client, HTTP_CONTINUE, NULL, 0))
3565 return (0);
3566 }
3567
3568 switch (client->request->request.op.operation_id)
3569 {
3570 case IPP_PRINT_JOB :
3571 ipp_print_job(client);
3572 break;
3573
3574 case IPP_VALIDATE_JOB :
3575 ipp_validate_job(client);
3576 break;
3577
3578 case IPP_CANCEL_JOB :
3579 ipp_cancel_job(client);
3580 break;
3581
3582 case IPP_GET_JOB_ATTRIBUTES :
3583 ipp_get_job_attributes(client);
3584 break;
3585
3586 case IPP_GET_JOBS :
3587 ipp_get_jobs(client);
3588 break;
3589
3590 case IPP_GET_PRINTER_ATTRIBUTES :
3591 ipp_get_printer_attributes(client);
3592 break;
3593
3594 default :
3595 respond_ipp(client, IPP_OPERATION_NOT_SUPPORTED,
3596 "Operation not supported.");
3597 break;
3598 }
3599 }
3600 }
3601 }
3602
3603 /*
3604 * Send the HTTP header and return...
3605 */
3606
3607 if (client->http.state != HTTP_POST_SEND)
3608 httpFlush(&(client->http)); /* Flush trailing (junk) data */
3609
3610 return (respond_http(client, HTTP_OK, "application/ipp",
3611 ippLength(client->response)));
3612 }
3613
3614
3615 /*
3616 * 'process_job()' - Process a print job.
3617 */
3618
3619 static void * /* O - Thread exit status */
3620 process_job(_ipp_job_t *job) /* I - Job */
3621 {
3622 job->state = IPP_JOB_PROCESSING;
3623 job->printer->state = IPP_PRINTER_PROCESSING;
3624
3625 sleep(5);
3626
3627 if (job->cancel)
3628 job->state = IPP_JOB_CANCELED;
3629 else
3630 job->state = IPP_JOB_COMPLETED;
3631
3632 job->completed = time(NULL);
3633 job->printer->state = IPP_PRINTER_IDLE;
3634 job->printer->active_job = NULL;
3635
3636 return (NULL);
3637 }
3638
3639
3640 #ifdef HAVE_DNSSD
3641 /*
3642 * 'register_printer()' - Register a printer object via Bonjour.
3643 */
3644
3645 static int /* O - 1 on success, 0 on error */
3646 register_printer(
3647 _ipp_printer_t *printer, /* I - Printer */
3648 const char *location, /* I - Location */
3649 const char *make, /* I - Manufacturer */
3650 const char *model, /* I - Model name */
3651 const char *formats, /* I - Supported formats */
3652 const char *adminurl, /* I - Web interface URL */
3653 int color, /* I - 1 = color, 0 = monochrome */
3654 int duplex, /* I - 1 = duplex, 0 = simplex */
3655 const char *regtype) /* I - Service type */
3656 {
3657 DNSServiceErrorType error; /* Error from Bonjour */
3658 char make_model[256],/* Make and model together */
3659 product[256]; /* Product string */
3660
3661
3662 /*
3663 * Build the TXT record for IPP...
3664 */
3665
3666 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
3667 snprintf(product, sizeof(product), "(%s)", model);
3668
3669 TXTRecordCreate(&(printer->ipp_txt), 1024, NULL);
3670 TXTRecordSetValue(&(printer->ipp_txt), "txtvers", 1, "1");
3671 TXTRecordSetValue(&(printer->ipp_txt), "qtotal", 1, "1");
3672 TXTRecordSetValue(&(printer->ipp_txt), "rp", 3, "ipp");
3673 TXTRecordSetValue(&(printer->ipp_txt), "ty", (uint8_t)strlen(make_model),
3674 make_model);
3675 TXTRecordSetValue(&(printer->ipp_txt), "adminurl", (uint8_t)strlen(adminurl),
3676 adminurl);
3677 TXTRecordSetValue(&(printer->ipp_txt), "note", (uint8_t)strlen(location),
3678 location);
3679 TXTRecordSetValue(&(printer->ipp_txt), "priority", 1, "0");
3680 TXTRecordSetValue(&(printer->ipp_txt), "product", (uint8_t)strlen(product),
3681 product);
3682 TXTRecordSetValue(&(printer->ipp_txt), "pdl", (uint8_t)strlen(formats),
3683 formats);
3684 TXTRecordSetValue(&(printer->ipp_txt), "Color", 1, color ? "T" : "F");
3685 TXTRecordSetValue(&(printer->ipp_txt), "Duplex", 1, duplex ? "T" : "F");
3686 TXTRecordSetValue(&(printer->ipp_txt), "usb_MFG", (uint8_t)strlen(make),
3687 make);
3688 TXTRecordSetValue(&(printer->ipp_txt), "usb_MDL", (uint8_t)strlen(model),
3689 model);
3690 TXTRecordSetValue(&(printer->ipp_txt), "air", 4, "none");
3691
3692 /*
3693 * Create a shared service reference for Bonjour...
3694 */
3695
3696 if ((error = DNSServiceCreateConnection(&(printer->common_ref)))
3697 != kDNSServiceErr_NoError)
3698 {
3699 fprintf(stderr, "Unable to create mDNSResponder connection: %d\n", error);
3700 return (0);
3701 }
3702
3703 /*
3704 * Register the _printer._tcp (LPD) service type with a port number of 0 to
3705 * defend our service name but not actually support LPD...
3706 */
3707
3708 printer->printer_ref = printer->common_ref;
3709
3710 if ((error = DNSServiceRegister(&(printer->printer_ref),
3711 kDNSServiceFlagsShareConnection,
3712 0 /* interfaceIndex */, printer->dnssd_name,
3713 "_printer._tcp", NULL /* domain */,
3714 NULL /* host */, 0 /* port */, 0 /* txtLen */,
3715 NULL /* txtRecord */,
3716 (DNSServiceRegisterReply)dnssd_callback,
3717 printer)) != kDNSServiceErr_NoError)
3718 {
3719 fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
3720 printer->dnssd_name, error);
3721 return (0);
3722 }
3723
3724 /*
3725 * Then register the _ipp._tcp (IPP) service type with the real port number to
3726 * advertise our IPP printer...
3727 */
3728
3729 printer->ipp_ref = printer->common_ref;
3730
3731 if ((error = DNSServiceRegister(&(printer->ipp_ref),
3732 kDNSServiceFlagsShareConnection,
3733 0 /* interfaceIndex */, printer->dnssd_name,
3734 regtype, NULL /* domain */,
3735 NULL /* host */, htons(printer->port),
3736 TXTRecordGetLength(&(printer->ipp_txt)),
3737 TXTRecordGetBytesPtr(&(printer->ipp_txt)),
3738 (DNSServiceRegisterReply)dnssd_callback,
3739 printer)) != kDNSServiceErr_NoError)
3740 {
3741 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
3742 printer->dnssd_name, regtype, error);
3743 return (0);
3744 }
3745
3746 /*
3747 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
3748 * real port number to advertise our IPP printer...
3749 */
3750
3751 printer->http_ref = printer->common_ref;
3752
3753 if ((error = DNSServiceRegister(&(printer->http_ref),
3754 kDNSServiceFlagsShareConnection,
3755 0 /* interfaceIndex */, printer->dnssd_name,
3756 "_http._tcp,_printer", NULL /* domain */,
3757 NULL /* host */, htons(printer->port),
3758 0 /* txtLen */, NULL, /* txtRecord */
3759 (DNSServiceRegisterReply)dnssd_callback,
3760 printer)) != kDNSServiceErr_NoError)
3761 {
3762 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
3763 printer->dnssd_name, regtype, error);
3764 return (0);
3765 }
3766
3767 return (1);
3768 }
3769 #endif /* HAVE_DNSSD */
3770
3771
3772 /*
3773 * 'respond_http()' - Send a HTTP response.
3774 */
3775
3776 int /* O - 1 on success, 0 on failure */
3777 respond_http(_ipp_client_t *client, /* I - Client */
3778 http_status_t code, /* I - HTTP status of response */
3779 const char *type, /* I - MIME type of response */
3780 size_t length) /* I - Length of response */
3781 {
3782 char message[1024]; /* Text message */
3783
3784
3785 fprintf(stderr, "%s %s\n", client->http.hostname, httpStatus(code));
3786
3787 if (code == HTTP_CONTINUE)
3788 {
3789 /*
3790 * 100-continue doesn't send any headers...
3791 */
3792
3793 return (httpPrintf(&(client->http), "HTTP/%d.%d 100 Continue\r\n\r\n",
3794 client->http.version / 100,
3795 client->http.version % 100) > 0);
3796 }
3797
3798 /*
3799 * Format an error message...
3800 */
3801
3802 if (!type && !length && code != HTTP_OK)
3803 {
3804 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
3805
3806 type = "text/plain";
3807 length = strlen(message);
3808 }
3809 else
3810 message[0] = '\0';
3811
3812 /*
3813 * Send the HTTP status header...
3814 */
3815
3816 httpFlushWrite(&(client->http));
3817
3818 client->http.data_encoding = HTTP_ENCODE_FIELDS;
3819
3820 if (httpPrintf(&(client->http), "HTTP/%d.%d %d %s\r\n", client->http.version / 100,
3821 client->http.version % 100, code, httpStatus(code)) < 0)
3822 return (0);
3823
3824 /*
3825 * Follow the header with the response fields...
3826 */
3827
3828 if (httpPrintf(&(client->http), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
3829 return (0);
3830
3831 if (client->http.keep_alive && client->http.version >= HTTP_1_0)
3832 {
3833 if (httpPrintf(&(client->http),
3834 "Connection: Keep-Alive\r\n"
3835 "Keep-Alive: timeout=10\r\n") < 0)
3836 return (0);
3837 }
3838
3839 if (code == HTTP_METHOD_NOT_ALLOWED || client->operation == HTTP_OPTIONS)
3840 {
3841 if (httpPrintf(&(client->http), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
3842 return (0);
3843 }
3844
3845 if (type)
3846 {
3847 if (!strcmp(type, "text/html"))
3848 {
3849 if (httpPrintf(&(client->http),
3850 "Content-Type: text/html; charset=utf-8\r\n") < 0)
3851 return (0);
3852 }
3853 else if (httpPrintf(&(client->http), "Content-Type: %s\r\n", type) < 0)
3854 return (0);
3855 }
3856
3857 if (length == 0 && !message[0])
3858 {
3859 if (httpPrintf(&(client->http), "Transfer-Encoding: chunked\r\n\r\n") < 0)
3860 return (0);
3861 }
3862 else if (httpPrintf(&(client->http), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
3863 CUPS_LLCAST length) < 0)
3864 return (0);
3865
3866 if (httpFlushWrite(&(client->http)) < 0)
3867 return (0);
3868
3869 /*
3870 * Send the response data...
3871 */
3872
3873 if (message[0])
3874 {
3875 /*
3876 * Send a plain text message.
3877 */
3878
3879 if (httpPrintf(&(client->http), "%s", message) < 0)
3880 return (0);
3881 }
3882 else if (client->response)
3883 {
3884 /*
3885 * Send an IPP response...
3886 */
3887
3888 debug_attributes("Response", client->response);
3889
3890 client->http.data_encoding = HTTP_ENCODE_LENGTH;
3891 client->http.data_remaining = (off_t)ippLength(client->response);
3892 client->response->state = IPP_IDLE;
3893
3894 if (ippWrite(&(client->http), client->response) != IPP_DATA)
3895 return (0);
3896 }
3897 else
3898 client->http.data_encoding = HTTP_ENCODE_CHUNKED;
3899
3900 /*
3901 * Flush the data and return...
3902 */
3903
3904 return (httpFlushWrite(&(client->http)) >= 0);
3905 }
3906
3907
3908 /*
3909 * 'respond_ipp()' - Send an IPP response.
3910 */
3911
3912 static void
3913 respond_ipp(_ipp_client_t *client, /* I - Client */
3914 ipp_status_t status, /* I - status-code */
3915 const char *message, /* I - printf-style status-message */
3916 ...) /* I - Additional args as needed */
3917 {
3918 va_list ap; /* Pointer to additional args */
3919 char formatted[1024]; /* Formatted errror message */
3920
3921
3922 client->response->request.status.status_code = status;
3923
3924 if (!client->response->attrs)
3925 {
3926 ippAddString(client->response, IPP_TAG_OPERATION,
3927 IPP_TAG_CHARSET | IPP_TAG_COPY, "attributes-charset", NULL,
3928 "utf-8");
3929 ippAddString(client->response, IPP_TAG_OPERATION,
3930 IPP_TAG_LANGUAGE | IPP_TAG_COPY, "attributes-natural-language",
3931 NULL, "en-us");
3932 }
3933
3934 if (message)
3935 {
3936 va_start(ap, message);
3937 vsnprintf(formatted, sizeof(formatted), message, ap);
3938 va_end(ap);
3939
3940 ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
3941 "status-message", NULL, formatted);
3942 }
3943 else
3944 formatted[0] = '\0';
3945
3946 fprintf(stderr, "%s %s %s (%s)\n", client->http.hostname,
3947 ippOpString(client->operation_id), ippErrorString(status), formatted);
3948 }
3949
3950
3951 /*
3952 * 'run_printer()' - Run the printer service.
3953 */
3954
3955 static void
3956 run_printer(_ipp_printer_t *printer) /* I - Printer */
3957 {
3958 int num_fds; /* Number of file descriptors */
3959 struct pollfd polldata[3]; /* poll() data */
3960 int timeout; /* Timeout for poll() */
3961 _ipp_client_t *client; /* New client */
3962
3963
3964 /*
3965 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
3966 */
3967
3968 polldata[0].fd = printer->ipv4;
3969 polldata[0].events = POLLIN;
3970
3971 polldata[1].fd = printer->ipv6;
3972 polldata[1].events = POLLIN;
3973
3974 num_fds = 2;
3975
3976 #ifdef HAVE_DNSSD
3977 polldata[num_fds ].fd = DNSServiceRefSockFD(printer->common_ref);
3978 polldata[num_fds ++].events = POLLIN;
3979 #endif /* HAVE_DNSSD */
3980
3981 /*
3982 * Loop until we are killed or have a hard error...
3983 */
3984
3985 for (;;)
3986 {
3987 if (cupsArrayCount(printer->jobs))
3988 timeout = 10;
3989 else
3990 timeout = -1;
3991
3992 if (poll(polldata, num_fds, timeout) < 0 && errno != EINTR)
3993 {
3994 perror("poll() failed");
3995 break;
3996 }
3997
3998 if (polldata[0].revents & POLLIN)
3999 {
4000 if ((client = create_client(printer, printer->ipv4)) != NULL)
4001 {
4002 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
4003 {
4004 perror("Unable to create client thread");
4005 delete_client(client);
4006 }
4007 }
4008 }
4009
4010 if (polldata[1].revents & POLLIN)
4011 {
4012 if ((client = create_client(printer, printer->ipv6)) != NULL)
4013 {
4014 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
4015 {
4016 perror("Unable to create client thread");
4017 delete_client(client);
4018 }
4019 }
4020 }
4021
4022 #ifdef HAVE_DNSSD
4023 if (polldata[2].revents & POLLIN)
4024 DNSServiceProcessResult(printer->common_ref);
4025 #endif /* HAVE_DNSSD */
4026
4027 /*
4028 * Clean out old jobs...
4029 */
4030
4031 clean_jobs(printer);
4032 }
4033 }
4034
4035
4036 /*
4037 * 'usage()' - Show program usage.
4038 */
4039
4040 static void
4041 usage(int status) /* O - Exit status */
4042 {
4043 if (!status)
4044 {
4045 puts(CUPS_SVERSION " - Copyright 2010 by Apple Inc. All rights reserved.");
4046 puts("");
4047 }
4048
4049 puts("Usage: ippserver [options] \"name\"");
4050 puts("");
4051 puts("Options:");
4052 puts("-2 Supports 2-sided printing (default=1-sided)");
4053 puts("-M manufacturer Manufacturer name (default=Test)");
4054 printf("-d spool-directory Spool directory "
4055 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4056 puts("-f type/subtype[,...] List of supported types "
4057 "(default=application/pdf,image/jpeg)");
4058 puts("-h Show program help");
4059 puts("-i iconfile.png PNG icon file (default=printer.png)");
4060 puts("-l location Location of printer (default=empty string)");
4061 puts("-m model Model name (default=Printer)");
4062 puts("-n hostname Hostname for printer");
4063 puts("-p port Port number (default=auto)");
4064 puts("-r regtype Bonjour service type (default=_ipp._tcp)");
4065 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4066 puts("-v[vvv] Be (very) verbose");
4067
4068 exit(status);
4069 }
4070
4071
4072 /*
4073 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
4074 *
4075 * When one or more job attributes are invalid, this function adds a suitable
4076 * response and attributes to the unsupported group.
4077 */
4078
4079 static int /* O - 1 if valid, 0 if not */
4080 valid_job_attributes(
4081 _ipp_client_t *client) /* I - Client */
4082 {
4083 int i; /* Looping var */
4084 ipp_attribute_t *attr, /* Current attribute */
4085 *supported; /* document-format-supported */
4086 const char *format = NULL; /* document-format value */
4087 int valid = 1; /* Valid attributes? */
4088
4089
4090 /*
4091 * Check operation attributes...
4092 */
4093
4094 #define respond_unsupported(client, attr) \
4095 if (valid) \
4096 { \
4097 respond_ipp(client, IPP_ATTRIBUTES, "Unsupported %s %s%s value.", \
4098 attr->name, attr->num_values > 1 ? "1setOf " : "", \
4099 ippTagString(attr->value_tag)); \
4100 valid = 0; \
4101 } \
4102 copy_attribute(client->response, attr, IPP_TAG_UNSUPPORTED_GROUP, 0)
4103
4104 if ((attr = ippFindAttribute(client->request, "compression",
4105 IPP_TAG_ZERO)) != NULL)
4106 {
4107 /*
4108 * If compression is specified, only accept "none"...
4109 */
4110
4111 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_KEYWORD ||
4112 strcmp(attr->values[0].string.text, "none"))
4113 {
4114 respond_unsupported(client, attr);
4115 }
4116 else
4117 fprintf(stderr, "%s Print-Job compression=\"%s\"\n",
4118 client->http.hostname, attr->values[0].string.text);
4119 }
4120
4121 /*
4122 * Is it a format we support?
4123 */
4124
4125 if ((attr = ippFindAttribute(client->request, "document-format",
4126 IPP_TAG_ZERO)) != NULL)
4127 {
4128 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_MIMETYPE)
4129 {
4130 respond_unsupported(client, attr);
4131 }
4132 else
4133 {
4134 format = attr->values[0].string.text;
4135
4136 fprintf(stderr, "%s %s document-format=\"%s\"\n",
4137 client->http.hostname,
4138 ippOpString(client->request->request.op.operation_id), format);
4139 }
4140 }
4141 else
4142 format = "application/octet-stream";
4143
4144 if (!strcmp(format, "application/octet-stream") &&
4145 client->request->request.op.operation_id != IPP_VALIDATE_JOB)
4146 {
4147 /*
4148 * Auto-type the file using the first 4 bytes of the file...
4149 */
4150
4151 unsigned char header[4]; /* First 4 bytes of file */
4152
4153 memset(header, 0, sizeof(header));
4154 _httpPeek(&(client->http), (char *)header, sizeof(header));
4155
4156 if (!memcmp(header, "%PDF", 4))
4157 format = "application/pdf";
4158 else if (!memcmp(header, "%!", 2))
4159 format = "application/postscript";
4160 else if (!memcmp(header, "\377\330\377", 3) &&
4161 header[3] >= 0xe0 && header[3] <= 0xef)
4162 format = "image/jpeg";
4163 else if (!memcmp(header, "\211PNG", 4))
4164 format = "image/png";
4165
4166 if (format)
4167 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
4168 client->http.hostname,
4169 ippOpString(client->request->request.op.operation_id), format);
4170
4171 if (!attr)
4172 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
4173 "document-format", NULL, format);
4174 else
4175 {
4176 _cupsStrFree(attr->values[0].string.text);
4177 attr->values[0].string.text = _cupsStrAlloc(format);
4178 }
4179 }
4180
4181 if ((supported = ippFindAttribute(client->printer->attrs,
4182 "document-format-supported",
4183 IPP_TAG_MIMETYPE)) != NULL)
4184 {
4185 for (i = 0; i < supported->num_values; i ++)
4186 if (!_cups_strcasecmp(format, supported->values[i].string.text))
4187 break;
4188
4189 if (i >= supported->num_values)
4190 {
4191 respond_unsupported(client, attr);
4192 }
4193 }
4194
4195 /*
4196 * Check the various job template attributes...
4197 */
4198
4199 if ((attr = ippFindAttribute(client->request, "copies",
4200 IPP_TAG_ZERO)) != NULL)
4201 {
4202 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_INTEGER ||
4203 attr->values[0].integer < 1 || attr->values[0].integer > 999)
4204 {
4205 respond_unsupported(client, attr);
4206 }
4207 }
4208
4209 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity",
4210 IPP_TAG_ZERO)) != NULL)
4211 {
4212 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_BOOLEAN)
4213 {
4214 respond_unsupported(client, attr);
4215 }
4216 }
4217
4218 if ((attr = ippFindAttribute(client->request, "job-hold-until",
4219 IPP_TAG_ZERO)) != NULL)
4220 {
4221 if (attr->num_values != 1 ||
4222 (attr->value_tag != IPP_TAG_NAME &&
4223 attr->value_tag != IPP_TAG_NAMELANG &&
4224 attr->value_tag != IPP_TAG_KEYWORD) ||
4225 strcmp(attr->values[0].string.text, "no-hold"))
4226 {
4227 respond_unsupported(client, attr);
4228 }
4229 }
4230
4231 if ((attr = ippFindAttribute(client->request, "job-name",
4232 IPP_TAG_ZERO)) != NULL)
4233 {
4234 if (attr->num_values != 1 ||
4235 (attr->value_tag != IPP_TAG_NAME &&
4236 attr->value_tag != IPP_TAG_NAMELANG))
4237 {
4238 respond_unsupported(client, attr);
4239 }
4240 }
4241
4242 if ((attr = ippFindAttribute(client->request, "job-priority",
4243 IPP_TAG_ZERO)) != NULL)
4244 {
4245 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_INTEGER ||
4246 attr->values[0].integer < 1 || attr->values[0].integer > 100)
4247 {
4248 respond_unsupported(client, attr);
4249 }
4250 }
4251
4252 if ((attr = ippFindAttribute(client->request, "job-sheets",
4253 IPP_TAG_ZERO)) != NULL)
4254 {
4255 if (attr->num_values != 1 ||
4256 (attr->value_tag != IPP_TAG_NAME &&
4257 attr->value_tag != IPP_TAG_NAMELANG &&
4258 attr->value_tag != IPP_TAG_KEYWORD) ||
4259 strcmp(attr->values[0].string.text, "none"))
4260 {
4261 respond_unsupported(client, attr);
4262 }
4263 }
4264
4265 if ((attr = ippFindAttribute(client->request, "media",
4266 IPP_TAG_ZERO)) != NULL)
4267 {
4268 if (attr->num_values != 1 ||
4269 (attr->value_tag != IPP_TAG_NAME &&
4270 attr->value_tag != IPP_TAG_NAMELANG &&
4271 attr->value_tag != IPP_TAG_KEYWORD))
4272 {
4273 respond_unsupported(client, attr);
4274 }
4275 else
4276 {
4277 for (i = 0;
4278 i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
4279 i ++)
4280 if (!strcmp(attr->values[0].string.text, media_supported[i]))
4281 break;
4282
4283 if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
4284 {
4285 respond_unsupported(client, attr);
4286 }
4287 }
4288 }
4289
4290 if ((attr = ippFindAttribute(client->request, "media-col",
4291 IPP_TAG_ZERO)) != NULL)
4292 {
4293 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_BEGIN_COLLECTION)
4294 {
4295 respond_unsupported(client, attr);
4296 }
4297 /* TODO: check for valid media-col */
4298 }
4299
4300 if ((attr = ippFindAttribute(client->request, "multiple-document-handling",
4301 IPP_TAG_ZERO)) != NULL)
4302 {
4303 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_KEYWORD ||
4304 (strcmp(attr->values[0].string.text,
4305 "separate-documents-uncollated-copies") &&
4306 strcmp(attr->values[0].string.text,
4307 "separate-documents-collated-copies")))
4308 {
4309 respond_unsupported(client, attr);
4310 }
4311 }
4312
4313 if ((attr = ippFindAttribute(client->request, "orientation-requested",
4314 IPP_TAG_ZERO)) != NULL)
4315 {
4316 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_ENUM ||
4317 attr->values[0].integer < IPP_PORTRAIT ||
4318 attr->values[0].integer > IPP_REVERSE_PORTRAIT)
4319 {
4320 respond_unsupported(client, attr);
4321 }
4322 }
4323
4324 if ((attr = ippFindAttribute(client->request, "page-ranges",
4325 IPP_TAG_ZERO)) != NULL)
4326 {
4327 respond_unsupported(client, attr);
4328 }
4329
4330 if ((attr = ippFindAttribute(client->request, "print-quality",
4331 IPP_TAG_ZERO)) != NULL)
4332 {
4333 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_ENUM ||
4334 attr->values[0].integer < IPP_QUALITY_DRAFT ||
4335 attr->values[0].integer > IPP_QUALITY_HIGH)
4336 {
4337 respond_unsupported(client, attr);
4338 }
4339 }
4340
4341 if ((attr = ippFindAttribute(client->request, "printer-resolution",
4342 IPP_TAG_ZERO)) != NULL)
4343 {
4344 respond_unsupported(client, attr);
4345 }
4346
4347 if ((attr = ippFindAttribute(client->request, "sides",
4348 IPP_TAG_ZERO)) != NULL)
4349 {
4350 if (attr->num_values != 1 || attr->value_tag != IPP_TAG_KEYWORD)
4351 {
4352 respond_unsupported(client, attr);
4353 }
4354
4355 if ((supported = ippFindAttribute(client->printer->attrs, "sides",
4356 IPP_TAG_KEYWORD)) != NULL)
4357 {
4358 for (i = 0; i < supported->num_values; i ++)
4359 if (!strcmp(attr->values[0].string.text,
4360 supported->values[i].string.text))
4361 break;
4362
4363 if (i >= supported->num_values)
4364 {
4365 respond_unsupported(client, attr);
4366 }
4367 }
4368 else
4369 {
4370 respond_unsupported(client, attr);
4371 }
4372 }
4373
4374 return (valid);
4375 }
4376
4377
4378 /*
4379 * End of "$Id$".
4380 */