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