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