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