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