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