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