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