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