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