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