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