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