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