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