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