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