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