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