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