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