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