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