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