4 * Sample IPP/2.0 server for CUPS.
6 * Copyright 2010-2011 by Apple Inc.
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/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * main() - Main entry to the sample server.
19 * clean_jobs() - Clean out old (completed) jobs.
20 * compare_jobs() - Compare two jobs.
21 * copy_attribute() - Copy a single attribute.
22 * copy_attributes() - Copy attributes from one request to
24 * copy_job_attrs() - Copy job attributes to the response.
25 * create_client() - Accept a new network connection and create
27 * create_job() - Create a new job object from a Print-Job or
29 * create_listener() - Create a listener socket.
30 * create_media_col() - Create a media-col value.
31 * create_printer() - Create, register, and listen for
32 * connections to a printer object.
33 * create_requested_array() - Create an array for requested-attributes.
34 * debug_attributes() - Print attributes in a request or response.
35 * delete_client() - Close the socket and free all memory used
37 * delete_job() - Remove from the printer and free all memory
38 * used by a job object.
39 * delete_printer() - Unregister, close listen sockets, and free
40 * all memory used by a printer object.
41 * dnssd_callback() - Handle Bonjour registration events.
42 * find_job() - Find a job specified in a request.
43 * html_escape() - Write a HTML-safe string.
44 * html_printf() - Send formatted text to the client, quoting
46 * ipp_cancel_job() - Cancel a job.
47 * ipp_create_job() - Create a job object.
48 * ipp_get_job_attributes() - Get the attributes for a job object.
49 * ipp_get_jobs() - Get a list of job objects.
50 * ipp_get_printer_attributes() - Get the attributes for a printer object.
51 * ipp_print_job() - Create a job object with an attached
53 * ipp_print_uri() - Create a job object with a referenced
55 * ipp_send_document() - Add an attached document to a job object
56 * created with Create-Job.
57 * ipp_send_uri() - Add a referenced document to a job object
58 * created with Create-Job.
59 * ipp_validate_job() - Validate job creation attributes.
60 * process_client() - Process client requests on a thread.
61 * process_http() - Process a HTTP request.
62 * process_ipp() - Process an IPP request.
63 * process_job() - Process a print job.
64 * register_printer() - Register a printer object via Bonjour.
65 * respond_http() - Send a HTTP response.
66 * respond_ipp() - Send an IPP response.
67 * respond_unsupported() - Respond with an unsupported attribute.
68 * run_printer() - Run the printer service.
69 * usage() - Show program usage.
70 * valid_doc_attributes() - Determine whether the document attributes
72 * valid_job_attributes() - Determine whether the job attributes are
77 * Include necessary headers...
80 #include <cups/cups-private.h>
83 #endif /* HAVE_DNSSD */
86 #ifdef HAVE_SYS_MOUNT_H
87 # include <sys/mount.h>
88 #endif /* HAVE_SYS_MOUNT_H */
89 #ifdef HAVE_SYS_STATFS_H
90 # include <sys/statfs.h>
91 #endif /* HAVE_SYS_STATFS_H */
92 #ifdef HAVE_SYS_STATVFS_H
93 # include <sys/statvfs.h>
94 #endif /* HAVE_SYS_STATVFS_H */
97 #endif /* HAVE_SYS_VFS_H */
104 enum _ipp_preasons_e
/* printer-state-reasons bit values */
106 _IPP_PRINTER_NONE
= 0x0000, /* none */
107 _IPP_PRINTER_OTHER
= 0x0001, /* other */
108 _IPP_PRINTER_COVER_OPEN
= 0x0002, /* cover-open */
109 _IPP_PRINTER_INPUT_TRAY_MISSING
= 0x0004,
110 /* input-tray-missing */
111 _IPP_PRINTER_MARKER_SUPPLY_EMPTY
= 0x0008,
112 /* marker-supply-empty */
113 _IPP_PRINTER_MARKER_SUPPLY_LOW
= 0x0010,
114 /* marker-suply-low */
115 _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL
= 0x0020,
116 /* marker-waste-almost-full */
117 _IPP_PRINTER_MARKER_WASTE_FULL
= 0x0040,
118 /* marker-waste-full */
119 _IPP_PRINTER_MEDIA_EMPTY
= 0x0080, /* media-empty */
120 _IPP_PRINTER_MEDIA_JAM
= 0x0100, /* media-jam */
121 _IPP_PRINTER_MEDIA_LOW
= 0x0200, /* media-low */
122 _IPP_PRINTER_MEDIA_NEEDED
= 0x0400, /* media-needed */
123 _IPP_PRINTER_MOVING_TO_PAUSED
= 0x0800,
124 /* moving-to-paused */
125 _IPP_PRINTER_PAUSED
= 0x1000, /* paused */
126 _IPP_PRINTER_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
127 _IPP_PRINTER_TONER_EMPTY
= 0x4000, /* toner-empty */
128 _IPP_PRINTER_TONER_LOW
= 0x8000 /* toner-low */
130 typedef unsigned int _ipp_preasons_t
; /* Bitfield for printer-state-reasons */
132 typedef enum _ipp_media_class_e
134 _IPP_GENERAL
, /* General-purpose size */
135 _IPP_PHOTO_ONLY
, /* Photo-only size */
136 _IPP_ENV_ONLY
/* Envelope-only size */
137 } _ipp_media_class_t
;
139 static const char * const media_supported
[] =
140 { /* media-supported values */
141 "iso_a4_210x297mm", /* A4 */
142 "iso_a5_148x210mm", /* A5 */
143 "iso_a6_105x148mm", /* A6 */
144 "iso_dl_110x220mm", /* DL */
145 "na_legal_8.5x14in", /* Legal */
146 "na_letter_8.5x11in", /* Letter */
147 "na_number-10_4.125x9.5in", /* #10 */
148 "na_index-3x5_3x5in", /* 3x5 */
149 "oe_photo-l_3.5x5in", /* L */
150 "na_index-4x6_4x6in", /* 4x6 */
151 "na_5x7_5x7in" /* 5x7 aka 2L */
153 static const int media_col_sizes
[][3] =
154 { /* media-col-database sizes */
155 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
156 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
157 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
158 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
159 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
160 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
161 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
162 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
163 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
164 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
165 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
167 static const char * const media_type_supported
[] =
168 /* media-type-supported values */
175 "photographic-glossy",
176 "photographic-high-gloss",
177 "photographic-matte",
178 "photographic-satin",
179 "photographic-semi-gloss",
181 "stationery-letterhead",
190 typedef struct _ipp_job_s _ipp_job_t
;
192 typedef struct _ipp_printer_s
/**** Printer data ****/
194 int ipv4
, /* IPv4 listener */
195 ipv6
; /* IPv6 listener */
197 DNSServiceRef common_ref
, /* Shared service connection */
198 ipp_ref
, /* Bonjour IPP service */
199 http_ref
, /* Bonjour HTTP service */
200 printer_ref
; /* Bonjour LPD service */
201 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
202 char *dnssd_name
; /* printer-dnssd-name */
203 #endif /* HAVE_DNSSD */
204 char *name
, /* printer-name */
205 *icon
, /* Icon filename */
206 *directory
, /* Spool directory */
207 *hostname
, /* Hostname */
208 *uri
; /* printer-uri-supported */
210 size_t urilen
; /* Length of printer URI */
211 ipp_t
*attrs
; /* Static attributes */
212 ipp_pstate_t state
; /* printer-state value */
213 _ipp_preasons_t state_reasons
; /* printer-state-reasons values */
214 cups_array_t
*jobs
; /* Jobs */
215 _ipp_job_t
*active_job
; /* Current active/pending job */
216 int next_job_id
; /* Next job-id value */
217 _cups_rwlock_t rwlock
; /* Printer lock */
220 struct _ipp_job_s
/**** Job data ****/
223 char *name
, /* job-name */
224 *username
, /* job-originating-user-name */
225 *format
; /* document-format */
226 ipp_jstate_t state
; /* job-state value */
227 time_t processing
, /* time-at-processing value */
228 completed
; /* time-at-completed value */
229 ipp_t
*attrs
; /* Static attributes */
230 int cancel
; /* Non-zero when job canceled */
231 char *filename
; /* Print file name */
232 int fd
; /* Print file descriptor */
233 _ipp_printer_t
*printer
; /* Printer */
236 typedef struct _ipp_client_s
/**** Client data ****/
238 http_t http
; /* HTTP connection */
239 ipp_t
*request
, /* IPP request */
240 *response
; /* IPP response */
241 time_t start
; /* Request start time */
242 http_state_t operation
; /* Request operation */
243 ipp_op_t operation_id
; /* IPP operation-id */
244 char uri
[1024]; /* Request URI */
245 http_addr_t addr
; /* Client address */
246 _ipp_printer_t
*printer
; /* Printer */
247 _ipp_job_t
*job
; /* Current job, if any */
255 static void clean_jobs(_ipp_printer_t
*printer
);
256 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
257 static ipp_attribute_t
*copy_attribute(ipp_t
*to
, ipp_attribute_t
*attr
,
258 ipp_tag_t group_tag
, int quickcopy
);
259 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
260 ipp_tag_t group_tag
, int quickcopy
);
261 static void copy_job_attributes(_ipp_client_t
*client
,
262 _ipp_job_t
*job
, cups_array_t
*ra
);
263 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
264 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
265 static int create_listener(int family
, int *port
);
266 static ipp_t
*create_media_col(const char *media
, const char *type
,
267 int width
, int length
, int margins
);
268 static _ipp_printer_t
*create_printer(const char *servername
,
269 const char *name
, const char *location
,
270 const char *make
, const char *model
,
272 const char *docformats
, int ppm
,
273 int ppm_color
, int duplex
, int port
,
276 #endif /* HAVE_DNSSD */
277 const char *directory
);
278 static cups_array_t
*create_requested_array(_ipp_client_t
*client
);
279 static void debug_attributes(const char *title
, ipp_t
*ipp
,
281 static void delete_client(_ipp_client_t
*client
);
282 static void delete_job(_ipp_job_t
*job
);
283 static void delete_printer(_ipp_printer_t
*printer
);
285 static void dnssd_callback(DNSServiceRef sdRef
,
286 DNSServiceFlags flags
,
287 DNSServiceErrorType errorCode
,
291 _ipp_printer_t
*printer
);
292 #endif /* HAVE_DNSSD */
293 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
294 static void html_escape(_ipp_client_t
*client
, const char *s
,
296 static void html_printf(_ipp_client_t
*client
, const char *format
,
297 ...) __attribute__((__format__(__printf__
,
299 static void ipp_cancel_job(_ipp_client_t
*client
);
300 static void ipp_create_job(_ipp_client_t
*client
);
301 static void ipp_get_job_attributes(_ipp_client_t
*client
);
302 static void ipp_get_jobs(_ipp_client_t
*client
);
303 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
304 static void ipp_print_job(_ipp_client_t
*client
);
305 static void ipp_print_uri(_ipp_client_t
*client
);
306 static void ipp_send_document(_ipp_client_t
*client
);
307 static void ipp_send_uri(_ipp_client_t
*client
);
308 static void ipp_validate_job(_ipp_client_t
*client
);
309 static void *process_client(_ipp_client_t
*client
);
310 static int process_http(_ipp_client_t
*client
);
311 static int process_ipp(_ipp_client_t
*client
);
312 static void *process_job(_ipp_job_t
*job
);
314 static int register_printer(_ipp_printer_t
*printer
,
315 const char *location
, const char *make
,
316 const char *model
, const char *formats
,
317 const char *adminurl
, int color
,
318 int duplex
, const char *regtype
);
319 #endif /* HAVE_DNSSD */
320 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
321 const char *type
, size_t length
);
322 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
323 const char *message
, ...)
324 __attribute__ ((__format__ (__printf__
, 3, 4)));
325 static void respond_unsupported(_ipp_client_t
*client
,
326 ipp_attribute_t
*attr
);
327 static void run_printer(_ipp_printer_t
*printer
);
328 static void usage(int status
) __attribute__((noreturn
));
329 static int valid_doc_attributes(_ipp_client_t
*client
);
330 static int valid_job_attributes(_ipp_client_t
*client
);
337 static int KeepFiles
= 0,
342 * 'main()' - Main entry to the sample server.
345 int /* O - Exit status */
346 main(int argc
, /* I - Number of command-line args */
347 char *argv
[]) /* I - Command-line arguments */
349 int i
; /* Looping var */
350 const char *opt
, /* Current option character */
351 *servername
= NULL
, /* Server host name */
352 *name
= NULL
, /* Printer name */
353 *location
= "", /* Location of printer */
354 *make
= "Test", /* Manufacturer */
355 *model
= "Printer", /* Model */
356 *icon
= "printer.png", /* Icon file */
357 *formats
= "application/pdf,image/jpeg";
358 /* Supported formats */
360 const char *regtype
= "_ipp._tcp"; /* Bonjour service type */
361 #endif /* HAVE_DNSSD */
362 int port
= 8631, /* Port number (0 = auto) TODO: FIX */
363 duplex
= 0, /* Duplex mode */
364 ppm
= 10, /* Pages per minute for mono */
365 ppm_color
= 0; /* Pages per minute for color */
366 char directory
[1024] = ""; /* Spool directory */
367 _ipp_printer_t
*printer
; /* Printer object */
371 * Parse command-line arguments...
374 for (i
= 1; i
< argc
; i
++)
375 if (argv
[i
][0] == '-')
377 for (opt
= argv
[i
] + 1; *opt
; opt
++)
380 case '2' : /* -2 (enable 2-sided printing) */
384 case 'M' : /* -M manufacturer */
391 case 'd' : /* -d spool-directory */
395 strlcpy(directory
, argv
[i
], sizeof(directory
));
398 case 'f' : /* -f type/subtype[,...] */
405 case 'h' : /* -h (show help) */
409 case 'i' : /* -i icon.png */
416 case 'k' : /* -k (keep files) */
420 case 'l' : /* -l location */
427 case 'm' : /* -m model */
434 case 'n' : /* -n hostname */
438 servername
= argv
[i
];
441 case 'p' : /* -p port */
443 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
445 port
= atoi(argv
[i
]);
449 case 'r' : /* -r regtype */
455 #endif /* HAVE_DNSSD */
457 case 's' : /* -s speed[,color-speed] */
461 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
465 case 'v' : /* -v (be verbose) */
469 default : /* Unknown */
470 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
481 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
489 * Apply defaults as needed...
494 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
496 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
498 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
499 directory
, strerror(errno
));
504 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
508 * Create the printer...
511 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
512 formats
, ppm
, ppm_color
, duplex
, port
,
515 #endif /* HAVE_DNSSD */
520 * Run the print service...
523 run_printer(printer
);
526 * Destroy the printer and exit...
529 delete_printer(printer
);
536 * 'clean_jobs()' - Clean out old (completed) jobs.
540 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
542 _ipp_job_t
*job
; /* Current job */
543 time_t cleantime
; /* Clean time */
546 if (cupsArrayCount(printer
->jobs
) == 0)
549 cleantime
= time(NULL
) - 60;
551 _cupsRWLockWrite(&(printer
->rwlock
));
552 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
554 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
555 if (job
->completed
&& job
->completed
< cleantime
)
557 cupsArrayRemove(printer
->jobs
, job
);
562 _cupsRWUnlock(&(printer
->rwlock
));
567 * 'compare_jobs()' - Compare two jobs.
570 static int /* O - Result of comparison */
571 compare_jobs(_ipp_job_t
*a
, /* I - First job */
572 _ipp_job_t
*b
) /* I - Second job */
574 return (b
->id
- a
->id
);
579 * 'copy_attribute()' - Copy a single attribute.
582 static ipp_attribute_t
* /* O - New attribute */
584 ipp_t
*to
, /* O - Destination request/response */
585 ipp_attribute_t
*attr
, /* I - Attribute to copy */
586 ipp_tag_t group_tag
, /* I - Group to put the copy in */
587 int quickcopy
) /* I - Do a quick copy? */
589 int i
; /* Looping var */
590 ipp_attribute_t
*toattr
; /* Destination attribute */
593 if (Verbosity
&& attr
->name
)
595 char buffer
[2048]; /* Attribute value */
597 _ippAttrString(attr
, buffer
, sizeof(buffer
));
599 fprintf(stderr
, "Copying %s (%s%s) %s\n", attr
->name
,
600 attr
->num_values
> 1 ? "1setOf " : "",
601 ippTagString(attr
->value_tag
& ~IPP_TAG_COPY
), buffer
);
604 switch (attr
->value_tag
& ~IPP_TAG_COPY
)
607 toattr
= ippAddSeparator(to
);
610 case IPP_TAG_INTEGER
:
612 toattr
= ippAddIntegers(to
, group_tag
, attr
->value_tag
,
613 attr
->name
, attr
->num_values
, NULL
);
615 for (i
= 0; i
< attr
->num_values
; i
++)
616 toattr
->values
[i
].integer
= attr
->values
[i
].integer
;
619 case IPP_TAG_BOOLEAN
:
620 toattr
= ippAddBooleans(to
, group_tag
, attr
->name
,
621 attr
->num_values
, NULL
);
623 for (i
= 0; i
< attr
->num_values
; i
++)
624 toattr
->values
[i
].boolean
= attr
->values
[i
].boolean
;
629 case IPP_TAG_KEYWORD
:
631 case IPP_TAG_URISCHEME
:
632 case IPP_TAG_CHARSET
:
633 case IPP_TAG_LANGUAGE
:
634 case IPP_TAG_MIMETYPE
:
635 toattr
= ippAddStrings(to
, group_tag
,
636 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
637 attr
->name
, attr
->num_values
, NULL
, NULL
);
641 for (i
= 0; i
< attr
->num_values
; i
++)
642 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
646 for (i
= 0; i
< attr
->num_values
; i
++)
647 toattr
->values
[i
].string
.text
=
648 _cupsStrAlloc(attr
->values
[i
].string
.text
);
653 toattr
= ippAddDate(to
, group_tag
, attr
->name
,
654 attr
->values
[0].date
);
657 case IPP_TAG_RESOLUTION
:
658 toattr
= ippAddResolutions(to
, group_tag
, attr
->name
,
659 attr
->num_values
, IPP_RES_PER_INCH
,
662 for (i
= 0; i
< attr
->num_values
; i
++)
664 toattr
->values
[i
].resolution
.xres
= attr
->values
[i
].resolution
.xres
;
665 toattr
->values
[i
].resolution
.yres
= attr
->values
[i
].resolution
.yres
;
666 toattr
->values
[i
].resolution
.units
= attr
->values
[i
].resolution
.units
;
671 toattr
= ippAddRanges(to
, group_tag
, attr
->name
,
672 attr
->num_values
, NULL
, NULL
);
674 for (i
= 0; i
< attr
->num_values
; i
++)
676 toattr
->values
[i
].range
.lower
= attr
->values
[i
].range
.lower
;
677 toattr
->values
[i
].range
.upper
= attr
->values
[i
].range
.upper
;
681 case IPP_TAG_TEXTLANG
:
682 case IPP_TAG_NAMELANG
:
683 toattr
= ippAddStrings(to
, group_tag
,
684 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
685 attr
->name
, attr
->num_values
, NULL
, NULL
);
689 for (i
= 0; i
< attr
->num_values
; i
++)
691 toattr
->values
[i
].string
.charset
= attr
->values
[i
].string
.charset
;
692 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
697 for (i
= 0; i
< attr
->num_values
; i
++)
700 toattr
->values
[i
].string
.charset
=
701 _cupsStrAlloc(attr
->values
[i
].string
.charset
);
703 toattr
->values
[i
].string
.charset
=
704 toattr
->values
[0].string
.charset
;
706 toattr
->values
[i
].string
.text
=
707 _cupsStrAlloc(attr
->values
[i
].string
.text
);
712 case IPP_TAG_BEGIN_COLLECTION
:
713 toattr
= ippAddCollections(to
, group_tag
, attr
->name
,
714 attr
->num_values
, NULL
);
716 for (i
= 0; i
< attr
->num_values
; i
++)
718 toattr
->values
[i
].collection
= attr
->values
[i
].collection
;
719 attr
->values
[i
].collection
->use
++;
723 case IPP_TAG_STRING
:
726 toattr
= ippAddOctetString(to
, group_tag
, attr
->name
, NULL
, 0);
727 toattr
->value_tag
|= quickcopy
;
728 toattr
->values
[0].unknown
.data
= attr
->values
[0].unknown
.data
;
729 toattr
->values
[0].unknown
.length
= attr
->values
[0].unknown
.length
;
732 toattr
= ippAddOctetString(to
, attr
->group_tag
, attr
->name
,
733 attr
->values
[0].unknown
.data
,
734 attr
->values
[0].unknown
.length
);
738 toattr
= ippAddIntegers(to
, group_tag
, attr
->value_tag
,
739 attr
->name
, attr
->num_values
, NULL
);
741 for (i
= 0; i
< attr
->num_values
; i
++)
743 toattr
->values
[i
].unknown
.length
= attr
->values
[i
].unknown
.length
;
745 if (toattr
->values
[i
].unknown
.length
> 0)
747 if ((toattr
->values
[i
].unknown
.data
=
748 malloc(toattr
->values
[i
].unknown
.length
)) == NULL
)
749 toattr
->values
[i
].unknown
.length
= 0;
751 memcpy(toattr
->values
[i
].unknown
.data
,
752 attr
->values
[i
].unknown
.data
,
753 toattr
->values
[i
].unknown
.length
);
756 break; /* anti-compiler-warning-code */
764 * 'copy_attributes()' - Copy attributes from one request to another.
768 copy_attributes(ipp_t
*to
, /* I - Destination request */
769 ipp_t
*from
, /* I - Source request */
770 cups_array_t
*ra
, /* I - Requested attributes */
771 ipp_tag_t group_tag
, /* I - Group to copy */
772 int quickcopy
) /* I - Do a quick copy? */
774 ipp_attribute_t
*fromattr
; /* Source attribute */
780 for (fromattr
= from
->attrs
; fromattr
; fromattr
= fromattr
->next
)
783 * Filter attributes as needed...
786 if ((group_tag
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group_tag
&&
787 fromattr
->group_tag
!= IPP_TAG_ZERO
) || !fromattr
->name
)
790 if (!ra
|| cupsArrayFind(ra
, fromattr
->name
))
791 copy_attribute(to
, fromattr
, fromattr
->group_tag
, quickcopy
);
797 * 'copy_job_attrs()' - Copy job attributes to the response.
802 _ipp_client_t
*client
, /* I - Client */
803 _ipp_job_t
*job
, /* I - Job */
804 cups_array_t
*ra
) /* I - requested-attributes */
806 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_ZERO
, 0);
808 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
809 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
810 "job-printer-up-time", (int)time(NULL
));
812 if (!ra
|| cupsArrayFind(ra
, "job-state"))
813 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
814 "job-state", job
->state
);
816 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
820 case IPP_JOB_PENDING
:
821 ippAddString(client
->response
, IPP_TAG_JOB
,
822 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
828 ippAddString(client
->response
, IPP_TAG_JOB
,
829 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
830 NULL
, "job-incoming");
831 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
832 ippAddString(client
->response
, IPP_TAG_JOB
,
833 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
834 NULL
, "job-hold-until-specified");
836 ippAddString(client
->response
, IPP_TAG_JOB
,
837 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
838 NULL
, "job-data-insufficient");
841 case IPP_JOB_PROCESSING
:
843 ippAddString(client
->response
, IPP_TAG_JOB
,
844 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
845 NULL
, "processing-to-stop-point");
847 ippAddString(client
->response
, IPP_TAG_JOB
,
848 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
849 NULL
, "job-printing");
852 case IPP_JOB_STOPPED
:
853 ippAddString(client
->response
, IPP_TAG_JOB
,
854 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
855 NULL
, "job-stopped");
858 case IPP_JOB_CANCELED
:
859 ippAddString(client
->response
, IPP_TAG_JOB
,
860 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
861 NULL
, "job-canceled-by-user");
864 case IPP_JOB_ABORTED
:
865 ippAddString(client
->response
, IPP_TAG_JOB
,
866 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
867 NULL
, "aborted-by-system");
870 case IPP_JOB_COMPLETED
:
871 ippAddString(client
->response
, IPP_TAG_JOB
,
872 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
873 NULL
, "job-completed-successfully");
878 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
879 ippAddInteger(client
->response
, IPP_TAG_JOB
,
880 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
881 "time-at-completed", job
->completed
);
883 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
884 ippAddInteger(client
->response
, IPP_TAG_JOB
,
885 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
886 "time-at-processing", job
->processing
);
891 * 'create_client()' - Accept a new network connection and create a client
895 static _ipp_client_t
* /* O - Client */
896 create_client(_ipp_printer_t
*printer
, /* I - Printer */
897 int sock
) /* I - Listen socket */
899 _ipp_client_t
*client
; /* Client */
900 int val
; /* Parameter value */
901 socklen_t addrlen
; /* Length of address */
904 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
906 perror("Unable to allocate memory for client");
910 client
->printer
= printer
;
911 client
->http
.activity
= time(NULL
);
912 client
->http
.hostaddr
= &(client
->addr
);
913 client
->http
.blocking
= 1;
914 client
->http
.wait_value
= 60000;
917 * Accept the client and get the remote address...
920 addrlen
= sizeof(http_addr_t
);
922 if ((client
->http
.fd
= accept(sock
, (struct sockaddr
*)&(client
->addr
),
925 perror("Unable to accept client connection");
932 httpAddrString(&(client
->addr
), client
->http
.hostname
,
933 sizeof(client
->http
.hostname
));
936 fprintf(stderr
, "Accepted connection from %s (%s)\n", client
->http
.hostname
,
937 client
->http
.hostaddr
->addr
.sa_family
== AF_INET
? "IPv4" : "IPv6");
940 * Using TCP_NODELAY improves responsiveness, especially on systems
941 * with a slow loopback interface. Since we write large buffers
942 * when sending print files and requests, there shouldn't be any
943 * performance penalty for this...
947 setsockopt(client
->http
.fd
, IPPROTO_TCP
, TCP_NODELAY
, (char *)&val
,
955 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
959 static _ipp_job_t
* /* O - Job */
960 create_job(_ipp_client_t
*client
) /* I - Client */
962 _ipp_job_t
*job
; /* Job */
963 ipp_attribute_t
*attr
; /* Job attribute */
964 char uri
[1024]; /* job-uri value */
967 _cupsRWLockWrite(&(client
->printer
->rwlock
));
968 if (client
->printer
->active_job
&&
969 client
->printer
->active_job
->state
< IPP_JOB_CANCELED
)
972 * Only accept a single job at a time...
975 _cupsRWLockWrite(&(client
->printer
->rwlock
));
980 * Allocate and initialize the job object...
983 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
985 perror("Unable to allocate memory for job");
989 job
->printer
= client
->printer
;
990 job
->attrs
= client
->request
;
991 job
->state
= IPP_JOB_HELD
;
993 client
->request
= NULL
;
996 * Set all but the first two attributes to the job attributes group...
999 for (attr
= job
->attrs
->attrs
->next
->next
; attr
; attr
= attr
->next
)
1000 attr
->group_tag
= IPP_TAG_JOB
;
1003 * Get the requesting-user-name, document format, and priority...
1006 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
1007 IPP_TAG_NAME
)) != NULL
)
1009 _cupsStrFree(attr
->name
);
1010 attr
->name
= _cupsStrAlloc("job-originating-user-name");
1013 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
| IPP_TAG_COPY
,
1014 "job-originating-user-name", NULL
, "anonymous");
1017 job
->username
= attr
->values
[0].string
.text
;
1019 job
->username
= "anonymous";
1021 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
1022 IPP_TAG_MIMETYPE
)) != NULL
)
1023 job
->format
= attr
->values
[0].string
.text
;
1025 job
->format
= "application/octet-stream";
1028 * Add job description attributes and add to the jobs array...
1031 job
->id
= client
->printer
->next_job_id
++;
1033 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1035 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1036 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1037 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
1038 client
->printer
->uri
);
1039 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
1042 cupsArrayAdd(client
->printer
->jobs
, job
);
1043 client
->printer
->active_job
= job
;
1045 _cupsRWUnlock(&(client
->printer
->rwlock
));
1052 * 'create_listener()' - Create a listener socket.
1055 static int /* O - Listener socket or -1 on error */
1056 create_listener(int family
, /* I - Address family */
1057 int *port
) /* IO - Port number */
1059 int sock
, /* Listener socket */
1060 val
; /* Socket value */
1061 http_addr_t address
; /* Listen address */
1062 socklen_t addrlen
; /* Length of listen address */
1065 if ((sock
= socket(family
, SOCK_STREAM
, 0)) < 0)
1069 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, &val
, sizeof(val
));
1072 if (family
== AF_INET6
)
1073 setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, &val
, sizeof(val
));
1074 #endif /* IPV6_V6ONLY */
1079 * Get the auto-assigned port number for the IPv4 socket...
1082 /* TODO: This code does not appear to work - port is always 0... */
1083 addrlen
= sizeof(address
);
1084 if (getsockname(sock
, (struct sockaddr
*)&address
, &addrlen
))
1086 perror("getsockname() failed");
1090 *port
= _httpAddrPort(&address
);
1092 fprintf(stderr
, "Listening on port %d.\n", *port
);
1095 memset(&address
, 0, sizeof(address
));
1096 address
.addr
.sa_family
= family
;
1097 _httpAddrSetPort(&address
, *port
);
1099 if (bind(sock
, (struct sockaddr
*)&address
, httpAddrLength(&address
)))
1105 if (listen(sock
, 5))
1116 * 'create_media_col()' - Create a media-col value.
1119 static ipp_t
* /* O - media-col collection */
1120 create_media_col(const char *media
, /* I - Media name */
1121 const char *type
, /* I - Nedua type */
1122 int width
, /* I - x-dimension in 2540ths */
1123 int length
, /* I - y-dimension in 2540ths */
1124 int margins
) /* I - Value for margins */
1126 ipp_t
*media_col
= ippNew(), /* media-col value */
1127 *media_size
= ippNew(); /* media-size value */
1128 char media_key
[256]; /* media-key value */
1131 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1133 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1136 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
1137 margins
== 0 ? "_borderless" : "");
1139 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1141 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1142 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1143 "media-bottom-margin", margins
);
1144 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1145 "media-left-margin", margins
);
1146 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1147 "media-right-margin", margins
);
1148 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1149 "media-top-margin", margins
);
1150 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
1153 ippDelete(media_size
);
1160 * 'create_printer()' - Create, register, and listen for connections to a
1164 static _ipp_printer_t
* /* O - Printer */
1165 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1166 const char *name
, /* I - printer-name */
1167 const char *location
, /* I - printer-location */
1168 const char *make
, /* I - printer-make-and-model */
1169 const char *model
, /* I - printer-make-and-model */
1170 const char *icon
, /* I - printer-icons */
1171 const char *docformats
, /* I - document-format-supported */
1172 int ppm
, /* I - Pages per minute in grayscale */
1173 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1174 int duplex
, /* I - 1 = duplex, 0 = simplex */
1175 int port
, /* I - Port for listeners or 0 for auto */
1177 const char *regtype
, /* I - Bonjour service type */
1178 #endif /* HAVE_DNSSD */
1179 const char *directory
) /* I - Spool directory */
1181 int i
, j
; /* Looping vars */
1182 _ipp_printer_t
*printer
; /* Printer */
1183 char hostname
[256], /* Hostname */
1184 uri
[1024], /* Printer URI */
1185 icons
[1024], /* printer-icons URI */
1186 adminurl
[1024], /* printer-more-info URI */
1187 device_id
[1024],/* printer-device-id */
1188 make_model
[128];/* printer-make-and-model */
1189 int num_formats
; /* Number of document-format-supported values */
1190 char *defformat
, /* document-format-default value */
1191 *formats
[100], /* document-format-supported values */
1192 *ptr
; /* Pointer into string */
1193 const char *prefix
; /* Prefix string */
1194 int num_database
; /* Number of database values */
1195 ipp_attribute_t
*media_col_database
;
1196 /* media-col-database value */
1197 ipp_t
*media_col_default
;
1198 /* media-col-default value */
1199 ipp_value_t
*media_col_value
;
1200 /* Current media-col-database value */
1201 int k_supported
; /* Maximum file size supported */
1203 struct statvfs spoolinfo
; /* FS info for spool directory */
1204 double spoolsize
; /* FS size */
1205 #elif defined(HAVE_STATFS)
1206 struct statfs spoolinfo
; /* FS info for spool directory */
1207 double spoolsize
; /* FS size */
1208 #endif /* HAVE_STATVFS */
1209 static const int orients
[4] = /* orientation-requested-supported values */
1213 IPP_REVERSE_LANDSCAPE
,
1214 IPP_REVERSE_PORTRAIT
1216 static const char * const versions
[] =/* ipp-versions-supported values */
1222 static const int ops
[] = /* operations-supported values */
1231 IPP_GET_JOB_ATTRIBUTES
,
1233 IPP_GET_PRINTER_ATTRIBUTES
1235 static const char * const charsets
[] =/* charset-supported values */
1240 static const char * const job_creation
[] =
1241 { /* job-creation-attributes-supported values */
1243 "ipp-attribute-fidelity",
1248 "multiple-document-handling",
1249 "orientation-requested",
1253 static const char * const media_col_supported
[] =
1254 { /* media-col-supported values */
1255 "media-bottom-margin",
1256 "media-left-margin",
1257 "media-right-margin",
1262 static const int media_xxx_margin_supported
[] =
1263 { /* media-xxx-margin-supported values */
1267 static const char * const multiple_document_handling
[] =
1268 { /* multiple-document-handling-supported values */
1269 "separate-documents-uncollated-copies",
1270 "separate-documents-collated-copies"
1272 static const int print_quality_supported
[] =
1273 { /* print-quality-supported values */
1278 static const char * const referenced_uri_scheme_supported
[] =
1279 { /* referenced-uri-scheme-supported */
1284 #endif /* HAVE_SSL */
1286 static const char * const sides_supported
[] =
1287 { /* sides-supported values */
1289 "two-sided-long-edge",
1290 "two-sided-short-edge"
1292 static const char * const which_jobs
[] =
1293 { /* which-jobs-supported values */
1302 "processing-stopped"
1307 * Allocate memory for the printer...
1310 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1312 perror("Unable to allocate memory for printer");
1318 printer
->name
= _cupsStrAlloc(name
);
1320 printer
->dnssd_name
= _cupsStrRetain(printer
->name
);
1321 #endif /* HAVE_DNSSD */
1322 printer
->directory
= _cupsStrAlloc(directory
);
1323 printer
->hostname
= _cupsStrAlloc(servername
? servername
:
1324 httpGetHostname(NULL
, hostname
,
1326 printer
->port
= port
;
1327 printer
->state
= IPP_PRINTER_IDLE
;
1328 printer
->state_reasons
= _IPP_PRINTER_NONE
;
1329 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1330 printer
->next_job_id
= 1;
1332 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1333 printer
->hostname
, printer
->port
, "/ipp");
1334 printer
->uri
= _cupsStrAlloc(uri
);
1335 printer
->urilen
= strlen(uri
);
1337 _cupsRWInit(&(printer
->rwlock
));
1340 * Create the listener sockets...
1343 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1345 perror("Unable to create IPv4 listener");
1349 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1351 perror("Unable to create IPv6 listener");
1356 * Prepare values for the printer attributes...
1359 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1360 printer
->hostname
, printer
->port
, "/icon.png");
1361 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1362 printer
->hostname
, printer
->port
, "/");
1366 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1367 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1370 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1373 formats
[0] = strdup(docformats
);
1374 defformat
= formats
[0];
1375 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1378 formats
[num_formats
++] = ptr
;
1380 if (!_cups_strcasecmp(ptr
, "application/octet-stream"))
1384 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1385 ptr
= device_id
+ strlen(device_id
);
1387 for (i
= 0; i
< num_formats
; i
++)
1389 if (!_cups_strcasecmp(formats
[i
], "application/pdf"))
1390 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPDF", prefix
);
1391 else if (!_cups_strcasecmp(formats
[i
], "application/postscript"))
1392 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPS", prefix
);
1393 else if (!_cups_strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1394 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPCL", prefix
);
1395 else if (!_cups_strcasecmp(formats
[i
], "image/jpeg"))
1396 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sJPEG", prefix
);
1397 else if (!_cups_strcasecmp(formats
[i
], "image/png"))
1398 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPNG", prefix
);
1399 else if (_cups_strcasecmp(formats
[i
], "application/octet-stream"))
1400 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%s%s", prefix
,
1406 strlcat(device_id
, ";", sizeof(device_id
));
1409 * Get the maximum spool size based on the size of the filesystem used for
1410 * the spool directory. If the host OS doesn't support the statfs call
1411 * or the filesystem is larger than 2TiB, always report INT_MAX.
1415 if (statvfs(printer
->directory
, &spoolinfo
))
1416 k_supported
= INT_MAX
;
1417 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1418 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1419 k_supported
= INT_MAX
;
1421 k_supported
= (int)spoolsize
;
1423 #elif defined(HAVE_STATFS)
1424 if (statfs(printer
->directory
, &spoolinfo
))
1425 k_supported
= INT_MAX
;
1426 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1427 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1428 k_supported
= INT_MAX
;
1430 k_supported
= (int)spoolsize
;
1433 k_supported
= INT_MAX
;
1434 #endif /* HAVE_STATVFS */
1437 * Create the printer attributes. This list of attributes is sorted to improve
1438 * performance when the client provides a requested-attributes attribute...
1441 printer
->attrs
= ippNew();
1443 /* charset-configured */
1444 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_CHARSET
| IPP_TAG_COPY
,
1445 "charset-configured", NULL
, "utf-8");
1447 /* charset-supported */
1448 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_CHARSET
| IPP_TAG_COPY
,
1449 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1452 /* color-supported */
1453 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1456 /* compression-supported */
1457 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1458 "compression-supported", NULL
, "none");
1460 /* copies-default */
1461 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1462 "copies-default", 1);
1464 /* copies-supported */
1465 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1467 /* document-format-default */
1468 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1469 "document-format-default", NULL
, defformat
);
1471 /* document-format-supported */
1472 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1473 "document-format-supported", num_formats
, NULL
,
1474 (const char * const *)formats
);
1476 /* finishings-default */
1477 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1478 "finishings-default", IPP_FINISHINGS_NONE
);
1480 /* finishings-supported */
1481 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1482 "finishings-supported", IPP_FINISHINGS_NONE
);
1484 /* generated-natural-language-supported */
1485 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_LANGUAGE
| IPP_TAG_COPY
,
1486 "generated-natural-language-supported", NULL
, "en");
1488 /* ipp-versions-supported */
1489 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1490 "ipp-versions-supported",
1491 sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1493 /* job-creation-attributes-supported */
1494 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1495 "job-creation-attributes-supported",
1496 sizeof(job_creation
) / sizeof(job_creation
[0]),
1497 NULL
, job_creation
);
1499 /* job-k-octets-supported */
1500 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1503 /* job-priority-default */
1504 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1505 "job-priority-default", 50);
1507 /* job-priority-supported */
1508 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1509 "job-priority-supported", 100);
1511 /* job-sheets-default */
1512 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
| IPP_TAG_COPY
,
1513 "job-sheets-default", NULL
, "none");
1515 /* job-sheets-supported */
1516 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
| IPP_TAG_COPY
,
1517 "job-sheets-supported", NULL
, "none");
1519 /* media-bottom-margin-supported */
1520 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1521 "media-bottom-margin-supported",
1522 (int)(sizeof(media_xxx_margin_supported
) /
1523 sizeof(media_xxx_margin_supported
[0])),
1524 media_xxx_margin_supported
);
1526 /* media-col-database */
1527 for (num_database
= 0, i
= 0;
1528 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1531 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1532 num_database
+= 2; /* auto + envelope */
1533 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1534 num_database
+= 12; /* auto + photographic-* + borderless */
1536 num_database
+= (int)(sizeof(media_type_supported
) /
1537 sizeof(media_type_supported
[0])) + 6;
1538 /* All types + borderless */
1541 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1542 "media-col-database", num_database
,
1544 for (media_col_value
= media_col_database
->values
, i
= 0;
1545 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1549 j
< (int)(sizeof(media_type_supported
) /
1550 sizeof(media_type_supported
[0]));
1553 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1554 strcmp(media_type_supported
[j
], "auto") &&
1555 strcmp(media_type_supported
[j
], "envelope"))
1557 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1558 strcmp(media_type_supported
[j
], "auto") &&
1559 strncmp(media_type_supported
[j
], "photographic-", 13))
1562 media_col_value
->collection
=
1563 create_media_col(media_supported
[i
], media_type_supported
[j
],
1564 media_col_sizes
[i
][0], media_col_sizes
[i
][1],
1565 media_xxx_margin_supported
[1]);
1568 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1569 (!strcmp(media_type_supported
[j
], "auto") ||
1570 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1573 * Add borderless version for this combination...
1576 media_col_value
->collection
=
1577 create_media_col(media_supported
[i
], media_type_supported
[j
],
1578 media_col_sizes
[i
][0], media_col_sizes
[i
][1],
1579 media_xxx_margin_supported
[0]);
1585 /* media-col-default */
1586 media_col_default
= create_media_col(media_supported
[0],
1587 media_type_supported
[0],
1588 media_col_sizes
[0][0],
1589 media_col_sizes
[0][1],
1590 media_xxx_margin_supported
[1]);
1592 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1594 ippDelete(media_col_default
);
1596 /* media-col-supported */
1597 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1598 "media-col-supported",
1599 (int)(sizeof(media_col_supported
) /
1600 sizeof(media_col_supported
[0])), NULL
,
1601 media_col_supported
);
1604 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1605 "media-default", NULL
, media_supported
[0]);
1607 /* media-left-margin-supported */
1608 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1609 "media-left-margin-supported",
1610 (int)(sizeof(media_xxx_margin_supported
) /
1611 sizeof(media_xxx_margin_supported
[0])),
1612 media_xxx_margin_supported
);
1614 /* media-right-margin-supported */
1615 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1616 "media-right-margin-supported",
1617 (int)(sizeof(media_xxx_margin_supported
) /
1618 sizeof(media_xxx_margin_supported
[0])),
1619 media_xxx_margin_supported
);
1621 /* media-supported */
1622 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1624 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1625 NULL
, media_supported
);
1627 /* media-top-margin-supported */
1628 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1629 "media-top-margin-supported",
1630 (int)(sizeof(media_xxx_margin_supported
) /
1631 sizeof(media_xxx_margin_supported
[0])),
1632 media_xxx_margin_supported
);
1634 /* multiple-document-handling-supported */
1635 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1636 "multiple-document-handling-supported",
1637 sizeof(multiple_document_handling
) /
1638 sizeof(multiple_document_handling
[0]), NULL
,
1639 multiple_document_handling
);
1641 /* multiple-document-jobs-supported */
1642 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1643 "multiple-document-jobs-supported", 0);
1645 /* natural-language-configured */
1646 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_LANGUAGE
| IPP_TAG_COPY
,
1647 "natural-language-configured", NULL
, "en");
1649 /* number-up-default */
1650 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1651 "number-up-default", 1);
1653 /* number-up-supported */
1654 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1655 "number-up-supported", 1);
1657 /* operations-supported */
1658 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1659 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1661 /* orientation-requested-default */
1662 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1663 "orientation-requested-default", 0);
1665 /* orientation-requested-supported */
1666 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1667 "orientation-requested-supported", 4, orients
);
1669 /* output-bin-default */
1670 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1671 "output-bin-default", NULL
, "face-down");
1673 /* output-bin-supported */
1674 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1675 "output-bin-supported", NULL
, "face-down");
1677 /* pages-per-minute */
1678 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1679 "pages-per-minute", ppm
);
1681 /* pages-per-minute-color */
1683 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1684 "pages-per-minute-color", ppm_color
);
1686 /* pdl-override-supported */
1687 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1688 "pdl-override-supported", NULL
, "attempted");
1690 /* print-quality-default */
1691 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1692 "print-quality-default", IPP_QUALITY_NORMAL
);
1694 /* print-quality-supported */
1695 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1696 "print-quality-supported",
1697 (int)(sizeof(print_quality_supported
) /
1698 sizeof(print_quality_supported
[0])),
1699 print_quality_supported
);
1701 /* printer-device-id */
1702 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1703 "printer-device-id", NULL
, device_id
);
1706 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1707 "printer-icons", NULL
, icons
);
1709 /* printer-is-accepting-jobs */
1710 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1714 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1717 /* printer-location */
1718 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1719 "printer-location", NULL
, location
);
1721 /* printer-make-and-model */
1722 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1723 "printer-make-and-model", NULL
, make_model
);
1725 /* printer-more-info */
1726 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1727 "printer-more-info", NULL
, adminurl
);
1730 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1733 /* printer-resolution-default */
1734 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1735 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1737 /* printer-resolution-supported */
1738 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1739 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1741 /* printer-uri-supported */
1742 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1743 "printer-uri-supported", NULL
, uri
);
1745 /* referenced-uri-scheme-supported */
1746 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1747 IPP_TAG_URISCHEME
| IPP_TAG_COPY
,
1748 "referenced-uri-scheme-supported",
1749 (int)(sizeof(referenced_uri_scheme_supported
) /
1750 sizeof(referenced_uri_scheme_supported
[0])),
1751 NULL
, referenced_uri_scheme_supported
);
1754 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1755 "sides-default", NULL
, "one-sided");
1757 /* sides-supported */
1758 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1759 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1761 /* uri-authentication-supported */
1762 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1763 "uri-authentication-supported", NULL
, "none");
1765 /* uri-security-supported */
1766 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1767 "uri-security-supported", NULL
, "none");
1769 /* which-jobs-supported */
1770 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1771 "which-jobs-supported",
1772 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1776 debug_attributes("Printer", printer
->attrs
, 0);
1780 * Register the printer with Bonjour...
1783 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1784 ppm_color
> 0, duplex
, regtype
))
1786 #endif /* HAVE_DNSSD */
1796 * If we get here we were unable to create the printer...
1801 delete_printer(printer
);
1807 * 'create_requested_array()' - Create an array for requested-attributes.
1810 static cups_array_t
* /* O - requested-attributes array */
1811 create_requested_array(
1812 _ipp_client_t
*client
) /* I - Client */
1814 int i
; /* Looping var */
1815 ipp_attribute_t
*requested
; /* requested-attributes attribute */
1816 cups_array_t
*ra
; /* Requested attributes array */
1817 char *value
; /* Current value */
1821 * Get the requested-attributes attribute, and return NULL if we don't
1825 if ((requested
= ippFindAttribute(client
->request
, "requested-attributes",
1826 IPP_TAG_KEYWORD
)) == NULL
)
1830 * If the attribute contains a single "all" keyword, return NULL...
1833 if (requested
->num_values
== 1 &&
1834 !strcmp(requested
->values
[0].string
.text
, "all"))
1838 * Create an array using "strcmp" as the comparison function...
1841 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1843 for (i
= 0; i
< requested
->num_values
; i
++)
1845 value
= requested
->values
[i
].string
.text
;
1847 if (!strcmp(value
, "job-template"))
1849 cupsArrayAdd(ra
, "copies");
1850 cupsArrayAdd(ra
, "copies-default");
1851 cupsArrayAdd(ra
, "copies-supported");
1852 cupsArrayAdd(ra
, "finishings");
1853 cupsArrayAdd(ra
, "finishings-default");
1854 cupsArrayAdd(ra
, "finishings-supported");
1855 cupsArrayAdd(ra
, "job-hold-until");
1856 cupsArrayAdd(ra
, "job-hold-until-default");
1857 cupsArrayAdd(ra
, "job-hold-until-supported");
1858 cupsArrayAdd(ra
, "job-priority");
1859 cupsArrayAdd(ra
, "job-priority-default");
1860 cupsArrayAdd(ra
, "job-priority-supported");
1861 cupsArrayAdd(ra
, "job-sheets");
1862 cupsArrayAdd(ra
, "job-sheets-default");
1863 cupsArrayAdd(ra
, "job-sheets-supported");
1864 cupsArrayAdd(ra
, "media");
1865 cupsArrayAdd(ra
, "media-col");
1866 cupsArrayAdd(ra
, "media-col-default");
1867 cupsArrayAdd(ra
, "media-col-supported");
1868 cupsArrayAdd(ra
, "media-default");
1869 cupsArrayAdd(ra
, "media-source-supported");
1870 cupsArrayAdd(ra
, "media-supported");
1871 cupsArrayAdd(ra
, "media-type-supported");
1872 cupsArrayAdd(ra
, "multiple-document-handling");
1873 cupsArrayAdd(ra
, "multiple-document-handling-default");
1874 cupsArrayAdd(ra
, "multiple-document-handling-supported");
1875 cupsArrayAdd(ra
, "number-up");
1876 cupsArrayAdd(ra
, "number-up-default");
1877 cupsArrayAdd(ra
, "number-up-supported");
1878 cupsArrayAdd(ra
, "orientation-requested");
1879 cupsArrayAdd(ra
, "orientation-requested-default");
1880 cupsArrayAdd(ra
, "orientation-requested-supported");
1881 cupsArrayAdd(ra
, "page-ranges");
1882 cupsArrayAdd(ra
, "page-ranges-supported");
1883 cupsArrayAdd(ra
, "printer-resolution");
1884 cupsArrayAdd(ra
, "printer-resolution-default");
1885 cupsArrayAdd(ra
, "printer-resolution-supported");
1886 cupsArrayAdd(ra
, "print-quality");
1887 cupsArrayAdd(ra
, "print-quality-default");
1888 cupsArrayAdd(ra
, "print-quality-supported");
1889 cupsArrayAdd(ra
, "sides");
1890 cupsArrayAdd(ra
, "sides-default");
1891 cupsArrayAdd(ra
, "sides-supported");
1893 else if (!strcmp(value
, "job-description"))
1895 cupsArrayAdd(ra
, "date-time-at-completed");
1896 cupsArrayAdd(ra
, "date-time-at-creation");
1897 cupsArrayAdd(ra
, "date-time-at-processing");
1898 cupsArrayAdd(ra
, "job-detailed-status-message");
1899 cupsArrayAdd(ra
, "job-document-access-errors");
1900 cupsArrayAdd(ra
, "job-id");
1901 cupsArrayAdd(ra
, "job-impressions");
1902 cupsArrayAdd(ra
, "job-impressions-completed");
1903 cupsArrayAdd(ra
, "job-k-octets");
1904 cupsArrayAdd(ra
, "job-k-octets-processed");
1905 cupsArrayAdd(ra
, "job-media-sheets");
1906 cupsArrayAdd(ra
, "job-media-sheets-completed");
1907 cupsArrayAdd(ra
, "job-message-from-operator");
1908 cupsArrayAdd(ra
, "job-more-info");
1909 cupsArrayAdd(ra
, "job-name");
1910 cupsArrayAdd(ra
, "job-originating-user-name");
1911 cupsArrayAdd(ra
, "job-printer-up-time");
1912 cupsArrayAdd(ra
, "job-printer-uri");
1913 cupsArrayAdd(ra
, "job-state");
1914 cupsArrayAdd(ra
, "job-state-message");
1915 cupsArrayAdd(ra
, "job-state-reasons");
1916 cupsArrayAdd(ra
, "job-uri");
1917 cupsArrayAdd(ra
, "number-of-documents");
1918 cupsArrayAdd(ra
, "number-of-intervening-jobs");
1919 cupsArrayAdd(ra
, "output-device-assigned");
1920 cupsArrayAdd(ra
, "time-at-completed");
1921 cupsArrayAdd(ra
, "time-at-creation");
1922 cupsArrayAdd(ra
, "time-at-processing");
1924 else if (!strcmp(value
, "printer-description"))
1926 cupsArrayAdd(ra
, "charset-configured");
1927 cupsArrayAdd(ra
, "charset-supported");
1928 cupsArrayAdd(ra
, "color-supported");
1929 cupsArrayAdd(ra
, "compression-supported");
1930 cupsArrayAdd(ra
, "document-format-default");
1931 cupsArrayAdd(ra
, "document-format-supported");
1932 cupsArrayAdd(ra
, "generated-natural-language-supported");
1933 cupsArrayAdd(ra
, "ipp-versions-supported");
1934 cupsArrayAdd(ra
, "job-impressions-supported");
1935 cupsArrayAdd(ra
, "job-k-octets-supported");
1936 cupsArrayAdd(ra
, "job-media-sheets-supported");
1937 cupsArrayAdd(ra
, "multiple-document-jobs-supported");
1938 cupsArrayAdd(ra
, "multiple-operation-time-out");
1939 cupsArrayAdd(ra
, "natural-language-configured");
1940 cupsArrayAdd(ra
, "notify-attributes-supported");
1941 cupsArrayAdd(ra
, "notify-lease-duration-default");
1942 cupsArrayAdd(ra
, "notify-lease-duration-supported");
1943 cupsArrayAdd(ra
, "notify-max-events-supported");
1944 cupsArrayAdd(ra
, "notify-events-default");
1945 cupsArrayAdd(ra
, "notify-events-supported");
1946 cupsArrayAdd(ra
, "notify-pull-method-supported");
1947 cupsArrayAdd(ra
, "notify-schemes-supported");
1948 cupsArrayAdd(ra
, "operations-supported");
1949 cupsArrayAdd(ra
, "pages-per-minute");
1950 cupsArrayAdd(ra
, "pages-per-minute-color");
1951 cupsArrayAdd(ra
, "pdl-override-supported");
1952 cupsArrayAdd(ra
, "printer-alert");
1953 cupsArrayAdd(ra
, "printer-alert-description");
1954 cupsArrayAdd(ra
, "printer-current-time");
1955 cupsArrayAdd(ra
, "printer-driver-installer");
1956 cupsArrayAdd(ra
, "printer-info");
1957 cupsArrayAdd(ra
, "printer-is-accepting-jobs");
1958 cupsArrayAdd(ra
, "printer-location");
1959 cupsArrayAdd(ra
, "printer-make-and-model");
1960 cupsArrayAdd(ra
, "printer-message-from-operator");
1961 cupsArrayAdd(ra
, "printer-more-info");
1962 cupsArrayAdd(ra
, "printer-more-info-manufacturer");
1963 cupsArrayAdd(ra
, "printer-name");
1964 cupsArrayAdd(ra
, "printer-state");
1965 cupsArrayAdd(ra
, "printer-state-message");
1966 cupsArrayAdd(ra
, "printer-state-reasons");
1967 cupsArrayAdd(ra
, "printer-up-time");
1968 cupsArrayAdd(ra
, "printer-uri-supported");
1969 cupsArrayAdd(ra
, "queued-job-count");
1970 cupsArrayAdd(ra
, "reference-uri-schemes-supported");
1971 cupsArrayAdd(ra
, "uri-authentication-supported");
1972 cupsArrayAdd(ra
, "uri-security-supported");
1974 else if (!strcmp(value
, "printer-defaults"))
1976 cupsArrayAdd(ra
, "copies-default");
1977 cupsArrayAdd(ra
, "document-format-default");
1978 cupsArrayAdd(ra
, "finishings-default");
1979 cupsArrayAdd(ra
, "job-hold-until-default");
1980 cupsArrayAdd(ra
, "job-priority-default");
1981 cupsArrayAdd(ra
, "job-sheets-default");
1982 cupsArrayAdd(ra
, "media-default");
1983 cupsArrayAdd(ra
, "media-col-default");
1984 cupsArrayAdd(ra
, "number-up-default");
1985 cupsArrayAdd(ra
, "orientation-requested-default");
1986 cupsArrayAdd(ra
, "sides-default");
1988 else if (!strcmp(value
, "subscription-template"))
1990 cupsArrayAdd(ra
, "notify-attributes");
1991 cupsArrayAdd(ra
, "notify-charset");
1992 cupsArrayAdd(ra
, "notify-events");
1993 cupsArrayAdd(ra
, "notify-lease-duration");
1994 cupsArrayAdd(ra
, "notify-natural-language");
1995 cupsArrayAdd(ra
, "notify-pull-method");
1996 cupsArrayAdd(ra
, "notify-recipient-uri");
1997 cupsArrayAdd(ra
, "notify-time-interval");
1998 cupsArrayAdd(ra
, "notify-user-data");
2001 cupsArrayAdd(ra
, value
);
2009 * 'debug_attributes()' - Print attributes in a request or response.
2013 debug_attributes(const char *title
, /* I - Title */
2014 ipp_t
*ipp
, /* I - Request/response */
2015 int type
) /* I - 0 = object, 1 = request, 2 = response */
2017 ipp_tag_t group_tag
; /* Current group */
2018 ipp_attribute_t
*attr
; /* Current attribute */
2019 char buffer
[2048]; /* String buffer for value */
2025 fprintf(stderr
, "%s:\n", title
);
2026 fprintf(stderr
, " version=%d.%d\n", ipp
->request
.any
.version
[0],
2027 ipp
->request
.any
.version
[1]);
2029 fprintf(stderr
, " operation-id=%s(%04x)\n",
2030 ippOpString(ipp
->request
.op
.operation_id
),
2031 ipp
->request
.op
.operation_id
);
2033 fprintf(stderr
, " status-code=%s(%04x)\n",
2034 ippErrorString(ipp
->request
.status
.status_code
),
2035 ipp
->request
.status
.status_code
);
2036 fprintf(stderr
, " request-id=%d\n\n", ipp
->request
.any
.request_id
);
2038 for (attr
= ipp
->attrs
, group_tag
= IPP_TAG_ZERO
; attr
; attr
= attr
->next
)
2040 if (attr
->group_tag
!= group_tag
)
2042 group_tag
= attr
->group_tag
;
2043 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2048 _ippAttrString(attr
, buffer
, sizeof(buffer
));
2049 fprintf(stderr
, " %s (%s%s) %s\n", attr
->name
,
2050 attr
->num_values
> 1 ? "1setOf " : "",
2051 ippTagString(attr
->value_tag
), buffer
);
2058 * 'delete_client()' - Close the socket and free all memory used by a client
2063 delete_client(_ipp_client_t
*client
) /* I - Client */
2066 fprintf(stderr
, "Closing connection from %s (%s)\n", client
->http
.hostname
,
2067 client
->http
.hostaddr
->addr
.sa_family
== AF_INET
? "IPv4" : "IPv6");
2070 * Flush pending writes before closing...
2073 httpFlushWrite(&(client
->http
));
2075 if (client
->http
.fd
>= 0)
2076 close(client
->http
.fd
);
2082 httpClearCookie(&(client
->http
));
2083 httpClearFields(&(client
->http
));
2085 ippDelete(client
->request
);
2087 ippDelete(client
->response
);
2094 * 'delete_job()' - Remove from the printer and free all memory used by a job
2099 delete_job(_ipp_job_t
*job
) /* I - Job */
2102 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2104 ippDelete(job
->attrs
);
2109 unlink(job
->filename
);
2111 free(job
->filename
);
2119 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2120 * used by a printer object.
2124 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2126 if (printer
->ipv4
>= 0)
2127 close(printer
->ipv4
);
2129 if (printer
->ipv6
>= 0)
2130 close(printer
->ipv6
);
2133 if (printer
->printer_ref
)
2134 DNSServiceRefDeallocate(printer
->printer_ref
);
2136 if (printer
->ipp_ref
)
2137 DNSServiceRefDeallocate(printer
->ipp_ref
);
2139 if (printer
->http_ref
)
2140 DNSServiceRefDeallocate(printer
->http_ref
);
2142 if (printer
->common_ref
)
2143 DNSServiceRefDeallocate(printer
->common_ref
);
2145 TXTRecordDeallocate(&(printer
->ipp_txt
));
2147 if (printer
->dnssd_name
)
2148 _cupsStrFree(printer
->dnssd_name
);
2149 #endif /* HAVE_DNSSD */
2152 _cupsStrFree(printer
->name
);
2154 _cupsStrFree(printer
->icon
);
2155 if (printer
->directory
)
2156 _cupsStrFree(printer
->directory
);
2157 if (printer
->hostname
)
2158 _cupsStrFree(printer
->hostname
);
2160 _cupsStrFree(printer
->uri
);
2162 ippDelete(printer
->attrs
);
2163 cupsArrayDelete(printer
->jobs
);
2171 * 'dnssd_callback()' - Handle Bonjour registration events.
2176 DNSServiceRef sdRef
, /* I - Service reference */
2177 DNSServiceFlags flags
, /* I - Status flags */
2178 DNSServiceErrorType errorCode
, /* I - Error, if any */
2179 const char *name
, /* I - Service name */
2180 const char *regtype
, /* I - Service type */
2181 const char *domain
, /* I - Domain for service */
2182 _ipp_printer_t
*printer
) /* I - Printer */
2186 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2187 regtype
, (int)errorCode
);
2190 else if (_cups_strcasecmp(name
, printer
->dnssd_name
))
2193 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2195 /* No lock needed since only the main thread accesses/changes this */
2196 _cupsStrFree(printer
->dnssd_name
);
2197 printer
->dnssd_name
= _cupsStrAlloc(name
);
2200 #endif /* HAVE_DNSSD */
2204 * 'find_job()' - Find a job specified in a request.
2207 static _ipp_job_t
* /* O - Job or NULL */
2208 find_job(_ipp_client_t
*client
) /* I - Client */
2210 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2211 _ipp_job_t key
, /* Job search key */
2212 *job
; /* Matching job, if any */
2217 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
2218 IPP_TAG_URI
)) != NULL
)
2220 if (!strncmp(attr
->values
[0].string
.text
, client
->printer
->uri
,
2221 client
->printer
->urilen
) &&
2222 attr
->values
[0].string
.text
[client
->printer
->urilen
] == '/')
2223 key
.id
= atoi(attr
->values
[0].string
.text
+ client
->printer
->urilen
+ 1);
2225 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
2226 IPP_TAG_INTEGER
)) != NULL
)
2227 key
.id
= attr
->values
[0].integer
;
2229 _cupsRWLockRead(&(client
->printer
->rwlock
));
2230 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2231 _cupsRWUnlock(&(client
->printer
->rwlock
));
2238 * 'html_escape()' - Write a HTML-safe string.
2242 html_escape(_ipp_client_t
*client
, /* I - Client */
2243 const char *s
, /* I - String to write */
2244 size_t slen
) /* I - Number of characters to write */
2246 const char *start
, /* Start of segment */
2247 *end
; /* End of string */
2251 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2253 while (*s
&& s
< end
)
2255 if (*s
== '&' || *s
== '<')
2258 httpWrite2(&(client
->http
), start
, s
- start
);
2261 httpWrite2(&(client
->http
), "&", 5);
2263 httpWrite2(&(client
->http
), "<", 4);
2272 httpWrite2(&(client
->http
), start
, s
- start
);
2277 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2281 html_printf(_ipp_client_t
*client
, /* I - Client */
2282 const char *format
, /* I - Printf-style format string */
2283 ...) /* I - Additional arguments as needed */
2285 va_list ap
; /* Pointer to arguments */
2286 const char *start
; /* Start of string */
2287 char size
, /* Size character (h, l, L) */
2288 type
; /* Format type character */
2289 int width
, /* Width of field */
2290 prec
; /* Number of characters of precision */
2291 char tformat
[100], /* Temporary format string for sprintf() */
2292 *tptr
, /* Pointer into temporary format */
2293 temp
[1024]; /* Buffer for formatted numbers */
2294 char *s
; /* Pointer to string */
2298 * Loop through the format string, formatting as needed...
2301 va_start(ap
, format
);
2309 httpWrite2(&(client
->http
), start
, format
- start
);
2312 *tptr
++ = *format
++;
2316 httpWrite2(&(client
->http
), "%", 1);
2320 else if (strchr(" -+#\'", *format
))
2321 *tptr
++ = *format
++;
2326 * Get width from argument...
2330 width
= va_arg(ap
, int);
2332 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", width
);
2333 tptr
+= strlen(tptr
);
2339 while (isdigit(*format
& 255))
2341 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2344 width
= width
* 10 + *format
++ - '0';
2350 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2358 * Get precision from argument...
2362 prec
= va_arg(ap
, int);
2364 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", prec
);
2365 tptr
+= strlen(tptr
);
2371 while (isdigit(*format
& 255))
2373 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2376 prec
= prec
* 10 + *format
++ - '0';
2381 if (*format
== 'l' && format
[1] == 'l')
2385 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2393 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2395 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2410 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2419 case 'E' : /* Floating point formats */
2424 if ((width
+ 2) > sizeof(temp
))
2427 sprintf(temp
, tformat
, va_arg(ap
, double));
2429 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2432 case 'B' : /* Integer formats */
2440 if ((width
+ 2) > sizeof(temp
))
2443 # ifdef HAVE_LONG_LONG
2445 sprintf(temp
, tformat
, va_arg(ap
, long long));
2447 # endif /* HAVE_LONG_LONG */
2449 sprintf(temp
, tformat
, va_arg(ap
, long));
2451 sprintf(temp
, tformat
, va_arg(ap
, int));
2453 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2456 case 'p' : /* Pointer value */
2457 if ((width
+ 2) > sizeof(temp
))
2460 sprintf(temp
, tformat
, va_arg(ap
, void *));
2462 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2465 case 'c' : /* Character or character array */
2468 temp
[0] = va_arg(ap
, int);
2470 html_escape(client
, temp
, 1);
2473 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2476 case 's' : /* String */
2477 if ((s
= va_arg(ap
, char *)) == NULL
)
2480 html_escape(client
, s
, strlen(s
));
2489 httpWrite2(&(client
->http
), start
, format
- start
);
2496 * 'ipp_cancel_job()' - Cancel a job.
2500 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2502 _ipp_job_t
*job
; /* Job information */
2509 if ((job
= find_job(client
)) == NULL
)
2511 respond_ipp(client
, IPP_NOT_FOUND
, "Job does not exist.");
2516 * See if the job is already completed, canceled, or aborted; if so,
2517 * we can't cancel...
2522 case IPP_JOB_CANCELED
:
2523 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2524 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2527 case IPP_JOB_ABORTED
:
2528 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2529 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2532 case IPP_JOB_COMPLETED
:
2533 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2534 "Job #%d is already completed - can\'t cancel.", job
->id
);
2542 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2544 if (job
->state
== IPP_JOB_PROCESSING
||
2545 (job
->state
== IPP_JOB_HELD
&& job
->fd
>= 0))
2549 job
->state
= IPP_JOB_CANCELED
;
2550 job
->completed
= time(NULL
);
2553 _cupsRWUnlock(&(client
->printer
->rwlock
));
2555 respond_ipp(client
, IPP_OK
, NULL
);
2562 * 'ipp_create_job()' - Create a job object.
2566 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2568 _ipp_job_t
*job
; /* New job */
2569 cups_array_t
*ra
; /* Attributes to send in response */
2573 * Validate print job attributes...
2576 if (!valid_job_attributes(client
))
2578 httpFlush(&(client
->http
));
2583 * Do we have a file to print?
2586 if (client
->http
.state
== HTTP_POST_RECV
)
2588 respond_ipp(client
, IPP_BAD_REQUEST
,
2589 "Unexpected document data following request.");
2597 if ((job
= create_job(client
)) == NULL
)
2599 respond_ipp(client
, IPP_PRINTER_BUSY
, "Currently printing another job.");
2604 * Return the job info...
2607 respond_ipp(client
, IPP_OK
, NULL
);
2609 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2610 cupsArrayAdd(ra
, "job-id");
2611 cupsArrayAdd(ra
, "job-state");
2612 cupsArrayAdd(ra
, "job-state-reasons");
2613 cupsArrayAdd(ra
, "job-uri");
2615 copy_job_attributes(client
, job
, ra
);
2616 cupsArrayDelete(ra
);
2621 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2625 ipp_get_job_attributes(
2626 _ipp_client_t
*client
) /* I - Client */
2628 _ipp_job_t
*job
; /* Job */
2629 cups_array_t
*ra
; /* requested-attributes */
2632 if ((job
= find_job(client
)) == NULL
)
2634 respond_ipp(client
, IPP_NOT_FOUND
, "Job not found.");
2638 respond_ipp(client
, IPP_OK
, NULL
);
2640 ra
= create_requested_array(client
);
2641 copy_job_attributes(client
, job
, ra
);
2642 cupsArrayDelete(ra
);
2647 * 'ipp_get_jobs()' - Get a list of job objects.
2651 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2653 ipp_attribute_t
*attr
; /* Current attribute */
2654 int job_comparison
; /* Job comparison */
2655 ipp_jstate_t job_state
; /* job-state value */
2656 int first_job_id
, /* First job ID */
2657 limit
, /* Maximum number of jobs to return */
2658 count
; /* Number of jobs that match */
2659 const char *username
; /* Username */
2660 _ipp_job_t
*job
; /* Current job pointer */
2661 cups_array_t
*ra
; /* Requested attributes array */
2665 * See if the "which-jobs" attribute have been specified...
2668 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2669 IPP_TAG_KEYWORD
)) != NULL
)
2670 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->http
.hostname
,
2671 attr
->values
[0].string
.text
);
2673 if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "not-completed"))
2675 job_comparison
= -1;
2676 job_state
= IPP_JOB_STOPPED
;
2678 else if (!strcmp(attr
->values
[0].string
.text
, "completed"))
2681 job_state
= IPP_JOB_CANCELED
;
2683 else if (!strcmp(attr
->values
[0].string
.text
, "aborted"))
2686 job_state
= IPP_JOB_ABORTED
;
2688 else if (!strcmp(attr
->values
[0].string
.text
, "all"))
2691 job_state
= IPP_JOB_PENDING
;
2693 else if (!strcmp(attr
->values
[0].string
.text
, "canceled"))
2696 job_state
= IPP_JOB_CANCELED
;
2698 else if (!strcmp(attr
->values
[0].string
.text
, "pending"))
2701 job_state
= IPP_JOB_PENDING
;
2703 else if (!strcmp(attr
->values
[0].string
.text
, "pending-held"))
2706 job_state
= IPP_JOB_HELD
;
2708 else if (!strcmp(attr
->values
[0].string
.text
, "processing"))
2711 job_state
= IPP_JOB_PROCESSING
;
2713 else if (!strcmp(attr
->values
[0].string
.text
, "processing-stopped"))
2716 job_state
= IPP_JOB_STOPPED
;
2720 respond_ipp(client
, IPP_ATTRIBUTES
,
2721 "The which-jobs value \"%s\" is not supported.",
2722 attr
->values
[0].string
.text
);
2723 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2724 "which-jobs", NULL
, attr
->values
[0].string
.text
);
2729 * See if they want to limit the number of jobs reported...
2732 if ((attr
= ippFindAttribute(client
->request
, "limit",
2733 IPP_TAG_INTEGER
)) != NULL
)
2735 limit
= attr
->values
[0].integer
;
2737 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->http
.hostname
, limit
);
2742 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2743 IPP_TAG_INTEGER
)) != NULL
)
2745 first_job_id
= attr
->values
[0].integer
;
2747 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->http
.hostname
,
2754 * See if we only want to see jobs for a specific user...
2759 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2760 IPP_TAG_BOOLEAN
)) != NULL
)
2762 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->http
.hostname
,
2763 attr
->values
[0].boolean
? "true" : "false");
2765 if (attr
->values
[0].boolean
)
2767 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2768 IPP_TAG_NAME
)) == NULL
)
2770 respond_ipp(client
, IPP_BAD_REQUEST
,
2771 "Need requesting-user-name with my-jobs.");
2775 username
= attr
->values
[0].string
.text
;
2777 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2778 client
->http
.hostname
, username
);
2783 * OK, build a list of jobs for this printer...
2786 if ((ra
= create_requested_array(client
)) == NULL
&&
2787 !ippFindAttribute(client
->request
, "requested-attributes",
2791 * IPP conformance - Get-Jobs has a default requested-attributes value of
2792 * "job-id" and "job-uri".
2795 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2796 cupsArrayAdd(ra
, "job-id");
2797 cupsArrayAdd(ra
, "job-uri");
2800 respond_ipp(client
, IPP_OK
, NULL
);
2802 _cupsRWLockRead(&(client
->printer
->rwlock
));
2804 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2805 (limit
<= 0 || count
< limit
) && job
;
2806 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2809 * Filter out jobs that don't match...
2812 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2813 (job_comparison
== 0 && job
->state
!= job_state
) ||
2814 (job_comparison
> 0 && job
->state
< job_state
) ||
2815 job
->id
< first_job_id
||
2816 (username
&& job
->username
&& _cups_strcasecmp(username
, job
->username
)))
2820 ippAddSeparator(client
->response
);
2823 copy_job_attributes(client
, job
, ra
);
2826 cupsArrayDelete(ra
);
2828 _cupsRWUnlock(&(client
->printer
->rwlock
));
2833 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2837 ipp_get_printer_attributes(
2838 _ipp_client_t
*client
) /* I - Client */
2840 cups_array_t
*ra
; /* Requested attributes array */
2841 _ipp_printer_t
*printer
; /* Printer */
2845 * Send the attributes...
2848 ra
= create_requested_array(client
);
2849 printer
= client
->printer
;
2851 respond_ipp(client
, IPP_OK
, NULL
);
2853 _cupsRWLockRead(&(printer
->rwlock
));
2855 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2858 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2859 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2860 "printer-state", printer
->state
);
2862 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2864 if (printer
->state_reasons
== _IPP_PRINTER_NONE
)
2865 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2866 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "printer-state-reasons",
2870 int num_reasons
= 0;/* Number of reasons */
2871 const char *reasons
[32]; /* Reason strings */
2873 if (printer
->state_reasons
& _IPP_PRINTER_OTHER
)
2874 reasons
[num_reasons
++] = "other";
2875 if (printer
->state_reasons
& _IPP_PRINTER_COVER_OPEN
)
2876 reasons
[num_reasons
++] = "cover-open";
2877 if (printer
->state_reasons
& _IPP_PRINTER_INPUT_TRAY_MISSING
)
2878 reasons
[num_reasons
++] = "input-tray-missing";
2879 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_SUPPLY_EMPTY
)
2880 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2881 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_SUPPLY_LOW
)
2882 reasons
[num_reasons
++] = "marker-supply-low-report";
2883 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL
)
2884 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2885 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_WASTE_FULL
)
2886 reasons
[num_reasons
++] = "marker-waste-full-warning";
2887 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_EMPTY
)
2888 reasons
[num_reasons
++] = "media-empty-warning";
2889 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_JAM
)
2890 reasons
[num_reasons
++] = "media-jam-warning";
2891 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_LOW
)
2892 reasons
[num_reasons
++] = "media-low-report";
2893 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_NEEDED
)
2894 reasons
[num_reasons
++] = "media-needed-report";
2895 if (printer
->state_reasons
& _IPP_PRINTER_MOVING_TO_PAUSED
)
2896 reasons
[num_reasons
++] = "moving-to-paused";
2897 if (printer
->state_reasons
& _IPP_PRINTER_PAUSED
)
2898 reasons
[num_reasons
++] = "paused";
2899 if (printer
->state_reasons
& _IPP_PRINTER_SPOOL_AREA_FULL
)
2900 reasons
[num_reasons
++] = "spool-area-full";
2901 if (printer
->state_reasons
& _IPP_PRINTER_TONER_EMPTY
)
2902 reasons
[num_reasons
++] = "toner-empty-warning";
2903 if (printer
->state_reasons
& _IPP_PRINTER_TONER_LOW
)
2904 reasons
[num_reasons
++] = "toner-low-report";
2906 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2907 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "printer-state-reasons",
2908 num_reasons
, NULL
, reasons
);
2912 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2913 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2914 "printer-up-time", (int)time(NULL
));
2916 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2917 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2919 printer
->active_job
&&
2920 printer
->active_job
->state
< IPP_JOB_CANCELED
);
2922 _cupsRWUnlock(&(printer
->rwlock
));
2924 cupsArrayDelete(ra
);
2929 * 'ipp_print_job()' - Create a job object with an attached document.
2933 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2935 _ipp_job_t
*job
; /* New job */
2936 char filename
[1024], /* Filename buffer */
2937 buffer
[4096]; /* Copy buffer */
2938 ssize_t bytes
; /* Bytes read */
2939 cups_array_t
*ra
; /* Attributes to send in response */
2943 * Validate print job attributes...
2946 if (!valid_job_attributes(client
))
2948 httpFlush(&(client
->http
));
2953 * Do we have a file to print?
2956 if (client
->http
.state
== HTTP_POST_SEND
)
2958 respond_ipp(client
, IPP_BAD_REQUEST
, "No file in request.");
2966 if ((job
= create_job(client
)) == NULL
)
2968 respond_ipp(client
, IPP_PRINTER_BUSY
, "Currently printing another job.");
2973 * Create a file for the request data...
2976 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2977 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2978 client
->printer
->directory
, job
->id
);
2979 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2980 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2981 client
->printer
->directory
, job
->id
);
2982 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2983 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2984 client
->printer
->directory
, job
->id
);
2985 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2986 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2987 client
->printer
->directory
, job
->id
);
2989 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2990 client
->printer
->directory
, job
->id
);
2992 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2994 job
->state
= IPP_JOB_ABORTED
;
2996 respond_ipp(client
, IPP_INTERNAL_ERROR
,
2997 "Unable to create print file: %s", strerror(errno
));
3001 while ((bytes
= httpRead2(&(client
->http
), buffer
, sizeof(buffer
))) > 0)
3003 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3005 int error
= errno
; /* Write error */
3007 job
->state
= IPP_JOB_ABORTED
;
3014 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3015 "Unable to write print file: %s", strerror(error
));
3023 * Got an error while reading the print data, so abort this job.
3026 job
->state
= IPP_JOB_ABORTED
;
3033 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to read print file.");
3039 int error
= errno
; /* Write error */
3041 job
->state
= IPP_JOB_ABORTED
;
3046 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
3052 job
->filename
= strdup(filename
);
3053 job
->state
= IPP_JOB_PENDING
;
3056 * Process the job...
3060 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3062 job
->state
= IPP_JOB_ABORTED
;
3063 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
3072 * Return the job info...
3075 respond_ipp(client
, IPP_OK
, NULL
);
3077 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3078 cupsArrayAdd(ra
, "job-id");
3079 cupsArrayAdd(ra
, "job-state");
3080 cupsArrayAdd(ra
, "job-state-reasons");
3081 cupsArrayAdd(ra
, "job-uri");
3083 copy_job_attributes(client
, job
, ra
);
3084 cupsArrayDelete(ra
);
3089 * 'ipp_print_uri()' - Create a job object with a referenced document.
3093 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3095 _ipp_job_t
*job
; /* New job */
3096 ipp_attribute_t
*uri
; /* document-uri */
3097 char scheme
[256], /* URI scheme */
3098 userpass
[256], /* Username and password info */
3099 hostname
[256], /* Hostname */
3100 resource
[1024]; /* Resource path */
3101 int port
; /* Port number */
3102 http_uri_status_t uri_status
; /* URI decode status */
3103 http_encryption_t encryption
; /* Encryption to use, if any */
3104 http_t
*http
; /* Connection for http/https URIs */
3105 http_status_t status
; /* Access status for http/https URIs */
3106 int infile
; /* Input file for local file URIs */
3107 char filename
[1024], /* Filename buffer */
3108 buffer
[4096]; /* Copy buffer */
3109 ssize_t bytes
; /* Bytes read */
3110 cups_array_t
*ra
; /* Attributes to send in response */
3111 static const char * const uri_status_strings
[] =
3112 { /* URI decode errors */
3114 "Bad arguments to function.",
3115 "Bad resource in URI.",
3116 "Bad port number in URI.",
3117 "Bad hostname in URI.",
3118 "Bad username in URI.",
3119 "Bad scheme in URI.",
3125 * Validate print job attributes...
3128 if (!valid_job_attributes(client
))
3130 httpFlush(&(client
->http
));
3135 * Do we have a file to print?
3138 if (client
->http
.state
== HTTP_POST_RECV
)
3140 respond_ipp(client
, IPP_BAD_REQUEST
,
3141 "Unexpected document data following request.");
3146 * Do we have a document URI?
3149 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3150 IPP_TAG_URI
)) == NULL
)
3152 respond_ipp(client
, IPP_BAD_REQUEST
, "Missing document-uri.");
3156 if (uri
->num_values
!= 1)
3158 respond_ipp(client
, IPP_BAD_REQUEST
, "Too many document-uri values.");
3162 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
,
3163 scheme
, sizeof(scheme
), userpass
,
3164 sizeof(userpass
), hostname
, sizeof(hostname
),
3165 &port
, resource
, sizeof(resource
));
3166 if (uri_status
< HTTP_URI_OK
)
3168 respond_ipp(client
, IPP_BAD_REQUEST
, "Bad document-uri: %s",
3169 uri_status_strings
[uri_status
- HTTP_URI_OVERFLOW
]);
3173 if (strcmp(scheme
, "file") &&
3175 strcmp(scheme
, "https") &&
3176 #endif /* HAVE_SSL */
3177 strcmp(scheme
, "http"))
3179 respond_ipp(client
, IPP_URI_SCHEME
, "URI scheme \"%s\" not supported.",
3184 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3186 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to access URI: %s",
3195 if ((job
= create_job(client
)) == NULL
)
3197 respond_ipp(client
, IPP_PRINTER_BUSY
, "Currently printing another job.");
3202 * Create a file for the request data...
3205 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3206 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3207 client
->printer
->directory
, job
->id
);
3208 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3209 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3210 client
->printer
->directory
, job
->id
);
3211 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3212 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3213 client
->printer
->directory
, job
->id
);
3214 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3215 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3216 client
->printer
->directory
, job
->id
);
3218 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3219 client
->printer
->directory
, job
->id
);
3221 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3223 job
->state
= IPP_JOB_ABORTED
;
3225 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3226 "Unable to create print file: %s", strerror(errno
));
3230 if (!strcmp(scheme
, "file"))
3232 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3234 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to access URI: %s",
3241 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3242 (errno
== EAGAIN
|| errno
== EINTR
))
3244 else if (bytes
> 0 && write(job
->fd
, buffer
, bytes
) < bytes
)
3246 int error
= errno
; /* Write error */
3248 job
->state
= IPP_JOB_ABORTED
;
3256 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3257 "Unable to write print file: %s", strerror(error
));
3268 if (port
== 443 || !strcmp(scheme
, "https"))
3269 encryption
= HTTP_ENCRYPT_ALWAYS
;
3271 #endif /* HAVE_SSL */
3272 encryption
= HTTP_ENCRYPT_IF_REQUESTED
;
3274 if ((http
= httpConnectEncrypt(hostname
, port
, encryption
)) == NULL
)
3276 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
,
3277 "Unable to connect to %s: %s", hostname
,
3278 cupsLastErrorString());
3279 job
->state
= IPP_JOB_ABORTED
;
3288 httpClearFields(http
);
3289 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3290 if (httpGet(http
, resource
))
3292 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to GET URI: %s",
3295 job
->state
= IPP_JOB_ABORTED
;
3305 while ((status
= httpUpdate(http
)) == HTTP_CONTINUE
);
3307 if (status
!= HTTP_OK
)
3309 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to GET URI: %s",
3310 httpStatus(status
));
3312 job
->state
= IPP_JOB_ABORTED
;
3322 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3324 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3326 int error
= errno
; /* Write error */
3328 job
->state
= IPP_JOB_ABORTED
;
3336 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3337 "Unable to write print file: %s", strerror(error
));
3347 int error
= errno
; /* Write error */
3349 job
->state
= IPP_JOB_ABORTED
;
3354 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
3360 job
->filename
= strdup(filename
);
3361 job
->state
= IPP_JOB_PENDING
;
3364 * Process the job...
3368 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3370 job
->state
= IPP_JOB_ABORTED
;
3371 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
3380 * Return the job info...
3383 respond_ipp(client
, IPP_OK
, NULL
);
3385 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3386 cupsArrayAdd(ra
, "job-id");
3387 cupsArrayAdd(ra
, "job-state");
3388 cupsArrayAdd(ra
, "job-state-reasons");
3389 cupsArrayAdd(ra
, "job-uri");
3391 copy_job_attributes(client
, job
, ra
);
3392 cupsArrayDelete(ra
);
3397 * 'ipp_send_document()' - Add an attached document to a job object created with
3402 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3404 _ipp_job_t
*job
; /* Job information */
3405 char filename
[1024], /* Filename buffer */
3406 buffer
[4096]; /* Copy buffer */
3407 ssize_t bytes
; /* Bytes read */
3408 ipp_attribute_t
*attr
; /* Current attribute */
3409 cups_array_t
*ra
; /* Attributes to send in response */
3416 if ((job
= find_job(client
)) == NULL
)
3418 respond_ipp(client
, IPP_NOT_FOUND
, "Job does not exist.");
3419 httpFlush(&(client
->http
));
3424 * See if we already have a document for this job or the job has already
3425 * in a non-pending state...
3428 if (job
->state
> IPP_JOB_HELD
)
3430 respond_ipp(client
, IPP_NOT_POSSIBLE
, "Job is not in a pending state.");
3431 httpFlush(&(client
->http
));
3434 else if (job
->filename
|| job
->fd
>= 0)
3436 respond_ipp(client
, IPP_MULTIPLE_JOBS_NOT_SUPPORTED
,
3437 "Multiple document jobs are not supported.");
3438 httpFlush(&(client
->http
));
3442 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3443 IPP_TAG_ZERO
)) == NULL
)
3445 respond_ipp(client
, IPP_BAD_REQUEST
,
3446 "Missing required last-document attribute.");
3447 httpFlush(&(client
->http
));
3450 else if (attr
->value_tag
!= IPP_TAG_BOOLEAN
|| attr
->num_values
!= 1 ||
3451 !attr
->values
[0].boolean
)
3453 respond_unsupported(client
, attr
);
3454 httpFlush(&(client
->http
));
3459 * Validate document attributes...
3462 if (!valid_doc_attributes(client
))
3464 httpFlush(&(client
->http
));
3469 * Get the document format for the job...
3472 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3474 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3475 IPP_TAG_MIMETYPE
)) != NULL
)
3476 job
->format
= attr
->values
[0].string
.text
;
3478 job
->format
= "application/octet-stream";
3481 * Create a file for the request data...
3484 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3485 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3486 client
->printer
->directory
, job
->id
);
3487 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3488 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3489 client
->printer
->directory
, job
->id
);
3490 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3491 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3492 client
->printer
->directory
, job
->id
);
3493 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3494 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3495 client
->printer
->directory
, job
->id
);
3497 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3498 client
->printer
->directory
, job
->id
);
3500 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3502 _cupsRWUnlock(&(client
->printer
->rwlock
));
3506 job
->state
= IPP_JOB_ABORTED
;
3508 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3509 "Unable to create print file: %s", strerror(errno
));
3513 while ((bytes
= httpRead2(&(client
->http
), buffer
, sizeof(buffer
))) > 0)
3515 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3517 int error
= errno
; /* Write error */
3519 job
->state
= IPP_JOB_ABORTED
;
3526 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3527 "Unable to write print file: %s", strerror(error
));
3535 * Got an error while reading the print data, so abort this job.
3538 job
->state
= IPP_JOB_ABORTED
;
3545 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to read print file.");
3551 int error
= errno
; /* Write error */
3553 job
->state
= IPP_JOB_ABORTED
;
3558 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
3563 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3566 job
->filename
= strdup(filename
);
3567 job
->state
= IPP_JOB_PENDING
;
3569 _cupsRWUnlock(&(client
->printer
->rwlock
));
3572 * Process the job...
3576 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3578 job
->state
= IPP_JOB_ABORTED
;
3579 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
3588 * Return the job info...
3591 respond_ipp(client
, IPP_OK
, NULL
);
3593 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3594 cupsArrayAdd(ra
, "job-id");
3595 cupsArrayAdd(ra
, "job-state");
3596 cupsArrayAdd(ra
, "job-state-reasons");
3597 cupsArrayAdd(ra
, "job-uri");
3599 copy_job_attributes(client
, job
, ra
);
3600 cupsArrayDelete(ra
);
3605 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3610 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3612 _ipp_job_t
*job
; /* Job information */
3613 ipp_attribute_t
*uri
; /* document-uri */
3614 char scheme
[256], /* URI scheme */
3615 userpass
[256], /* Username and password info */
3616 hostname
[256], /* Hostname */
3617 resource
[1024]; /* Resource path */
3618 int port
; /* Port number */
3619 http_uri_status_t uri_status
; /* URI decode status */
3620 http_encryption_t encryption
; /* Encryption to use, if any */
3621 http_t
*http
; /* Connection for http/https URIs */
3622 http_status_t status
; /* Access status for http/https URIs */
3623 int infile
; /* Input file for local file URIs */
3624 char filename
[1024], /* Filename buffer */
3625 buffer
[4096]; /* Copy buffer */
3626 ssize_t bytes
; /* Bytes read */
3627 ipp_attribute_t
*attr
; /* Current attribute */
3628 cups_array_t
*ra
; /* Attributes to send in response */
3629 static const char * const uri_status_strings
[] =
3630 { /* URI decode errors */
3632 "Bad arguments to function.",
3633 "Bad resource in URI.",
3634 "Bad port number in URI.",
3635 "Bad hostname in URI.",
3636 "Bad username in URI.",
3637 "Bad scheme in URI.",
3646 if ((job
= find_job(client
)) == NULL
)
3648 respond_ipp(client
, IPP_NOT_FOUND
, "Job does not exist.");
3649 httpFlush(&(client
->http
));
3654 * See if we already have a document for this job or the job has already
3655 * in a non-pending state...
3658 if (job
->state
> IPP_JOB_HELD
)
3660 respond_ipp(client
, IPP_NOT_POSSIBLE
, "Job is not in a pending state.");
3661 httpFlush(&(client
->http
));
3664 else if (job
->filename
|| job
->fd
>= 0)
3666 respond_ipp(client
, IPP_MULTIPLE_JOBS_NOT_SUPPORTED
,
3667 "Multiple document jobs are not supported.");
3668 httpFlush(&(client
->http
));
3672 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3673 IPP_TAG_ZERO
)) == NULL
)
3675 respond_ipp(client
, IPP_BAD_REQUEST
,
3676 "Missing required last-document attribute.");
3677 httpFlush(&(client
->http
));
3680 else if (attr
->value_tag
!= IPP_TAG_BOOLEAN
|| attr
->num_values
!= 1 ||
3681 !attr
->values
[0].boolean
)
3683 respond_unsupported(client
, attr
);
3684 httpFlush(&(client
->http
));
3689 * Validate document attributes...
3692 if (!valid_doc_attributes(client
))
3694 httpFlush(&(client
->http
));
3699 * Do we have a file to print?
3702 if (client
->http
.state
== HTTP_POST_RECV
)
3704 respond_ipp(client
, IPP_BAD_REQUEST
,
3705 "Unexpected document data following request.");
3710 * Do we have a document URI?
3713 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3714 IPP_TAG_URI
)) == NULL
)
3716 respond_ipp(client
, IPP_BAD_REQUEST
, "Missing document-uri.");
3720 if (uri
->num_values
!= 1)
3722 respond_ipp(client
, IPP_BAD_REQUEST
, "Too many document-uri values.");
3726 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, uri
->values
[0].string
.text
,
3727 scheme
, sizeof(scheme
), userpass
,
3728 sizeof(userpass
), hostname
, sizeof(hostname
),
3729 &port
, resource
, sizeof(resource
));
3730 if (uri_status
< HTTP_URI_OK
)
3732 respond_ipp(client
, IPP_BAD_REQUEST
, "Bad document-uri: %s",
3733 uri_status_strings
[uri_status
- HTTP_URI_OVERFLOW
]);
3737 if (strcmp(scheme
, "file") &&
3739 strcmp(scheme
, "https") &&
3740 #endif /* HAVE_SSL */
3741 strcmp(scheme
, "http"))
3743 respond_ipp(client
, IPP_URI_SCHEME
, "URI scheme \"%s\" not supported.",
3748 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3750 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to access URI: %s",
3756 * Get the document format for the job...
3759 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3761 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3762 IPP_TAG_MIMETYPE
)) != NULL
)
3763 job
->format
= attr
->values
[0].string
.text
;
3765 job
->format
= "application/octet-stream";
3768 * Create a file for the request data...
3771 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3772 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3773 client
->printer
->directory
, job
->id
);
3774 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3775 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3776 client
->printer
->directory
, job
->id
);
3777 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3778 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3779 client
->printer
->directory
, job
->id
);
3780 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3781 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3782 client
->printer
->directory
, job
->id
);
3784 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3785 client
->printer
->directory
, job
->id
);
3787 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3789 _cupsRWUnlock(&(client
->printer
->rwlock
));
3793 job
->state
= IPP_JOB_ABORTED
;
3795 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3796 "Unable to create print file: %s", strerror(errno
));
3800 if (!strcmp(scheme
, "file"))
3802 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3804 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to access URI: %s",
3811 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3812 (errno
== EAGAIN
|| errno
== EINTR
))
3814 else if (bytes
> 0 && write(job
->fd
, buffer
, bytes
) < bytes
)
3816 int error
= errno
; /* Write error */
3818 job
->state
= IPP_JOB_ABORTED
;
3826 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3827 "Unable to write print file: %s", strerror(error
));
3838 if (port
== 443 || !strcmp(scheme
, "https"))
3839 encryption
= HTTP_ENCRYPT_ALWAYS
;
3841 #endif /* HAVE_SSL */
3842 encryption
= HTTP_ENCRYPT_IF_REQUESTED
;
3844 if ((http
= httpConnectEncrypt(hostname
, port
, encryption
)) == NULL
)
3846 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
,
3847 "Unable to connect to %s: %s", hostname
,
3848 cupsLastErrorString());
3849 job
->state
= IPP_JOB_ABORTED
;
3858 httpClearFields(http
);
3859 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3860 if (httpGet(http
, resource
))
3862 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to GET URI: %s",
3865 job
->state
= IPP_JOB_ABORTED
;
3875 while ((status
= httpUpdate(http
)) == HTTP_CONTINUE
);
3877 if (status
!= HTTP_OK
)
3879 respond_ipp(client
, IPP_DOCUMENT_ACCESS_ERROR
, "Unable to GET URI: %s",
3880 httpStatus(status
));
3882 job
->state
= IPP_JOB_ABORTED
;
3892 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3894 if (write(job
->fd
, buffer
, bytes
) < bytes
)
3896 int error
= errno
; /* Write error */
3898 job
->state
= IPP_JOB_ABORTED
;
3906 respond_ipp(client
, IPP_INTERNAL_ERROR
,
3907 "Unable to write print file: %s", strerror(error
));
3917 int error
= errno
; /* Write error */
3919 job
->state
= IPP_JOB_ABORTED
;
3924 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
3929 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3932 job
->filename
= strdup(filename
);
3933 job
->state
= IPP_JOB_PENDING
;
3935 _cupsRWUnlock(&(client
->printer
->rwlock
));
3938 * Process the job...
3942 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3944 job
->state
= IPP_JOB_ABORTED
;
3945 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
3954 * Return the job info...
3957 respond_ipp(client
, IPP_OK
, NULL
);
3959 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3960 cupsArrayAdd(ra
, "job-id");
3961 cupsArrayAdd(ra
, "job-state");
3962 cupsArrayAdd(ra
, "job-state-reasons");
3963 cupsArrayAdd(ra
, "job-uri");
3965 copy_job_attributes(client
, job
, ra
);
3966 cupsArrayDelete(ra
);
3971 * 'ipp_validate_job()' - Validate job creation attributes.
3975 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3977 if (valid_job_attributes(client
))
3978 respond_ipp(client
, IPP_OK
, NULL
);
3983 * 'process_client()' - Process client requests on a thread.
3986 static void * /* O - Exit status */
3987 process_client(_ipp_client_t
*client
) /* I - Client */
3990 * Loop until we are out of requests or timeout (30 seconds)...
3993 while (httpWait(&(client
->http
), 30000))
3994 if (!process_http(client
))
3998 * Close the conection to the client and return...
4001 delete_client(client
);
4008 * 'process_http()' - Process a HTTP request.
4011 int /* O - 1 on success, 0 on failure */
4012 process_http(_ipp_client_t
*client
) /* I - Client connection */
4014 char line
[4096], /* Line from client... */
4015 operation
[64], /* Operation code from socket */
4016 uri
[1024], /* URI */
4017 version
[64], /* HTTP version number string */
4018 *ptr
; /* Pointer into strings */
4019 int major
, minor
; /* HTTP version numbers */
4020 http_status_t status
; /* Transfer status */
4021 ipp_state_t state
; /* State of IPP transfer */
4025 * Abort if we have an error on the connection...
4028 if (client
->http
.error
)
4032 * Clear state variables...
4035 httpClearFields(&(client
->http
));
4036 ippDelete(client
->request
);
4037 ippDelete(client
->response
);
4039 client
->http
.activity
= time(NULL
);
4040 client
->http
.version
= HTTP_1_1
;
4041 client
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
4042 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
4043 client
->http
.data_remaining
= 0;
4044 client
->request
= NULL
;
4045 client
->response
= NULL
;
4046 client
->operation
= HTTP_WAITING
;
4049 * Read a request from the connection...
4052 while ((ptr
= httpGets(line
, sizeof(line
) - 1, &(client
->http
))) != NULL
)
4060 * Parse the request line...
4063 fprintf(stderr
, "%s %s\n", client
->http
.hostname
, line
);
4065 switch (sscanf(line
, "%63s%1023s%63s", operation
, uri
, version
))
4068 fprintf(stderr
, "%s Bad request line.\n", client
->http
.hostname
);
4069 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4073 client
->http
.version
= HTTP_0_9
;
4077 if (sscanf(version
, "HTTP/%d.%d", &major
, &minor
) != 2)
4079 fprintf(stderr
, "%s Bad HTTP version.\n", client
->http
.hostname
);
4080 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4086 client
->http
.version
= (http_version_t
)(major
* 100 + minor
);
4087 if (client
->http
.version
== HTTP_1_1
)
4088 client
->http
.keep_alive
= HTTP_KEEPALIVE_ON
;
4090 client
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
4094 respond_http(client
, HTTP_NOT_SUPPORTED
, NULL
, 0);
4101 * Handle full URLs in the request line...
4104 if (!strncmp(client
->uri
, "http:", 5) || !strncmp(client
->uri
, "ipp:", 4))
4106 char scheme
[32], /* Method/scheme */
4107 userpass
[128], /* Username:password */
4108 hostname
[HTTP_MAX_HOST
];/* Hostname */
4109 int port
; /* Port number */
4112 * Separate the URI into its components...
4115 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4116 userpass
, sizeof(userpass
),
4117 hostname
, sizeof(hostname
), &port
,
4118 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_OK
)
4120 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->http
.hostname
, uri
);
4121 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4131 if (!_httpDecodeURI(client
->uri
, uri
, sizeof(client
->uri
)))
4133 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->http
.hostname
, uri
);
4134 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4140 * Process the request...
4143 if (!strcmp(operation
, "GET"))
4144 client
->http
.state
= HTTP_GET
;
4145 else if (!strcmp(operation
, "POST"))
4146 client
->http
.state
= HTTP_POST
;
4147 else if (!strcmp(operation
, "OPTIONS"))
4148 client
->http
.state
= HTTP_OPTIONS
;
4149 else if (!strcmp(operation
, "HEAD"))
4150 client
->http
.state
= HTTP_HEAD
;
4153 fprintf(stderr
, "%s Bad operation \"%s\".\n", client
->http
.hostname
,
4155 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4159 client
->start
= time(NULL
);
4160 client
->operation
= client
->http
.state
;
4161 client
->http
.status
= HTTP_OK
;
4164 * Parse incoming parameters until the status changes...
4167 while ((status
= httpUpdate(&(client
->http
))) == HTTP_CONTINUE
);
4169 if (status
!= HTTP_OK
)
4171 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4175 if (!client
->http
.fields
[HTTP_FIELD_HOST
][0] &&
4176 client
->http
.version
>= HTTP_1_1
)
4179 * HTTP/1.1 and higher require the "Host:" field...
4182 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4187 * Handle HTTP Upgrade...
4190 if (!_cups_strcasecmp(client
->http
.fields
[HTTP_FIELD_CONNECTION
], "Upgrade"))
4192 if (!respond_http(client
, HTTP_NOT_IMPLEMENTED
, NULL
, 0))
4197 * Handle HTTP Expect...
4200 if (client
->http
.expect
&&
4201 (client
->operation
== HTTP_POST
|| client
->operation
== HTTP_PUT
))
4203 if (client
->http
.expect
== HTTP_CONTINUE
)
4206 * Send 100-continue header...
4209 if (!respond_http(client
, HTTP_CONTINUE
, NULL
, 0))
4215 * Send 417-expectation-failed header...
4218 if (!respond_http(client
, HTTP_EXPECTATION_FAILED
, NULL
, 0))
4221 httpPrintf(&(client
->http
), "Content-Length: 0\r\n");
4222 httpPrintf(&(client
->http
), "\r\n");
4223 httpFlushWrite(&(client
->http
));
4224 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
4229 * Handle new transfers...
4232 switch (client
->operation
)
4236 * Do HEAD/OPTIONS command...
4239 return (respond_http(client
, HTTP_OK
, NULL
, 0));
4242 if (!strcmp(client
->uri
, "/icon.png"))
4243 return (respond_http(client
, HTTP_OK
, "image/png", 0));
4244 else if (!strcmp(client
->uri
, "/"))
4245 return (respond_http(client
, HTTP_OK
, "text/html", 0));
4247 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
4251 if (!strcmp(client
->uri
, "/icon.png"))
4254 * Send PNG icon file.
4257 int fd
; /* Icon file */
4258 struct stat fileinfo
; /* Icon file information */
4259 char buffer
[4096]; /* Copy buffer */
4260 ssize_t bytes
; /* Bytes */
4262 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4263 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4265 if (!respond_http(client
, HTTP_OK
, "image/png", fileinfo
.st_size
))
4271 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4272 httpWrite2(&(client
->http
), buffer
, bytes
);
4274 httpFlushWrite(&(client
->http
));
4279 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
4281 else if (!strcmp(client
->uri
, "/"))
4284 * Show web status page...
4287 if (!respond_http(client
, HTTP_OK
, "text/html", 0))
4291 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4292 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4295 "<title>%s</title>\n"
4296 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4297 "type=\"image/png\">\n"
4302 "<p>%s, %d job(s).</p>\n"
4305 client
->printer
->name
, client
->printer
->name
,
4306 client
->printer
->state
== IPP_PRINTER_IDLE
? "Idle" :
4307 client
->printer
->state
== IPP_PRINTER_PROCESSING
?
4308 "Printing" : "Stopped",
4309 cupsArrayCount(client
->printer
->jobs
));
4310 httpWrite2(&(client
->http
), "", 0);
4315 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
4319 if (client
->http
.data_remaining
< 0 ||
4320 (!client
->http
.fields
[HTTP_FIELD_CONTENT_LENGTH
][0] &&
4321 client
->http
.data_encoding
== HTTP_ENCODE_LENGTH
))
4324 * Negative content lengths are invalid...
4327 return (respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0));
4330 if (strcmp(client
->http
.fields
[HTTP_FIELD_CONTENT_TYPE
],
4334 * Not an IPP request...
4337 return (respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0));
4341 * Read the IPP request...
4344 client
->request
= ippNew();
4346 while ((state
= ippRead(&(client
->http
), client
->request
)) != IPP_DATA
)
4347 if (state
== IPP_ERROR
)
4349 fprintf(stderr
, "%s IPP read error (%s).\n", client
->http
.hostname
,
4350 cupsLastErrorString());
4351 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
4356 * Now that we have the IPP request, process the request...
4359 return (process_ipp(client
));
4362 break; /* Anti-compiler-warning-code */
4370 * 'process_ipp()' - Process an IPP request.
4373 static int /* O - 1 on success, 0 on error */
4374 process_ipp(_ipp_client_t
*client
) /* I - Client */
4376 ipp_tag_t group
; /* Current group tag */
4377 ipp_attribute_t
*attr
; /* Current attribute */
4378 ipp_attribute_t
*charset
; /* Character set attribute */
4379 ipp_attribute_t
*language
; /* Language attribute */
4380 ipp_attribute_t
*uri
; /* Printer URI attribute */
4383 debug_attributes("Request", client
->request
, 1);
4386 * First build an empty response message for this request...
4389 client
->operation_id
= client
->request
->request
.op
.operation_id
;
4390 client
->response
= ippNew();
4392 client
->response
->request
.status
.version
[0] =
4393 client
->request
->request
.op
.version
[0];
4394 client
->response
->request
.status
.version
[1] =
4395 client
->request
->request
.op
.version
[1];
4396 client
->response
->request
.status
.request_id
=
4397 client
->request
->request
.op
.request_id
;
4400 * Then validate the request header and required attributes...
4403 if (client
->request
->request
.any
.version
[0] < 1 ||
4404 client
->request
->request
.any
.version
[0] > 2)
4407 * Return an error, since we only support IPP 1.x and 2.x.
4410 respond_ipp(client
, IPP_VERSION_NOT_SUPPORTED
,
4411 "Bad request version number %d.%d.",
4412 client
->request
->request
.any
.version
[0],
4413 client
->request
->request
.any
.version
[1]);
4415 else if (client
->request
->request
.any
.request_id
<= 0)
4416 respond_ipp(client
, IPP_BAD_REQUEST
, "Bad request-id %d.",
4417 client
->request
->request
.any
.request_id
);
4418 else if (!client
->request
->attrs
)
4419 respond_ipp(client
, IPP_BAD_REQUEST
, "No attributes in request.");
4423 * Make sure that the attributes are provided in the correct order and
4424 * don't repeat groups...
4427 for (attr
= client
->request
->attrs
, group
= attr
->group_tag
;
4430 if (attr
->group_tag
< group
&& attr
->group_tag
!= IPP_TAG_ZERO
)
4433 * Out of order; return an error...
4436 respond_ipp(client
, IPP_BAD_REQUEST
,
4437 "Attribute groups are out of order (%x < %x).",
4438 attr
->group_tag
, group
);
4442 group
= attr
->group_tag
;
4447 * Then make sure that the first three attributes are:
4449 * attributes-charset
4450 * attributes-natural-language
4451 * printer-uri/job-uri
4454 attr
= client
->request
->attrs
;
4455 if (attr
&& attr
->name
&&
4456 !strcmp(attr
->name
, "attributes-charset") &&
4457 (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_CHARSET
)
4465 if (attr
&& attr
->name
&&
4466 !strcmp(attr
->name
, "attributes-natural-language") &&
4467 (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_LANGUAGE
)
4472 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4473 IPP_TAG_URI
)) != NULL
)
4475 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4476 IPP_TAG_URI
)) != NULL
)
4481 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
4482 "attributes-charset", NULL
,
4483 charset
? charset
->values
[0].string
.text
: "utf-8");
4485 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
4486 "attributes-natural-language", NULL
,
4487 language
? language
->values
[0].string
.text
: "en");
4490 _cups_strcasecmp(charset
->values
[0].string
.text
, "us-ascii") &&
4491 _cups_strcasecmp(charset
->values
[0].string
.text
, "utf-8"))
4494 * Bad character set...
4497 respond_ipp(client
, IPP_BAD_REQUEST
,
4498 "Unsupported character set \"%s\".",
4499 charset
->values
[0].string
.text
);
4501 else if (!charset
|| !language
|| !uri
)
4504 * Return an error, since attributes-charset,
4505 * attributes-natural-language, and printer-uri/job-uri are required
4506 * for all operations.
4509 respond_ipp(client
, IPP_BAD_REQUEST
, "Missing required attributes.");
4511 else if (strcmp(uri
->values
[0].string
.text
, client
->printer
->uri
) &&
4512 strncmp(uri
->values
[0].string
.text
, client
->printer
->uri
,
4513 client
->printer
->urilen
))
4515 respond_ipp(client
, IPP_NOT_FOUND
, "%s %s not found.", uri
->name
,
4516 uri
->values
[0].string
.text
);
4521 * Try processing the operation...
4524 if (client
->http
.expect
== HTTP_CONTINUE
)
4527 * Send 100-continue header...
4530 if (!respond_http(client
, HTTP_CONTINUE
, NULL
, 0))
4534 switch (client
->request
->request
.op
.operation_id
)
4536 case IPP_PRINT_JOB
:
4537 ipp_print_job(client
);
4540 case IPP_PRINT_URI
:
4541 ipp_print_uri(client
);
4544 case IPP_VALIDATE_JOB
:
4545 ipp_validate_job(client
);
4548 case IPP_CREATE_JOB
:
4549 ipp_create_job(client
);
4552 case IPP_SEND_DOCUMENT
:
4553 ipp_send_document(client
);
4557 ipp_send_uri(client
);
4560 case IPP_CANCEL_JOB
:
4561 ipp_cancel_job(client
);
4564 case IPP_GET_JOB_ATTRIBUTES
:
4565 ipp_get_job_attributes(client
);
4569 ipp_get_jobs(client
);
4572 case IPP_GET_PRINTER_ATTRIBUTES
:
4573 ipp_get_printer_attributes(client
);
4577 respond_ipp(client
, IPP_OPERATION_NOT_SUPPORTED
,
4578 "Operation not supported.");
4586 * Send the HTTP header and return...
4589 if (client
->http
.state
!= HTTP_POST_SEND
)
4590 httpFlush(&(client
->http
)); /* Flush trailing (junk) data */
4592 return (respond_http(client
, HTTP_OK
, "application/ipp",
4593 ippLength(client
->response
)));
4598 * 'process_job()' - Process a print job.
4601 static void * /* O - Thread exit status */
4602 process_job(_ipp_job_t
*job
) /* I - Job */
4604 job
->state
= IPP_JOB_PROCESSING
;
4605 job
->printer
->state
= IPP_PRINTER_PROCESSING
;
4610 job
->state
= IPP_JOB_CANCELED
;
4612 job
->state
= IPP_JOB_COMPLETED
;
4614 job
->completed
= time(NULL
);
4615 job
->printer
->state
= IPP_PRINTER_IDLE
;
4616 job
->printer
->active_job
= NULL
;
4624 * 'register_printer()' - Register a printer object via Bonjour.
4627 static int /* O - 1 on success, 0 on error */
4629 _ipp_printer_t
*printer
, /* I - Printer */
4630 const char *location
, /* I - Location */
4631 const char *make
, /* I - Manufacturer */
4632 const char *model
, /* I - Model name */
4633 const char *formats
, /* I - Supported formats */
4634 const char *adminurl
, /* I - Web interface URL */
4635 int color
, /* I - 1 = color, 0 = monochrome */
4636 int duplex
, /* I - 1 = duplex, 0 = simplex */
4637 const char *regtype
) /* I - Service type */
4639 DNSServiceErrorType error
; /* Error from Bonjour */
4640 char make_model
[256],/* Make and model together */
4641 product
[256]; /* Product string */
4645 * Build the TXT record for IPP...
4648 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4649 snprintf(product
, sizeof(product
), "(%s)", model
);
4651 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
4652 TXTRecordSetValue(&(printer
->ipp_txt
), "txtvers", 1, "1");
4653 TXTRecordSetValue(&(printer
->ipp_txt
), "qtotal", 1, "1");
4654 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 3, "ipp");
4655 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
4657 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
4659 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
4661 TXTRecordSetValue(&(printer
->ipp_txt
), "priority", 1, "0");
4662 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
4664 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
4666 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
4667 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
4668 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
4670 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
4672 TXTRecordSetValue(&(printer
->ipp_txt
), "air", 4, "none");
4675 * Create a shared service reference for Bonjour...
4678 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
4679 != kDNSServiceErr_NoError
)
4681 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
4686 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4687 * defend our service name but not actually support LPD...
4690 printer
->printer_ref
= printer
->common_ref
;
4692 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
4693 kDNSServiceFlagsShareConnection
,
4694 0 /* interfaceIndex */, printer
->dnssd_name
,
4695 "_printer._tcp", NULL
/* domain */,
4696 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
4697 NULL
/* txtRecord */,
4698 (DNSServiceRegisterReply
)dnssd_callback
,
4699 printer
)) != kDNSServiceErr_NoError
)
4701 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
4702 printer
->dnssd_name
, error
);
4707 * Then register the _ipp._tcp (IPP) service type with the real port number to
4708 * advertise our IPP printer...
4711 printer
->ipp_ref
= printer
->common_ref
;
4713 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
4714 kDNSServiceFlagsShareConnection
,
4715 0 /* interfaceIndex */, printer
->dnssd_name
,
4716 regtype
, NULL
/* domain */,
4717 NULL
/* host */, htons(printer
->port
),
4718 TXTRecordGetLength(&(printer
->ipp_txt
)),
4719 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4720 (DNSServiceRegisterReply
)dnssd_callback
,
4721 printer
)) != kDNSServiceErr_NoError
)
4723 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4724 printer
->dnssd_name
, regtype
, error
);
4729 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4730 * real port number to advertise our IPP printer...
4733 printer
->http_ref
= printer
->common_ref
;
4735 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
4736 kDNSServiceFlagsShareConnection
,
4737 0 /* interfaceIndex */, printer
->dnssd_name
,
4738 "_http._tcp,_printer", NULL
/* domain */,
4739 NULL
/* host */, htons(printer
->port
),
4740 0 /* txtLen */, NULL
, /* txtRecord */
4741 (DNSServiceRegisterReply
)dnssd_callback
,
4742 printer
)) != kDNSServiceErr_NoError
)
4744 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4745 printer
->dnssd_name
, regtype
, error
);
4751 #endif /* HAVE_DNSSD */
4755 * 'respond_http()' - Send a HTTP response.
4758 int /* O - 1 on success, 0 on failure */
4759 respond_http(_ipp_client_t
*client
, /* I - Client */
4760 http_status_t code
, /* I - HTTP status of response */
4761 const char *type
, /* I - MIME type of response */
4762 size_t length
) /* I - Length of response */
4764 char message
[1024]; /* Text message */
4767 fprintf(stderr
, "%s %s\n", client
->http
.hostname
, httpStatus(code
));
4769 if (code
== HTTP_CONTINUE
)
4772 * 100-continue doesn't send any headers...
4775 return (httpPrintf(&(client
->http
), "HTTP/%d.%d 100 Continue\r\n\r\n",
4776 client
->http
.version
/ 100,
4777 client
->http
.version
% 100) > 0);
4781 * Format an error message...
4784 if (!type
&& !length
&& code
!= HTTP_OK
)
4786 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
4788 type
= "text/plain";
4789 length
= strlen(message
);
4795 * Send the HTTP status header...
4798 httpFlushWrite(&(client
->http
));
4800 client
->http
.data_encoding
= HTTP_ENCODE_FIELDS
;
4802 if (httpPrintf(&(client
->http
), "HTTP/%d.%d %d %s\r\n", client
->http
.version
/ 100,
4803 client
->http
.version
% 100, code
, httpStatus(code
)) < 0)
4807 * Follow the header with the response fields...
4810 if (httpPrintf(&(client
->http
), "Date: %s\r\n", httpGetDateString(time(NULL
))) < 0)
4813 if (client
->http
.keep_alive
&& client
->http
.version
>= HTTP_1_0
)
4815 if (httpPrintf(&(client
->http
),
4816 "Connection: Keep-Alive\r\n"
4817 "Keep-Alive: timeout=10\r\n") < 0)
4821 if (code
== HTTP_METHOD_NOT_ALLOWED
|| client
->operation
== HTTP_OPTIONS
)
4823 if (httpPrintf(&(client
->http
), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
4829 if (!strcmp(type
, "text/html"))
4831 if (httpPrintf(&(client
->http
),
4832 "Content-Type: text/html; charset=utf-8\r\n") < 0)
4835 else if (httpPrintf(&(client
->http
), "Content-Type: %s\r\n", type
) < 0)
4839 if (length
== 0 && !message
[0])
4841 if (httpPrintf(&(client
->http
), "Transfer-Encoding: chunked\r\n\r\n") < 0)
4844 else if (httpPrintf(&(client
->http
), "Content-Length: " CUPS_LLFMT
"\r\n\r\n",
4845 CUPS_LLCAST length
) < 0)
4848 if (httpFlushWrite(&(client
->http
)) < 0)
4852 * Send the response data...
4858 * Send a plain text message.
4861 if (httpPrintf(&(client
->http
), "%s", message
) < 0)
4864 else if (client
->response
)
4867 * Send an IPP response...
4870 debug_attributes("Response", client
->response
, 2);
4872 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
4873 client
->http
.data_remaining
= (off_t
)ippLength(client
->response
);
4874 client
->response
->state
= IPP_IDLE
;
4876 if (ippWrite(&(client
->http
), client
->response
) != IPP_DATA
)
4880 client
->http
.data_encoding
= HTTP_ENCODE_CHUNKED
;
4883 * Flush the data and return...
4886 return (httpFlushWrite(&(client
->http
)) >= 0);
4891 * 'respond_ipp()' - Send an IPP response.
4895 respond_ipp(_ipp_client_t
*client
, /* I - Client */
4896 ipp_status_t status
, /* I - status-code */
4897 const char *message
, /* I - printf-style status-message */
4898 ...) /* I - Additional args as needed */
4900 va_list ap
; /* Pointer to additional args */
4901 char formatted
[1024]; /* Formatted errror message */
4904 client
->response
->request
.status
.status_code
= status
;
4906 if (!client
->response
->attrs
)
4908 ippAddString(client
->response
, IPP_TAG_OPERATION
,
4909 IPP_TAG_CHARSET
| IPP_TAG_COPY
, "attributes-charset", NULL
,
4911 ippAddString(client
->response
, IPP_TAG_OPERATION
,
4912 IPP_TAG_LANGUAGE
| IPP_TAG_COPY
, "attributes-natural-language",
4918 va_start(ap
, message
);
4919 vsnprintf(formatted
, sizeof(formatted
), message
, ap
);
4922 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
4923 "status-message", NULL
, formatted
);
4926 formatted
[0] = '\0';
4928 fprintf(stderr
, "%s %s %s (%s)\n", client
->http
.hostname
,
4929 ippOpString(client
->operation_id
), ippErrorString(status
), formatted
);
4934 * 'respond_unsupported()' - Respond with an unsupported attribute.
4938 respond_unsupported(
4939 _ipp_client_t
*client
, /* I - Client */
4940 ipp_attribute_t
*attr
) /* I - Atribute */
4942 if (!client
->response
->attrs
)
4943 respond_ipp(client
, IPP_ATTRIBUTES
, "Unsupported %s %s%s value.",
4944 attr
->name
, attr
->num_values
> 1 ? "1setOf " : "",
4945 ippTagString(attr
->value_tag
));
4947 copy_attribute(client
->response
, attr
, IPP_TAG_UNSUPPORTED_GROUP
, 0);
4952 * 'run_printer()' - Run the printer service.
4956 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
4958 int num_fds
; /* Number of file descriptors */
4959 struct pollfd polldata
[3]; /* poll() data */
4960 int timeout
; /* Timeout for poll() */
4961 _ipp_client_t
*client
; /* New client */
4965 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4968 polldata
[0].fd
= printer
->ipv4
;
4969 polldata
[0].events
= POLLIN
;
4971 polldata
[1].fd
= printer
->ipv6
;
4972 polldata
[1].events
= POLLIN
;
4977 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
4978 polldata
[num_fds
++].events
= POLLIN
;
4979 #endif /* HAVE_DNSSD */
4982 * Loop until we are killed or have a hard error...
4987 if (cupsArrayCount(printer
->jobs
))
4992 if (poll(polldata
, num_fds
, timeout
) < 0 && errno
!= EINTR
)
4994 perror("poll() failed");
4998 if (polldata
[0].revents
& POLLIN
)
5000 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
5002 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5004 perror("Unable to create client thread");
5005 delete_client(client
);
5010 if (polldata
[1].revents
& POLLIN
)
5012 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5014 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5016 perror("Unable to create client thread");
5017 delete_client(client
);
5023 if (polldata
[2].revents
& POLLIN
)
5024 DNSServiceProcessResult(printer
->common_ref
);
5025 #endif /* HAVE_DNSSD */
5028 * Clean out old jobs...
5031 clean_jobs(printer
);
5037 * 'usage()' - Show program usage.
5041 usage(int status
) /* O - Exit status */
5045 puts(CUPS_SVERSION
" - Copyright 2010 by Apple Inc. All rights reserved.");
5049 puts("Usage: ippserver [options] \"name\"");
5052 puts("-2 Supports 2-sided printing (default=1-sided)");
5053 puts("-M manufacturer Manufacturer name (default=Test)");
5054 printf("-d spool-directory Spool directory "
5055 "(default=/tmp/ippserver.%d)\n", (int)getpid());
5056 puts("-f type/subtype[,...] List of supported types "
5057 "(default=application/pdf,image/jpeg)");
5058 puts("-h Show program help");
5059 puts("-i iconfile.png PNG icon file (default=printer.png)");
5060 puts("-l location Location of printer (default=empty string)");
5061 puts("-m model Model name (default=Printer)");
5062 puts("-n hostname Hostname for printer");
5063 puts("-p port Port number (default=auto)");
5064 puts("-r regtype Bonjour service type (default=_ipp._tcp)");
5065 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
5066 puts("-v[vvv] Be (very) verbose");
5073 * 'valid_doc_attributes()' - Determine whether the document attributes are
5076 * When one or more document attributes are invalid, this function adds a
5077 * suitable response and attributes to the unsupported group.
5080 static int /* O - 1 if valid, 0 if not */
5081 valid_doc_attributes(
5082 _ipp_client_t
*client
) /* I - Client */
5084 int i
; /* Looping var */
5085 ipp_attribute_t
*attr
, /* Current attribute */
5086 *supported
; /* document-format-supported */
5087 const char *format
= NULL
; /* document-format value */
5091 * Check operation attributes...
5094 if ((attr
= ippFindAttribute(client
->request
, "compression",
5095 IPP_TAG_ZERO
)) != NULL
)
5098 * If compression is specified, only accept "none"...
5101 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
||
5102 strcmp(attr
->values
[0].string
.text
, "none"))
5103 respond_unsupported(client
, attr
);
5105 fprintf(stderr
, "%s %s compression=\"%s\"\n",
5106 client
->http
.hostname
,
5107 ippOpString(client
->request
->request
.op
.operation_id
),
5108 attr
->values
[0].string
.text
);
5112 * Is it a format we support?
5115 if ((attr
= ippFindAttribute(client
->request
, "document-format",
5116 IPP_TAG_ZERO
)) != NULL
)
5118 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_MIMETYPE
)
5119 respond_unsupported(client
, attr
);
5122 format
= attr
->values
[0].string
.text
;
5124 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
5125 client
->http
.hostname
,
5126 ippOpString(client
->request
->request
.op
.operation_id
), format
);
5130 format
= "application/octet-stream";
5132 if (!strcmp(format
, "application/octet-stream") &&
5133 (client
->request
->request
.op
.operation_id
== IPP_PRINT_JOB
||
5134 client
->request
->request
.op
.operation_id
== IPP_SEND_DOCUMENT
))
5137 * Auto-type the file using the first 4 bytes of the file...
5140 unsigned char header
[4]; /* First 4 bytes of file */
5142 memset(header
, 0, sizeof(header
));
5143 _httpPeek(&(client
->http
), (char *)header
, sizeof(header
));
5145 if (!memcmp(header
, "%PDF", 4))
5146 format
= "application/pdf";
5147 else if (!memcmp(header
, "%!", 2))
5148 format
= "application/postscript";
5149 else if (!memcmp(header
, "\377\330\377", 3) &&
5150 header
[3] >= 0xe0 && header
[3] <= 0xef)
5151 format
= "image/jpeg";
5152 else if (!memcmp(header
, "\211PNG", 4))
5153 format
= "image/png";
5156 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
5157 client
->http
.hostname
,
5158 ippOpString(client
->request
->request
.op
.operation_id
), format
);
5161 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5162 "document-format", NULL
, format
);
5165 _cupsStrFree(attr
->values
[0].string
.text
);
5166 attr
->values
[0].string
.text
= _cupsStrAlloc(format
);
5170 if (client
->request
->request
.op
.operation_id
!= IPP_CREATE_JOB
&&
5171 (supported
= ippFindAttribute(client
->printer
->attrs
,
5172 "document-format-supported",
5173 IPP_TAG_MIMETYPE
)) != NULL
)
5175 for (i
= 0; i
< supported
->num_values
; i
++)
5176 if (!_cups_strcasecmp(format
, supported
->values
[i
].string
.text
))
5179 if (i
>= supported
->num_values
&& attr
)
5180 respond_unsupported(client
, attr
);
5183 return (!client
->response
->attrs
||
5184 !client
->response
->attrs
->next
||
5185 !client
->response
->attrs
->next
->next
);
5190 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5192 * When one or more job attributes are invalid, this function adds a suitable
5193 * response and attributes to the unsupported group.
5196 static int /* O - 1 if valid, 0 if not */
5197 valid_job_attributes(
5198 _ipp_client_t
*client
) /* I - Client */
5200 int i
; /* Looping var */
5201 ipp_attribute_t
*attr
, /* Current attribute */
5202 *supported
; /* xxx-supported attribute */
5206 * Check operation attributes...
5209 valid_doc_attributes(client
);
5212 * Check the various job template attributes...
5215 if ((attr
= ippFindAttribute(client
->request
, "copies",
5216 IPP_TAG_ZERO
)) != NULL
)
5218 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_INTEGER
||
5219 attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> 999)
5221 respond_unsupported(client
, attr
);
5225 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
5226 IPP_TAG_ZERO
)) != NULL
)
5228 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_BOOLEAN
)
5230 respond_unsupported(client
, attr
);
5234 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
5235 IPP_TAG_ZERO
)) != NULL
)
5237 if (attr
->num_values
!= 1 ||
5238 (attr
->value_tag
!= IPP_TAG_NAME
&&
5239 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
5240 attr
->value_tag
!= IPP_TAG_KEYWORD
) ||
5241 strcmp(attr
->values
[0].string
.text
, "no-hold"))
5243 respond_unsupported(client
, attr
);
5247 if ((attr
= ippFindAttribute(client
->request
, "job-name",
5248 IPP_TAG_ZERO
)) != NULL
)
5250 if (attr
->num_values
!= 1 ||
5251 (attr
->value_tag
!= IPP_TAG_NAME
&&
5252 attr
->value_tag
!= IPP_TAG_NAMELANG
))
5254 respond_unsupported(client
, attr
);
5258 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5259 IPP_TAG_ZERO
)) != NULL
)
5261 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_INTEGER
||
5262 attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> 100)
5264 respond_unsupported(client
, attr
);
5268 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5269 IPP_TAG_ZERO
)) != NULL
)
5271 if (attr
->num_values
!= 1 ||
5272 (attr
->value_tag
!= IPP_TAG_NAME
&&
5273 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
5274 attr
->value_tag
!= IPP_TAG_KEYWORD
) ||
5275 strcmp(attr
->values
[0].string
.text
, "none"))
5277 respond_unsupported(client
, attr
);
5281 if ((attr
= ippFindAttribute(client
->request
, "media",
5282 IPP_TAG_ZERO
)) != NULL
)
5284 if (attr
->num_values
!= 1 ||
5285 (attr
->value_tag
!= IPP_TAG_NAME
&&
5286 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
5287 attr
->value_tag
!= IPP_TAG_KEYWORD
))
5289 respond_unsupported(client
, attr
);
5294 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5296 if (!strcmp(attr
->values
[0].string
.text
, media_supported
[i
]))
5299 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5301 respond_unsupported(client
, attr
);
5306 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5307 IPP_TAG_ZERO
)) != NULL
)
5309 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_BEGIN_COLLECTION
)
5311 respond_unsupported(client
, attr
);
5313 /* TODO: check for valid media-col */
5316 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5317 IPP_TAG_ZERO
)) != NULL
)
5319 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
||
5320 (strcmp(attr
->values
[0].string
.text
,
5321 "separate-documents-uncollated-copies") &&
5322 strcmp(attr
->values
[0].string
.text
,
5323 "separate-documents-collated-copies")))
5325 respond_unsupported(client
, attr
);
5329 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5330 IPP_TAG_ZERO
)) != NULL
)
5332 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_ENUM
||
5333 attr
->values
[0].integer
< IPP_PORTRAIT
||
5334 attr
->values
[0].integer
> IPP_REVERSE_PORTRAIT
)
5336 respond_unsupported(client
, attr
);
5340 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5341 IPP_TAG_ZERO
)) != NULL
)
5343 respond_unsupported(client
, attr
);
5346 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
5347 IPP_TAG_ZERO
)) != NULL
)
5349 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_ENUM
||
5350 attr
->values
[0].integer
< IPP_QUALITY_DRAFT
||
5351 attr
->values
[0].integer
> IPP_QUALITY_HIGH
)
5353 respond_unsupported(client
, attr
);
5357 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
5358 IPP_TAG_ZERO
)) != NULL
)
5360 respond_unsupported(client
, attr
);
5363 if ((attr
= ippFindAttribute(client
->request
, "sides",
5364 IPP_TAG_ZERO
)) != NULL
)
5366 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
)
5368 respond_unsupported(client
, attr
);
5371 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides",
5372 IPP_TAG_KEYWORD
)) != NULL
)
5374 for (i
= 0; i
< supported
->num_values
; i
++)
5375 if (!strcmp(attr
->values
[0].string
.text
,
5376 supported
->values
[i
].string
.text
))
5379 if (i
>= supported
->num_values
)
5381 respond_unsupported(client
, attr
);
5386 respond_unsupported(client
, attr
);
5390 return (!client
->response
->attrs
||
5391 !client
->response
->attrs
->next
||
5392 !client
->response
->attrs
->next
->next
);