]> git.ipfire.org Git - thirdparty/cups.git/blame - test/ippserver.c
ippfind fixes:
[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
3744 /*
3745 * Get the document format for the job...
3746 */
3747
3748 _cupsRWLockWrite(&(client->printer->rwlock));
3749
404dde30
MS
3750 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3751 job->format = ippGetString(attr, 0, NULL);
3752 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
a469f8a5 3753 job->format = ippGetString(attr, 0, NULL);
83e08001
MS
3754 else
3755 job->format = "application/octet-stream";
3756
3757 /*
3758 * Create a file for the request data...
3759 */
3760
404dde30 3761 create_job_filename(client->printer, job, filename, sizeof(filename));
83e08001 3762
291e4727
MS
3763 if (Verbosity)
3764 fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
3765
83e08001
MS
3766 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3767
3768 _cupsRWUnlock(&(client->printer->rwlock));
3769
3770 if (job->fd < 0)
3771 {
a469f8a5 3772 job->state = IPP_JSTATE_ABORTED;
83e08001 3773
a469f8a5 3774 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
3775 "Unable to create print file: %s", strerror(errno));
3776 return;
3777 }
3778
a469f8a5 3779 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
83e08001 3780 {
7e86f2f6 3781 if (write(job->fd, buffer, (size_t)bytes) < bytes)
83e08001
MS
3782 {
3783 int error = errno; /* Write error */
3784
a469f8a5 3785 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3786
3787 close(job->fd);
3788 job->fd = -1;
3789
3790 unlink(filename);
3791
a469f8a5 3792 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
3793 "Unable to write print file: %s", strerror(error));
3794 return;
3795 }
3796 }
3797
3798 if (bytes < 0)
3799 {
3800 /*
3801 * Got an error while reading the print data, so abort this job.
3802 */
3803
a469f8a5 3804 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3805
3806 close(job->fd);
3807 job->fd = -1;
3808
3809 unlink(filename);
3810
a469f8a5
MS
3811 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3812 "Unable to read print file.");
83e08001
MS
3813 return;
3814 }
3815
3816 if (close(job->fd))
3817 {
dcb445bc 3818 int error = errno; /* Write error */
83e08001 3819
a469f8a5 3820 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
3821 job->fd = -1;
3822
3823 unlink(filename);
3824
a469f8a5
MS
3825 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3826 "Unable to write print file: %s", strerror(error));
83e08001
MS
3827 return;
3828 }
3829
3830 _cupsRWLockWrite(&(client->printer->rwlock));
3831
3832 job->fd = -1;
3833 job->filename = strdup(filename);
a469f8a5 3834 job->state = IPP_JSTATE_PENDING;
83e08001
MS
3835
3836 _cupsRWUnlock(&(client->printer->rwlock));
3837
3838 /*
3839 * Process the job...
3840 */
3841
3842#if 0
3843 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3844 {
a469f8a5
MS
3845 job->state = IPP_JSTATE_ABORTED;
3846 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
83e08001
MS
3847 return;
3848 }
3849
3850#else
3851 process_job(job);
3852#endif /* 0 */
3853
3854 /*
3855 * Return the job info...
3856 */
3857
a469f8a5 3858 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
3859
3860 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3861 cupsArrayAdd(ra, "job-id");
3862 cupsArrayAdd(ra, "job-state");
3863 cupsArrayAdd(ra, "job-state-reasons");
3864 cupsArrayAdd(ra, "job-uri");
3865
3866 copy_job_attributes(client, job, ra);
3867 cupsArrayDelete(ra);
3868}
3869
3870
3871/*
3872 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3873 * Create-Job.
3874 */
3875
3876static void
3877ipp_send_uri(_ipp_client_t *client) /* I - Client */
3878{
3879 _ipp_job_t *job; /* Job information */
3880 ipp_attribute_t *uri; /* document-uri */
3881 char scheme[256], /* URI scheme */
3882 userpass[256], /* Username and password info */
3883 hostname[256], /* Hostname */
3884 resource[1024]; /* Resource path */
3885 int port; /* Port number */
3886 http_uri_status_t uri_status; /* URI decode status */
3887 http_encryption_t encryption; /* Encryption to use, if any */
3888 http_t *http; /* Connection for http/https URIs */
3889 http_status_t status; /* Access status for http/https URIs */
3890 int infile; /* Input file for local file URIs */
3891 char filename[1024], /* Filename buffer */
3892 buffer[4096]; /* Copy buffer */
3893 ssize_t bytes; /* Bytes read */
3894 ipp_attribute_t *attr; /* Current attribute */
3895 cups_array_t *ra; /* Attributes to send in response */
3896 static const char * const uri_status_strings[] =
3897 { /* URI decode errors */
3898 "URI too large.",
3899 "Bad arguments to function.",
3900 "Bad resource in URI.",
3901 "Bad port number in URI.",
3902 "Bad hostname in URI.",
3903 "Bad username in URI.",
3904 "Bad scheme in URI.",
3905 "Bad/empty URI."
3906 };
3907
3908
3909 /*
3910 * Get the job...
3911 */
3912
3913 if ((job = find_job(client)) == NULL)
3914 {
a469f8a5
MS
3915 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3916 httpFlush(client->http);
83e08001
MS
3917 return;
3918 }
3919
3920 /*
3921 * See if we already have a document for this job or the job has already
3922 * in a non-pending state...
3923 */
3924
a469f8a5 3925 if (job->state > IPP_JSTATE_HELD)
83e08001 3926 {
a469f8a5
MS
3927 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3928 "Job is not in a pending state.");
3929 httpFlush(client->http);
83e08001
MS
3930 return;
3931 }
3932 else if (job->filename || job->fd >= 0)
3933 {
a469f8a5 3934 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
83e08001 3935 "Multiple document jobs are not supported.");
a469f8a5 3936 httpFlush(client->http);
83e08001
MS
3937 return;
3938 }
3939
3940 if ((attr = ippFindAttribute(client->request, "last-document",
3941 IPP_TAG_ZERO)) == NULL)
3942 {
a469f8a5 3943 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001 3944 "Missing required last-document attribute.");
a469f8a5 3945 httpFlush(client->http);
83e08001
MS
3946 return;
3947 }
a469f8a5
MS
3948 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
3949 !ippGetBoolean(attr, 0))
83e08001
MS
3950 {
3951 respond_unsupported(client, attr);
a469f8a5 3952 httpFlush(client->http);
83e08001
MS
3953 return;
3954 }
3955
3956 /*
3957 * Validate document attributes...
3958 */
3959
3960 if (!valid_doc_attributes(client))
3961 {
a469f8a5 3962 httpFlush(client->http);
83e08001
MS
3963 return;
3964 }
3965
3966 /*
3967 * Do we have a file to print?
3968 */
3969
a469f8a5 3970 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
83e08001 3971 {
a469f8a5 3972 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
83e08001
MS
3973 "Unexpected document data following request.");
3974 return;
3975 }
3976
3977 /*
3978 * Do we have a document URI?
3979 */
3980
3981 if ((uri = ippFindAttribute(client->request, "document-uri",
3982 IPP_TAG_URI)) == NULL)
3983 {
a469f8a5 3984 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
83e08001
MS
3985 return;
3986 }
3987
a469f8a5 3988 if (ippGetCount(uri) != 1)
83e08001 3989 {
a469f8a5
MS
3990 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3991 "Too many document-uri values.");
83e08001
MS
3992 return;
3993 }
3994
a469f8a5 3995 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
83e08001
MS
3996 scheme, sizeof(scheme), userpass,
3997 sizeof(userpass), hostname, sizeof(hostname),
3998 &port, resource, sizeof(resource));
a469f8a5 3999 if (uri_status < HTTP_URI_STATUS_OK)
83e08001 4000 {
a469f8a5
MS
4001 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
4002 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
83e08001
MS
4003 return;
4004 }
4005
4006 if (strcmp(scheme, "file") &&
4007#ifdef HAVE_SSL
4008 strcmp(scheme, "https") &&
4009#endif /* HAVE_SSL */
4010 strcmp(scheme, "http"))
4011 {
a469f8a5
MS
4012 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
4013 "URI scheme \"%s\" not supported.", scheme);
83e08001
MS
4014 return;
4015 }
4016
4017 if (!strcmp(scheme, "file") && access(resource, R_OK))
4018 {
a469f8a5
MS
4019 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4020 "Unable to access URI: %s", strerror(errno));
83e08001
MS
4021 return;
4022 }
4023
4024 /*
4025 * Get the document format for the job...
4026 */
4027
4028 _cupsRWLockWrite(&(client->printer->rwlock));
4029
4030 if ((attr = ippFindAttribute(job->attrs, "document-format",
4031 IPP_TAG_MIMETYPE)) != NULL)
a469f8a5 4032 job->format = ippGetString(attr, 0, NULL);
83e08001
MS
4033 else
4034 job->format = "application/octet-stream";
4035
4036 /*
4037 * Create a file for the request data...
4038 */
4039
2cadf0f4 4040 if (!strcasecmp(job->format, "image/jpeg"))
83e08001
MS
4041 snprintf(filename, sizeof(filename), "%s/%d.jpg",
4042 client->printer->directory, job->id);
2cadf0f4 4043 else if (!strcasecmp(job->format, "image/png"))
83e08001
MS
4044 snprintf(filename, sizeof(filename), "%s/%d.png",
4045 client->printer->directory, job->id);
2cadf0f4 4046 else if (!strcasecmp(job->format, "application/pdf"))
83e08001
MS
4047 snprintf(filename, sizeof(filename), "%s/%d.pdf",
4048 client->printer->directory, job->id);
2cadf0f4 4049 else if (!strcasecmp(job->format, "application/postscript"))
83e08001
MS
4050 snprintf(filename, sizeof(filename), "%s/%d.ps",
4051 client->printer->directory, job->id);
4052 else
4053 snprintf(filename, sizeof(filename), "%s/%d.prn",
4054 client->printer->directory, job->id);
4055
4056 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
4057
4058 _cupsRWUnlock(&(client->printer->rwlock));
4059
4060 if (job->fd < 0)
4061 {
a469f8a5 4062 job->state = IPP_JSTATE_ABORTED;
83e08001 4063
a469f8a5 4064 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
4065 "Unable to create print file: %s", strerror(errno));
4066 return;
4067 }
4068
4069 if (!strcmp(scheme, "file"))
4070 {
4071 if ((infile = open(resource, O_RDONLY)) < 0)
4072 {
a469f8a5
MS
4073 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4074 "Unable to access URI: %s", strerror(errno));
83e08001
MS
4075 return;
4076 }
4077
4078 do
4079 {
4080 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
4081 (errno == EAGAIN || errno == EINTR))
4082 bytes = 1;
7e86f2f6 4083 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
83e08001
MS
4084 {
4085 int error = errno; /* Write error */
4086
a469f8a5 4087 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
4088
4089 close(job->fd);
4090 job->fd = -1;
4091
4092 unlink(filename);
4093 close(infile);
4094
a469f8a5 4095 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
4096 "Unable to write print file: %s", strerror(error));
4097 return;
4098 }
4099 }
4100 while (bytes > 0);
4101
4102 close(infile);
4103 }
4104 else
4105 {
4106#ifdef HAVE_SSL
4107 if (port == 443 || !strcmp(scheme, "https"))
a469f8a5 4108 encryption = HTTP_ENCRYPTION_ALWAYS;
83e08001
MS
4109 else
4110#endif /* HAVE_SSL */
a469f8a5 4111 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
83e08001 4112
a469f8a5
MS
4113 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
4114 1, 30000, NULL)) == NULL)
83e08001 4115 {
a469f8a5 4116 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
83e08001
MS
4117 "Unable to connect to %s: %s", hostname,
4118 cupsLastErrorString());
a469f8a5 4119 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
4120
4121 close(job->fd);
4122 job->fd = -1;
4123
4124 unlink(filename);
4125 return;
4126 }
4127
4128 httpClearFields(http);
4129 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
4130 if (httpGet(http, resource))
4131 {
a469f8a5
MS
4132 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4133 "Unable to GET URI: %s", strerror(errno));
83e08001 4134
a469f8a5 4135 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
4136
4137 close(job->fd);
4138 job->fd = -1;
4139
4140 unlink(filename);
4141 httpClose(http);
4142 return;
4143 }
4144
a469f8a5 4145 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
83e08001 4146
a469f8a5 4147 if (status != HTTP_STATUS_OK)
83e08001 4148 {
a469f8a5
MS
4149 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4150 "Unable to GET URI: %s", httpStatus(status));
83e08001 4151
a469f8a5 4152 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
4153
4154 close(job->fd);
4155 job->fd = -1;
4156
4157 unlink(filename);
4158 httpClose(http);
4159 return;
4160 }
4161
4162 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
4163 {
7e86f2f6 4164 if (write(job->fd, buffer, (size_t)bytes) < bytes)
83e08001
MS
4165 {
4166 int error = errno; /* Write error */
4167
a469f8a5 4168 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
4169
4170 close(job->fd);
4171 job->fd = -1;
4172
4173 unlink(filename);
4174 httpClose(http);
4175
a469f8a5 4176 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
83e08001
MS
4177 "Unable to write print file: %s", strerror(error));
4178 return;
4179 }
4180 }
4181
4182 httpClose(http);
4183 }
4184
4185 if (close(job->fd))
4186 {
4187 int error = errno; /* Write error */
4188
a469f8a5 4189 job->state = IPP_JSTATE_ABORTED;
83e08001
MS
4190 job->fd = -1;
4191
4192 unlink(filename);
4193
a469f8a5
MS
4194 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4195 "Unable to write print file: %s", strerror(error));
83e08001
MS
4196 return;
4197 }
4198
4199 _cupsRWLockWrite(&(client->printer->rwlock));
4200
4201 job->fd = -1;
4202 job->filename = strdup(filename);
a469f8a5 4203 job->state = IPP_JSTATE_PENDING;
83e08001
MS
4204
4205 _cupsRWUnlock(&(client->printer->rwlock));
4206
4207 /*
4208 * Process the job...
4209 */
4210
4211#if 0
4212 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
4213 {
a469f8a5
MS
4214 job->state = IPP_JSTATE_ABORTED;
4215 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
83e08001
MS
4216 return;
4217 }
4218
4219#else
4220 process_job(job);
4221#endif /* 0 */
4222
4223 /*
4224 * Return the job info...
4225 */
4226
a469f8a5 4227 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
4228
4229 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4230 cupsArrayAdd(ra, "job-id");
4231 cupsArrayAdd(ra, "job-state");
4232 cupsArrayAdd(ra, "job-state-reasons");
4233 cupsArrayAdd(ra, "job-uri");
4234
4235 copy_job_attributes(client, job, ra);
4236 cupsArrayDelete(ra);
4237}
4238
4239
4240/*
4241 * 'ipp_validate_job()' - Validate job creation attributes.
4242 */
4243
4244static void
4245ipp_validate_job(_ipp_client_t *client) /* I - Client */
4246{
4247 if (valid_job_attributes(client))
a469f8a5 4248 respond_ipp(client, IPP_STATUS_OK, NULL);
83e08001
MS
4249}
4250
4251
0b5ce83f
MS
4252/*
4253 * 'parse_options()' - Parse URL options into CUPS options.
4254 *
4255 * The client->options string is destroyed by this function.
4256 */
4257
4258static int /* O - Number of options */
4259parse_options(_ipp_client_t *client, /* I - Client */
4260 cups_option_t **options) /* O - Options */
4261{
4262 char *name, /* Name */
4263 *value, /* Value */
4264 *next; /* Next name=value pair */
4265 int num_options = 0; /* Number of options */
4266
4267
4268 *options = NULL;
4269
4270 for (name = client->options; name && *name; name = next)
4271 {
4272 if ((value = strchr(name, '=')) == NULL)
4273 break;
4274
4275 *value++ = '\0';
4276 if ((next = strchr(value, '&')) != NULL)
4277 *next++ = '\0';
4278
4279 num_options = cupsAddOption(name, value, num_options, options);
4280 }
4281
4282 return (num_options);
4283}
4284
4285
83e08001
MS
4286/*
4287 * 'process_client()' - Process client requests on a thread.
4288 */
4289
4290static void * /* O - Exit status */
4291process_client(_ipp_client_t *client) /* I - Client */
4292{
4293 /*
4294 * Loop until we are out of requests or timeout (30 seconds)...
4295 */
4296
f93b32b6
MS
4297#ifdef HAVE_SSL
4298 int first_time = 1; /* First time request? */
4299#endif /* HAVE_SSL */
4300
a469f8a5 4301 while (httpWait(client->http, 30000))
f93b32b6
MS
4302 {
4303#ifdef HAVE_SSL
4304 if (first_time)
4305 {
4306 /*
4307 * See if we need to negotiate a TLS connection...
4308 */
4309
4310 char buf[1]; /* First byte from client */
4311
4312 if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
4313 {
fe202ff4 4314 fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
f93b32b6
MS
4315
4316 if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
4317 {
4318 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
4319 break;
4320 }
fe202ff4
MS
4321
4322 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
f93b32b6
MS
4323 }
4324
4325 first_time = 0;
4326 }
4327#endif /* HAVE_SSL */
4328
83e08001
MS
4329 if (!process_http(client))
4330 break;
f93b32b6 4331 }
83e08001
MS
4332
4333 /*
4334 * Close the conection to the client and return...
4335 */
4336
4337 delete_client(client);
4338
4339 return (NULL);
4340}
4341
4342
4343/*
4344 * 'process_http()' - Process a HTTP request.
4345 */
4346
4347int /* O - 1 on success, 0 on failure */
4348process_http(_ipp_client_t *client) /* I - Client connection */
4349{
a469f8a5
MS
4350 char uri[1024]; /* URI */
4351 http_state_t http_state; /* HTTP state */
4352 http_status_t http_status; /* HTTP status */
4353 ipp_state_t ipp_state; /* State of IPP transfer */
4354 char scheme[32], /* Method/scheme */
4355 userpass[128], /* Username:password */
4356 hostname[HTTP_MAX_HOST];
4357 /* Hostname */
4358 int port; /* Port number */
4359 const char *encoding; /* Content-Encoding value */
4360 static const char * const http_states[] =
4361 { /* Strings for logging HTTP method */
4362 "WAITING",
4363 "OPTIONS",
4364 "GET",
4365 "GET_SEND",
4366 "HEAD",
4367 "POST",
4368 "POST_RECV",
4369 "POST_SEND",
4370 "PUT",
4371 "PUT_RECV",
4372 "DELETE",
4373 "TRACE",
4374 "CONNECT",
4375 "STATUS",
4376 "UNKNOWN_METHOD",
4377 "UNKNOWN_VERSION"
4378 };
83e08001 4379
83e08001
MS
4380
4381 /*
4382 * Clear state variables...
4383 */
4384
83e08001
MS
4385 ippDelete(client->request);
4386 ippDelete(client->response);
4387
a469f8a5
MS
4388 client->request = NULL;
4389 client->response = NULL;
4390 client->operation = HTTP_STATE_WAITING;
83e08001
MS
4391
4392 /*
4393 * Read a request from the connection...
4394 */
4395
a469f8a5
MS
4396 while ((http_state = httpReadRequest(client->http, uri,
4397 sizeof(uri))) == HTTP_STATE_WAITING)
4398 usleep(1);
83e08001
MS
4399
4400 /*
4401 * Parse the request line...
4402 */
4403
a469f8a5 4404 if (http_state == HTTP_STATE_ERROR)
83e08001 4405 {
a469f8a5
MS
4406 if (httpError(client->http) == EPIPE)
4407 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
4408 else
db8b865d
MS
4409 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname,
4410 strerror(httpError(client->http)));
83e08001 4411
a469f8a5 4412 return (0);
83e08001 4413 }
a469f8a5 4414 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
83e08001 4415 {
a469f8a5
MS
4416 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
4417 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4418 return (0);
1106b00e 4419 }
a469f8a5 4420 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
1106b00e 4421 {
a469f8a5
MS
4422 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
4423 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4424 return (0);
1106b00e
MS
4425 }
4426
a469f8a5
MS
4427 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state],
4428 uri);
4429
1106b00e 4430 /*
a469f8a5 4431 * Separate the URI into its components...
1106b00e
MS
4432 */
4433
a469f8a5
MS
4434 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
4435 userpass, sizeof(userpass),
4436 hostname, sizeof(hostname), &port,
fe202ff4
MS
4437 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
4438 (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
1106b00e 4439 {
a469f8a5
MS
4440 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
4441 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
4442 return (0);
4443 }
4444
0b5ce83f
MS
4445 if ((client->options = strchr(client->uri, '?')) != NULL)
4446 *(client->options)++ = '\0';
4447
a469f8a5
MS
4448 /*
4449 * Process the request...
4450 */
4451
4452 client->start = time(NULL);
4453 client->operation = httpGetState(client->http);
1106b00e
MS
4454
4455 /*
4456 * Parse incoming parameters until the status changes...
4457 */
4458
a469f8a5 4459 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
1106b00e 4460
a469f8a5 4461 if (http_status != HTTP_STATUS_OK)
1106b00e 4462 {
a469f8a5 4463 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
4464 return (0);
4465 }
4466
a469f8a5
MS
4467 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
4468 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
1106b00e
MS
4469 {
4470 /*
4471 * HTTP/1.1 and higher require the "Host:" field...
4472 */
4473
a469f8a5 4474 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
4475 return (0);
4476 }
4477
4478 /*
4479 * Handle HTTP Upgrade...
4480 */
4481
2cadf0f4 4482 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
a469f8a5 4483 "Upgrade"))
1106b00e 4484 {
fe202ff4
MS
4485#ifdef HAVE_SSL
4486 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
4487 {
4488 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
4489 return (0);
4490
4491 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
4492
4493 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
4494 {
4495 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
4496 return (0);
4497 }
4498
4499 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
4500 }
4501 else
4502#endif /* HAVE_SSL */
4503
a469f8a5 4504 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
1106b00e
MS
4505 return (0);
4506 }
4507
e60ec91f
MS
4508 /*
4509 * Handle HTTP Expect...
4510 */
4511
a469f8a5
MS
4512 if (httpGetExpect(client->http) &&
4513 (client->operation == HTTP_STATE_POST ||
4514 client->operation == HTTP_STATE_PUT))
e60ec91f 4515 {
a469f8a5 4516 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
e60ec91f
MS
4517 {
4518 /*
4519 * Send 100-continue header...
4520 */
4521
a469f8a5 4522 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
e60ec91f
MS
4523 return (0);
4524 }
4525 else
4526 {
4527 /*
4528 * Send 417-expectation-failed header...
4529 */
4530
a469f8a5 4531 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
e60ec91f 4532 return (0);
e60ec91f
MS
4533 }
4534 }
4535
1106b00e
MS
4536 /*
4537 * Handle new transfers...
4538 */
4539
a469f8a5
MS
4540 encoding = httpGetContentEncoding(client->http);
4541
1106b00e
MS
4542 switch (client->operation)
4543 {
a469f8a5 4544 case HTTP_STATE_OPTIONS :
1106b00e 4545 /*
fe202ff4 4546 * Do OPTIONS command...
1106b00e
MS
4547 */
4548
a469f8a5 4549 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
1106b00e 4550
a469f8a5 4551 case HTTP_STATE_HEAD :
1106b00e 4552 if (!strcmp(client->uri, "/icon.png"))
a469f8a5 4553 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
0b5ce83f 4554 else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
a469f8a5 4555 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
1106b00e 4556 else
a469f8a5 4557 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e 4558
a469f8a5 4559 case HTTP_STATE_GET :
1106b00e
MS
4560 if (!strcmp(client->uri, "/icon.png"))
4561 {
4562 /*
4563 * Send PNG icon file.
4564 */
4565
4566 int fd; /* Icon file */
4567 struct stat fileinfo; /* Icon file information */
4568 char buffer[4096]; /* Copy buffer */
4569 ssize_t bytes; /* Bytes */
4570
a469f8a5
MS
4571 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
4572
1106b00e
MS
4573 if (!stat(client->printer->icon, &fileinfo) &&
4574 (fd = open(client->printer->icon, O_RDONLY)) >= 0)
4575 {
a469f8a5 4576 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png",
7e86f2f6 4577 (size_t)fileinfo.st_size))
1106b00e
MS
4578 {
4579 close(fd);
4580 return (0);
4581 }
4582
4583 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
7e86f2f6 4584 httpWrite2(client->http, buffer, (size_t)bytes);
1106b00e 4585
a469f8a5 4586 httpFlushWrite(client->http);
1106b00e
MS
4587
4588 close(fd);
4589 }
4590 else
a469f8a5 4591 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e
MS
4592 }
4593 else if (!strcmp(client->uri, "/"))
4594 {
4595 /*
4596 * Show web status page...
4597 */
4598
0b5ce83f
MS
4599 _ipp_job_t *job; /* Current job */
4600 int i; /* Looping var */
4601 _ipp_preason_t reason; /* Current reason */
4602 static const char * const reasons[] =
4603 { /* Reason strings */
4604 "Other",
4605 "Cover Open",
4606 "Input Tray Missing",
4607 "Marker Supply Empty",
4608 "Marker Supply Low",
4609 "Marker Waste Almost Full",
4610 "Marker Waste Full",
4611 "Media Empty",
4612 "Media Jam",
4613 "Media Low",
4614 "Media Needed",
4615 "Moving to Paused",
4616 "Paused",
4617 "Spool Area Full",
4618 "Toner Empty",
4619 "Toner Low"
4620 };
4621
a469f8a5 4622 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
1106b00e
MS
4623 return (0);
4624
0b5ce83f 4625 html_header(client, client->printer->name);
1106b00e 4626 html_printf(client,
0b5ce83f
MS
4627 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION ")</b></p>\n"
4628 "<p>%s, %d job(s).", client->printer->state == IPP_PSTATE_IDLE ? "Idle" : client->printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(client->printer->jobs));
4629 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
4630 if (client->printer->state_reasons & reason)
4631 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
4632 html_printf(client, "</p>\n");
4633
4634 if (cupsArrayCount(client->printer->jobs) > 0)
4635 {
4636 _cupsRWLockRead(&(client->printer->rwlock));
4637
4638 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");
4639 for (job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs); job; job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
4640 {
4641 char when[256], /* When job queued/started/finished */
4642 hhmmss[64]; /* Time HH:MM:SS */
4643
4644 switch (job->state)
4645 {
4646 case IPP_JSTATE_PENDING :
4647 case IPP_JSTATE_HELD :
4648 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
4649 break;
4650 case IPP_JSTATE_PROCESSING :
4651 case IPP_JSTATE_STOPPED :
4652 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
4653 break;
4654 case IPP_JSTATE_ABORTED :
4655 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
4656 break;
4657 case IPP_JSTATE_CANCELED :
4658 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
4659 break;
4660 case IPP_JSTATE_COMPLETED :
4661 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
4662 break;
4663 }
4664
4665 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);
4666 }
4667 html_printf(client, "</tbody></table>\n");
4668
4669 _cupsRWUnlock(&(client->printer->rwlock));
4670 }
4671 html_footer(client);
4672
4673 return (1);
4674 }
4675 else if (!strcmp(client->uri, "/media"))
4676 {
4677 /*
4678 * Show web media page...
4679 */
4680
4681 int i, /* Looping var */
4682 num_options; /* Number of form options */
4683 cups_option_t *options; /* Form options */
4684 static const char * const sizes[] =
4685 { /* Size strings */
4686 "ISO A4",
4687 "ISO A5",
4688 "ISO A6",
4689 "DL Envelope",
4690 "US Legal",
4691 "US Letter",
4692 "#10 Envelope",
4693 "3x5 Photo",
4694 "3.5x5 Photo",
4695 "4x6 Photo",
4696 "5x7 Photo"
4697 };
4698 static const char * const types[] =
4699 /* Type strings */
4700 {
4701 "Auto",
4702 "Cardstock",
4703 "Envelope",
4704 "Labels",
4705 "Other",
4706 "Glossy Photo",
4707 "High-Gloss Photo",
4708 "Matte Photo",
4709 "Satin Photo",
4710 "Semi-Gloss Photo",
4711 "Plain",
4712 "Letterhead",
4713 "Transparency"
4714 };
4715 static const int sheets[] = /* Number of sheets */
4716 {
4717 250,
4718 100,
4719 25,
4720 5,
4721 0
4722 };
4723
4724 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
4725 return (0);
4726
4727 html_header(client, client->printer->name);
4728
4729 if ((num_options = parse_options(client, &options)) > 0)
4730 {
e3a57e0b
MS
4731 /*
4732 * WARNING: A real printer/server implementation MUST NOT implement
4733 * media updates via a GET request - GET requests are supposed to be
4734 * idempotent (without side-effects) and we obviously are not
4735 * authenticating access here. This form is provided solely to
4736 * enable testing and development!
4737 */
4738
0b5ce83f
MS
4739 const char *val; /* Form value */
4740
4741 if ((val = cupsGetOption("main_size", num_options, options)) != NULL)
4742 client->printer->main_size = atoi(val);
4743 if ((val = cupsGetOption("main_type", num_options, options)) != NULL)
4744 client->printer->main_type = atoi(val);
4745 if ((val = cupsGetOption("main_level", num_options, options)) != NULL)
4746 client->printer->main_level = atoi(val);
4747
4748 if ((val = cupsGetOption("envelope_size", num_options, options)) != NULL)
4749 client->printer->envelope_size = atoi(val);
4750 if ((val = cupsGetOption("envelope_level", num_options, options)) != NULL)
4751 client->printer->envelope_level = atoi(val);
4752
4753 if ((val = cupsGetOption("photo_size", num_options, options)) != NULL)
4754 client->printer->photo_size = atoi(val);
4755 if ((val = cupsGetOption("photo_type", num_options, options)) != NULL)
4756 client->printer->photo_type = atoi(val);
4757 if ((val = cupsGetOption("photo_level", num_options, options)) != NULL)
4758 client->printer->photo_level = atoi(val);
4759
4760 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))
4761 client->printer->state_reasons |= _IPP_PREASON_MEDIA_LOW;
4762 else
4763 client->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_LOW;
4764
4765 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))
4766 {
4767 client->printer->state_reasons |= _IPP_PREASON_MEDIA_EMPTY;
4768 if (client->printer->active_job)
4769 client->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
4770 }
4771 else
4772 client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MEDIA_EMPTY | _IPP_PREASON_MEDIA_NEEDED);
4773
4774 html_printf(client, "<blockquote>Media updated.</blockquote>\n");
4775 }
4776
4777 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
4778
4779 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
4780 html_printf(client, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
4781 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
4782 if (!strstr(sizes[i], "Envelope") && !strstr(sizes[i], "Photo"))
4783 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_size ? " selected" : "", sizes[i]);
4784 html_printf(client, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
4785 for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
4786 if (!strstr(types[i], "Photo"))
4787 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_type ? " selected" : "", types[i]);
4788 html_printf(client, "</select> <select name=\"main_level\">");
4789 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
4790 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->main_level ? " selected" : "", sheets[i]);
4791 html_printf(client, "</select></td></tr>\n");
4792
4793 html_printf(client,
4794 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
4795 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
4796 if (strstr(sizes[i], "Envelope"))
4797 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->envelope_size ? " selected" : "", sizes[i]);
4798 html_printf(client, "</select> <select name=\"envelope_level\">");
4799 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
4800 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->envelope_level ? " selected" : "", sheets[i]);
4801 html_printf(client, "</select></td></tr>\n");
4802
4803 html_printf(client,
4804 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
4805 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
4806 if (strstr(sizes[i], "Photo"))
4807 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_size ? " selected" : "", sizes[i]);
4808 html_printf(client, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
4809 for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
4810 if (strstr(types[i], "Photo"))
4811 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_type ? " selected" : "", types[i]);
4812 html_printf(client, "</select> <select name=\"photo_level\">");
4813 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
4814 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->photo_level ? " selected" : "", sheets[i]);
4815 html_printf(client, "</select></td></tr>\n");
4816
4817 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
4818 html_footer(client);
4819
4820 return (1);
4821 }
4822 else if (!strcmp(client->uri, "/supplies"))
4823 {
4824 /*
4825 * Show web supplies page...
4826 */
4827
4828 int i, j, /* Looping vars */
4829 num_options; /* Number of form options */
4830 cups_option_t *options; /* Form options */
4831 static const int levels[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
4832
4833 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
4834 return (0);
4835
4836 html_header(client, client->printer->name);
4837
4838 if ((num_options = parse_options(client, &options)) > 0)
4839 {
e3a57e0b
MS
4840 /*
4841 * WARNING: A real printer/server implementation MUST NOT implement
4842 * supply updates via a GET request - GET requests are supposed to be
4843 * idempotent (without side-effects) and we obviously are not
4844 * authenticating access here. This form is provided solely to
4845 * enable testing and development!
4846 */
4847
0b5ce83f
MS
4848 char name[64]; /* Form field */
4849 const char *val; /* Form value */
4850
4851 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);
4852
4853 for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
4854 {
4855 snprintf(name, sizeof(name), "supply_%d", i);
4856 if ((val = cupsGetOption(name, num_options, options)) != NULL)
4857 {
4858 int level = client->printer->supplies[i] = atoi(val);
4859 /* New level */
4860
4861 if (i < 4)
4862 {
4863 if (level == 0)
4864 client->printer->state_reasons |= _IPP_PREASON_TONER_EMPTY;
4865 else if (level < 10)
4866 client->printer->state_reasons |= _IPP_PREASON_TONER_LOW;
4867 }
4868 else
4869 {
4870 if (level == 100)
4871 client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_FULL;
4872 else if (level > 90)
4873 client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL;
4874 }
4875 }
4876 }
4877
4878 html_printf(client, "<blockquote>Supplies updated.</blockquote>\n");
4879 }
4880
4881 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
4882
4883 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
4884 for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
4885 {
4886 html_printf(client, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies[i], i);
4887 for (j = 0; j < (int)(sizeof(levels) / sizeof(levels[0])); j ++)
4888 html_printf(client, "<option value=\"%d\"%s>%d%%</option>", levels[j], levels[j] == client->printer->supplies[i] ? " selected" : "", levels[j]);
4889 html_printf(client, "</select></td></tr>\n");
4890 }
4891 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
4892 html_footer(client);
1106b00e
MS
4893
4894 return (1);
4895 }
4896 else
a469f8a5 4897 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
1106b00e
MS
4898 break;
4899
a469f8a5
MS
4900 case HTTP_STATE_POST :
4901 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
1106b00e
MS
4902 "application/ipp"))
4903 {
4904 /*
4905 * Not an IPP request...
4906 */
4907
a469f8a5 4908 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
1106b00e
MS
4909 }
4910
4911 /*
4912 * Read the IPP request...
4913 */
4914
4915 client->request = ippNew();
4916
a469f8a5
MS
4917 while ((ipp_state = ippRead(client->http,
4918 client->request)) != IPP_STATE_DATA)
4919 {
4920 if (ipp_state == IPP_STATE_ERROR)
1106b00e 4921 {
a469f8a5 4922 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname,
83e08001 4923 cupsLastErrorString());
a469f8a5 4924 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
1106b00e
MS
4925 return (0);
4926 }
a469f8a5 4927 }
1106b00e
MS
4928
4929 /*
4930 * Now that we have the IPP request, process the request...
4931 */
4932
4933 return (process_ipp(client));
4934
4935 default :
4936 break; /* Anti-compiler-warning-code */
4937 }
4938
4939 return (1);
4940}
4941
4942
4943/*
4944 * 'process_ipp()' - Process an IPP request.
4945 */
4946
4947static int /* O - 1 on success, 0 on error */
4948process_ipp(_ipp_client_t *client) /* I - Client */
4949{
4950 ipp_tag_t group; /* Current group tag */
4951 ipp_attribute_t *attr; /* Current attribute */
4952 ipp_attribute_t *charset; /* Character set attribute */
4953 ipp_attribute_t *language; /* Language attribute */
4954 ipp_attribute_t *uri; /* Printer URI attribute */
a469f8a5
MS
4955 int major, minor; /* Version number */
4956 const char *name; /* Name of attribute */
1106b00e
MS
4957
4958
83e08001 4959 debug_attributes("Request", client->request, 1);
1106b00e
MS
4960
4961 /*
4962 * First build an empty response message for this request...
4963 */
4964
a469f8a5
MS
4965 client->operation_id = ippGetOperation(client->request);
4966 client->response = ippNewResponse(client->request);
1106b00e
MS
4967
4968 /*
4969 * Then validate the request header and required attributes...
4970 */
4971
a469f8a5
MS
4972 major = ippGetVersion(client->request, &minor);
4973
4974 if (major < 1 || major > 2)
1106b00e
MS
4975 {
4976 /*
4977 * Return an error, since we only support IPP 1.x and 2.x.
4978 */
4979
a469f8a5
MS
4980 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED,
4981 "Bad request version number %d.%d.", major, minor);
1106b00e 4982 }
a469f8a5
MS
4983 else if (ippGetRequestId(client->request) <= 0)
4984 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.",
4985 ippGetRequestId(client->request));
4986 else if (!ippFirstAttribute(client->request))
4987 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4988 "No attributes in request.");
1106b00e
MS
4989 else
4990 {
4991 /*
4992 * Make sure that the attributes are provided in the correct order and
4993 * don't repeat groups...
4994 */
4995
a469f8a5
MS
4996 for (attr = ippFirstAttribute(client->request),
4997 group = ippGetGroupTag(attr);
1106b00e 4998 attr;
a469f8a5
MS
4999 attr = ippNextAttribute(client->request))
5000 {
5001 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
1106b00e
MS
5002 {
5003 /*
5004 * Out of order; return an error...
5005 */
5006
a469f8a5
MS
5007 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5008 "Attribute groups are out of order (%x < %x).",
5009 ippGetGroupTag(attr), group);
1106b00e
MS
5010 break;
5011 }
5012 else
a469f8a5
MS
5013 group = ippGetGroupTag(attr);
5014 }
1106b00e
MS
5015
5016 if (!attr)
5017 {
5018 /*
5019 * Then make sure that the first three attributes are:
5020 *
5021 * attributes-charset
5022 * attributes-natural-language
5023 * printer-uri/job-uri
5024 */
5025
a469f8a5
MS
5026 attr = ippFirstAttribute(client->request);
5027 name = ippGetName(attr);
5028 if (attr && name && !strcmp(name, "attributes-charset") &&
5029 ippGetValueTag(attr) == IPP_TAG_CHARSET)
1106b00e
MS
5030 charset = attr;
5031 else
5032 charset = NULL;
5033
a469f8a5
MS
5034 attr = ippNextAttribute(client->request);
5035 name = ippGetName(attr);
1106b00e 5036
a469f8a5
MS
5037 if (attr && name && !strcmp(name, "attributes-natural-language") &&
5038 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
1106b00e
MS
5039 language = attr;
5040 else
5041 language = NULL;
5042
5043 if ((attr = ippFindAttribute(client->request, "printer-uri",
5044 IPP_TAG_URI)) != NULL)
5045 uri = attr;
5046 else if ((attr = ippFindAttribute(client->request, "job-uri",
5047 IPP_TAG_URI)) != NULL)
5048 uri = attr;
5049 else
5050 uri = NULL;
5051
1106b00e 5052 if (charset &&
2cadf0f4
MS
5053 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
5054 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
1106b00e
MS
5055 {
5056 /*
5057 * Bad character set...
5058 */
5059
a469f8a5 5060 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
1106b00e 5061 "Unsupported character set \"%s\".",
a469f8a5 5062 ippGetString(charset, 0, NULL));
1106b00e
MS
5063 }
5064 else if (!charset || !language || !uri)
5065 {
5066 /*
5067 * Return an error, since attributes-charset,
5068 * attributes-natural-language, and printer-uri/job-uri are required
5069 * for all operations.
5070 */
5071
a469f8a5
MS
5072 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
5073 "Missing required attributes.");
1106b00e 5074 }
1106b00e
MS
5075 else
5076 {
8a78aa37
MS
5077 char scheme[32], /* URI scheme */
5078 userpass[32], /* Username/password in URI */
5079 host[256], /* Host name in URI */
5080 resource[256]; /* Resource path in URI */
5081 int port; /* Port number in URI */
5082
5083 name = ippGetName(uri);
5084
5085 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
5086 scheme, sizeof(scheme),
5087 userpass, sizeof(userpass),
5088 host, sizeof(host), &port,
5089 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
5090 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
5091 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
5092 else if ((!strcmp(name, "job-uri") &&
5093 strncmp(resource, "/ipp/print/", 11)) ||
5094 (!strcmp(name, "printer-uri") &&
5095 strcmp(resource, "/ipp/print")))
5096 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
5097 name, ippGetString(uri, 0, NULL));
5098 else
1106b00e
MS
5099 {
5100 /*
8a78aa37 5101 * Try processing the operation...
1106b00e
MS
5102 */
5103
8a78aa37
MS
5104 switch (ippGetOperation(client->request))
5105 {
5106 case IPP_OP_PRINT_JOB :
5107 ipp_print_job(client);
5108 break;
5109
5110 case IPP_OP_PRINT_URI :
5111 ipp_print_uri(client);
5112 break;
5113
5114 case IPP_OP_VALIDATE_JOB :
5115 ipp_validate_job(client);
5116 break;
5117
5118 case IPP_OP_CREATE_JOB :
5119 ipp_create_job(client);
5120 break;
5121
5122 case IPP_OP_SEND_DOCUMENT :
5123 ipp_send_document(client);
5124 break;
5125
5126 case IPP_OP_SEND_URI :
5127 ipp_send_uri(client);
5128 break;
5129
5130 case IPP_OP_CANCEL_JOB :
5131 ipp_cancel_job(client);
5132 break;
5133
5134 case IPP_OP_GET_JOB_ATTRIBUTES :
5135 ipp_get_job_attributes(client);
5136 break;
5137
5138 case IPP_OP_GET_JOBS :
5139 ipp_get_jobs(client);
5140 break;
5141
5142 case IPP_OP_GET_PRINTER_ATTRIBUTES :
5143 ipp_get_printer_attributes(client);
5144 break;
5145
2cadf0f4
MS
5146 case IPP_OP_CLOSE_JOB :
5147 ipp_close_job(client);
5148 break;
5149
5150 case IPP_OP_IDENTIFY_PRINTER :
5151 ipp_identify_printer(client);
5152 break;
5153
8a78aa37
MS
5154 default :
5155 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
5156 "Operation not supported.");
5157 break;
5158 }
1106b00e
MS
5159 }
5160 }
5161 }
5162 }
5163
5164 /*
5165 * Send the HTTP header and return...
5166 */
5167
a469f8a5
MS
5168 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
5169 httpFlush(client->http); /* Flush trailing (junk) data */
1106b00e 5170
a469f8a5 5171 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
1106b00e
MS
5172 ippLength(client->response)));
5173}
5174
5175
5176/*
5177 * 'process_job()' - Process a print job.
5178 */
5179
5180static void * /* O - Thread exit status */
5181process_job(_ipp_job_t *job) /* I - Job */
5182{
a469f8a5
MS
5183 job->state = IPP_JSTATE_PROCESSING;
5184 job->printer->state = IPP_PSTATE_PROCESSING;
4a838088 5185 job->processing = time(NULL);
1106b00e 5186
0b5ce83f
MS
5187 while (job->printer->state_reasons & _IPP_PREASON_MEDIA_EMPTY)
5188 {
5189 job->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
5190
5191 sleep(1);
5192 }
5193
5194 job->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_NEEDED;
5195
db8b865d
MS
5196 if (job->printer->command)
5197 {
5198 /*
5199 * Execute a command with the job spool file and wait for it to complete...
5200 */
5201
5202 int pid, /* Process ID */
5203 status; /* Exit status */
5204 time_t start, /* Start time */
5205 end; /* End time */
5206
5207 fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command,
5208 job->filename);
5209 time(&start);
5210
5211 if ((pid = fork()) == 0)
5212 {
5213 /*
5214 * Child comes here...
5215 */
5216
5217 execlp(job->printer->command, job->printer->command, job->filename,
5218 (void *)NULL);
5219 exit(errno);
5220 }
5221 else if (pid < 0)
5222 {
5223 /*
5224 * Unable to fork process...
5225 */
5226
5227 perror("Unable to start job processing command");
5228 }
5229 else
5230 {
5231 /*
5232 * Wait for child to complete...
5233 */
5234
5235#ifdef HAVE_WAITPID
5236 while (waitpid(pid, &status, 0) < 0);
5237#else
5238 while (wait(&status) < 0);
5239#endif /* HAVE_WAITPID */
5240
5241 if (status)
5242 {
5243 if (WIFEXITED(status))
5244 fprintf(stderr, "Command \"%s\" exited with status %d.\n",
5245 job->printer->command, WEXITSTATUS(status));
5246 else
5247 fprintf(stderr, "Command \"%s\" terminated with signal %d.\n",
5248 job->printer->command, WTERMSIG(status));
4a838088
MS
5249
5250 job->state = IPP_JSTATE_ABORTED;
db8b865d
MS
5251 }
5252 else
5253 fprintf(stderr, "Command \"%s\" completed successfully.\n",
5254 job->printer->command);
5255 }
5256
5257 /*
5258 * Make sure processing takes at least 5 seconds...
5259 */
5260
5261 time(&end);
5262 if ((end - start) < 5)
5263 sleep(5);
5264 }
5265 else
5266 {
5267 /*
5268 * Sleep for a random amount of time to simulate job processing.
5269 */
5270
5271 sleep(5 + (rand() % 11));
5272 }
1106b00e
MS
5273
5274 if (job->cancel)
a469f8a5 5275 job->state = IPP_JSTATE_CANCELED;
4a838088 5276 else if (job->state == IPP_JSTATE_PROCESSING)
a469f8a5 5277 job->state = IPP_JSTATE_COMPLETED;
1106b00e
MS
5278
5279 job->completed = time(NULL);
a469f8a5 5280 job->printer->state = IPP_PSTATE_IDLE;
1106b00e
MS
5281 job->printer->active_job = NULL;
5282
5283 return (NULL);
5284}
5285
5286
0268488e 5287#ifdef HAVE_DNSSD
1106b00e
MS
5288/*
5289 * 'register_printer()' - Register a printer object via Bonjour.
5290 */
5291
5292static int /* O - 1 on success, 0 on error */
5293register_printer(
5294 _ipp_printer_t *printer, /* I - Printer */
5295 const char *location, /* I - Location */
5296 const char *make, /* I - Manufacturer */
5297 const char *model, /* I - Model name */
5298 const char *formats, /* I - Supported formats */
5299 const char *adminurl, /* I - Web interface URL */
fe202ff4 5300 const char *uuid, /* I - Printer UUID */
1106b00e
MS
5301 int color, /* I - 1 = color, 0 = monochrome */
5302 int duplex, /* I - 1 = duplex, 0 = simplex */
a469f8a5 5303 const char *subtype) /* I - Service subtype */
1106b00e
MS
5304{
5305 DNSServiceErrorType error; /* Error from Bonjour */
5306 char make_model[256],/* Make and model together */
a469f8a5
MS
5307 product[256], /* Product string */
5308 regtype[256]; /* Bonjour service type */
1106b00e
MS
5309
5310
5311 /*
5312 * Build the TXT record for IPP...
5313 */
5314
5315 snprintf(make_model, sizeof(make_model), "%s %s", make, model);
5316 snprintf(product, sizeof(product), "(%s)", model);
5317
5318 TXTRecordCreate(&(printer->ipp_txt), 1024, NULL);
8a78aa37 5319 TXTRecordSetValue(&(printer->ipp_txt), "rp", 9, "ipp/print");
1106b00e
MS
5320 TXTRecordSetValue(&(printer->ipp_txt), "ty", (uint8_t)strlen(make_model),
5321 make_model);
5322 TXTRecordSetValue(&(printer->ipp_txt), "adminurl", (uint8_t)strlen(adminurl),
5323 adminurl);
a469f8a5
MS
5324 if (*location)
5325 TXTRecordSetValue(&(printer->ipp_txt), "note", (uint8_t)strlen(location),
5326 location);
1106b00e
MS
5327 TXTRecordSetValue(&(printer->ipp_txt), "product", (uint8_t)strlen(product),
5328 product);
5329 TXTRecordSetValue(&(printer->ipp_txt), "pdl", (uint8_t)strlen(formats),
5330 formats);
5331 TXTRecordSetValue(&(printer->ipp_txt), "Color", 1, color ? "T" : "F");
5332 TXTRecordSetValue(&(printer->ipp_txt), "Duplex", 1, duplex ? "T" : "F");
5333 TXTRecordSetValue(&(printer->ipp_txt), "usb_MFG", (uint8_t)strlen(make),
5334 make);
5335 TXTRecordSetValue(&(printer->ipp_txt), "usb_MDL", (uint8_t)strlen(model),
5336 model);
fe202ff4
MS
5337 TXTRecordSetValue(&(printer->ipp_txt), "UUID", (uint8_t)strlen(uuid), uuid);
5338# ifdef HAVE_SSL
5339 TXTRecordSetValue(&(printer->ipp_txt), "TLS", 3, "1.2");
5340# endif /* HAVE_SSL */
1106b00e
MS
5341
5342 /*
5343 * Create a shared service reference for Bonjour...
5344 */
5345
5346 if ((error = DNSServiceCreateConnection(&(printer->common_ref)))
5347 != kDNSServiceErr_NoError)
5348 {
5349 fprintf(stderr, "Unable to create mDNSResponder connection: %d\n", error);
5350 return (0);
5351 }
5352
5353 /*
5354 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5355 * defend our service name but not actually support LPD...
5356 */
5357
5358 printer->printer_ref = printer->common_ref;
5359
5360 if ((error = DNSServiceRegister(&(printer->printer_ref),
5361 kDNSServiceFlagsShareConnection,
5362 0 /* interfaceIndex */, printer->dnssd_name,
5363 "_printer._tcp", NULL /* domain */,
5364 NULL /* host */, 0 /* port */, 0 /* txtLen */,
5365 NULL /* txtRecord */,
5366 (DNSServiceRegisterReply)dnssd_callback,
5367 printer)) != kDNSServiceErr_NoError)
5368 {
5369 fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
5370 printer->dnssd_name, error);
5371 return (0);
5372 }
5373
5374 /*
5375 * Then register the _ipp._tcp (IPP) service type with the real port number to
5376 * advertise our IPP printer...
5377 */
5378
5379 printer->ipp_ref = printer->common_ref;
5380
a469f8a5
MS
5381 if (subtype && *subtype)
5382 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtype);
5383 else
5384 strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
5385
1106b00e
MS
5386 if ((error = DNSServiceRegister(&(printer->ipp_ref),
5387 kDNSServiceFlagsShareConnection,
5388 0 /* interfaceIndex */, printer->dnssd_name,
5389 regtype, NULL /* domain */,
5390 NULL /* host */, htons(printer->port),
5391 TXTRecordGetLength(&(printer->ipp_txt)),
5392 TXTRecordGetBytesPtr(&(printer->ipp_txt)),
5393 (DNSServiceRegisterReply)dnssd_callback,
5394 printer)) != kDNSServiceErr_NoError)
5395 {
5396 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
5397 printer->dnssd_name, regtype, error);
5398 return (0);
5399 }
5400
f93b32b6 5401# ifdef HAVE_SSL
a469f8a5
MS
5402 /*
5403 * Then register the _ipps._tcp (IPP) service type with the real port number to
5404 * advertise our IPP printer...
5405 */
5406
5407 printer->ipps_ref = printer->common_ref;
5408
5409 if (subtype && *subtype)
5410 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtype);
5411 else
5412 strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
5413
5414 if ((error = DNSServiceRegister(&(printer->ipps_ref),
5415 kDNSServiceFlagsShareConnection,
5416 0 /* interfaceIndex */, printer->dnssd_name,
5417 regtype, NULL /* domain */,
5418 NULL /* host */, htons(printer->port),
5419 TXTRecordGetLength(&(printer->ipp_txt)),
5420 TXTRecordGetBytesPtr(&(printer->ipp_txt)),
5421 (DNSServiceRegisterReply)dnssd_callback,
5422 printer)) != kDNSServiceErr_NoError)
5423 {
5424 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
5425 printer->dnssd_name, regtype, error);
5426 return (0);
5427 }
5428# endif /* HAVE_SSL */
5429
1106b00e
MS
5430 /*
5431 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5432 * real port number to advertise our IPP printer...
5433 */
5434
5435 printer->http_ref = printer->common_ref;
5436
5437 if ((error = DNSServiceRegister(&(printer->http_ref),
5438 kDNSServiceFlagsShareConnection,
5439 0 /* interfaceIndex */, printer->dnssd_name,
5440 "_http._tcp,_printer", NULL /* domain */,
5441 NULL /* host */, htons(printer->port),
5442 0 /* txtLen */, NULL, /* txtRecord */
5443 (DNSServiceRegisterReply)dnssd_callback,
5444 printer)) != kDNSServiceErr_NoError)
5445 {
5446 fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
5447 printer->dnssd_name, regtype, error);
5448 return (0);
5449 }
5450
5451 return (1);
5452}
0268488e 5453#endif /* HAVE_DNSSD */
1106b00e
MS
5454
5455
5456/*
5457 * 'respond_http()' - Send a HTTP response.
5458 */
5459
5460int /* O - 1 on success, 0 on failure */
a469f8a5
MS
5461respond_http(
5462 _ipp_client_t *client, /* I - Client */
5463 http_status_t code, /* I - HTTP status of response */
5464 const char *content_encoding, /* I - Content-Encoding of response */
5465 const char *type, /* I - MIME media type of response */
5466 size_t length) /* I - Length of response */
1106b00e
MS
5467{
5468 char message[1024]; /* Text message */
5469
5470
a469f8a5 5471 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
1106b00e 5472
a469f8a5 5473 if (code == HTTP_STATUS_CONTINUE)
1106b00e
MS
5474 {
5475 /*
5476 * 100-continue doesn't send any headers...
5477 */
5478
a469f8a5 5479 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
1106b00e
MS
5480 }
5481
5482 /*
5483 * Format an error message...
5484 */
5485
fe202ff4 5486 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
1106b00e
MS
5487 {
5488 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
5489
5490 type = "text/plain";
5491 length = strlen(message);
5492 }
5493 else
5494 message[0] = '\0';
5495
5496 /*
a469f8a5 5497 * Send the HTTP response header...
1106b00e
MS
5498 */
5499
a469f8a5 5500 httpClearFields(client->http);
1106b00e 5501
a469f8a5
MS
5502 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
5503 client->operation == HTTP_STATE_OPTIONS)
5504 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
1106b00e
MS
5505
5506 if (type)
5507 {
5508 if (!strcmp(type, "text/html"))
a469f8a5
MS
5509 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
5510 "text/html; charset=utf-8");
5511 else
5512 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
1106b00e 5513
a469f8a5
MS
5514 if (content_encoding)
5515 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
1106b00e 5516 }
1106b00e 5517
a469f8a5
MS
5518 httpSetLength(client->http, length);
5519
5520 if (httpWriteResponse(client->http, code) < 0)
1106b00e
MS
5521 return (0);
5522
5523 /*
5524 * Send the response data...
5525 */
5526
5527 if (message[0])
5528 {
5529 /*
5530 * Send a plain text message.
5531 */
5532
a469f8a5
MS
5533 if (httpPrintf(client->http, "%s", message) < 0)
5534 return (0);
5535
5536 if (httpWrite2(client->http, "", 0) < 0)
1106b00e
MS
5537 return (0);
5538 }
5539 else if (client->response)
5540 {
5541 /*
5542 * Send an IPP response...
5543 */
5544
83e08001 5545 debug_attributes("Response", client->response, 2);
1106b00e 5546
a469f8a5 5547 ippSetState(client->response, IPP_STATE_IDLE);
1106b00e 5548
a469f8a5 5549 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
1106b00e
MS
5550 return (0);
5551 }
1106b00e 5552
a469f8a5 5553 return (1);
1106b00e
MS
5554}
5555
5556
5557/*
5558 * 'respond_ipp()' - Send an IPP response.
5559 */
5560
5561static void
5562respond_ipp(_ipp_client_t *client, /* I - Client */
5563 ipp_status_t status, /* I - status-code */
5564 const char *message, /* I - printf-style status-message */
5565 ...) /* I - Additional args as needed */
5566{
a469f8a5 5567 const char *formatted = NULL; /* Formatted message */
1106b00e
MS
5568
5569
a469f8a5 5570 ippSetStatusCode(client->response, status);
1106b00e
MS
5571
5572 if (message)
5573 {
a469f8a5
MS
5574 va_list ap; /* Pointer to additional args */
5575 ipp_attribute_t *attr; /* New status-message attribute */
5576
1106b00e 5577 va_start(ap, message);
a469f8a5
MS
5578 if ((attr = ippFindAttribute(client->response, "status-message",
5579 IPP_TAG_TEXT)) != NULL)
5580 ippSetStringfv(client->response, &attr, 0, message, ap);
5581 else
5582 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
5583 "status-message", NULL, message, ap);
1106b00e
MS
5584 va_end(ap);
5585
a469f8a5 5586 formatted = ippGetString(attr, 0, NULL);
1106b00e 5587 }
1106b00e 5588
a469f8a5
MS
5589 if (formatted)
5590 fprintf(stderr, "%s %s %s (%s)\n", client->hostname,
5591 ippOpString(client->operation_id), ippErrorString(status),
5592 formatted);
5593 else
5594 fprintf(stderr, "%s %s %s\n", client->hostname,
5595 ippOpString(client->operation_id), ippErrorString(status));
1106b00e
MS
5596}
5597
5598
83e08001
MS
5599/*
5600 * 'respond_unsupported()' - Respond with an unsupported attribute.
5601 */
5602
5603static void
5604respond_unsupported(
5605 _ipp_client_t *client, /* I - Client */
5606 ipp_attribute_t *attr) /* I - Atribute */
5607{
a2326b5b
MS
5608 ipp_attribute_t *temp; /* Copy of attribute */
5609
5610
a469f8a5
MS
5611 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
5612 "Unsupported %s %s%s value.", ippGetName(attr),
5613 ippGetCount(attr) > 1 ? "1setOf " : "",
5614 ippTagString(ippGetValueTag(attr)));
83e08001 5615
a2326b5b
MS
5616 temp = ippCopyAttribute(client->response, attr, 0);
5617 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
83e08001
MS
5618}
5619
5620
1106b00e
MS
5621/*
5622 * 'run_printer()' - Run the printer service.
5623 */
5624
5625static void
5626run_printer(_ipp_printer_t *printer) /* I - Printer */
5627{
0268488e 5628 int num_fds; /* Number of file descriptors */
1106b00e
MS
5629 struct pollfd polldata[3]; /* poll() data */
5630 int timeout; /* Timeout for poll() */
5631 _ipp_client_t *client; /* New client */
5632
5633
5634 /*
5635 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
5636 */
5637
5638 polldata[0].fd = printer->ipv4;
5639 polldata[0].events = POLLIN;
5640
5641 polldata[1].fd = printer->ipv6;
5642 polldata[1].events = POLLIN;
5643
0268488e
MS
5644 num_fds = 2;
5645
5646#ifdef HAVE_DNSSD
5647 polldata[num_fds ].fd = DNSServiceRefSockFD(printer->common_ref);
5648 polldata[num_fds ++].events = POLLIN;
5649#endif /* HAVE_DNSSD */
1106b00e
MS
5650
5651 /*
5652 * Loop until we are killed or have a hard error...
5653 */
5654
5655 for (;;)
5656 {
5657 if (cupsArrayCount(printer->jobs))
5658 timeout = 10;
5659 else
5660 timeout = -1;
5661
7e86f2f6 5662 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
1106b00e
MS
5663 {
5664 perror("poll() failed");
5665 break;
5666 }
5667
5668 if (polldata[0].revents & POLLIN)
5669 {
5670 if ((client = create_client(printer, printer->ipv4)) != NULL)
5671 {
5672 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
5673 {
5674 perror("Unable to create client thread");
5675 delete_client(client);
5676 }
5677 }
5678 }
5679
5680 if (polldata[1].revents & POLLIN)
5681 {
5682 if ((client = create_client(printer, printer->ipv6)) != NULL)
5683 {
5684 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
5685 {
5686 perror("Unable to create client thread");
5687 delete_client(client);
5688 }
5689 }
5690 }
5691
0268488e 5692#ifdef HAVE_DNSSD
1106b00e
MS
5693 if (polldata[2].revents & POLLIN)
5694 DNSServiceProcessResult(printer->common_ref);
0268488e 5695#endif /* HAVE_DNSSD */
1106b00e
MS
5696
5697 /*
5698 * Clean out old jobs...
5699 */
5700
5701 clean_jobs(printer);
5702 }
5703}
5704
5705
0b5ce83f
MS
5706/*
5707 * 'time_string()' - Return the local time in hours, minutes, and seconds.
5708 */
5709
5710static char *
5711time_string(time_t tv, /* I - Time value */
5712 char *buffer, /* I - Buffer */
5713 size_t bufsize) /* I - Size of buffer */
5714{
5715 struct tm *curtime = localtime(&tv);
5716 /* Local time */
5717
5718 strftime(buffer, bufsize, "%X", curtime);
5719 return (buffer);
5720}
5721
5722
1106b00e
MS
5723/*
5724 * 'usage()' - Show program usage.
5725 */
5726
5727static void
5728usage(int status) /* O - Exit status */
5729{
5730 if (!status)
5731 {
d1f0f86b 5732 puts(CUPS_SVERSION " - Copyright 2010-2013 by Apple Inc. All rights "
a469f8a5 5733 "reserved.");
1106b00e
MS
5734 puts("");
5735 }
5736
5737 puts("Usage: ippserver [options] \"name\"");
5738 puts("");
5739 puts("Options:");
5740 puts("-2 Supports 2-sided printing (default=1-sided)");
5741 puts("-M manufacturer Manufacturer name (default=Test)");
d1f0f86b 5742 puts("-P PIN printing mode");
d48a1002 5743 puts("-c command Run command for every print job");
1106b00e
MS
5744 printf("-d spool-directory Spool directory "
5745 "(default=/tmp/ippserver.%d)\n", (int)getpid());
5746 puts("-f type/subtype[,...] List of supported types "
5747 "(default=application/pdf,image/jpeg)");
5748 puts("-h Show program help");
5749 puts("-i iconfile.png PNG icon file (default=printer.png)");
d1f0f86b 5750 puts("-k Keep job spool files");
1106b00e
MS
5751 puts("-l location Location of printer (default=empty string)");
5752 puts("-m model Model name (default=Printer)");
5753 puts("-n hostname Hostname for printer");
5754 puts("-p port Port number (default=auto)");
d1f0f86b 5755#ifdef HAVE_DNSSD
a469f8a5 5756 puts("-r subtype Bonjour service subtype (default=_print)");
d1f0f86b 5757#endif /* HAVE_DNSSD */
1106b00e
MS
5758 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
5759 puts("-v[vvv] Be (very) verbose");
5760
5761 exit(status);
5762}
5763
5764
5765/*
83e08001
MS
5766 * 'valid_doc_attributes()' - Determine whether the document attributes are
5767 * valid.
1106b00e 5768 *
83e08001
MS
5769 * When one or more document attributes are invalid, this function adds a
5770 * suitable response and attributes to the unsupported group.
1106b00e
MS
5771 */
5772
5773static int /* O - 1 if valid, 0 if not */
83e08001 5774valid_doc_attributes(
1106b00e
MS
5775 _ipp_client_t *client) /* I - Client */
5776{
a469f8a5
MS
5777 int valid = 1; /* Valid attributes? */
5778 ipp_op_t op = ippGetOperation(client->request);
5779 /* IPP operation */
5780 const char *op_name = ippOpString(op);
5781 /* IPP operation name */
1106b00e 5782 ipp_attribute_t *attr, /* Current attribute */
a469f8a5
MS
5783 *supported; /* xxx-supported attribute */
5784 const char *compression = NULL,
5785 /* compression value */
5786 *format = NULL; /* document-format value */
1106b00e
MS
5787
5788
5789 /*
5790 * Check operation attributes...
5791 */
5792
404dde30 5793 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
1106b00e
MS
5794 {
5795 /*
a469f8a5
MS
5796 * If compression is specified, only accept a supported value in a Print-Job
5797 * or Send-Document request...
1106b00e
MS
5798 */
5799
a469f8a5
MS
5800 compression = ippGetString(attr, 0, NULL);
5801 supported = ippFindAttribute(client->printer->attrs,
5802 "compression-supported", IPP_TAG_KEYWORD);
5803
5804 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
5805 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
db8b865d
MS
5806 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
5807 op != IPP_OP_VALIDATE_JOB) ||
a469f8a5
MS
5808 !ippContainsString(supported, compression))
5809 {
1106b00e 5810 respond_unsupported(client, attr);
a469f8a5
MS
5811 valid = 0;
5812 }
1106b00e 5813 else
a469f8a5 5814 {
404dde30
MS
5815 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
5816
5817 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
a469f8a5
MS
5818
5819 if (strcmp(compression, "none"))
a654c79d
MS
5820 {
5821 if (Verbosity)
5822 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
a469f8a5 5823 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
a654c79d 5824 }
a469f8a5 5825 }
1106b00e
MS
5826 }
5827
5828 /*
5829 * Is it a format we support?
5830 */
5831
404dde30 5832 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
1106b00e 5833 {
a469f8a5
MS
5834 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
5835 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
5836 {
1106b00e 5837 respond_unsupported(client, attr);
a469f8a5
MS
5838 valid = 0;
5839 }
1106b00e 5840 else
e60ec91f 5841 {
a469f8a5 5842 format = ippGetString(attr, 0, NULL);
e60ec91f
MS
5843
5844 fprintf(stderr, "%s %s document-format=\"%s\"\n",
a469f8a5 5845 client->hostname, op_name, format);
4a838088
MS
5846
5847 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
e60ec91f 5848 }
1106b00e
MS
5849 }
5850 else
a469f8a5 5851 {
404dde30 5852 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8a78aa37
MS
5853 if (!format)
5854 format = "application/octet-stream"; /* Should never happen */
5855
4a838088 5856 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
a469f8a5 5857 }
1106b00e 5858
404dde30 5859 if (!strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
1106b00e
MS
5860 {
5861 /*
404dde30 5862 * Auto-type the file using the first 8 bytes of the file...
1106b00e
MS
5863 */
5864
404dde30 5865 unsigned char header[8]; /* First 8 bytes of file */
1106b00e
MS
5866
5867 memset(header, 0, sizeof(header));
a469f8a5 5868 httpPeek(client->http, (char *)header, sizeof(header));
1106b00e
MS
5869
5870 if (!memcmp(header, "%PDF", 4))
5871 format = "application/pdf";
5872 else if (!memcmp(header, "%!", 2))
5873 format = "application/postscript";
404dde30 5874 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
1106b00e
MS
5875 format = "image/jpeg";
5876 else if (!memcmp(header, "\211PNG", 4))
5877 format = "image/png";
404dde30
MS
5878 else if (!memcmp(header, "RAS2", 4))
5879 format = "image/pwg-raster";
5880 else if (!memcmp(header, "UNIRAST", 8))
5881 format = "image/urf";
5882 else
5883 format = NULL;
1106b00e
MS
5884
5885 if (format)
404dde30 5886 {
1106b00e 5887 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
a469f8a5 5888 client->hostname, op_name, format);
1106b00e 5889
404dde30
MS
5890 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
5891 }
1106b00e
MS
5892 }
5893
404dde30 5894 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
1106b00e 5895 {
a469f8a5
MS
5896 respond_unsupported(client, attr);
5897 valid = 0;
1106b00e
MS
5898 }
5899
404dde30
MS
5900 /*
5901 * document-name
5902 */
5903
5904 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
5905 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
5906
a469f8a5 5907 return (valid);
83e08001
MS
5908}
5909
5910
5911/*
5912 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5913 *
5914 * When one or more job attributes are invalid, this function adds a suitable
5915 * response and attributes to the unsupported group.
5916 */
5917
5918static int /* O - 1 if valid, 0 if not */
5919valid_job_attributes(
5920 _ipp_client_t *client) /* I - Client */
5921{
a469f8a5
MS
5922 int i, /* Looping var */
5923 valid = 1; /* Valid attributes? */
83e08001
MS
5924 ipp_attribute_t *attr, /* Current attribute */
5925 *supported; /* xxx-supported attribute */
5926
5927
5928 /*
5929 * Check operation attributes...
5930 */
5931
a469f8a5 5932 valid = valid_doc_attributes(client);
83e08001 5933
1106b00e
MS
5934 /*
5935 * Check the various job template attributes...
5936 */
5937
404dde30 5938 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
1106b00e 5939 {
a469f8a5
MS
5940 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
5941 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
1106b00e
MS
5942 {
5943 respond_unsupported(client, attr);
a469f8a5 5944 valid = 0;
1106b00e
MS
5945 }
5946 }
5947
404dde30 5948 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
1106b00e 5949 {
a469f8a5 5950 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
1106b00e
MS
5951 {
5952 respond_unsupported(client, attr);
a469f8a5 5953 valid = 0;
1106b00e
MS
5954 }
5955 }
5956
404dde30 5957 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
1106b00e 5958 {
a469f8a5
MS
5959 if (ippGetCount(attr) != 1 ||
5960 (ippGetValueTag(attr) != IPP_TAG_NAME &&
5961 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
5962 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
5963 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
1106b00e
MS
5964 {
5965 respond_unsupported(client, attr);
a469f8a5 5966 valid = 0;
1106b00e
MS
5967 }
5968 }
5969
404dde30
MS
5970 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
5971 {
5972 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
5973 {
5974 respond_unsupported(client, attr);
5975 valid = 0;
5976 }
5977 }
5978
5979 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
1106b00e 5980 {
a469f8a5
MS
5981 if (ippGetCount(attr) != 1 ||
5982 (ippGetValueTag(attr) != IPP_TAG_NAME &&
5983 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
1106b00e
MS
5984 {
5985 respond_unsupported(client, attr);
a469f8a5 5986 valid = 0;
1106b00e 5987 }
404dde30
MS
5988
5989 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
1106b00e 5990 }
404dde30
MS
5991 else
5992 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
1106b00e 5993
404dde30 5994 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
1106b00e 5995 {
a469f8a5
MS
5996 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
5997 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
1106b00e
MS
5998 {
5999 respond_unsupported(client, attr);
a469f8a5 6000 valid = 0;
1106b00e
MS
6001 }
6002 }
6003
404dde30 6004 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
1106b00e 6005 {
a469f8a5
MS
6006 if (ippGetCount(attr) != 1 ||
6007 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6008 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6009 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
6010 strcmp(ippGetString(attr, 0, NULL), "none"))
1106b00e
MS
6011 {
6012 respond_unsupported(client, attr);
a469f8a5 6013 valid = 0;
1106b00e
MS
6014 }
6015 }
6016
404dde30 6017 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
1106b00e 6018 {
a469f8a5
MS
6019 if (ippGetCount(attr) != 1 ||
6020 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6021 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6022 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
1106b00e
MS
6023 {
6024 respond_unsupported(client, attr);
a469f8a5 6025 valid = 0;
1106b00e
MS
6026 }
6027 else
6028 {
6029 for (i = 0;
6030 i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
6031 i ++)
a469f8a5 6032 if (!strcmp(ippGetString(attr, 0, NULL), media_supported[i]))
1106b00e
MS
6033 break;
6034
6035 if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
6036 {
6037 respond_unsupported(client, attr);
a469f8a5 6038 valid = 0;
1106b00e
MS
6039 }
6040 }
6041 }
6042
404dde30 6043 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
1106b00e 6044 {
a469f8a5
MS
6045 if (ippGetCount(attr) != 1 ||
6046 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
1106b00e
MS
6047 {
6048 respond_unsupported(client, attr);
a469f8a5 6049 valid = 0;
1106b00e
MS
6050 }
6051 /* TODO: check for valid media-col */
6052 }
6053
404dde30 6054 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
1106b00e 6055 {
a469f8a5
MS
6056 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
6057 (strcmp(ippGetString(attr, 0, NULL),
1106b00e 6058 "separate-documents-uncollated-copies") &&
a469f8a5 6059 strcmp(ippGetString(attr, 0, NULL),
1106b00e
MS
6060 "separate-documents-collated-copies")))
6061 {
6062 respond_unsupported(client, attr);
a469f8a5 6063 valid = 0;
1106b00e
MS
6064 }
6065 }
6066
404dde30 6067 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
1106b00e 6068 {
a469f8a5
MS
6069 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
6070 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
6071 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
1106b00e
MS
6072 {
6073 respond_unsupported(client, attr);
a469f8a5 6074 valid = 0;
1106b00e
MS
6075 }
6076 }
6077
404dde30 6078 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
1106b00e 6079 {
c2c30ebc
MS
6080 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
6081 {
6082 respond_unsupported(client, attr);
a469f8a5 6083 valid = 0;
c2c30ebc 6084 }
1106b00e
MS
6085 }
6086
404dde30 6087 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
1106b00e 6088 {
a469f8a5
MS
6089 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
6090 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
6091 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
1106b00e
MS
6092 {
6093 respond_unsupported(client, attr);
a469f8a5 6094 valid = 0;
1106b00e
MS
6095 }
6096 }
6097
404dde30 6098 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
1106b00e 6099 {
4a838088 6100 supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
c2c30ebc
MS
6101
6102 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
6103 !supported)
6104 {
6105 respond_unsupported(client, attr);
6106 valid = 0;
6107 }
6108 else
6109 {
4a838088
MS
6110 int count, /* Number of supported values */
6111 xdpi, /* Horizontal resolution for job template attribute */
6112 ydpi, /* Vertical resolution for job template attribute */
6113 sydpi; /* Vertical resolution for supported value */
6114 ipp_res_t units, /* Units for job template attribute */
6115 sunits; /* Units for supported value */
c2c30ebc
MS
6116
6117 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
6118 count = ippGetCount(supported);
6119
6120 for (i = 0; i < count; i ++)
6121 {
6122 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
6123 break;
6124 }
6125
6126 if (i >= count)
6127 {
6128 respond_unsupported(client, attr);
6129 valid = 0;
6130 }
6131 }
1106b00e
MS
6132 }
6133
404dde30 6134 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
1106b00e 6135 {
404dde30
MS
6136 const char *sides = ippGetString(attr, 0, NULL);
6137 /* "sides" value... */
ad29aeab 6138
a469f8a5 6139 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
1106b00e
MS
6140 {
6141 respond_unsupported(client, attr);
a469f8a5 6142 valid = 0;
1106b00e 6143 }
404dde30 6144 else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
1106b00e 6145 {
ad29aeab 6146 if (!ippContainsString(supported, sides))
1106b00e
MS
6147 {
6148 respond_unsupported(client, attr);
a469f8a5 6149 valid = 0;
1106b00e
MS
6150 }
6151 }
ad29aeab 6152 else if (strcmp(sides, "one-sided"))
1106b00e
MS
6153 {
6154 respond_unsupported(client, attr);
a469f8a5 6155 valid = 0;
1106b00e
MS
6156 }
6157 }
6158
a469f8a5 6159 return (valid);
1106b00e
MS
6160}
6161
6162
6163/*
6164 * End of "$Id$".
6165 */