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