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