]> git.ipfire.org Git - thirdparty/cups.git/blob - test/ippinfra.c
Initial Get-Notifications implementation (no notify-wait support).
[thirdparty/cups.git] / test / ippinfra.c
1 /*
2 * "$Id$"
3 *
4 * Sample IPP INFRA server for CUPS.
5 *
6 * Copyright 2010-2014 by Apple Inc.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 */
16
17 /*
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
20 */
21
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
24
25
26 /*
27 * Include necessary headers...
28 */
29
30 #include <config.h> /* CUPS configuration header */
31 #include <cups/cups.h> /* Public API */
32 #include <cups/string-private.h> /* CUPS string functions */
33 #include <cups/thread-private.h> /* For multithreading functions */
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <sys/stat.h>
41
42 #ifdef WIN32
43 # include <fcntl.h>
44 # include <io.h>
45 # include <process.h>
46 # define WEXITSTATUS(s) (s)
47 # include <winsock2.h>
48 typedef ULONG nfds_t;
49 # define poll WSAPoll
50 #else
51 extern char **environ;
52
53 # include <sys/fcntl.h>
54 # include <sys/wait.h>
55 # include <poll.h>
56 #endif /* WIN32 */
57
58 #ifdef HAVE_SYS_MOUNT_H
59 # include <sys/mount.h>
60 #endif /* HAVE_SYS_MOUNT_H */
61 #ifdef HAVE_SYS_STATFS_H
62 # include <sys/statfs.h>
63 #endif /* HAVE_SYS_STATFS_H */
64 #ifdef HAVE_SYS_STATVFS_H
65 # include <sys/statvfs.h>
66 #endif /* HAVE_SYS_STATVFS_H */
67 #ifdef HAVE_SYS_VFS_H
68 # include <sys/vfs.h>
69 #endif /* HAVE_SYS_VFS_H */
70
71 #ifdef HAVE_PTHREAD_H
72 typedef pthread_cond_t _cups_cond_t;
73 # define _CUPS_COND_INITIALIZER PTHREAD_COND_INITIALIZER
74 # define _cupsCondBroadcast(c) pthread_cond_broadcast(c)
75 # define _cupsCondDeinit(c) pthread_cond_destroy(c)
76 # define _cupsCondInit(c) pthread_cond_init((c), NULL)
77 # define _cupsCondWait(c,m) pthread_cond_wait((c),(m))
78 # define _cupsMutexDeinit(m) pthread_mutex_destroy(m)
79 # define _cupsRWDeinit(rw) pthread_rwlock_destroy(rw)
80 #else
81 typedef char _cups_cond_t;
82 # define _CUPS_COND_INITIALIZER 0
83 # define _cupsCondBroadcast(c)
84 # define _cupsCondDeinit(c)
85 # define _cupsCondInit(c) *(c)=0
86 # define _cupsCondWait(c,m) 0
87 # define _cupsMutexDeinit(m)
88 # define _cupsRWDeinit(rw)
89 #endif /* HAVE_PTHREAD_H */
90
91
92 /*
93 * Constants...
94 */
95
96 /* New IPP operation codes from IPP INFRA */
97 # define _IPP_OP_ACKNOWLEDGE_DOCUMENT (ipp_op_t)0x003f
98 # define _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER (ipp_op_t)0x0040
99 # define _IPP_OP_ACKNOWLEDGE_JOB (ipp_op_t)0x0041
100 # define _IPP_OP_FETCH_DOCUMENT (ipp_op_t)0x0042
101 # define _IPP_OP_FETCH_JOB (ipp_op_t)0x0043
102 # define _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES (ipp_op_t)0x0044
103 # define _IPP_OP_UPDATE_ACTIVE_JOBS (ipp_op_t)0x0045
104 # define _IPP_OP_UPDATE_DOCUMENT_STATUS (ipp_op_t)0x0047
105 # define _IPP_OP_UPDATE_JOB_STATUS (ipp_op_t)0x0048
106 # define _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES (ipp_op_t)0x0049
107 # define _IPP_OP_DEREGISTER_OUTPUT_DEVICE (ipp_op_t)0x204b
108
109 /* New IPP status code from IPP INFRA */
110 # define _IPP_STATUS_ERROR_NOT_FETCHABLE (ipp_status_t)0x0420
111
112 /* Maximum lease duration value from RFC 3995 - 2^26-1 or ~2 years */
113 # define _IPP_NOTIFY_LEASE_DURATION_MAX 67108863
114 /* But a value of 0 means "never expires"... */
115 # define _IPP_NOTIFY_LEASE_DURATION_FOREVER 0
116 /* Default duration is 1 day */
117 # define _IPP_NOTIFY_LEASE_DURATION_DEFAULT 86400
118
119
120 /*
121 * Event mask enumeration...
122 */
123
124 enum _ipp_event_e /* notify-events bit values */
125 {
126 _IPP_EVENT_DOCUMENT_COMPLETED = 0x00000001,
127 _IPP_EVENT_DOCUMENT_CONFIG_CHANGED = 0x00000002,
128 _IPP_EVENT_DOCUMENT_CREATED = 0x00000004,
129 _IPP_EVENT_DOCUMENT_FETCHABLE = 0x00000008,
130 _IPP_EVENT_DOCUMENT_STATE_CHANGED = 0x00000010,
131 _IPP_EVENT_DOCUMENT_STOPPED = 0x00000020,
132 _IPP_EVENT_JOB_COMPLETED = 0x00000040,
133 _IPP_EVENT_JOB_CONFIG_CHANGED = 0x00000080,
134 _IPP_EVENT_JOB_CREATED = 0x00000100,
135 _IPP_EVENT_JOB_FETCHABLE = 0x00000200,
136 _IPP_EVENT_JOB_PROGRESS = 0x00000400,
137 _IPP_EVENT_JOB_STATE_CHANGED = 0x00000800,
138 _IPP_EVENT_JOB_STOPPED = 0x00001000,
139 _IPP_EVENT_PRINTER_CONFIG_CHANGED = 0x00002000,
140 _IPP_EVENT_PRINTER_FINISHINGS_CHANGED = 0x00004000,
141 _IPP_EVENT_PRINTER_MEDIA_CHANGED = 0x00008000,
142 _IPP_EVENT_PRINTER_QUEUE_ORDER_CHANGED = 0x00010000,
143 _IPP_EVENT_PRINTER_RESTARTED = 0x00020000,
144 _IPP_EVENT_PRINTER_SHUTDOWN = 0x00040000,
145 _IPP_EVENT_PRINTER_STATE_CHANGED = 0x00080000,
146 _IPP_EVENT_PRINTER_STOPPED = 0x00100000,
147
148 /* "Wildcard" values... */
149 _IPP_EVENT_NONE = 0x00000000, /* Nothing */
150 _IPP_EVENT_DOCUMENT_ALL = 0x0000003f,
151 _IPP_EVENT_DOCUMENT_STATE_ALL = 0x00000037,
152 _IPP_EVENT_JOB_ALL = 0x00001fc0,
153 _IPP_EVENT_JOB_STATE_ALL = 0x00001940,
154 _IPP_EVENT_PRINTER_ALL = 0x001fe000,
155 _IPP_EVENT_PRINTER_CONFIG_ALL = 0x0000e000,
156 _IPP_EVENT_PRINTER_STATE_ALL = 0x001e0000,
157 _IPP_EVENT_ALL = 0x001fffff /* Everything */
158 };
159 typedef unsigned int _ipp_event_t; /* Bitfield for notify-events */
160 #define _IPP_EVENT_DEFAULT _IPP_EVENT_JOB_COMPLETED
161 #define _IPP_EVENT_DEFAULT_STRING "job-completed"
162 static const char * const _ipp_events[] =
163 { /* Strings for bits */
164 "document-completed",
165 "document-config-changed",
166 "document-created",
167 "document-fetchable",
168 "document-state-changed",
169 "document-stopped",
170 "job-completed",
171 "job-config-changed",
172 "job-created",
173 "job-fetchable",
174 "job-progress",
175 "job-state-changed",
176 "job-stopped",
177 "printer-config-changed",
178 "printer-finishings-changed",
179 "printer-media-changed",
180 "printer-queue-order-changed",
181 "printer-restarted",
182 "printer-shutdown",
183 "printer-state-changed",
184 "printer-stopped"
185 };
186
187 enum _ipp_jreason_e /* job-state-reasons bit values */
188 {
189 _IPP_JREASON_NONE = 0x00000000, /* none */
190 _IPP_JREASON_ABORTED_BY_SYSTEM = 0x00000001,
191 _IPP_JREASON_COMPRESSION_ERROR = 0x00000002,
192 _IPP_JREASON_DOCUMENT_ACCESS_ERROR = 0x00000004,
193 _IPP_JREASON_DOCUMENT_FORMAT_ERROR = 0x00000008,
194 _IPP_JREASON_DOCUMENT_PASSWORD_ERROR = 0x00000010,
195 _IPP_JREASON_DOCUMENT_PERMISSION_ERROR = 0x00000020,
196 _IPP_JREASON_DOCUMENT_SECURITY_ERROR = 0x00000040,
197 _IPP_JREASON_DOCUMENT_UNPRINTABLE_ERROR = 0x00000080,
198 _IPP_JREASON_ERRORS_DETECTED = 0x00000100,
199 _IPP_JREASON_JOB_CANCELED_AT_DEVICE = 0x00000200,
200 _IPP_JREASON_JOB_CANCELED_BY_USER = 0x00000400,
201 _IPP_JREASON_JOB_COMPLETED_SUCCESSFULLY = 0x00000800,
202 _IPP_JREASON_JOB_COMPLETED_WITH_ERRORS = 0x00001000,
203 _IPP_JREASON_JOB_COMPLETED_WITH_WARNINGS = 0x00002000,
204 _IPP_JREASON_JOB_DATA_INSUFFICIENT = 0x00004000,
205 _IPP_JREASON_JOB_FETCHABLE = 0x00008000,
206 _IPP_JREASON_JOB_INCOMING = 0x00010000,
207 _IPP_JREASON_JOB_PASSWORD_WAIT = 0x00020000,
208 _IPP_JREASON_JOB_PRINTING = 0x00040000,
209 _IPP_JREASON_JOB_QUEUED = 0x00080000,
210 _IPP_JREASON_JOB_SPOOLING = 0x00100000,
211 _IPP_JREASON_JOB_STOPPED = 0x00200000,
212 _IPP_JREASON_JOB_TRANSFORMING = 0x00400000,
213 _IPP_JREASON_PRINTER_STOPPED = 0x00800000,
214 _IPP_JREASON_PRINTER_STOPPED_PARTLY = 0x01000000,
215 _IPP_JREASON_PROCESSING_TO_STOP_POINT = 0x02000000,
216 _IPP_JREASON_QUEUED_IN_DEVICE = 0x04000000,
217 _IPP_JREASON_WARNINGS_DETECTED = 0x08000000
218 };
219 typedef unsigned int _ipp_jreason_t; /* Bitfield for job-state-reasons */
220 static const char * const _ipp_jreasons[] =
221 { /* Strings for bits */
222 "aborted-by-system",
223 "compression-error",
224 "document-access-error",
225 "document-format-error",
226 "document-password-error",
227 "document-permission-error",
228 "document-security-error",
229 "document-unprintable-error",
230 "errors-detected",
231 "job-canceled-at-device",
232 "job-canceled-by-user",
233 "job-completed-successfully",
234 "job-completed-with-errors",
235 "job-completed-with-warnings",
236 "job-data-insufficient",
237 "job-fetchable",
238 "job-incoming",
239 "job-password-wait",
240 "job-printing",
241 "job-queued",
242 "job-spooling",
243 "job-stopped",
244 "job-transforming",
245 "printer-stopped",
246 "printer-stopped-partly",
247 "processing-to-stop-point",
248 "queued-in-device",
249 "warnings-detected"
250 };
251
252 enum _ipp_preason_e /* printer-state-reasons bit values */
253 {
254 _IPP_PREASON_NONE = 0x0000, /* none */
255 _IPP_PREASON_OTHER = 0x0001, /* other */
256 _IPP_PREASON_COVER_OPEN = 0x0002, /* cover-open */
257 _IPP_PREASON_INPUT_TRAY_MISSING = 0x0004,
258 /* input-tray-missing */
259 _IPP_PREASON_MARKER_SUPPLY_EMPTY = 0x0008,
260 /* marker-supply-empty */
261 _IPP_PREASON_MARKER_SUPPLY_LOW = 0x0010,
262 /* marker-supply-low */
263 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020,
264 /* marker-waste-almost-full */
265 _IPP_PREASON_MARKER_WASTE_FULL = 0x0040,
266 /* marker-waste-full */
267 _IPP_PREASON_MEDIA_EMPTY = 0x0080, /* media-empty */
268 _IPP_PREASON_MEDIA_JAM = 0x0100, /* media-jam */
269 _IPP_PREASON_MEDIA_LOW = 0x0200, /* media-low */
270 _IPP_PREASON_MEDIA_NEEDED = 0x0400, /* media-needed */
271 _IPP_PREASON_MOVING_TO_PAUSED = 0x0800,
272 /* moving-to-paused */
273 _IPP_PREASON_PAUSED = 0x1000, /* paused */
274 _IPP_PREASON_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
275 _IPP_PREASON_TONER_EMPTY = 0x4000, /* toner-empty */
276 _IPP_PREASON_TONER_LOW = 0x8000 /* toner-low */
277 };
278 typedef unsigned int _ipp_preason_t; /* Bitfield for printer-state-reasons */
279 static const char * const _ipp_preasons[] =
280 { /* Strings for bits */
281 "other",
282 "cover-open",
283 "input-tray-missing",
284 "marker-supply-empty",
285 "marker-supply-low",
286 "marker-waste-almost-full",
287 "marker-waste-full",
288 "media-empty",
289 "media-jam",
290 "media-low",
291 "media-needed",
292 "moving-to-paused",
293 "paused",
294 "spool-area-full",
295 "toner-empty",
296 "toner-low"
297 };
298
299
300 /*
301 * Structures...
302 */
303
304 typedef struct _ipp_filter_s /**** Attribute filter ****/
305 {
306 cups_array_t *ra; /* Requested attributes */
307 ipp_tag_t group_tag; /* Group to copy */
308 } _ipp_filter_t;
309
310 typedef struct _ipp_job_s _ipp_job_t;
311
312 typedef struct _ipp_device_s /**** Output Device data ****/
313 {
314 _cups_rwlock_t rwlock; /* Printer lock */
315 char *name, /* printer-name (mapped to output-device) */
316 *uuid; /* output-device-uuid */
317 ipp_t *attrs; /* All printer attributes */
318 ipp_pstate_t state; /* printer-state value */
319 _ipp_preason_t reasons; /* printer-state-reasons values */
320 } _ipp_device_t;
321
322 typedef struct _ipp_printer_s /**** Printer data ****/
323 {
324 _cups_rwlock_t rwlock; /* Printer lock */
325 int ipv4, /* IPv4 listener */
326 ipv6; /* IPv6 listener */
327 char *name, /* printer-name */
328 *directory, /* Spool directory */
329 *hostname, /* Hostname */
330 *uri, /* printer-uri-supported */
331 *proxy_user, /* Proxy username */
332 *proxy_pass; /* Proxy password */
333 int port; /* Port */
334 size_t urilen; /* Length of printer URI */
335 cups_array_t *devices; /* Associated devices */
336 ipp_t *attrs; /* Static attributes */
337 ipp_t *dev_attrs; /* Current device attributes */
338 time_t start_time; /* Startup time */
339 time_t config_time; /* printer-config-change-time */
340 ipp_pstate_t state, /* printer-state value */
341 dev_state; /* Current device printer-state value */
342 _ipp_preason_t state_reasons, /* printer-state-reasons values */
343 dev_reasons; /* Current device printer-state-reasons values */
344 time_t state_time; /* printer-state-change-time */
345 cups_array_t *jobs, /* Jobs */
346 *active_jobs, /* Active jobs */
347 *completed_jobs;/* Completed jobs */
348 _ipp_job_t *processing_job;/* Current processing job */
349 int next_job_id; /* Next job-id value */
350 cups_array_t *subscriptions; /* Subscriptions */
351 int next_sub_id; /* Next notify-subscription-id value */
352 } _ipp_printer_t;
353
354 struct _ipp_job_s /**** Job data ****/
355 {
356 int id; /* job-id */
357 _cups_rwlock_t rwlock; /* Job lock */
358 const char *name, /* job-name */
359 *username, /* job-originating-user-name */
360 *format; /* document-format */
361 int priority; /* job-priority */
362 char *dev_uuid; /* output-device-uuid-assigned */
363 ipp_jstate_t state, /* job-state value */
364 dev_state; /* output-device-job-state value */
365 _ipp_jreason_t state_reasons, /* job-state-reasons values */
366 dev_state_reasons;
367 /* output-device-job-state-reasons values */
368 char *dev_state_message;
369 /* output-device-job-state-message value */
370 time_t created, /* time-at-creation value */
371 processing, /* time-at-processing value */
372 completed; /* time-at-completed value */
373 int impressions, /* job-impressions value */
374 impcompleted; /* job-impressions-completed value */
375 ipp_t *attrs; /* Attributes */
376 int cancel; /* Non-zero when job canceled */
377 char *filename; /* Print file name */
378 int fd; /* Print file descriptor */
379 _ipp_printer_t *printer; /* Printer */
380 };
381
382 typedef struct _ipp_subscription_s /**** Subscription data ****/
383 {
384 int id; /* notify-subscription-id */
385 const char *uuid; /* notify-subscription-uuid */
386 _cups_rwlock_t rwlock; /* Subscription lock */
387 _ipp_event_t mask; /* Event mask */
388 _ipp_printer_t *printer; /* Printer */
389 _ipp_job_t *job; /* Job, if any */
390 ipp_t *attrs; /* Attributes */
391 const char *username; /* notify-subscriber-user-name */
392 int lease; /* notify-lease-duration */
393 int interval; /* notify-time-interval */
394 time_t expire; /* Lease expiration time */
395 int first_sequence, /* First notify-sequence-number in cache */
396 last_sequence; /* Last notify-sequence-number used */
397 cups_array_t *events; /* Events (ipp_t *'s) */
398 int pending_delete; /* Non-zero when the subscription is about to be deleted/canceled */
399 } _ipp_subscription_t;
400
401 typedef struct _ipp_client_s /**** Client data ****/
402 {
403 http_t *http; /* HTTP connection */
404 ipp_t *request, /* IPP request */
405 *response; /* IPP response */
406 time_t start; /* Request start time */
407 http_state_t operation; /* Request operation */
408 ipp_op_t operation_id; /* IPP operation-id */
409 char uri[1024], /* Request URI */
410 *options; /* URI options */
411 http_addr_t addr; /* Client address */
412 char hostname[256], /* Client hostname */
413 username[32]; /* Client authenticated username */
414 _ipp_printer_t *printer; /* Printer */
415 _ipp_job_t *job; /* Current job, if any */
416 int fetch_compression,
417 /* Compress file? */
418 fetch_file; /* File to fetch */
419 } _ipp_client_t;
420
421
422 /*
423 * Local functions...
424 */
425
426 static void add_event(_ipp_printer_t *printer, _ipp_job_t *job, _ipp_event_t event, const char *message, ...) __attribute__((__format__(__printf__, 4, 5)));
427 static void check_jobs(_ipp_printer_t *printer);
428 static void clean_jobs(_ipp_printer_t *printer);
429 static int compare_active_jobs(_ipp_job_t *a, _ipp_job_t *b);
430 static int compare_completed_jobs(_ipp_job_t *a, _ipp_job_t *b);
431 static int compare_devices(_ipp_device_t *a, _ipp_device_t *b);
432 static int compare_jobs(_ipp_job_t *a, _ipp_job_t *b);
433 static void copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra,
434 ipp_tag_t group_tag, int quickcopy);
435 static void copy_job_attributes(_ipp_client_t *client,
436 _ipp_job_t *job, cups_array_t *ra);
437 static void copy_job_state_reasons(ipp_t *ipp, ipp_tag_t group_tag, _ipp_job_t *job);
438 static void copy_printer_state_reasons(ipp_t *ipp, ipp_tag_t group_tag, _ipp_printer_t *printer);
439 static void copy_subscription_attributes(_ipp_client_t *client, _ipp_subscription_t *sub, cups_array_t *ra);
440 static _ipp_client_t *create_client(_ipp_printer_t *printer, int sock);
441 static _ipp_device_t *create_device(_ipp_client_t *client);
442 static _ipp_job_t *create_job(_ipp_client_t *client);
443 static void create_job_filename(_ipp_printer_t *printer, _ipp_job_t *job, const char *format, char *fname, size_t fnamesize);
444 static int create_listener(int family, int port);
445 static _ipp_subscription_t *create_subscription(_ipp_printer_t *printer, _ipp_job_t *job, int interval, int lease, const char *username, ipp_attribute_t *notify_events, ipp_attribute_t *notify_attributes, ipp_attribute_t *notify_user_data);
446 static _ipp_printer_t *create_printer(const char *servername, int port, const char *name, const char *directory, const char *proxy_user, const char *proxy_pass);
447 static void debug_attributes(const char *title, ipp_t *ipp,
448 int response);
449 static void delete_client(_ipp_client_t *client);
450 static void delete_device(_ipp_device_t *device);
451 static void delete_job(_ipp_job_t *job);
452 static void delete_printer(_ipp_printer_t *printer);
453 static void delete_subscription(_ipp_subscription_t *sub);
454 static int filter_cb(_ipp_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr);
455 static _ipp_device_t *find_device(_ipp_client_t *client);
456 static _ipp_job_t *find_job(_ipp_client_t *client, int job_id);
457 static _ipp_subscription_t *find_subscription(_ipp_client_t *client, int sub_id);
458 static _ipp_jreason_t get_job_state_reasons_bits(ipp_attribute_t *attr);
459 static _ipp_event_t get_notify_events_bits(ipp_attribute_t *attr);
460 static const char *get_notify_subscribed_event(_ipp_event_t event);
461 static _ipp_preason_t get_printer_state_reasons_bits(ipp_attribute_t *attr);
462 static void html_escape(_ipp_client_t *client, const char *s,
463 size_t slen);
464 static void html_footer(_ipp_client_t *client);
465 static void html_header(_ipp_client_t *client, const char *title);
466 static void html_printf(_ipp_client_t *client, const char *format, ...) __attribute__((__format__(__printf__, 2, 3)));
467 static void ipp_acknowledge_document(_ipp_client_t *client);
468 static void ipp_acknowledge_identify_printer(_ipp_client_t *client);
469 static void ipp_acknowledge_job(_ipp_client_t *client);
470 static void ipp_cancel_job(_ipp_client_t *client);
471 static void ipp_cancel_my_jobs(_ipp_client_t *client);
472 static void ipp_cancel_subscription(_ipp_client_t *client);
473 static void ipp_close_job(_ipp_client_t *client);
474 static void ipp_create_job(_ipp_client_t *client);
475 static void ipp_create_xxx_subscriptions(_ipp_client_t *client);
476 static void ipp_deregister_output_device(_ipp_client_t *client);
477 static void ipp_fetch_document(_ipp_client_t *client);
478 static void ipp_fetch_job(_ipp_client_t *client);
479 static void ipp_get_document_attributes(_ipp_client_t *client);
480 static void ipp_get_documents(_ipp_client_t *client);
481 static void ipp_get_job_attributes(_ipp_client_t *client);
482 static void ipp_get_jobs(_ipp_client_t *client);
483 static void ipp_get_notifications(_ipp_client_t *client);
484 static void ipp_get_output_device_attributes(_ipp_client_t *client);
485 static void ipp_get_printer_attributes(_ipp_client_t *client);
486 static void ipp_get_printer_supported_values(_ipp_client_t *client);
487 static void ipp_get_subscription_attributes(_ipp_client_t *client);
488 static void ipp_get_subscriptions(_ipp_client_t *client);
489 static void ipp_identify_printer(_ipp_client_t *client);
490 static void ipp_print_job(_ipp_client_t *client);
491 static void ipp_print_uri(_ipp_client_t *client);
492 static void ipp_renew_subscription(_ipp_client_t *client);
493 static void ipp_send_document(_ipp_client_t *client);
494 static void ipp_send_uri(_ipp_client_t *client);
495 static void ipp_update_active_jobs(_ipp_client_t *client);
496 static void ipp_update_document_status(_ipp_client_t *client);
497 static void ipp_update_job_status(_ipp_client_t *client);
498 static void ipp_update_output_device_attributes(_ipp_client_t *client);
499 static void ipp_validate_document(_ipp_client_t *client);
500 static void ipp_validate_job(_ipp_client_t *client);
501 //static int parse_options(_ipp_client_t *client, cups_option_t **options);
502 static void *process_client(_ipp_client_t *client);
503 static int process_http(_ipp_client_t *client);
504 static int process_ipp(_ipp_client_t *client);
505 static void *process_job(_ipp_job_t *job);
506 static int respond_http(_ipp_client_t *client, http_status_t code,
507 const char *content_coding,
508 const char *type, size_t length);
509 static void respond_ipp(_ipp_client_t *client, ipp_status_t status,
510 const char *message, ...)
511 __attribute__ ((__format__ (__printf__, 3, 4)));
512 static void respond_unsupported(_ipp_client_t *client,
513 ipp_attribute_t *attr);
514 static void run_printer(_ipp_printer_t *printer);
515 static char *time_string(time_t tv, char *buffer, size_t bufsize);
516 static void update_device_attributes_no_lock(_ipp_printer_t *printer);
517 static void update_device_state_no_lock(_ipp_printer_t *printer);
518 static void usage(int status) __attribute__((noreturn));
519 static int valid_doc_attributes(_ipp_client_t *client);
520 static int valid_job_attributes(_ipp_client_t *client);
521
522
523 /*
524 * Globals...
525 */
526
527 static int KeepFiles = 0,
528 Verbosity = 0;
529 //static _cups_mutex_t SubscriptionMutex = _CUPS_MUTEX_INITIALIZER;
530 static _cups_cond_t SubscriptionCondition = _CUPS_COND_INITIALIZER;
531
532
533 /*
534 * 'main()' - Main entry to the sample infrastructure server.
535 */
536
537 int /* O - Exit status */
538 main(int argc, /* I - Number of command-line args */
539 char *argv[]) /* I - Command-line arguments */
540 {
541 int i; /* Looping var */
542 const char *opt, /* Current option character */
543 *servername = NULL, /* Server host name */
544 *name = NULL; /* Printer name */
545 #ifdef HAVE_SSL
546 const char *keypath = NULL; /* Keychain path */
547 #endif /* HAVE_SSL */
548 int port = 0; /* Port number (0 = auto) */
549 char directory[1024] = "", /* Spool directory */
550 hostname[1024], /* Auto-detected hostname */
551 proxy_user[256] = "", /* Proxy username */
552 *proxy_pass = NULL; /* Proxy password */
553 _ipp_printer_t *printer; /* Printer object */
554
555
556 /*
557 * Parse command-line arguments...
558 */
559
560 for (i = 1; i < argc; i ++)
561 if (argv[i][0] == '-')
562 {
563 for (opt = argv[i] + 1; *opt; opt ++)
564 {
565 switch (*opt)
566 {
567 #ifdef HAVE_SSL
568 case 'K' : /* -K keypath */
569 i ++;
570 if (i >= argc)
571 usage(1);
572 keypath = argv[i];
573 break;
574 #endif /* HAVE_SSL */
575
576 case 'd' : /* -d spool-directory */
577 i ++;
578 if (i >= argc)
579 usage(1);
580 strlcpy(directory, argv[i], sizeof(directory));
581 break;
582
583 case 'h' : /* -h (show help) */
584 usage(0);
585
586 case 'k' : /* -k (keep files) */
587 KeepFiles = 1;
588 break;
589
590 case 'n' : /* -n hostname */
591 i ++;
592 if (i >= argc)
593 usage(1);
594 servername = argv[i];
595 break;
596
597 case 'p' : /* -p port */
598 i ++;
599 if (i >= argc || !isdigit(argv[i][0] & 255))
600 usage(1);
601 port = atoi(argv[i]);
602 break;
603
604 case 'u' : /* -u user:pass */
605 i ++;
606 if (i >= argc)
607 usage(1);
608 strlcpy(proxy_user, argv[i], sizeof(proxy_user));
609 if ((proxy_pass = strchr(proxy_user, ':')) != NULL)
610 *proxy_pass++ = '\0';
611 break;
612
613 case 'v' : /* -v (be verbose) */
614 Verbosity ++;
615 break;
616
617 default : /* Unknown */
618 fprintf(stderr, "Unknown option \"-%c\".\n", *opt);
619 usage(1);
620 }
621 }
622 }
623 else if (!name)
624 {
625 name = argv[i];
626 }
627 else
628 {
629 fprintf(stderr, "Unexpected command-line argument \"%s\"\n", argv[i]);
630 usage(1);
631 }
632
633 if (!name)
634 usage(1);
635
636 /*
637 * Apply defaults as needed...
638 */
639
640 if (!servername)
641 servername = httpGetHostname(NULL, hostname, sizeof(hostname));
642
643 if (!port)
644 {
645 #ifdef WIN32
646 /*
647 * Windows is almost always used as a single user system, so use a default port
648 * number of 8631.
649 */
650
651 port = 8631;
652
653 #else
654 /*
655 * Use 8000 + UID mod 1000 for the default port number...
656 */
657
658 port = 8000 + ((int)getuid() % 1000);
659 #endif /* WIN32 */
660
661 fprintf(stderr, "Listening on port %d.\n", port);
662 }
663
664 if (!directory[0])
665 {
666 snprintf(directory, sizeof(directory), "/tmp/ippserver.%d", (int)getpid());
667
668 if (mkdir(directory, 0777) && errno != EEXIST)
669 {
670 fprintf(stderr, "Unable to create spool directory \"%s\": %s\n",
671 directory, strerror(errno));
672 usage(1);
673 }
674
675 if (Verbosity)
676 fprintf(stderr, "Using spool directory \"%s\".\n", directory);
677 }
678
679 if (!proxy_user[0])
680 {
681 strlcpy(proxy_user, "test", sizeof(proxy_user));
682
683 if (Verbosity)
684 fputs("Using proxy username \"test\".\n", stderr);
685 }
686
687 if (!proxy_pass)
688 {
689 proxy_pass = "test123";
690
691 if (Verbosity)
692 fputs("Using proxy password \"test123\".\n", stderr);
693 }
694
695 #ifdef HAVE_SSL
696 cupsSetServerCredentials(keypath, servername, 1);
697 #endif /* HAVE_SSL */
698
699 /*
700 * Create the printer...
701 */
702
703 if ((printer = create_printer(servername, port, name, directory, proxy_user, proxy_pass)) == NULL)
704 return (1);
705
706 /*
707 * Run the print service...
708 */
709
710 run_printer(printer);
711
712 /*
713 * Destroy the printer and exit...
714 */
715
716 delete_printer(printer);
717
718 return (0);
719 }
720
721
722 /*
723 * 'add_event()' - Add an event to a subscription.
724 */
725
726 static void
727 add_event(_ipp_printer_t *printer, /* I - Printer */
728 _ipp_job_t *job, /* I - Job, if any */
729 _ipp_event_t event, /* I - Event */
730 const char *message, /* I - Printf-style notify-text message */
731 ...) /* I - Additional printf arguments */
732 {
733 _ipp_subscription_t *sub; /* Current subscription */
734 ipp_t *n; /* Notify attributes */
735 char text[1024]; /* notify-text value */
736 va_list ap; /* Argument pointer */
737
738
739 va_start(ap, message);
740 vsnprintf(text, sizeof(text), message, ap);
741 va_end(ap);
742
743 for (sub = (_ipp_subscription_t *)cupsArrayFirst(printer->subscriptions);
744 sub;
745 sub = (_ipp_subscription_t *)cupsArrayNext(printer->subscriptions))
746 {
747 if (sub->mask & event && (!sub->job || job == sub->job))
748 {
749 _cupsRWLockWrite(&sub->rwlock);
750
751 n = ippNew();
752 ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET, "notify-charset", NULL, "utf-8");
753 ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE, "notify-natural-language", NULL, "en");
754 ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-printer-up-time", time(NULL) - printer->start_time);
755 ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI, "notify-printer-uri", NULL, printer->uri);
756 if (job)
757 ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-job-id", job->id);
758 ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-subcription-id", sub->id);
759 ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI, "notify-subscription-uuid", NULL, sub->uuid);
760 ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-sequence-number", ++ sub->last_sequence);
761 ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "notify-subscribed-event", NULL, get_notify_subscribed_event(event));
762 ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT, "notify-text", NULL, text);
763 if (event & _IPP_EVENT_PRINTER_ALL)
764 {
765 ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM, "printer-state", printer->state);
766 copy_printer_state_reasons(n, IPP_TAG_EVENT_NOTIFICATION, printer);
767 }
768 if (event & _IPP_EVENT_JOB_ALL)
769 {
770 ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM, "job-state", job->state);
771 copy_job_state_reasons(n, IPP_TAG_EVENT_NOTIFICATION, job);
772 if (event == _IPP_EVENT_JOB_CREATED)
773 {
774 ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "job-name", NULL, job->name);
775 ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
776 }
777 }
778
779 cupsArrayAdd(sub->events, n);
780 if (cupsArrayCount(sub->events) > 100)
781 {
782 n = (ipp_t *)cupsArrayFirst(sub->events);
783 cupsArrayRemove(sub->events, n);
784 ippDelete(n);
785 sub->first_sequence ++;
786 }
787
788 _cupsRWUnlock(&sub->rwlock);
789 _cupsCondBroadcast(&SubscriptionCondition);
790 }
791 }
792 }
793
794
795 /*
796 * 'check_jobs()' - Check for new jobs to process.
797 */
798
799 static void
800 check_jobs(_ipp_printer_t *printer) /* I - Printer */
801 {
802 _ipp_job_t *job; /* Current job */
803
804
805 if (printer->processing_job)
806 return;
807
808 _cupsRWLockWrite(&(printer->rwlock));
809 for (job = (_ipp_job_t *)cupsArrayFirst(printer->active_jobs);
810 job;
811 job = (_ipp_job_t *)cupsArrayNext(printer->active_jobs))
812 {
813 if (job->state == IPP_JSTATE_PENDING)
814 {
815 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
816 {
817 job->state = IPP_JSTATE_ABORTED;
818 job->completed = time(NULL);
819
820 add_event(printer, job, _IPP_EVENT_JOB_COMPLETED, "Job aborted because creation of processing thread failed.");
821 }
822 break;
823 }
824 }
825 _cupsRWUnlock(&(printer->rwlock));
826 }
827
828
829 /*
830 * 'clean_jobs()' - Clean out old (completed) jobs.
831 */
832
833 static void
834 clean_jobs(_ipp_printer_t *printer) /* I - Printer */
835 {
836 _ipp_job_t *job; /* Current job */
837 time_t cleantime; /* Clean time */
838
839
840 if (cupsArrayCount(printer->jobs) == 0)
841 return;
842
843 cleantime = time(NULL) - 60;
844
845 _cupsRWLockWrite(&(printer->rwlock));
846 for (job = (_ipp_job_t *)cupsArrayFirst(printer->jobs);
847 job;
848 job = (_ipp_job_t *)cupsArrayNext(printer->jobs))
849 if (job->completed && job->completed < cleantime)
850 {
851 cupsArrayRemove(printer->jobs, job);
852 delete_job(job);
853 }
854 else
855 break;
856 _cupsRWUnlock(&(printer->rwlock));
857 }
858
859
860 /*
861 * 'compare_active_jobs()' - Compare two active jobs.
862 */
863
864 static int /* O - Result of comparison */
865 compare_active_jobs(_ipp_job_t *a, /* I - First job */
866 _ipp_job_t *b) /* I - Second job */
867 {
868 int diff; /* Difference */
869
870
871 if ((diff = b->priority - a->priority) == 0)
872 diff = b->id - a->id;
873
874 return (diff);
875 }
876
877
878 /*
879 * 'compare_completed_jobs()' - Compare two completed jobs.
880 */
881
882 static int /* O - Result of comparison */
883 compare_completed_jobs(_ipp_job_t *a, /* I - First job */
884 _ipp_job_t *b) /* I - Second job */
885 {
886 int diff; /* Difference */
887
888
889 if ((diff = a->completed - b->completed) == 0)
890 diff = b->id - a->id;
891
892 return (diff);
893 }
894
895
896 /*
897 * 'compare_devices()' - Compare two devices...
898 */
899
900 static int /* O - Result of comparison */
901 compare_devices(_ipp_device_t *a, /* I - First device */
902 _ipp_device_t *b) /* I - Second device */
903 {
904 return (strcmp(a->uuid, b->uuid));
905 }
906
907
908 /*
909 * 'compare_jobs()' - Compare two jobs.
910 */
911
912 static int /* O - Result of comparison */
913 compare_jobs(_ipp_job_t *a, /* I - First job */
914 _ipp_job_t *b) /* I - Second job */
915 {
916 return (b->id - a->id);
917 }
918
919
920 /*
921 * 'copy_attributes()' - Copy attributes from one request to another.
922 */
923
924 static void
925 copy_attributes(ipp_t *to, /* I - Destination request */
926 ipp_t *from, /* I - Source request */
927 cups_array_t *ra, /* I - Requested attributes */
928 ipp_tag_t group_tag, /* I - Group to copy */
929 int quickcopy) /* I - Do a quick copy? */
930 {
931 _ipp_filter_t filter; /* Filter data */
932
933
934 filter.ra = ra;
935 filter.group_tag = group_tag;
936
937 ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter);
938 }
939
940
941 /*
942 * 'copy_job_attrs()' - Copy job attributes to the response.
943 */
944
945 static void
946 copy_job_attributes(
947 _ipp_client_t *client, /* I - Client */
948 _ipp_job_t *job, /* I - Job */
949 cups_array_t *ra) /* I - requested-attributes */
950 {
951 copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
952
953 if (!ra || cupsArrayFind(ra, "date-time-at-completed"))
954 {
955 if (job->completed)
956 ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed));
957 else
958 ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed");
959 }
960
961 if (!ra || cupsArrayFind(ra, "date-time-at-processing"))
962 {
963 if (job->processing)
964 ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-processing", ippTimeToDate(job->processing));
965 else
966 ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing");
967 }
968
969 if (!ra || cupsArrayFind(ra, "job-impressions"))
970 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions", job->impressions);
971
972 if (!ra || cupsArrayFind(ra, "job-impressions-completed"))
973 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", job->impcompleted);
974
975 if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
976 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-printer-up-time", (int)(time(NULL) - client->printer->start_time));
977
978 if (!ra || cupsArrayFind(ra, "job-state"))
979 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM,
980 "job-state", job->state);
981
982 if (!ra || cupsArrayFind(ra, "job-state-message"))
983 {
984 if (job->dev_state_message)
985 {
986 ippAddString(client->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, job->dev_state_message);
987 }
988 else
989 {
990 const char *message = ""; /* Message string */
991
992 switch (job->state)
993 {
994 case IPP_JSTATE_PENDING :
995 message = "Job pending.";
996 break;
997
998 case IPP_JSTATE_HELD :
999 if (job->state_reasons & _IPP_JREASON_JOB_INCOMING)
1000 message = "Job incoming.";
1001 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
1002 message = "Job held.";
1003 else
1004 message = "Job created.";
1005 break;
1006
1007 case IPP_JSTATE_PROCESSING :
1008 if (job->state_reasons & _IPP_JREASON_PROCESSING_TO_STOP_POINT)
1009 {
1010 if (job->cancel)
1011 message = "Cancel in progress.";
1012 else
1013 message = "Abort in progress.";
1014 }
1015 else
1016 message = "Job printing.";
1017 break;
1018
1019 case IPP_JSTATE_STOPPED :
1020 message = "Job stopped.";
1021 break;
1022
1023 case IPP_JSTATE_CANCELED :
1024 message = "Job canceled.";
1025 break;
1026
1027 case IPP_JSTATE_ABORTED :
1028 message = "Job aborted.";
1029 break;
1030
1031 case IPP_JSTATE_COMPLETED :
1032 message = "Job completed.";
1033 break;
1034 }
1035
1036 ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, message);
1037 }
1038 }
1039
1040 if (!ra || cupsArrayFind(ra, "job-state-reasons"))
1041 copy_job_state_reasons(client->response, IPP_TAG_JOB, job);
1042 /*
1043 switch (job->state)
1044 {
1045 case IPP_JSTATE_PENDING :
1046 ippAddString(client->response, IPP_TAG_JOB,
1047 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1048 NULL, "none");
1049 break;
1050
1051 case IPP_JSTATE_HELD :
1052 if (job->fd >= 0)
1053 ippAddString(client->response, IPP_TAG_JOB,
1054 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1055 "job-state-reasons", NULL, "job-incoming");
1056 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
1057 ippAddString(client->response, IPP_TAG_JOB,
1058 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1059 "job-state-reasons", NULL, "job-hold-until-specified");
1060 else
1061 ippAddString(client->response, IPP_TAG_JOB,
1062 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1063 "job-state-reasons", NULL, "job-data-insufficient");
1064 break;
1065
1066 case IPP_JSTATE_PROCESSING :
1067 if (job->cancel)
1068 ippAddString(client->response, IPP_TAG_JOB,
1069 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1070 "job-state-reasons", NULL, "processing-to-stop-point");
1071 else
1072 ippAddString(client->response, IPP_TAG_JOB,
1073 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1074 "job-state-reasons", NULL, "job-printing");
1075 break;
1076
1077 case IPP_JSTATE_STOPPED :
1078 ippAddString(client->response, IPP_TAG_JOB,
1079 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1080 NULL, "job-stopped");
1081 break;
1082
1083 case IPP_JSTATE_CANCELED :
1084 ippAddString(client->response, IPP_TAG_JOB,
1085 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1086 NULL, "job-canceled-by-user");
1087 break;
1088
1089 case IPP_JSTATE_ABORTED :
1090 ippAddString(client->response, IPP_TAG_JOB,
1091 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1092 NULL, "aborted-by-system");
1093 break;
1094
1095 case IPP_JSTATE_COMPLETED :
1096 ippAddString(client->response, IPP_TAG_JOB,
1097 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1098 NULL, "job-completed-successfully");
1099 break;
1100 }
1101 */
1102
1103 if (!ra || cupsArrayFind(ra, "time-at-completed"))
1104 ippAddInteger(client->response, IPP_TAG_JOB,
1105 job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
1106 "time-at-completed", (int)(job->completed - client->printer->start_time));
1107
1108 if (!ra || cupsArrayFind(ra, "time-at-processing"))
1109 ippAddInteger(client->response, IPP_TAG_JOB,
1110 job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
1111 "time-at-processing", (int)(job->processing - client->printer->start_time));
1112 }
1113
1114
1115 /*
1116 * 'copy_job_state_reasons()' - Copy printer-state-reasons values.
1117 */
1118
1119 static void
1120 copy_job_state_reasons(
1121 ipp_t *ipp, /* I - Attributes */
1122 ipp_tag_t group_tag, /* I - Group */
1123 _ipp_job_t *job) /* I - Printer */
1124 {
1125 _ipp_jreason_t creasons; /* Combined job-state-reasons */
1126
1127
1128 creasons = job->state_reasons | job->dev_state_reasons;
1129
1130 if (!creasons)
1131 {
1132 ippAddString(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", NULL, "none");
1133 }
1134 else
1135 {
1136 int i, /* Looping var */
1137 num_reasons = 0;/* Number of reasons */
1138 _ipp_jreason_t reason; /* Current reason */
1139 const char *reasons[32]; /* Reason strings */
1140
1141 for (i = 0, reason = 1; i < (int)(sizeof(_ipp_jreasons) / sizeof(_ipp_jreasons[0])); i ++, reason <<= 1)
1142 {
1143 if (creasons & reason)
1144 reasons[num_reasons ++] = _ipp_jreasons[i];
1145 }
1146
1147 ippAddStrings(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", num_reasons, NULL, reasons);
1148 }
1149 }
1150
1151
1152 /*
1153 * 'copy_printer_state_reasons()' - Copy printer-state-reasons values.
1154 */
1155
1156 static void
1157 copy_printer_state_reasons(
1158 ipp_t *ipp, /* I - Attributes */
1159 ipp_tag_t group_tag, /* I - Group */
1160 _ipp_printer_t *printer) /* I - Printer */
1161 {
1162 _ipp_preason_t creasons = printer->state_reasons | printer->dev_reasons;
1163 /* Combined reasons */
1164
1165
1166 if (creasons == _IPP_PREASON_NONE)
1167 {
1168 ippAddString(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none");
1169 }
1170 else
1171 {
1172 int i, /* Looping var */ num_reasons = 0;/* Number of reasons */
1173 _ipp_preason_t reason; /* Current reason */
1174 const char *reasons[32]; /* Reason strings */
1175
1176 for (i = 0, reason = 1; i < (int)(sizeof(_ipp_preasons) / sizeof(_ipp_preasons[0])); i ++, reason <<= 1)
1177 {
1178 if (creasons & reason)
1179 reasons[num_reasons ++] = _ipp_preasons[i];
1180 }
1181
1182 ippAddStrings(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", num_reasons, NULL, reasons);
1183 }
1184 }
1185
1186
1187 /*
1188 * 'copy_sub_attrs()' - Copy job attributes to the response.
1189 */
1190
1191 static void
1192 copy_subscription_attributes(
1193 _ipp_client_t *client, /* I - Client */
1194 _ipp_subscription_t *sub, /* I - Subscription */
1195 cups_array_t *ra) /* I - requested-attributes */
1196 {
1197 copy_attributes(client->response, sub->attrs, ra, IPP_TAG_SUBSCRIPTION, 0);
1198
1199 if (!ra || cupsArrayFind(ra, "notify-lease-expiration-time"))
1200 ippAddInteger(client->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-lease-expiration-time", (int)(sub->expire - client->printer->start_time));
1201
1202 if (!ra || cupsArrayFind(ra, "notify-printer-up-time"))
1203 ippAddInteger(client->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-printer-up-time", (int)(time(NULL) - client->printer->start_time));
1204
1205 if (!ra || cupsArrayFind(ra, "notify-sequence-number"))
1206 ippAddInteger(client->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-sequence-number", sub->last_sequence);
1207 }
1208
1209
1210 /*
1211 * 'create_client()' - Accept a new network connection and create a client
1212 * object.
1213 */
1214
1215 static _ipp_client_t * /* O - Client */
1216 create_client(_ipp_printer_t *printer, /* I - Printer */
1217 int sock) /* I - Listen socket */
1218 {
1219 _ipp_client_t *client; /* Client */
1220
1221
1222 if ((client = calloc(1, sizeof(_ipp_client_t))) == NULL)
1223 {
1224 perror("Unable to allocate memory for client");
1225 return (NULL);
1226 }
1227
1228 client->printer = printer;
1229 client->fetch_file = -1;
1230
1231 /*
1232 * Accept the client and get the remote address...
1233 */
1234
1235 if ((client->http = httpAcceptConnection(sock, 1)) == NULL)
1236 {
1237 perror("Unable to accept client connection");
1238
1239 free(client);
1240
1241 return (NULL);
1242 }
1243
1244 httpGetHostname(client->http, client->hostname, sizeof(client->hostname));
1245
1246 if (Verbosity)
1247 fprintf(stderr, "Accepted connection from %s\n", client->hostname);
1248
1249 return (client);
1250 }
1251
1252
1253 /*
1254 * 'create_device()' - Create an output device tracking object.
1255 */
1256
1257 static _ipp_device_t * /* O - Device */
1258 create_device(_ipp_client_t *client) /* I - Client */
1259 {
1260 _ipp_device_t *device; /* Device */
1261 ipp_attribute_t *uuid; /* output-device-uuid */
1262
1263
1264 if ((uuid = ippFindAttribute(client->request, "output-device-uuid", IPP_TAG_URI)) == NULL)
1265 return (NULL);
1266
1267 if ((device = calloc(1, sizeof(_ipp_device_t))) == NULL)
1268 return (NULL);
1269
1270 _cupsRWInit(&device->rwlock);
1271
1272 device->uuid = strdup(ippGetString(uuid, 0, NULL));
1273 device->state = IPP_PSTATE_STOPPED;
1274
1275 _cupsRWLockWrite(&client->printer->rwlock);
1276 cupsArrayAdd(client->printer->devices, device);
1277 _cupsRWUnlock(&client->printer->rwlock);
1278
1279 return (device);
1280 }
1281
1282
1283 /*
1284 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1285 * request.
1286 */
1287
1288 static _ipp_job_t * /* O - Job */
1289 create_job(_ipp_client_t *client) /* I - Client */
1290 {
1291 _ipp_job_t *job; /* Job */
1292 ipp_attribute_t *attr; /* Job attribute */
1293 char uri[1024], /* job-uri value */
1294 uuid[64]; /* job-uuid value */
1295
1296
1297 _cupsRWLockWrite(&(client->printer->rwlock));
1298
1299 /*
1300 * Allocate and initialize the job object...
1301 */
1302
1303 if ((job = calloc(1, sizeof(_ipp_job_t))) == NULL)
1304 {
1305 perror("Unable to allocate memory for job");
1306 return (NULL);
1307 }
1308
1309 job->printer = client->printer;
1310 job->attrs = ippNew();
1311 job->state = IPP_JSTATE_HELD;
1312 job->fd = -1;
1313
1314 /*
1315 * Copy all of the job attributes...
1316 */
1317
1318 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
1319
1320 /*
1321 * Get the requesting-user-name, document format, and priority...
1322 */
1323
1324 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
1325 job->priority = ippGetInteger(attr, 0);
1326 else
1327 job->priority = 50;
1328
1329 if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
1330 job->username = ippGetString(attr, 0, NULL);
1331 else
1332 job->username = "anonymous";
1333
1334 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
1335
1336 if (ippGetOperation(client->request) != IPP_OP_CREATE_JOB)
1337 {
1338 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
1339 job->format = ippGetString(attr, 0, NULL);
1340 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
1341 job->format = ippGetString(attr, 0, NULL);
1342 else
1343 job->format = "application/octet-stream";
1344 }
1345
1346 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_INTEGER)) != NULL)
1347 job->impressions = ippGetInteger(attr, 0);
1348
1349 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_NAME)) != NULL)
1350 job->name = ippGetString(attr, 0, NULL);
1351
1352 /*
1353 * Add job description attributes and add to the jobs array...
1354 */
1355
1356 job->id = client->printer->next_job_id ++;
1357
1358 snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id);
1359 httpAssembleUUID(client->printer->hostname, client->printer->port, client->printer->name, job->id, uuid, sizeof(uuid));
1360
1361 ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(&job->created)));
1362 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1363 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
1364 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
1365 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, client->printer->uri);
1366 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time));
1367
1368 cupsArrayAdd(client->printer->jobs, job);
1369 cupsArrayAdd(client->printer->active_jobs, job);
1370
1371 _cupsRWUnlock(&(client->printer->rwlock));
1372
1373 return (job);
1374 }
1375
1376
1377 /*
1378 * 'create_job_filename()' - Create the filename for a document in a job.
1379 */
1380
1381 static void create_job_filename(
1382 _ipp_printer_t *printer, /* I - Printer */
1383 _ipp_job_t *job, /* I - Job */
1384 const char *format, /* I - Format or NULL */
1385 char *fname, /* I - Filename buffer */
1386 size_t fnamesize) /* I - Size of filename buffer */
1387 {
1388 char name[256], /* "Safe" filename */
1389 *nameptr; /* Pointer into filename */
1390 const char *ext, /* Filename extension */
1391 *job_name; /* job-name value */
1392 ipp_attribute_t *job_name_attr; /* job-name attribute */
1393
1394
1395 /*
1396 * Make a name from the job-name attribute...
1397 */
1398
1399 if ((job_name_attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME)) != NULL)
1400 job_name = ippGetString(job_name_attr, 0, NULL);
1401 else
1402 job_name = "untitled";
1403
1404 for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++)
1405 if (isalnum(*job_name & 255) || *job_name == '-')
1406 *nameptr++ = (char)tolower(*job_name & 255);
1407 else
1408 *nameptr++ = '_';
1409
1410 *nameptr = '\0';
1411
1412 /*
1413 * Figure out the extension...
1414 */
1415
1416 if (!format)
1417 format = job->format;
1418
1419 if (!strcasecmp(format, "image/jpeg"))
1420 ext = "jpg";
1421 else if (!strcasecmp(format, "image/png"))
1422 ext = "png";
1423 else if (!strcasecmp(format, "image/pwg-raster"))
1424 ext = "ras";
1425 else if (!strcasecmp(format, "image/urf"))
1426 ext = "urf";
1427 else if (!strcasecmp(format, "application/pdf"))
1428 ext = "pdf";
1429 else if (!strcasecmp(format, "application/postscript"))
1430 ext = "ps";
1431 else
1432 ext = "prn";
1433
1434 /*
1435 * Create a filename with the job-id, job-name, and document-format (extension)...
1436 */
1437
1438 snprintf(fname, fnamesize, "%s/%d-%s.%s", printer->directory, job->id, name, ext);
1439 }
1440
1441
1442 /*
1443 * 'create_listener()' - Create a listener socket.
1444 */
1445
1446 static int /* O - Listener socket or -1 on error */
1447 create_listener(int family, /* I - Address family */
1448 int port) /* I - Port number */
1449 {
1450 int sock; /* Listener socket */
1451 http_addrlist_t *addrlist; /* Listen address */
1452 char service[255]; /* Service port */
1453
1454
1455 snprintf(service, sizeof(service), "%d", port);
1456 if ((addrlist = httpAddrGetList(NULL, family, service)) == NULL)
1457 return (-1);
1458
1459 sock = httpAddrListen(&(addrlist->addr), port);
1460
1461 httpAddrFreeList(addrlist);
1462
1463 return (sock);
1464 }
1465
1466
1467 /*
1468 * 'create_printer()' - Create, register, and listen for connections to a
1469 * printer object.
1470 */
1471
1472 static _ipp_printer_t * /* O - Printer */
1473 create_printer(const char *servername, /* I - Server hostname (NULL for default) */
1474 int port, /* I - Port number */
1475 const char *name, /* I - printer-name */
1476 const char *directory, /* I - Spool directory */
1477 const char *proxy_user, /* I - Proxy account username */
1478 const char *proxy_pass) /* I - Proxy account password */
1479 {
1480 _ipp_printer_t *printer; /* Printer */
1481 char uri[1024], /* Printer URI */
1482 adminurl[1024], /* printer-more-info URI */
1483 supplyurl[1024],/* printer-supply-info-uri URI */
1484 uuid[128]; /* printer-uuid */
1485 int k_supported; /* Maximum file size supported */
1486 #ifdef HAVE_STATVFS
1487 struct statvfs spoolinfo; /* FS info for spool directory */
1488 double spoolsize; /* FS size */
1489 #elif defined(HAVE_STATFS)
1490 struct statfs spoolinfo; /* FS info for spool directory */
1491 double spoolsize; /* FS size */
1492 #endif /* HAVE_STATVFS */
1493 static const char * const versions[] =/* ipp-versions-supported values */
1494 {
1495 "1.0",
1496 "1.1",
1497 "2.0"
1498 };
1499 static const char * const features[] =/* ipp-features-supported values */
1500 {
1501 "document-object",
1502 "ipp-everywhere",
1503 "infrastructure-printer",
1504 "page-overrides"
1505 };
1506 static const int ops[] = /* operations-supported values */
1507 {
1508 IPP_OP_PRINT_JOB,
1509 IPP_OP_PRINT_URI,
1510 IPP_OP_VALIDATE_JOB,
1511 IPP_OP_CREATE_JOB,
1512 IPP_OP_SEND_DOCUMENT,
1513 IPP_OP_SEND_URI,
1514 IPP_OP_CANCEL_JOB,
1515 IPP_OP_GET_JOB_ATTRIBUTES,
1516 IPP_OP_GET_JOBS,
1517 IPP_OP_GET_PRINTER_ATTRIBUTES,
1518 IPP_OP_GET_PRINTER_SUPPORTED_VALUES,
1519 IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS,
1520 IPP_OP_CREATE_JOB_SUBSCRIPTIONS,
1521 IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES,
1522 IPP_OP_GET_SUBSCRIPTIONS,
1523 IPP_OP_RENEW_SUBSCRIPTION,
1524 IPP_OP_CANCEL_SUBSCRIPTION,
1525 IPP_OP_GET_NOTIFICATIONS,
1526 IPP_OP_GET_DOCUMENT_ATTRIBUTES,
1527 IPP_OP_GET_DOCUMENTS,
1528 IPP_OP_CANCEL_MY_JOBS,
1529 IPP_OP_CLOSE_JOB,
1530 IPP_OP_IDENTIFY_PRINTER,
1531 IPP_OP_VALIDATE_DOCUMENT,
1532 _IPP_OP_ACKNOWLEDGE_DOCUMENT,
1533 _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER,
1534 _IPP_OP_ACKNOWLEDGE_JOB,
1535 _IPP_OP_FETCH_DOCUMENT,
1536 _IPP_OP_FETCH_JOB,
1537 _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES,
1538 _IPP_OP_UPDATE_ACTIVE_JOBS,
1539 _IPP_OP_UPDATE_DOCUMENT_STATUS,
1540 _IPP_OP_UPDATE_JOB_STATUS,
1541 _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES,
1542 _IPP_OP_DEREGISTER_OUTPUT_DEVICE
1543 };
1544 static const char * const charsets[] =/* charset-supported values */
1545 {
1546 "us-ascii",
1547 "utf-8"
1548 };
1549 static const char * const compressions[] =/* compression-supported values */
1550 {
1551 #ifdef HAVE_LIBZ
1552 "deflate",
1553 "gzip",
1554 #endif /* HAVE_LIBZ */
1555 "none"
1556 };
1557 static const char * const notify_attributes[] =
1558 { /* notify-attributes-supported */
1559 "printer-state-change-time",
1560 "notify-lease-expiration-time",
1561 "notify-subscriber-user-name"
1562 };
1563 static const char * const reference_uri_schemes_supported[] =
1564 { /* reference-uri-schemes-supported */
1565 "file",
1566 "ftp",
1567 "http"
1568 #ifdef HAVE_SSL
1569 , "https"
1570 #endif /* HAVE_SSL */
1571 };
1572 static const char * const which_jobs[] =
1573 { /* which-jobs-supported values */
1574 "completed",
1575 "not-completed",
1576 "aborted",
1577 "all",
1578 "canceled",
1579 "pending",
1580 "pending-held",
1581 "processing",
1582 "processing-stopped"
1583 };
1584
1585
1586 /*
1587 * Allocate memory for the printer...
1588 */
1589
1590 if ((printer = calloc(1, sizeof(_ipp_printer_t))) == NULL)
1591 {
1592 perror("ippserver: Unable to allocate memory for printer");
1593 return (NULL);
1594 }
1595
1596 printer->ipv4 = -1;
1597 printer->ipv6 = -1;
1598 printer->name = strdup(name);
1599 printer->directory = strdup(directory);
1600 printer->hostname = strdup(servername);
1601 printer->port = port;
1602 printer->start_time = time(NULL);
1603 printer->config_time = printer->start_time;
1604 printer->state = IPP_PSTATE_IDLE;
1605 printer->state_reasons = _IPP_PREASON_NONE;
1606 printer->state_time = printer->start_time;
1607 printer->jobs = cupsArrayNew3((cups_array_func_t)compare_jobs, NULL, NULL, 0, NULL, (cups_afree_func_t)delete_job);
1608 printer->active_jobs = cupsArrayNew((cups_array_func_t)compare_active_jobs, NULL);
1609 printer->completed_jobs = cupsArrayNew((cups_array_func_t)compare_completed_jobs, NULL);
1610 printer->next_job_id = 1;
1611
1612 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1613 printer->hostname, printer->port, "/ipp/print");
1614 printer->uri = strdup(uri);
1615 printer->urilen = strlen(uri);
1616
1617 if (proxy_user)
1618 printer->proxy_user = strdup(proxy_user);
1619 if (proxy_pass)
1620 printer->proxy_pass = strdup(proxy_pass);
1621
1622 printer->devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
1623
1624 _cupsRWInit(&(printer->rwlock));
1625
1626 /*
1627 * Create the listener sockets...
1628 */
1629
1630 if ((printer->ipv4 = create_listener(AF_INET, printer->port)) < 0)
1631 {
1632 perror("Unable to create IPv4 listener");
1633 goto bad_printer;
1634 }
1635
1636 if ((printer->ipv6 = create_listener(AF_INET6, printer->port)) < 0)
1637 {
1638 perror("Unable to create IPv6 listener");
1639 goto bad_printer;
1640 }
1641
1642 /*
1643 * Prepare values for the printer attributes...
1644 */
1645
1646 httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), "http", NULL, printer->hostname, printer->port, "/");
1647 httpAssembleURI(HTTP_URI_CODING_ALL, supplyurl, sizeof(supplyurl), "http", NULL, printer->hostname, printer->port, "/supplies");
1648
1649 if (Verbosity)
1650 {
1651 fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
1652 fprintf(stderr, "printer-supply-info-uri=\"%s\"\n", supplyurl);
1653 fprintf(stderr, "printer-uri=\"%s\"\n", uri);
1654 }
1655
1656 /*
1657 * Get the maximum spool size based on the size of the filesystem used for
1658 * the spool directory. If the host OS doesn't support the statfs call
1659 * or the filesystem is larger than 2TiB, always report INT_MAX.
1660 */
1661
1662 #ifdef HAVE_STATVFS
1663 if (statvfs(printer->directory, &spoolinfo))
1664 k_supported = INT_MAX;
1665 else if ((spoolsize = (double)spoolinfo.f_frsize *
1666 spoolinfo.f_blocks / 1024) > INT_MAX)
1667 k_supported = INT_MAX;
1668 else
1669 k_supported = (int)spoolsize;
1670
1671 #elif defined(HAVE_STATFS)
1672 if (statfs(printer->directory, &spoolinfo))
1673 k_supported = INT_MAX;
1674 else if ((spoolsize = (double)spoolinfo.f_bsize *
1675 spoolinfo.f_blocks / 1024) > INT_MAX)
1676 k_supported = INT_MAX;
1677 else
1678 k_supported = (int)spoolsize;
1679
1680 #else
1681 k_supported = INT_MAX;
1682 #endif /* HAVE_STATVFS */
1683
1684 /*
1685 * Create the printer attributes. This list of attributes is sorted to improve
1686 * performance when the client provides a requested-attributes attribute...
1687 */
1688
1689 printer->attrs = ippNew();
1690
1691 /* charset-configured */
1692 ippAddString(printer->attrs, IPP_TAG_PRINTER,
1693 IPP_CONST_TAG(IPP_TAG_CHARSET),
1694 "charset-configured", NULL, "utf-8");
1695
1696 /* charset-supported */
1697 ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1698 IPP_CONST_TAG(IPP_TAG_CHARSET),
1699 "charset-supported", sizeof(charsets) / sizeof(charsets[0]),
1700 NULL, charsets);
1701
1702 /* compression-supported */
1703 ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1704 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1705 "compression-supported",
1706 (int)(sizeof(compressions) / sizeof(compressions[0])), NULL,
1707 compressions);
1708
1709 /* generated-natural-language-supported */
1710 ippAddString(printer->attrs, IPP_TAG_PRINTER,
1711 IPP_CONST_TAG(IPP_TAG_LANGUAGE),
1712 "generated-natural-language-supported", NULL, "en");
1713
1714 /* ipp-features-supported */
1715 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", sizeof(features) / sizeof(features[0]), NULL, features);
1716
1717 /* ipp-versions-supported */
1718 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]), NULL, versions);
1719
1720 /* ippget-event-life */
1721 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "ippget-event-life", 300);
1722
1723 /* job-ids-supported */
1724 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1);
1725
1726 /* job-k-octets-supported */
1727 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0,
1728 k_supported);
1729
1730 /* job-priority-default */
1731 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1732 "job-priority-default", 50);
1733
1734 /* job-priority-supported */
1735 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1736 "job-priority-supported", 100);
1737
1738 /* multiple-document-jobs-supported */
1739 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 0);
1740
1741 /* multiple-operation-time-out */
1742 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "multiple-operation-time-out", 60);
1743
1744 /* multiple-operation-time-out-action */
1745 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-operation-time-out-action", NULL, "abort-job");
1746
1747 /* natural-language-configured */
1748 ippAddString(printer->attrs, IPP_TAG_PRINTER,
1749 IPP_CONST_TAG(IPP_TAG_LANGUAGE),
1750 "natural-language-configured", NULL, "en");
1751
1752 /* notify-attributes-supported */
1753 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-attributes-supported", sizeof(notify_attributes) / sizeof(notify_attributes[0]), NULL, notify_attributes);
1754
1755 /* notify-events-default */
1756 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-events-default", NULL, "job-completed");
1757
1758 /* notify-events-supported */
1759 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-events-supported", sizeof(_ipp_events) / sizeof(_ipp_events[0]), NULL, _ipp_events);
1760
1761 /* notify-lease-duration-default */
1762 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "notify-lease-duration-default", 86400);
1763
1764 /* notify-lease-duration-supported */
1765 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "notify-lease-duration-supported", 0, _IPP_NOTIFY_LEASE_DURATION_MAX);
1766
1767 /* notify-max-events-supported */
1768 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "notify-lease-duration-default", (int)(sizeof(_ipp_events) / sizeof(_ipp_events[0])));
1769
1770 /* notify-pull-method-supported */
1771 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-pull-method-supported", NULL, "ippget");
1772
1773 /* operations-supported */
1774 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1775 "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1776
1777 /* printer-get-attributes-supported */
1778 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format");
1779
1780 /* printer-is-accepting-jobs */
1781 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1782
1783 /* printer-info */
1784 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, name);
1785
1786 /* printer-more-info */
1787 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, adminurl);
1788
1789 /* printer-name */
1790 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
1791
1792 /* printer-supply-info-uri */
1793 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, supplyurl);
1794
1795 /* printer-uri-supported */
1796 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
1797
1798 /* printer-uuid */
1799 httpAssembleUUID(printer->hostname, port, name, 0, uuid, sizeof(uuid));
1800 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid);
1801
1802 /* reference-uri-scheme-supported */
1803 ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1804 IPP_CONST_TAG(IPP_TAG_URISCHEME),
1805 "reference-uri-schemes-supported",
1806 (int)(sizeof(reference_uri_schemes_supported) /
1807 sizeof(reference_uri_schemes_supported[0])),
1808 NULL, reference_uri_schemes_supported);
1809
1810 /* uri-authentication-supported */
1811 ippAddString(printer->attrs, IPP_TAG_PRINTER,
1812 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1813 "uri-authentication-supported", NULL, "basic");
1814
1815 /* uri-security-supported */
1816 ippAddString(printer->attrs, IPP_TAG_PRINTER,
1817 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1818 "uri-security-supported", NULL, "tls");
1819
1820 /* which-jobs-supported */
1821 ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1822 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1823 "which-jobs-supported",
1824 sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
1825
1826 debug_attributes("Printer", printer->attrs, 0);
1827
1828 /*
1829 * Return it!
1830 */
1831
1832 return (printer);
1833
1834
1835 /*
1836 * If we get here we were unable to create the printer...
1837 */
1838
1839 bad_printer:
1840
1841 delete_printer(printer);
1842 return (NULL);
1843 }
1844
1845
1846 /*
1847 * 'create_subscription()' - Create a new subscription object from a
1848 * Print-Job, Create-Job, or Create-xxx-Subscription
1849 * request.
1850 */
1851
1852 static _ipp_subscription_t * /* O - Subscription object */
1853 create_subscription(
1854 _ipp_printer_t *printer, /* I - Printer */
1855 _ipp_job_t *job, /* I - Job, if any */
1856 int interval, /* I - Interval for progress events */
1857 int lease, /* I - Lease duration */
1858 const char *username, /* I - User creating the subscription */
1859 ipp_attribute_t *notify_events, /* I - Events to monitor */
1860 ipp_attribute_t *notify_attributes, /* I - Attributes to report */
1861 ipp_attribute_t *notify_user_data) /* I - User data, if any */
1862 {
1863 _ipp_subscription_t *sub; /* Subscription */
1864 ipp_attribute_t *attr; /* Subscription attribute */
1865 char uuid[64]; /* notify-subscription-uuid value */
1866
1867
1868 /*
1869 * Allocate and initialize the subscription object...
1870 */
1871
1872 if ((sub = calloc(1, sizeof(_ipp_subscription_t))) == NULL)
1873 {
1874 perror("Unable to allocate memory for subscription");
1875 return (NULL);
1876 }
1877
1878 _cupsRWLockWrite(&(printer->rwlock));
1879
1880 sub->id = printer->next_sub_id ++;
1881 sub->mask = notify_events ? get_notify_events_bits(notify_events) : _IPP_EVENT_DEFAULT;
1882 sub->printer = printer;
1883 sub->job = job;
1884 sub->interval = interval;
1885 sub->lease = lease;
1886 sub->attrs = ippNew();
1887
1888 if (lease)
1889 sub->expire = time(NULL) + sub->lease;
1890 else
1891 sub->expire = INT_MAX;
1892
1893 _cupsRWInit(&(sub->rwlock));
1894
1895 /*
1896 * Add subscription description attributes and add to the subscriptions
1897 * array...
1898 */
1899
1900 ippAddInteger(sub->attrs, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-subscription-id", sub->id);
1901
1902 httpAssembleUUID(printer->hostname, printer->port, printer->name, -sub->id, uuid, sizeof(uuid));
1903 attr = ippAddString(sub->attrs, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, "notify-subscription-uuid", NULL, uuid);
1904 sub->uuid = ippGetString(attr, 0, NULL);
1905
1906 ippAddString(sub->attrs, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, "notify-printer-uri", NULL, printer->uri);
1907
1908 if (job)
1909 ippAddInteger(sub->attrs, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-job-id", job->id);
1910 else
1911 ippAddInteger(sub->attrs, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-lease-duration", sub->lease);
1912
1913 attr = ippAddString(sub->attrs, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME, "notify-subscriber-user-name", NULL, username);
1914 sub->username = ippGetString(attr, 0, NULL);
1915
1916 if (notify_events)
1917 ippCopyAttribute(sub->attrs, notify_events, 0);
1918 else
1919 ippAddString(sub->attrs, IPP_TAG_SUBSCRIPTION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-events", NULL, _IPP_EVENT_DEFAULT_STRING);
1920
1921 ippAddString(sub->attrs, IPP_TAG_SUBSCRIPTION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-pull-method", NULL, "ippget");
1922
1923 if (notify_attributes)
1924 ippCopyAttribute(sub->attrs, notify_attributes, 0);
1925
1926 if (notify_user_data)
1927 ippCopyAttribute(sub->attrs, notify_user_data, 0);
1928
1929 sub->events = cupsArrayNew3(NULL, NULL, NULL, 0, NULL, (cups_afree_func_t)ippDelete);
1930
1931 cupsArrayAdd(printer->subscriptions, sub);
1932
1933 _cupsRWUnlock(&(printer->rwlock));
1934
1935 return (sub);
1936 }
1937
1938
1939 /*
1940 * 'debug_attributes()' - Print attributes in a request or response.
1941 */
1942
1943 static void
1944 debug_attributes(const char *title, /* I - Title */
1945 ipp_t *ipp, /* I - Request/response */
1946 int type) /* I - 0 = object, 1 = request, 2 = response */
1947 {
1948 ipp_tag_t group_tag; /* Current group */
1949 ipp_attribute_t *attr; /* Current attribute */
1950 char buffer[2048]; /* String buffer for value */
1951 int major, minor; /* Version */
1952
1953
1954 if (Verbosity <= 1)
1955 return;
1956
1957 fprintf(stderr, "%s:\n", title);
1958 major = ippGetVersion(ipp, &minor);
1959 fprintf(stderr, " version=%d.%d\n", major, minor);
1960 if (type == 1)
1961 fprintf(stderr, " operation-id=%s(%04x)\n",
1962 ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp));
1963 else if (type == 2)
1964 fprintf(stderr, " status-code=%s(%04x)\n",
1965 ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp));
1966 fprintf(stderr, " request-id=%d\n\n", ippGetRequestId(ipp));
1967
1968 for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO;
1969 attr;
1970 attr = ippNextAttribute(ipp))
1971 {
1972 if (ippGetGroupTag(attr) != group_tag)
1973 {
1974 group_tag = ippGetGroupTag(attr);
1975 fprintf(stderr, " %s\n", ippTagString(group_tag));
1976 }
1977
1978 if (ippGetName(attr))
1979 {
1980 ippAttributeString(attr, buffer, sizeof(buffer));
1981 fprintf(stderr, " %s (%s%s) %s\n", ippGetName(attr),
1982 ippGetCount(attr) > 1 ? "1setOf " : "",
1983 ippTagString(ippGetValueTag(attr)), buffer);
1984 }
1985 }
1986 }
1987
1988
1989 /*
1990 * 'delete_client()' - Close the socket and free all memory used by a client
1991 * object.
1992 */
1993
1994 static void
1995 delete_client(_ipp_client_t *client) /* I - Client */
1996 {
1997 if (Verbosity)
1998 fprintf(stderr, "Closing connection from %s\n", client->hostname);
1999
2000 /*
2001 * Flush pending writes before closing...
2002 */
2003
2004 httpFlushWrite(client->http);
2005
2006 /*
2007 * Free memory...
2008 */
2009
2010 httpClose(client->http);
2011
2012 ippDelete(client->request);
2013 ippDelete(client->response);
2014
2015 free(client);
2016 }
2017
2018
2019 /*
2020 * 'delete_device()' - Remove a device from a printer.
2021 *
2022 * Note: Caller is responsible for locking the printer object.
2023 */
2024
2025 static void
2026 delete_device(_ipp_device_t *device) /* I - Device */
2027 {
2028 /*
2029 * Free memory used for the device...
2030 */
2031
2032 _cupsRWDeinit(&device->rwlock);
2033
2034 if (device->name)
2035 free(device->name);
2036
2037 free(device->uuid);
2038
2039 ippDelete(device->attrs);
2040
2041 free(device);
2042 }
2043
2044
2045 /*
2046 * 'delete_job()' - Remove from the printer and free all memory used by a job
2047 * object.
2048 */
2049
2050 static void
2051 delete_job(_ipp_job_t *job) /* I - Job */
2052 {
2053 if (Verbosity)
2054 fprintf(stderr, "Removing job #%d from history.\n", job->id);
2055
2056 _cupsRWLockWrite(&job->rwlock);
2057
2058 ippDelete(job->attrs);
2059
2060 if (job->filename)
2061 {
2062 if (!KeepFiles)
2063 unlink(job->filename);
2064
2065 free(job->filename);
2066 }
2067
2068 _cupsRWDeinit(&job->rwlock);
2069
2070 free(job);
2071 }
2072
2073
2074 /*
2075 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2076 * used by a printer object.
2077 */
2078
2079 static void
2080 delete_printer(_ipp_printer_t *printer) /* I - Printer */
2081 {
2082 _cupsRWLockWrite(&printer->rwlock);
2083
2084 if (printer->ipv4 >= 0)
2085 close(printer->ipv4);
2086
2087 if (printer->ipv6 >= 0)
2088 close(printer->ipv6);
2089
2090 if (printer->name)
2091 free(printer->name);
2092 if (printer->directory)
2093 free(printer->directory);
2094 if (printer->hostname)
2095 free(printer->hostname);
2096 if (printer->uri)
2097 free(printer->uri);
2098 if (printer->proxy_user)
2099 free(printer->proxy_user);
2100 if (printer->proxy_pass)
2101 free(printer->proxy_pass);
2102
2103
2104 ippDelete(printer->attrs);
2105 ippDelete(printer->dev_attrs);
2106
2107 cupsArrayDelete(printer->active_jobs);
2108 cupsArrayDelete(printer->completed_jobs);
2109 cupsArrayDelete(printer->jobs);
2110 cupsArrayDelete(printer->subscriptions);
2111
2112 _cupsRWDeinit(&printer->rwlock);
2113
2114 free(printer);
2115 }
2116
2117
2118 /*
2119 * 'delete_subscription()' - Delete a subscription.
2120 */
2121
2122 static void
2123 delete_subscription(
2124 _ipp_subscription_t *sub) /* I - Subscription */
2125 {
2126 sub->pending_delete = 1;
2127
2128 _cupsCondBroadcast(&SubscriptionCondition);
2129
2130 _cupsRWLockWrite(&sub->rwlock);
2131
2132 ippDelete(sub->attrs);
2133 cupsArrayDelete(sub->events);
2134
2135 _cupsRWDeinit(&sub->rwlock);
2136
2137 free(sub);
2138 }
2139
2140
2141 /*
2142 * 'filter_cb()' - Filter printer attributes based on the requested array.
2143 */
2144
2145 static int /* O - 1 to copy, 0 to ignore */
2146 filter_cb(_ipp_filter_t *filter, /* I - Filter parameters */
2147 ipp_t *dst, /* I - Destination (unused) */
2148 ipp_attribute_t *attr) /* I - Source attribute */
2149 {
2150 /*
2151 * Filter attributes as needed...
2152 */
2153
2154 (void)dst;
2155
2156 ipp_tag_t group = ippGetGroupTag(attr);
2157 const char *name = ippGetName(attr);
2158
2159 if ((filter->group_tag != IPP_TAG_ZERO && group != filter->group_tag && group != IPP_TAG_ZERO) || !name || (!strcmp(name, "media-col-database") && !cupsArrayFind(filter->ra, (void *)name)))
2160 return (0);
2161
2162 return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL);
2163 }
2164
2165
2166 /*
2167 * 'find_device()' - Find a device.
2168 */
2169
2170 static _ipp_device_t * /* I - Device */
2171 find_device(_ipp_client_t *client) /* I - Client */
2172 {
2173 ipp_attribute_t *uuid; /* output-device-uuid */
2174 _ipp_device_t key, /* Search key */
2175 *device; /* Matching device */
2176
2177
2178 if ((uuid = ippFindAttribute(client->request, "output-device-uuid", IPP_TAG_URI)) == NULL)
2179 return (NULL);
2180
2181 key.uuid = (char *)ippGetString(uuid, 0, NULL);
2182
2183 _cupsRWLockRead(&client->printer->rwlock);
2184 device = (_ipp_device_t *)cupsArrayFind(client->printer->devices, &key);
2185 _cupsRWUnlock(&client->printer->rwlock);
2186
2187 return (device);
2188 }
2189
2190
2191 /*
2192 * 'find_job()' - Find a job specified in a request.
2193 */
2194
2195 static _ipp_job_t * /* O - Job or NULL */
2196 find_job(_ipp_client_t *client, /* I - Client */
2197 int job_id) /* I - Job ID to find or 0 to lookup */
2198 {
2199 ipp_attribute_t *attr; /* job-id or job-uri attribute */
2200 _ipp_job_t key, /* Job search key */
2201 *job; /* Matching job, if any */
2202
2203
2204 if (job_id > 0)
2205 {
2206 key.id = job_id;
2207 }
2208 else if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
2209 {
2210 const char *uri = ippGetString(attr, 0, NULL);
2211
2212 if (!strncmp(uri, client->printer->uri, client->printer->urilen) &&
2213 uri[client->printer->urilen] == '/')
2214 key.id = atoi(uri + client->printer->urilen + 1);
2215 else
2216 return (NULL);
2217 }
2218 else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
2219 {
2220 key.id = ippGetInteger(attr, 0);
2221 }
2222
2223 _cupsRWLockRead(&(client->printer->rwlock));
2224 job = (_ipp_job_t *)cupsArrayFind(client->printer->jobs, &key);
2225 _cupsRWUnlock(&(client->printer->rwlock));
2226
2227 return (job);
2228 }
2229
2230
2231 /*
2232 * 'find_subscription()' - Find a subcription.
2233 */
2234
2235 static _ipp_subscription_t * /* O - Subscription */
2236 find_subscription(_ipp_client_t *client,/* I - Client */
2237 int sub_id) /* I - Subscription ID or 0 */
2238 {
2239 ipp_attribute_t *notify_subscription_id;
2240 /* notify-subscription-id */
2241 _ipp_subscription_t key, /* Search key */
2242 *sub; /* Matching subscription */
2243
2244
2245 if (sub_id > 0)
2246 key.id = sub_id;
2247 else if ((notify_subscription_id = ippFindAttribute(client->request, "notify-subscription-id", IPP_TAG_INTEGER)) == NULL)
2248 return (NULL);
2249 else
2250 key.id = ippGetInteger(notify_subscription_id, 0);
2251
2252 _cupsRWLockRead(&client->printer->rwlock);
2253 sub = (_ipp_subscription_t *)cupsArrayFind(client->printer->subscriptions, &key);
2254 _cupsRWUnlock(&client->printer->rwlock);
2255
2256 return (sub);
2257 }
2258
2259
2260 /*
2261 * 'get_job_state_reasons_bits()' - Get the bits associates with "job-state-reasons" values.
2262 */
2263
2264 static _ipp_jreason_t /* O - Bits */
2265 get_job_state_reasons_bits(
2266 ipp_attribute_t *attr) /* I - "job-state-reasons" attribute */
2267 {
2268 int i, j, /* Looping vars */
2269 count; /* Number of "job-state-reasons" values */
2270 const char *keyword; /* "job-state-reasons" value */
2271 _ipp_jreason_t jreasons = _IPP_JREASON_NONE;
2272 /* Bits for "job-state-reasons" values */
2273
2274
2275 count = ippGetCount(attr);
2276 for (i = 0; i < count; i ++)
2277 {
2278 keyword = ippGetString(attr, i, NULL);
2279
2280 for (j = 0; j < (int)(sizeof(_ipp_jreasons) / sizeof(_ipp_jreasons[0])); j ++)
2281 {
2282 if (!strcmp(keyword, _ipp_jreasons[j]))
2283 {
2284 jreasons |= 1 << j;
2285 break;
2286 }
2287 }
2288 }
2289
2290 return (jreasons);
2291 }
2292
2293
2294 /*
2295 * 'get_notify_event_bits()' - Get the bits associated with "notify-events" values.
2296 */
2297
2298 static _ipp_event_t /* O - Bits */
2299 get_notify_events_bits(
2300 ipp_attribute_t *attr) /* I - "notify-events" attribute */
2301 {
2302 int i, j, /* Looping vars */
2303 count; /* Number of "notify-events" values */
2304 const char *keyword; /* "notify-events" value */
2305 _ipp_event_t events = _IPP_EVENT_NONE;
2306 /* Bits for "notify-events" values */
2307
2308
2309 count = ippGetCount(attr);
2310 for (i = 0; i < count; i ++)
2311 {
2312 keyword = ippGetString(attr, i, NULL);
2313
2314 for (j = 0; j < (int)(sizeof(_ipp_events) / sizeof(_ipp_events[0])); j ++)
2315 {
2316 if (!strcmp(keyword, _ipp_jreasons[j]))
2317 {
2318 events |= 1 << j;
2319 break;
2320 }
2321 }
2322 }
2323
2324 return (events);
2325 }
2326
2327
2328 /*
2329 * 'get_notify_subscribed_event()' - Get the event name.
2330 */
2331
2332 static const char * /* O - Event name */
2333 get_notify_subscribed_event(
2334 _ipp_event_t event) /* I - Event bit */
2335 {
2336 int i; /* Looping var */
2337 _ipp_event_t mask; /* Current mask */
2338
2339 for (i = 0, mask = 1; i < (int)(sizeof(_ipp_events) / sizeof(_ipp_events[0])); i ++, mask <<= 1)
2340 if (event & mask)
2341 return (_ipp_events[i]);
2342
2343 return ("none");
2344 }
2345
2346
2347 /*
2348 * 'get_printer_state_reasons_bits()' - Get the bits associated with "printer-state-reasons" values.
2349 */
2350
2351 static _ipp_preason_t /* O - Bits */
2352 get_printer_state_reasons_bits(
2353 ipp_attribute_t *attr) /* I - "printer-state-reasons" bits */
2354 {
2355 int i, j, /* Looping vars */
2356 count; /* Number of "printer-state-reasons" values */
2357 const char *keyword; /* "printer-state-reasons" value */
2358 _ipp_preason_t preasons = _IPP_PREASON_NONE;
2359 /* Bits for "printer-state-reasons" values */
2360
2361
2362 count = ippGetCount(attr);
2363 for (i = 0; i < count; i ++)
2364 {
2365 keyword = ippGetString(attr, i, NULL);
2366
2367 for (j = 0; j < (int)(sizeof(_ipp_preasons) / sizeof(_ipp_preasons[0])); j ++)
2368 {
2369 if (!strcmp(keyword, _ipp_preasons[j]))
2370 {
2371 preasons |= 1 << j;
2372 break;
2373 }
2374 }
2375 }
2376
2377 return (preasons);
2378 }
2379
2380
2381 /*
2382 * 'html_escape()' - Write a HTML-safe string.
2383 */
2384
2385 static void
2386 html_escape(_ipp_client_t *client, /* I - Client */
2387 const char *s, /* I - String to write */
2388 size_t slen) /* I - Number of characters to write */
2389 {
2390 const char *start, /* Start of segment */
2391 *end; /* End of string */
2392
2393
2394 start = s;
2395 end = s + (slen > 0 ? slen : strlen(s));
2396
2397 while (*s && s < end)
2398 {
2399 if (*s == '&' || *s == '<')
2400 {
2401 if (s > start)
2402 httpWrite2(client->http, start, (size_t)(s - start));
2403
2404 if (*s == '&')
2405 httpWrite2(client->http, "&amp;", 5);
2406 else
2407 httpWrite2(client->http, "&lt;", 4);
2408
2409 start = s + 1;
2410 }
2411
2412 s ++;
2413 }
2414
2415 if (s > start)
2416 httpWrite2(client->http, start, (size_t)(s - start));
2417 }
2418
2419
2420 /*
2421 * 'html_footer()' - Show the web interface footer.
2422 *
2423 * This function also writes the trailing 0-length chunk.
2424 */
2425
2426 static void
2427 html_footer(_ipp_client_t *client) /* I - Client */
2428 {
2429 html_printf(client,
2430 "</div>\n"
2431 "</body>\n"
2432 "</html>\n");
2433 httpWrite2(client->http, "", 0);
2434 }
2435
2436
2437 /*
2438 * 'html_header()' - Show the web interface header and title.
2439 */
2440
2441 static void
2442 html_header(_ipp_client_t *client, /* I - Client */
2443 const char *title) /* I - Title */
2444 {
2445 html_printf(client,
2446 "<!doctype html>\n"
2447 "<html>\n"
2448 "<head>\n"
2449 "<title>%s</title>\n"
2450 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2451 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2452 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2453 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2454 "<style>\n"
2455 "body { font-family: sans-serif; margin: 0; }\n"
2456 "div.body { padding: 0px 10px 10px; }\n"
2457 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2458 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2459 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2460 "table.form th { text-align: right; }\n"
2461 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2462 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2463 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2464 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2465 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2466 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2467 "table.nav td { margin: 0; text-align: center; }\n"
2468 "td.nav a, td.nav a:active, td.nav a:hover, td.nav a:hover:link, td.nav a:hover:link:visited, td.nav a:link, td.nav a:link:visited, td.nav a:visited { background: inherit; color: inherit; font-size: 80%%; text-decoration: none; }\n"
2469 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2470 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2471 "td.nav:hover { background: #666; color: #fff; }\n"
2472 "td.nav:active { background: #000; color: #ff0; }\n"
2473 "</style>\n"
2474 "</head>\n"
2475 "<body>\n"
2476 "<table class=\"nav\"><tr>"
2477 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2478 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2479 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2480 "</tr></table>\n"
2481 "<div class=\"body\">\n", title, !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
2482 }
2483
2484
2485 /*
2486 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2487 */
2488
2489 static void
2490 html_printf(_ipp_client_t *client, /* I - Client */
2491 const char *format, /* I - Printf-style format string */
2492 ...) /* I - Additional arguments as needed */
2493 {
2494 va_list ap; /* Pointer to arguments */
2495 const char *start; /* Start of string */
2496 char size, /* Size character (h, l, L) */
2497 type; /* Format type character */
2498 int width, /* Width of field */
2499 prec; /* Number of characters of precision */
2500 char tformat[100], /* Temporary format string for sprintf() */
2501 *tptr, /* Pointer into temporary format */
2502 temp[1024]; /* Buffer for formatted numbers */
2503 char *s; /* Pointer to string */
2504
2505
2506 /*
2507 * Loop through the format string, formatting as needed...
2508 */
2509
2510 va_start(ap, format);
2511 start = format;
2512
2513 while (*format)
2514 {
2515 if (*format == '%')
2516 {
2517 if (format > start)
2518 httpWrite2(client->http, start, (size_t)(format - start));
2519
2520 tptr = tformat;
2521 *tptr++ = *format++;
2522
2523 if (*format == '%')
2524 {
2525 httpWrite2(client->http, "%", 1);
2526 format ++;
2527 start = format;
2528 continue;
2529 }
2530 else if (strchr(" -+#\'", *format))
2531 *tptr++ = *format++;
2532
2533 if (*format == '*')
2534 {
2535 /*
2536 * Get width from argument...
2537 */
2538
2539 format ++;
2540 width = va_arg(ap, int);
2541
2542 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
2543 tptr += strlen(tptr);
2544 }
2545 else
2546 {
2547 width = 0;
2548
2549 while (isdigit(*format & 255))
2550 {
2551 if (tptr < (tformat + sizeof(tformat) - 1))
2552 *tptr++ = *format;
2553
2554 width = width * 10 + *format++ - '0';
2555 }
2556 }
2557
2558 if (*format == '.')
2559 {
2560 if (tptr < (tformat + sizeof(tformat) - 1))
2561 *tptr++ = *format;
2562
2563 format ++;
2564
2565 if (*format == '*')
2566 {
2567 /*
2568 * Get precision from argument...
2569 */
2570
2571 format ++;
2572 prec = va_arg(ap, int);
2573
2574 snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
2575 tptr += strlen(tptr);
2576 }
2577 else
2578 {
2579 prec = 0;
2580
2581 while (isdigit(*format & 255))
2582 {
2583 if (tptr < (tformat + sizeof(tformat) - 1))
2584 *tptr++ = *format;
2585
2586 prec = prec * 10 + *format++ - '0';
2587 }
2588 }
2589 }
2590
2591 if (*format == 'l' && format[1] == 'l')
2592 {
2593 size = 'L';
2594
2595 if (tptr < (tformat + sizeof(tformat) - 2))
2596 {
2597 *tptr++ = 'l';
2598 *tptr++ = 'l';
2599 }
2600
2601 format += 2;
2602 }
2603 else if (*format == 'h' || *format == 'l' || *format == 'L')
2604 {
2605 if (tptr < (tformat + sizeof(tformat) - 1))
2606 *tptr++ = *format;
2607
2608 size = *format++;
2609 }
2610 else
2611 size = 0;
2612
2613
2614 if (!*format)
2615 {
2616 start = format;
2617 break;
2618 }
2619
2620 if (tptr < (tformat + sizeof(tformat) - 1))
2621 *tptr++ = *format;
2622
2623 type = *format++;
2624 *tptr = '\0';
2625 start = format;
2626
2627 switch (type)
2628 {
2629 case 'E' : /* Floating point formats */
2630 case 'G' :
2631 case 'e' :
2632 case 'f' :
2633 case 'g' :
2634 if ((size_t)(width + 2) > sizeof(temp))
2635 break;
2636
2637 sprintf(temp, tformat, va_arg(ap, double));
2638
2639 httpWrite2(client->http, temp, strlen(temp));
2640 break;
2641
2642 case 'B' : /* Integer formats */
2643 case 'X' :
2644 case 'b' :
2645 case 'd' :
2646 case 'i' :
2647 case 'o' :
2648 case 'u' :
2649 case 'x' :
2650 if ((size_t)(width + 2) > sizeof(temp))
2651 break;
2652
2653 # ifdef HAVE_LONG_LONG
2654 if (size == 'L')
2655 sprintf(temp, tformat, va_arg(ap, long long));
2656 else
2657 # endif /* HAVE_LONG_LONG */
2658 if (size == 'l')
2659 sprintf(temp, tformat, va_arg(ap, long));
2660 else
2661 sprintf(temp, tformat, va_arg(ap, int));
2662
2663 httpWrite2(client->http, temp, strlen(temp));
2664 break;
2665
2666 case 'p' : /* Pointer value */
2667 if ((size_t)(width + 2) > sizeof(temp))
2668 break;
2669
2670 sprintf(temp, tformat, va_arg(ap, void *));
2671
2672 httpWrite2(client->http, temp, strlen(temp));
2673 break;
2674
2675 case 'c' : /* Character or character array */
2676 if (width <= 1)
2677 {
2678 temp[0] = (char)va_arg(ap, int);
2679 temp[1] = '\0';
2680 html_escape(client, temp, 1);
2681 }
2682 else
2683 html_escape(client, va_arg(ap, char *), (size_t)width);
2684 break;
2685
2686 case 's' : /* String */
2687 if ((s = va_arg(ap, char *)) == NULL)
2688 s = "(null)";
2689
2690 html_escape(client, s, strlen(s));
2691 break;
2692 }
2693 }
2694 else
2695 format ++;
2696 }
2697
2698 if (format > start)
2699 httpWrite2(client->http, start, (size_t)(format - start));
2700
2701 va_end(ap);
2702 }
2703
2704
2705 /*
2706 * 'ipp_acknowledge_document()' - Acknowledge receipt of a document.
2707 */
2708
2709 static void
2710 ipp_acknowledge_document(
2711 _ipp_client_t *client) /* I - Client */
2712 {
2713 _ipp_device_t *device; /* Device */
2714 _ipp_job_t *job; /* Job */
2715 ipp_attribute_t *attr; /* Attribute */
2716
2717
2718 if ((device = find_device(client)) == NULL)
2719 {
2720 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Device was not found.");
2721 return;
2722 }
2723
2724 if ((job = find_job(client, 0)) == NULL)
2725 {
2726 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job was not found.");
2727 return;
2728 }
2729
2730 if (!job->dev_uuid || strcmp(job->dev_uuid, device->uuid))
2731 {
2732 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job not assigned to device.");
2733 return;
2734 }
2735
2736 if ((attr = ippFindAttribute(client->request, "document-number", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(attr) != IPP_TAG_OPERATION || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetCount(attr) != 1 || ippGetInteger(attr, 0) != 1)
2737 {
2738 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, attr ? "Bad document-number attribute." : "Missing document-number attribute.");
2739 return;
2740 }
2741
2742 respond_ipp(client, IPP_STATUS_OK, NULL);
2743 }
2744
2745
2746 /*
2747 * 'ipp_acknowledge_identify_printer()' - Acknowledge an identify command.
2748 */
2749
2750 static void
2751 ipp_acknowledge_identify_printer(
2752 _ipp_client_t *client) /* I - Client */
2753 {
2754 // TODO: Implement this!
2755 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Need to implement this.");
2756 }
2757
2758
2759 /*
2760 * 'ipp_acknowledge_job()' - Acknowledge receipt of a job.
2761 */
2762
2763 static void
2764 ipp_acknowledge_job(
2765 _ipp_client_t *client) /* I - Client */
2766 {
2767 _ipp_device_t *device; /* Device */
2768 _ipp_job_t *job; /* Job */
2769
2770
2771 if ((device = find_device(client)) == NULL)
2772 {
2773 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Device was not found.");
2774 return;
2775 }
2776
2777 if ((job = find_job(client, 0)) == NULL)
2778 {
2779 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job was not found.");
2780 return;
2781 }
2782
2783 if (job->dev_uuid && strcmp(job->dev_uuid, device->uuid))
2784 {
2785 respond_ipp(client, IPP_STATUS_ERROR_NOT_AUTHORIZED, "Job not assigned to device.");
2786 return;
2787 }
2788
2789 if (!(job->state_reasons & _IPP_JREASON_JOB_FETCHABLE))
2790 {
2791 respond_ipp(client, _IPP_STATUS_ERROR_NOT_FETCHABLE, "Job not fetchable.");
2792 return;
2793 }
2794
2795 if (!job->dev_uuid)
2796 job->dev_uuid = strdup(device->uuid);
2797
2798 job->state_reasons &= (_ipp_jreason_t)~_IPP_JREASON_JOB_FETCHABLE;
2799
2800 add_event(client->printer, job, _IPP_EVENT_JOB_STATE_CHANGED, "Job acknowledged.");
2801
2802 respond_ipp(client, IPP_STATUS_OK, NULL);
2803 }
2804
2805
2806 /*
2807 * 'ipp_cancel_job()' - Cancel a job.
2808 */
2809
2810 static void
2811 ipp_cancel_job(_ipp_client_t *client) /* I - Client */
2812 {
2813 _ipp_job_t *job; /* Job information */
2814
2815
2816 /*
2817 * Get the job...
2818 */
2819
2820 if ((job = find_job(client, 0)) == NULL)
2821 {
2822 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
2823 return;
2824 }
2825
2826 /*
2827 * See if the job is already completed, canceled, or aborted; if so,
2828 * we can't cancel...
2829 */
2830
2831 switch (job->state)
2832 {
2833 case IPP_JSTATE_CANCELED :
2834 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2835 "Job #%d is already canceled - can\'t cancel.", job->id);
2836 break;
2837
2838 case IPP_JSTATE_ABORTED :
2839 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2840 "Job #%d is already aborted - can\'t cancel.", job->id);
2841 break;
2842
2843 case IPP_JSTATE_COMPLETED :
2844 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2845 "Job #%d is already completed - can\'t cancel.", job->id);
2846 break;
2847
2848 default :
2849 /*
2850 * Cancel the job...
2851 */
2852
2853 _cupsRWLockWrite(&(client->printer->rwlock));
2854
2855 if (job->state == IPP_JSTATE_PROCESSING ||
2856 (job->state == IPP_JSTATE_HELD && job->fd >= 0))
2857 job->cancel = 1;
2858 else
2859 {
2860 job->state = IPP_JSTATE_CANCELED;
2861 job->completed = time(NULL);
2862 }
2863
2864 _cupsRWUnlock(&(client->printer->rwlock));
2865
2866 add_event(client->printer, job, _IPP_EVENT_JOB_COMPLETED, NULL);
2867
2868 respond_ipp(client, IPP_STATUS_OK, NULL);
2869 break;
2870 }
2871 }
2872
2873
2874 /*
2875 * 'ipp_cancel_my_jobs()' - Cancel a user's jobs.
2876 */
2877
2878 static void
2879 ipp_cancel_my_jobs(
2880 _ipp_client_t *client) /* I - Client */
2881 {
2882 // TODO: Implement this!
2883 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Need to implement this.");
2884 }
2885
2886
2887 /*
2888 * 'ipp_cancel_subscription()' - Cancel a subscription.
2889 */
2890
2891 static void
2892 ipp_cancel_subscription(
2893 _ipp_client_t *client) /* I - Client */
2894 {
2895 _ipp_subscription_t *sub; /* Subscription */
2896
2897
2898 if ((sub = find_subscription(client, 0)) == NULL)
2899 {
2900 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Subscription was not found.");
2901 return;
2902 }
2903
2904 _cupsRWLockWrite(&client->printer->rwlock);
2905 cupsArrayRemove(client->printer->subscriptions, sub);
2906 delete_subscription(sub);
2907 _cupsRWUnlock(&client->printer->rwlock);
2908 respond_ipp(client, IPP_STATUS_OK, NULL);
2909 }
2910
2911
2912 /*
2913 * 'ipp_close_job()' - Close an open job.
2914 */
2915
2916 static void
2917 ipp_close_job(_ipp_client_t *client) /* I - Client */
2918 {
2919 _ipp_job_t *job; /* Job information */
2920
2921
2922 /*
2923 * Get the job...
2924 */
2925
2926 if ((job = find_job(client, 0)) == NULL)
2927 {
2928 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
2929 return;
2930 }
2931
2932 /*
2933 * See if the job is already completed, canceled, or aborted; if so,
2934 * we can't cancel...
2935 */
2936
2937 switch (job->state)
2938 {
2939 case IPP_JSTATE_CANCELED :
2940 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2941 "Job #%d is canceled - can\'t close.", job->id);
2942 break;
2943
2944 case IPP_JSTATE_ABORTED :
2945 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2946 "Job #%d is aborted - can\'t close.", job->id);
2947 break;
2948
2949 case IPP_JSTATE_COMPLETED :
2950 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2951 "Job #%d is completed - can\'t close.", job->id);
2952 break;
2953
2954 case IPP_JSTATE_PROCESSING :
2955 case IPP_JSTATE_STOPPED :
2956 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2957 "Job #%d is already closed.", job->id);
2958 break;
2959
2960 default :
2961 respond_ipp(client, IPP_STATUS_OK, NULL);
2962 break;
2963 }
2964 }
2965
2966
2967 /*
2968 * 'ipp_create_job()' - Create a job object.
2969 */
2970
2971 static void
2972 ipp_create_job(_ipp_client_t *client) /* I - Client */
2973 {
2974 _ipp_job_t *job; /* New job */
2975 cups_array_t *ra; /* Attributes to send in response */
2976
2977
2978 /*
2979 * Validate print job attributes...
2980 */
2981
2982 if (!valid_job_attributes(client))
2983 {
2984 httpFlush(client->http);
2985 return;
2986 }
2987
2988 /*
2989 * Do we have a file to print?
2990 */
2991
2992 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2993 {
2994 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
2995 "Unexpected document data following request.");
2996 return;
2997 }
2998
2999 /*
3000 * Create the job...
3001 */
3002
3003 if ((job = create_job(client)) == NULL)
3004 {
3005 respond_ipp(client, IPP_STATUS_ERROR_TOO_MANY_JOBS, "Too many jobs are queued.");
3006 return;
3007 }
3008
3009 /*
3010 * Return the job info...
3011 */
3012
3013 respond_ipp(client, IPP_STATUS_OK, NULL);
3014
3015 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3016 cupsArrayAdd(ra, "job-id");
3017 cupsArrayAdd(ra, "job-state");
3018 cupsArrayAdd(ra, "job-state-message");
3019 cupsArrayAdd(ra, "job-state-reasons");
3020 cupsArrayAdd(ra, "job-uri");
3021
3022 copy_job_attributes(client, job, ra);
3023 cupsArrayDelete(ra);
3024
3025 /*
3026 * Add any subscriptions...
3027 */
3028
3029 client->job = job;
3030 ipp_create_xxx_subscriptions(client);
3031 }
3032
3033
3034 /*
3035 * 'ipp_create_xxx_subscriptions()' - Create job and printer subscriptions.
3036 */
3037
3038 static void
3039 ipp_create_xxx_subscriptions(
3040 _ipp_client_t *client)
3041 {
3042 _ipp_subscription_t *sub; /* Subscription */
3043 ipp_attribute_t *attr; /* Subscription attribute */
3044 const char *username; /* requesting-user-name or
3045 authenticated username */
3046 int num_subs = 0, /* Number of subscriptions */
3047 ok_subs = 0; /* Number of good subscriptions */
3048
3049
3050 /*
3051 * For the Create-xxx-Subscriptions operations, queue up a successful-ok
3052 * response...
3053 */
3054
3055 if (ippGetOperation(client->request) == IPP_OP_CREATE_JOB_SUBSCRIPTIONS || ippGetOperation(client->request) == IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS)
3056 respond_ipp(client, IPP_STATUS_OK, NULL);
3057
3058 /*
3059 * Get the authenticated user name, if any...
3060 */
3061
3062 if (client->username[0])
3063 username = client->username;
3064 else if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL && ippGetGroupTag(attr) == IPP_TAG_OPERATION && ippGetCount(attr) == 1)
3065 username = ippGetString(attr, 0, NULL);
3066 else
3067 username = "guest";
3068
3069 /*
3070 * Skip past the initial attributes to the first subscription group.
3071 */
3072
3073 attr = ippFirstAttribute(client->request);
3074 while (attr && ippGetGroupTag(attr) != IPP_TAG_SUBSCRIPTION)
3075 attr = ippNextAttribute(client->request);
3076
3077 while (attr)
3078 {
3079 _ipp_job_t *job = NULL; /* Job */
3080 const char *attrname, /* Attribute name */
3081 *pullmethod = NULL;
3082 /* notify-pull-method */
3083 ipp_attribute_t *notify_attributes = NULL,
3084 /* notify-attributes */
3085 *notify_events = NULL,
3086 /* notify-events */
3087 *notify_user_data = NULL;
3088 /* notify-user-data */
3089 int interval = 0, /* notify-time-interval */
3090 lease = _IPP_NOTIFY_LEASE_DURATION_DEFAULT;
3091 /* notify-lease-duration */
3092 ipp_status_t status = IPP_STATUS_OK;
3093 /* notify-status-code */
3094
3095 num_subs ++;
3096
3097 while (attr)
3098 {
3099 if ((attrname = ippGetName(attr)) == NULL)
3100 break;
3101
3102 if (!strcmp(attrname, "notify-recipient-uri"))
3103 {
3104 /*
3105 * Push notifications not supported.
3106 */
3107
3108 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3109 ippCopyAttribute(client->response, attr, 0);
3110 }
3111 else if (!strcmp(attrname, "notify-pull-method"))
3112 {
3113 pullmethod = ippGetString(attr, 0, NULL);
3114
3115 if (ippGetValueTag(attr) != IPP_TAG_KEYWORD || ippGetCount(attr) != 1 || !pullmethod || strcmp(pullmethod, "ippget"))
3116 {
3117 ippCopyAttribute(client->response, attr, 0);
3118 pullmethod = NULL;
3119 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3120 }
3121 }
3122 else if (!strcmp(attrname, "notify-attributes"))
3123 {
3124 if (ippGetValueTag(attr) != IPP_TAG_KEYWORD)
3125 {
3126 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3127 ippCopyAttribute(client->response, attr, 0);
3128 }
3129
3130 notify_attributes = attr;
3131 }
3132 else if (!strcmp(attrname, "notify-charset"))
3133 {
3134 if (ippGetValueTag(attr) != IPP_TAG_CHARSET || ippGetCount(attr) != 1 ||
3135 (strcmp(ippGetString(attr, 0, NULL), "us-ascii") && strcmp(ippGetString(attr, 0, NULL), "utf-8")))
3136 {
3137 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3138 ippCopyAttribute(client->response, attr, 0);
3139 }
3140 }
3141 else if (!strcmp(attrname, "notify-natural-language"))
3142 {
3143 if (ippGetValueTag(attr) != IPP_TAG_LANGUAGE || ippGetCount(attr) != 1 || strcmp(ippGetString(attr, 0, NULL), "en"))
3144 {
3145 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3146 ippCopyAttribute(client->response, attr, 0);
3147 }
3148 }
3149 else if (!strcmp(attrname, "notify-user-data"))
3150 {
3151 int datalen; /* Length of data */
3152
3153 if (ippGetValueTag(attr) != IPP_TAG_STRING || ippGetCount(attr) != 1 || !ippGetOctetString(attr, 0, &datalen) || datalen > 63)
3154 {
3155 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3156 ippCopyAttribute(client->response, attr, 0);
3157 }
3158 else
3159 notify_user_data = attr;
3160 }
3161 else if (!strcmp(attrname, "notify-events"))
3162 {
3163 if (ippGetValueTag(attr) != IPP_TAG_KEYWORD)
3164 {
3165 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3166 ippCopyAttribute(client->response, attr, 0);
3167 }
3168 else
3169 notify_events = attr;
3170 }
3171 else if (!strcmp(attrname, "notify-lease-duration"))
3172 {
3173 if (ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetCount(attr) != 1 || ippGetInteger(attr, 0) < 0)
3174 {
3175 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3176 ippCopyAttribute(client->response, attr, 0);
3177 }
3178 else
3179 lease = ippGetInteger(attr, 0);
3180 }
3181 else if (!strcmp(attrname, "notify-time-interval"))
3182 {
3183 if (ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetCount(attr) != 1 || ippGetInteger(attr, 0) < 0)
3184 {
3185 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3186 ippCopyAttribute(client->response, attr, 0);
3187 }
3188 else
3189 interval = ippGetInteger(attr, 0);
3190 }
3191 else if (!strcmp(attrname, "notify-job-id"))
3192 {
3193 if (ippGetOperation(client->request) != IPP_OP_CREATE_JOB_SUBSCRIPTIONS || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 1)
3194 {
3195 status = IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES;
3196 ippCopyAttribute(client->response, attr, 0);
3197 }
3198 else if ((job = find_job(client, ippGetInteger(attr, 0))) == NULL)
3199 {
3200 status = IPP_STATUS_ERROR_NOT_FOUND;
3201 ippCopyAttribute(client->response, attr, 0);
3202 }
3203 }
3204
3205 attr = ippNextAttribute(client->request);
3206 }
3207
3208 if (status)
3209 {
3210 ippAddInteger(client->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", status);
3211 }
3212 else if (!pullmethod)
3213 {
3214 ippAddInteger(client->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", IPP_STATUS_ERROR_BAD_REQUEST);
3215 }
3216 else
3217 {
3218 switch (ippGetOperation(client->request))
3219 {
3220 case IPP_OP_PRINT_JOB :
3221 case IPP_OP_PRINT_URI :
3222 case IPP_OP_CREATE_JOB :
3223 job = client->job;
3224 break;
3225
3226 default :
3227 break;
3228 }
3229
3230 if ((sub = create_subscription(client->printer, job, interval, lease, username, notify_events, notify_attributes, notify_user_data)) == NULL)
3231 {
3232 ippAddInteger(client->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-subscription-id", sub->id);
3233 ok_subs ++;
3234 }
3235 else
3236 ippAddInteger(client->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", IPP_STATUS_ERROR_INTERNAL);
3237 }
3238 }
3239
3240 if (ok_subs == 0)
3241 ippSetStatusCode(client->response, IPP_STATUS_ERROR_IGNORED_ALL_SUBSCRIPTIONS);
3242 else if (ok_subs != num_subs)
3243 ippSetStatusCode(client->response, IPP_STATUS_OK_IGNORED_SUBSCRIPTIONS);
3244 }
3245
3246
3247 /*
3248 * 'ipp_deregister_output_device()' - Unregister an output device.
3249 */
3250
3251 static void
3252 ipp_deregister_output_device(
3253 _ipp_client_t *client) /* I - Client */
3254 {
3255 _ipp_device_t *device; /* Device */
3256
3257
3258 /*
3259 * Find the device...
3260 */
3261
3262 if ((device = find_device(client)) == NULL)
3263 {
3264 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Output device not found.");
3265 return;
3266 }
3267
3268 /*
3269 * Remove the device from the printer...
3270 */
3271
3272 _cupsRWLockWrite(&client->printer->rwlock);
3273
3274 cupsArrayRemove(client->printer->devices, device);
3275
3276 update_device_attributes_no_lock(client->printer);
3277 update_device_state_no_lock(client->printer);
3278
3279 _cupsRWUnlock(&client->printer->rwlock);
3280
3281 /*
3282 * Delete the device...
3283 */
3284
3285 delete_device(device);
3286
3287 respond_ipp(client, IPP_STATUS_OK, NULL);
3288 }
3289
3290
3291 /*
3292 * 'ipp_fetch_document()' - Download a document.
3293 */
3294
3295 static void
3296 ipp_fetch_document(
3297 _ipp_client_t *client) /* I - Client */
3298 {
3299 _ipp_device_t *device; /* Device */
3300 _ipp_job_t *job; /* Job */
3301 ipp_attribute_t *attr; /* Attribute */
3302 int compression; /* compression */
3303 char filename[1024]; /* Job filename */
3304 const char *format; /* document-format */
3305
3306
3307 if ((device = find_device(client)) == NULL)
3308 {
3309 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Device was not found.");
3310 return;
3311 }
3312
3313 if ((job = find_job(client, 0)) == NULL)
3314 {
3315 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job was not found.");
3316 return;
3317 }
3318
3319 if (!job->dev_uuid || strcmp(job->dev_uuid, device->uuid))
3320 {
3321 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job not assigned to device.");
3322 return;
3323 }
3324
3325 if ((attr = ippFindAttribute(client->request, "document-number", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(attr) != IPP_TAG_OPERATION || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetCount(attr) != 1 || ippGetInteger(attr, 0) != 1)
3326 {
3327 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, attr ? "Bad document-number attribute." : "Missing document-number attribute.");
3328 return;
3329 }
3330
3331 if ((attr = ippFindAttribute(client->request, "compression-accepted", IPP_TAG_KEYWORD)) != NULL)
3332 compression = !strcmp(ippGetString(attr, 0, NULL), "gzip");
3333 else
3334 compression = 0;
3335
3336 if ((attr = ippFindAttribute(client->request, "document-format-accepted", IPP_TAG_MIMETYPE)) != NULL)
3337 {
3338 int i, /* Looping var */
3339 count = ippGetCount(attr); /* Number of values */
3340
3341
3342 for (i = 0; i < count; i ++)
3343 {
3344 format = ippGetString(attr, i, NULL);
3345
3346 create_job_filename(client->printer, job, NULL, filename, sizeof(filename));
3347
3348 if (!access(filename, R_OK))
3349 break;
3350 }
3351
3352 if (i >= count)
3353 {
3354 respond_ipp(client, _IPP_STATUS_ERROR_NOT_FETCHABLE, "Document not available in requested format.");
3355 return;
3356 }
3357 }
3358 else if ((attr = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE)) != NULL)
3359 format = ippGetString(attr, 0, NULL);
3360 else
3361 {
3362 respond_ipp(client, _IPP_STATUS_ERROR_NOT_FETCHABLE, "Document format unknown.");
3363 return;
3364 }
3365
3366 respond_ipp(client, IPP_STATUS_OK, NULL);
3367 ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
3368 ippAddString(client->response, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "compression", NULL, compression ? "gzip" : "none");
3369
3370 client->fetch_file = open(filename, O_RDONLY);
3371 }
3372
3373
3374 /*
3375 * 'ipp_fetch_job()' - Download a job.
3376 */
3377
3378 static void
3379 ipp_fetch_job(_ipp_client_t *client) /* I - Client */
3380 {
3381 _ipp_device_t *device; /* Device */
3382 _ipp_job_t *job; /* Job */
3383
3384
3385 if ((device = find_device(client)) == NULL)
3386 {
3387 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Device was not found.");
3388 return;
3389 }
3390
3391 if ((job = find_job(client, 0)) == NULL)
3392 {
3393 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job was not found.");
3394 return;
3395 }
3396
3397 if (job->dev_uuid && strcmp(job->dev_uuid, device->uuid))
3398 {
3399 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job not assigned to device.");
3400 return;
3401 }
3402
3403 if (!(job->state_reasons & _IPP_JREASON_JOB_FETCHABLE))
3404 {
3405 respond_ipp(client, _IPP_STATUS_ERROR_NOT_FETCHABLE, "Job not fetchable.");
3406 return;
3407 }
3408
3409 respond_ipp(client, IPP_STATUS_OK, NULL);
3410 copy_attributes(client->response, job->attrs, NULL, IPP_TAG_JOB, 0);
3411 }
3412
3413
3414 /*
3415 * 'ipp_get_document_attributes()' - Get the attributes for a document object.
3416 *
3417 * Note: This implementation only supports single document jobs so we
3418 * synthesize the information for a single document from the job.
3419 */
3420
3421 static void
3422 ipp_get_document_attributes(
3423 _ipp_client_t *client) /* I - Client */
3424 {
3425 // TODO: Implement this!
3426 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Need to implement this.");
3427 }
3428
3429
3430 /*
3431 * 'ipp_get_documents()' - Get the list of documents in a job.
3432 *
3433 * Note: This implementation only supports single document jobs so we
3434 * synthesize the information for a single document from the job.
3435 */
3436
3437 static void
3438 ipp_get_documents(_ipp_client_t *client)/* I - Client */
3439 {
3440 // TODO: Implement this!
3441 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Need to implement this.");
3442 }
3443
3444
3445 /*
3446 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3447 */
3448
3449 static void
3450 ipp_get_job_attributes(
3451 _ipp_client_t *client) /* I - Client */
3452 {
3453 _ipp_job_t *job; /* Job */
3454 cups_array_t *ra; /* requested-attributes */
3455
3456
3457 if ((job = find_job(client, 0)) == NULL)
3458 {
3459 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
3460 return;
3461 }
3462
3463 respond_ipp(client, IPP_STATUS_OK, NULL);
3464
3465 ra = ippCreateRequestedArray(client->request);
3466 copy_job_attributes(client, job, ra);
3467 cupsArrayDelete(ra);
3468 }
3469
3470
3471 /*
3472 * 'ipp_get_jobs()' - Get a list of job objects.
3473 */
3474
3475 static void
3476 ipp_get_jobs(_ipp_client_t *client) /* I - Client */
3477 {
3478 ipp_attribute_t *attr; /* Current attribute */
3479 const char *which_jobs = NULL;
3480 /* which-jobs values */
3481 int job_comparison; /* Job comparison */
3482 ipp_jstate_t job_state; /* job-state value */
3483 int first_job_id, /* First job ID */
3484 limit, /* Maximum number of jobs to return */
3485 count; /* Number of jobs that match */
3486 const char *username; /* Username */
3487 _ipp_job_t *job; /* Current job pointer */
3488 cups_array_t *ra; /* Requested attributes array */
3489
3490
3491 /*
3492 * See if the "which-jobs" attribute have been specified...
3493 */
3494
3495 if ((attr = ippFindAttribute(client->request, "which-jobs",
3496 IPP_TAG_KEYWORD)) != NULL)
3497 {
3498 which_jobs = ippGetString(attr, 0, NULL);
3499 fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
3500 }
3501
3502 if (!which_jobs || !strcmp(which_jobs, "not-completed"))
3503 {
3504 job_comparison = -1;
3505 job_state = IPP_JSTATE_STOPPED;
3506 }
3507 else if (!strcmp(which_jobs, "completed"))
3508 {
3509 job_comparison = 1;
3510 job_state = IPP_JSTATE_CANCELED;
3511 }
3512 else if (!strcmp(which_jobs, "aborted"))
3513 {
3514 job_comparison = 0;
3515 job_state = IPP_JSTATE_ABORTED;
3516 }
3517 else if (!strcmp(which_jobs, "all"))
3518 {
3519 job_comparison = 1;
3520 job_state = IPP_JSTATE_PENDING;
3521 }
3522 else if (!strcmp(which_jobs, "canceled"))
3523 {
3524 job_comparison = 0;
3525 job_state = IPP_JSTATE_CANCELED;
3526 }
3527 else if (!strcmp(which_jobs, "pending"))
3528 {
3529 job_comparison = 0;
3530 job_state = IPP_JSTATE_PENDING;
3531 }
3532 else if (!strcmp(which_jobs, "pending-held"))
3533 {
3534 job_comparison = 0;
3535 job_state = IPP_JSTATE_HELD;
3536 }
3537 else if (!strcmp(which_jobs, "processing"))
3538 {
3539 job_comparison = 0;
3540 job_state = IPP_JSTATE_PROCESSING;
3541 }
3542 else if (!strcmp(which_jobs, "processing-stopped"))
3543 {
3544 job_comparison = 0;
3545 job_state = IPP_JSTATE_STOPPED;
3546 }
3547 else
3548 {
3549 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
3550 "The which-jobs value \"%s\" is not supported.", which_jobs);
3551 ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
3552 "which-jobs", NULL, which_jobs);
3553 return;
3554 }
3555
3556 /*
3557 * See if they want to limit the number of jobs reported...
3558 */
3559
3560 if ((attr = ippFindAttribute(client->request, "limit",
3561 IPP_TAG_INTEGER)) != NULL)
3562 {
3563 limit = ippGetInteger(attr, 0);
3564
3565 fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
3566 }
3567 else
3568 limit = 0;
3569
3570 if ((attr = ippFindAttribute(client->request, "first-job-id",
3571 IPP_TAG_INTEGER)) != NULL)
3572 {
3573 first_job_id = ippGetInteger(attr, 0);
3574
3575 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname,
3576 first_job_id);
3577 }
3578 else
3579 first_job_id = 1;
3580
3581 /*
3582 * See if we only want to see jobs for a specific user...
3583 */
3584
3585 username = NULL;
3586
3587 if ((attr = ippFindAttribute(client->request, "my-jobs",
3588 IPP_TAG_BOOLEAN)) != NULL)
3589 {
3590 int my_jobs = ippGetBoolean(attr, 0);
3591
3592 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname,
3593 my_jobs ? "true" : "false");
3594
3595 if (my_jobs)
3596 {
3597 if ((attr = ippFindAttribute(client->request, "requesting-user-name",
3598 IPP_TAG_NAME)) == NULL)
3599 {
3600 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3601 "Need requesting-user-name with my-jobs.");
3602 return;
3603 }
3604
3605 username = ippGetString(attr, 0, NULL);
3606
3607 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3608 client->hostname, username);
3609 }
3610 }
3611
3612 /*
3613 * OK, build a list of jobs for this printer...
3614 */
3615
3616 ra = ippCreateRequestedArray(client->request);
3617
3618 respond_ipp(client, IPP_STATUS_OK, NULL);
3619
3620 _cupsRWLockRead(&(client->printer->rwlock));
3621
3622 for (count = 0, job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs);
3623 (limit <= 0 || count < limit) && job;
3624 job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
3625 {
3626 /*
3627 * Filter out jobs that don't match...
3628 */
3629
3630 if ((job_comparison < 0 && job->state > job_state) ||
3631 (job_comparison == 0 && job->state != job_state) ||
3632 (job_comparison > 0 && job->state < job_state) ||
3633 job->id < first_job_id ||
3634 (username && job->username &&
3635 strcasecmp(username, job->username)))
3636 continue;
3637
3638 if (count > 0)
3639 ippAddSeparator(client->response);
3640
3641 count ++;
3642 copy_job_attributes(client, job, ra);
3643 }
3644
3645 cupsArrayDelete(ra);
3646
3647 _cupsRWUnlock(&(client->printer->rwlock));
3648 }
3649
3650
3651 /*
3652 * 'ipp_get_notifications()' - Get notification events for one or more subscriptions.
3653 */
3654
3655 static void
3656 ipp_get_notifications(
3657 _ipp_client_t *client) /* I - Client */
3658 {
3659 ipp_attribute_t *sub_ids, /* notify-subscription-ids */
3660 *seq_nums, /* notify-sequence-numbers */
3661 *notify_wait; /* Wait for events? */
3662 int i, /* Looping vars */
3663 count, /* Number of IDs */
3664 first = 1, /* First event? */
3665 seq_num; /* Sequence number */
3666 _ipp_subscription_t *sub; /* Current subscription */
3667 ipp_t *event; /* Current event */
3668
3669
3670 if ((sub_ids = ippFindAttribute(client->request, "notify-subscription-ids", IPP_TAG_INTEGER)) == NULL)
3671 {
3672 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing notify-subscription-ids attribute.");
3673 return;
3674 }
3675
3676 count = ippGetCount(sub_ids);
3677 seq_nums = ippFindAttribute(client->request, "notify-sequence-numbers", IPP_TAG_INTEGER);
3678 notify_wait = ippFindAttribute(client->request, "notify-wait", IPP_TAG_BOOLEAN);
3679
3680 if (seq_nums && count != ippGetCount(seq_nums))
3681 {
3682 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The notify-subscription-ids and notify-sequence-numbers attributes are different lengths.");
3683 return;
3684 }
3685
3686 respond_ipp(client, IPP_STATUS_OK, NULL);
3687 ippAddInteger(client->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-get-interval", 30);
3688
3689 for (i = 0; i < count; i ++)
3690 {
3691 if ((sub = find_subscription(client, ippGetInteger(sub_ids, i))) == NULL)
3692 continue;
3693
3694 seq_num = ippGetInteger(seq_nums, i);
3695 if (seq_num < sub->first_sequence)
3696 seq_num = sub->first_sequence;
3697
3698 if (seq_num > sub->last_sequence)
3699 continue;
3700
3701 for (event = (ipp_t *)cupsArrayIndex(sub->events, seq_num - sub->first_sequence);
3702 event;
3703 event = (ipp_t *)cupsArrayNext(sub->events))
3704 {
3705 if (first)
3706 first = 0;
3707 else
3708 ippAddSeparator(client->response);
3709
3710 ippCopyAttributes(client->response, event, 0, NULL, NULL);
3711 }
3712 }
3713 }
3714
3715
3716 /*
3717 * 'ipp_get_output_device_attributes()' - Get attributes for an output device.
3718 */
3719
3720 static void
3721 ipp_get_output_device_attributes(
3722 _ipp_client_t *client) /* I - Client */
3723 {
3724 // TODO: Implement this!
3725 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Need to implement this.");
3726 }
3727
3728
3729 /*
3730 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3731 */
3732
3733 static void
3734 ipp_get_printer_attributes(
3735 _ipp_client_t *client) /* I - Client */
3736 {
3737 cups_array_t *ra; /* Requested attributes array */
3738 _ipp_printer_t *printer; /* Printer */
3739
3740
3741 /*
3742 * Send the attributes...
3743 */
3744
3745 ra = ippCreateRequestedArray(client->request);
3746 printer = client->printer;
3747
3748 respond_ipp(client, IPP_STATUS_OK, NULL);
3749
3750 _cupsRWLockRead(&(printer->rwlock));
3751
3752 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
3753 IPP_TAG_CUPS_CONST);
3754 copy_attributes(client->response, printer->dev_attrs, ra, IPP_TAG_ZERO, IPP_TAG_ZERO);
3755
3756 if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
3757 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
3758
3759 if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
3760 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
3761
3762 if (!ra || cupsArrayFind(ra, "printer-current-time"))
3763 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
3764
3765
3766 if (!ra || cupsArrayFind(ra, "printer-state"))
3767 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3768 "printer-state", printer->state > printer->dev_state ? printer->state : printer->dev_state);
3769
3770 if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
3771 ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
3772
3773 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
3774 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
3775
3776 if (!ra || cupsArrayFind(ra, "printer-state-message"))
3777 {
3778 static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
3779
3780 if (printer->state > printer->dev_state)
3781 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
3782 else
3783 ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->dev_state - IPP_PSTATE_IDLE]);
3784 }
3785
3786 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
3787 copy_printer_state_reasons(client->response, IPP_TAG_PRINTER, printer);
3788
3789 if (!ra || cupsArrayFind(ra, "printer-up-time"))
3790 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
3791
3792 if (!ra || cupsArrayFind(ra, "queued-job-count"))
3793 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", cupsArrayCount(printer->active_jobs));
3794
3795 _cupsRWUnlock(&(printer->rwlock));
3796
3797 cupsArrayDelete(ra);
3798 }
3799
3800
3801 /*
3802 * 'ipp_get_printer_supported_values()' - Return the supported values for
3803 * the infrastructure printer.
3804 */
3805
3806 static void
3807 ipp_get_printer_supported_values(
3808 _ipp_client_t *client) /* I - Client */
3809 {
3810 cups_array_t *ra = ippCreateRequestedArray(client->request);
3811 /* Requested attributes */
3812
3813
3814 respond_ipp(client, IPP_STATUS_OK, NULL);
3815
3816 copy_attributes(client->response, client->printer->attrs, ra, IPP_TAG_PRINTER, 1);
3817
3818 cupsArrayDelete(ra);
3819 }
3820
3821
3822 /*
3823 * 'ipp_get_subscription_attributes()' - Get attributes for a subscription.
3824 */
3825
3826 static void
3827 ipp_get_subscription_attributes(
3828 _ipp_client_t *client) /* I - Client */
3829 {
3830 _ipp_subscription_t *sub; /* Subscription */
3831 cups_array_t *ra = ippCreateRequestedArray(client->request);
3832 /* Requested attributes */
3833
3834
3835 if ((sub = find_subscription(client, 0)) == NULL)
3836 {
3837 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Subscription was not found.");
3838 }
3839 else
3840 {
3841 respond_ipp(client, IPP_STATUS_OK, NULL);
3842 copy_subscription_attributes(client, sub, ra);
3843 }
3844
3845 cupsArrayDelete(ra);
3846 }
3847
3848
3849 /*
3850 * 'ipp_get_subscriptions()' - Get attributes for all subscriptions.
3851 */
3852
3853 static void
3854 ipp_get_subscriptions(
3855 _ipp_client_t *client) /* I - Client */
3856 {
3857 _ipp_subscription_t *sub; /* Current subscription */
3858 cups_array_t *ra = ippCreateRequestedArray(client->request);
3859 /* Requested attributes */
3860 int first = 1; /* First time? */
3861
3862
3863 respond_ipp(client, IPP_STATUS_OK, NULL);
3864 _cupsRWLockRead(&client->printer->rwlock);
3865 for (sub = (_ipp_subscription_t *)cupsArrayFirst(client->printer->subscriptions);
3866 sub;
3867 sub = (_ipp_subscription_t *)cupsArrayNext(client->printer->subscriptions))
3868 {
3869 if (first)
3870 first = 0;
3871 else
3872 ippAddSeparator(client->response);
3873
3874 copy_subscription_attributes(client, sub, ra);
3875 }
3876
3877 cupsArrayDelete(ra);
3878 }
3879
3880
3881 /*
3882 * 'ipp_identify_printer()' - Beep or display a message.
3883 */
3884
3885 static void
3886 ipp_identify_printer(
3887 _ipp_client_t *client) /* I - Client */
3888 {
3889 /* TODO: Do something */
3890
3891 respond_ipp(client, IPP_STATUS_OK, NULL);
3892 }
3893
3894
3895 /*
3896 * 'ipp_print_job()' - Create a job object with an attached document.
3897 */
3898
3899 static void
3900 ipp_print_job(_ipp_client_t *client) /* I - Client */
3901 {
3902 _ipp_job_t *job; /* New job */
3903 char filename[1024], /* Filename buffer */
3904 buffer[4096]; /* Copy buffer */
3905 ssize_t bytes; /* Bytes read */
3906 cups_array_t *ra; /* Attributes to send in response */
3907
3908
3909 /*
3910 * Validate print job attributes...
3911 */
3912
3913 if (!valid_job_attributes(client))
3914 {
3915 httpFlush(client->http);
3916 return;
3917 }
3918
3919 /*
3920 * Do we have a file to print?
3921 */
3922
3923 if (httpGetState(client->http) == HTTP_STATE_POST_SEND)
3924 {
3925 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
3926 return;
3927 }
3928
3929 /*
3930 * Print the job...
3931 */
3932
3933 if ((job = create_job(client)) == NULL)
3934 {
3935 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
3936 "Currently printing another job.");
3937 return;
3938 }
3939
3940 /*
3941 * Create a file for the request data...
3942 */
3943
3944 create_job_filename(client->printer, job, NULL, filename, sizeof(filename));
3945
3946 if (Verbosity)
3947 fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
3948
3949 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
3950 {
3951 job->state = IPP_JSTATE_ABORTED;
3952
3953 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3954 "Unable to create print file: %s", strerror(errno));
3955 return;
3956 }
3957
3958 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
3959 {
3960 if (write(job->fd, buffer, (size_t)bytes) < bytes)
3961 {
3962 int error = errno; /* Write error */
3963
3964 job->state = IPP_JSTATE_ABORTED;
3965
3966 close(job->fd);
3967 job->fd = -1;
3968
3969 unlink(filename);
3970
3971 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3972 "Unable to write print file: %s", strerror(error));
3973 return;
3974 }
3975 }
3976
3977 if (bytes < 0)
3978 {
3979 /*
3980 * Got an error while reading the print data, so abort this job.
3981 */
3982
3983 job->state = IPP_JSTATE_ABORTED;
3984
3985 close(job->fd);
3986 job->fd = -1;
3987
3988 unlink(filename);
3989
3990 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3991 "Unable to read print file.");
3992 return;
3993 }
3994
3995 if (close(job->fd))
3996 {
3997 int error = errno; /* Write error */
3998
3999 job->state = IPP_JSTATE_ABORTED;
4000 job->fd = -1;
4001
4002 unlink(filename);
4003
4004 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4005 "Unable to write print file: %s", strerror(error));
4006 return;
4007 }
4008
4009 job->fd = -1;
4010 job->filename = strdup(filename);
4011 job->state = IPP_JSTATE_PENDING;
4012
4013 /*
4014 * Process the job, if possible...
4015 */
4016
4017 check_jobs(client->printer);
4018
4019 /*
4020 * Return the job info...
4021 */
4022
4023 respond_ipp(client, IPP_STATUS_OK, NULL);
4024
4025 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4026 cupsArrayAdd(ra, "job-id");
4027 cupsArrayAdd(ra, "job-state");
4028 cupsArrayAdd(ra, "job-state-message");
4029 cupsArrayAdd(ra, "job-state-reasons");
4030 cupsArrayAdd(ra, "job-uri");
4031
4032 copy_job_attributes(client, job, ra);
4033 cupsArrayDelete(ra);
4034
4035 /*
4036 * Process any pending subscriptions...
4037 */
4038
4039 client->job = job;
4040 ipp_create_xxx_subscriptions(client);
4041 }
4042
4043
4044 /*
4045 * 'ipp_print_uri()' - Create a job object with a referenced document.
4046 */
4047
4048 static void
4049 ipp_print_uri(_ipp_client_t *client) /* I - Client */
4050 {
4051 _ipp_job_t *job; /* New job */
4052 ipp_attribute_t *uri; /* document-uri */
4053 char scheme[256], /* URI scheme */
4054 userpass[256], /* Username and password info */
4055 hostname[256], /* Hostname */
4056 resource[1024]; /* Resource path */
4057 int port; /* Port number */
4058 http_uri_status_t uri_status; /* URI decode status */
4059 http_encryption_t encryption; /* Encryption to use, if any */
4060 http_t *http; /* Connection for http/https URIs */
4061 http_status_t status; /* Access status for http/https URIs */
4062 int infile; /* Input file for local file URIs */
4063 char filename[1024], /* Filename buffer */
4064 buffer[4096]; /* Copy buffer */
4065 ssize_t bytes; /* Bytes read */
4066 cups_array_t *ra; /* Attributes to send in response */
4067 static const char * const uri_status_strings[] =
4068 { /* URI decode errors */
4069 "URI too large.",
4070 "Bad arguments to function.",
4071 "Bad resource in URI.",
4072 "Bad port number in URI.",
4073 "Bad hostname in URI.",
4074 "Bad username in URI.",
4075 "Bad scheme in URI.",
4076 "Bad/empty URI."
4077 };
4078
4079
4080 /*
4081 * Validate print job attributes...
4082 */
4083
4084 if (!valid_job_attributes(client))
4085 {
4086 httpFlush(client->http);
4087 return;
4088 }
4089
4090 /*
4091 * Do we have a file to print?
4092 */
4093
4094 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
4095 {
4096 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4097 "Unexpected document data following request.");
4098 return;
4099 }
4100
4101 /*
4102 * Do we have a document URI?
4103 */
4104
4105 if ((uri = ippFindAttribute(client->request, "document-uri",
4106 IPP_TAG_URI)) == NULL)
4107 {
4108 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
4109 return;
4110 }
4111
4112 if (ippGetCount(uri) != 1)
4113 {
4114 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4115 "Too many document-uri values.");
4116 return;
4117 }
4118
4119 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
4120 scheme, sizeof(scheme), userpass,
4121 sizeof(userpass), hostname, sizeof(hostname),
4122 &port, resource, sizeof(resource));
4123 if (uri_status < HTTP_URI_STATUS_OK)
4124 {
4125 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
4126 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
4127 return;
4128 }
4129
4130 if (strcmp(scheme, "file") &&
4131 #ifdef HAVE_SSL
4132 strcmp(scheme, "https") &&
4133 #endif /* HAVE_SSL */
4134 strcmp(scheme, "http"))
4135 {
4136 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
4137 "URI scheme \"%s\" not supported.", scheme);
4138 return;
4139 }
4140
4141 if (!strcmp(scheme, "file") && access(resource, R_OK))
4142 {
4143 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4144 "Unable to access URI: %s", strerror(errno));
4145 return;
4146 }
4147
4148 /*
4149 * Print the job...
4150 */
4151
4152 if ((job = create_job(client)) == NULL)
4153 {
4154 respond_ipp(client, IPP_STATUS_ERROR_BUSY,
4155 "Currently printing another job.");
4156 return;
4157 }
4158
4159 /*
4160 * Create a file for the request data...
4161 */
4162
4163 if (!strcasecmp(job->format, "image/jpeg"))
4164 snprintf(filename, sizeof(filename), "%s/%d.jpg",
4165 client->printer->directory, job->id);
4166 else if (!strcasecmp(job->format, "image/png"))
4167 snprintf(filename, sizeof(filename), "%s/%d.png",
4168 client->printer->directory, job->id);
4169 else if (!strcasecmp(job->format, "application/pdf"))
4170 snprintf(filename, sizeof(filename), "%s/%d.pdf",
4171 client->printer->directory, job->id);
4172 else if (!strcasecmp(job->format, "application/postscript"))
4173 snprintf(filename, sizeof(filename), "%s/%d.ps",
4174 client->printer->directory, job->id);
4175 else
4176 snprintf(filename, sizeof(filename), "%s/%d.prn",
4177 client->printer->directory, job->id);
4178
4179 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
4180 {
4181 job->state = IPP_JSTATE_ABORTED;
4182
4183 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4184 "Unable to create print file: %s", strerror(errno));
4185 return;
4186 }
4187
4188 if (!strcmp(scheme, "file"))
4189 {
4190 if ((infile = open(resource, O_RDONLY)) < 0)
4191 {
4192 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4193 "Unable to access URI: %s", strerror(errno));
4194 return;
4195 }
4196
4197 do
4198 {
4199 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
4200 (errno == EAGAIN || errno == EINTR))
4201 bytes = 1;
4202 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
4203 {
4204 int error = errno; /* Write error */
4205
4206 job->state = IPP_JSTATE_ABORTED;
4207
4208 close(job->fd);
4209 job->fd = -1;
4210
4211 unlink(filename);
4212 close(infile);
4213
4214 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4215 "Unable to write print file: %s", strerror(error));
4216 return;
4217 }
4218 }
4219 while (bytes > 0);
4220
4221 close(infile);
4222 }
4223 else
4224 {
4225 #ifdef HAVE_SSL
4226 if (port == 443 || !strcmp(scheme, "https"))
4227 encryption = HTTP_ENCRYPTION_ALWAYS;
4228 else
4229 #endif /* HAVE_SSL */
4230 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
4231
4232 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
4233 1, 30000, NULL)) == NULL)
4234 {
4235 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4236 "Unable to connect to %s: %s", hostname,
4237 cupsLastErrorString());
4238 job->state = IPP_JSTATE_ABORTED;
4239
4240 close(job->fd);
4241 job->fd = -1;
4242
4243 unlink(filename);
4244 return;
4245 }
4246
4247 httpClearFields(http);
4248 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
4249 if (httpGet(http, resource))
4250 {
4251 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4252 "Unable to GET URI: %s", strerror(errno));
4253
4254 job->state = IPP_JSTATE_ABORTED;
4255
4256 close(job->fd);
4257 job->fd = -1;
4258
4259 unlink(filename);
4260 httpClose(http);
4261 return;
4262 }
4263
4264 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
4265
4266 if (status != HTTP_STATUS_OK)
4267 {
4268 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4269 "Unable to GET URI: %s", httpStatus(status));
4270
4271 job->state = IPP_JSTATE_ABORTED;
4272
4273 close(job->fd);
4274 job->fd = -1;
4275
4276 unlink(filename);
4277 httpClose(http);
4278 return;
4279 }
4280
4281 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
4282 {
4283 if (write(job->fd, buffer, (size_t)bytes) < bytes)
4284 {
4285 int error = errno; /* Write error */
4286
4287 job->state = IPP_JSTATE_ABORTED;
4288
4289 close(job->fd);
4290 job->fd = -1;
4291
4292 unlink(filename);
4293 httpClose(http);
4294
4295 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4296 "Unable to write print file: %s", strerror(error));
4297 return;
4298 }
4299 }
4300
4301 httpClose(http);
4302 }
4303
4304 if (close(job->fd))
4305 {
4306 int error = errno; /* Write error */
4307
4308 job->state = IPP_JSTATE_ABORTED;
4309 job->fd = -1;
4310
4311 unlink(filename);
4312
4313 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4314 "Unable to write print file: %s", strerror(error));
4315 return;
4316 }
4317
4318 job->fd = -1;
4319 job->filename = strdup(filename);
4320 job->state = IPP_JSTATE_PENDING;
4321
4322 /* TODO: Do something different here - only process if the printer is idle */
4323 /*
4324 * Process the job...
4325 */
4326
4327 check_jobs(client->printer);
4328
4329 /*
4330 * Return the job info...
4331 */
4332
4333 respond_ipp(client, IPP_STATUS_OK, NULL);
4334
4335 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4336 cupsArrayAdd(ra, "job-id");
4337 cupsArrayAdd(ra, "job-state");
4338 cupsArrayAdd(ra, "job-state-reasons");
4339 cupsArrayAdd(ra, "job-uri");
4340
4341 copy_job_attributes(client, job, ra);
4342 cupsArrayDelete(ra);
4343
4344 /*
4345 * Process any pending subscriptions...
4346 */
4347
4348 client->job = job;
4349 ipp_create_xxx_subscriptions(client);
4350 }
4351
4352
4353 /*
4354 * 'ipp_renew_subscription()' - Renew a subscription.
4355 */
4356
4357 static void
4358 ipp_renew_subscription(
4359 _ipp_client_t *client) /* I - Client */
4360 {
4361 _ipp_subscription_t *sub; /* Subscription */
4362 ipp_attribute_t *attr; /* notify-lease-duration */
4363 int lease; /* Lease duration in seconds */
4364
4365
4366 if ((sub = find_subscription(client, 0)) == NULL)
4367 {
4368 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Subscription was not found.");
4369 return;
4370 }
4371
4372 if (sub->job)
4373 {
4374 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Per-job subscriptions cannot be renewed.");
4375 return;
4376 }
4377
4378 if ((attr = ippFindAttribute(client->request, "notify-lease-duration", IPP_TAG_ZERO)) != NULL)
4379 {
4380 if (ippGetGroupTag(attr) != IPP_TAG_SUBSCRIPTION || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetCount(attr) != 1 || ippGetInteger(attr, 0) < 0)
4381 {
4382 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Bad notify-lease-duration.");
4383 return;
4384 }
4385
4386 lease = ippGetInteger(attr, 0);
4387 }
4388 else
4389 lease = _IPP_NOTIFY_LEASE_DURATION_DEFAULT;
4390
4391 sub->lease = lease;
4392
4393 if (lease)
4394 sub->expire = time(NULL) + sub->lease;
4395 else
4396 sub->expire = INT_MAX;
4397
4398 respond_ipp(client, IPP_STATUS_OK, NULL);
4399 }
4400
4401
4402 /*
4403 * 'ipp_send_document()' - Add an attached document to a job object created with
4404 * Create-Job.
4405 */
4406
4407 static void
4408 ipp_send_document(_ipp_client_t *client)/* I - Client */
4409 {
4410 _ipp_job_t *job; /* Job information */
4411 char filename[1024], /* Filename buffer */
4412 buffer[4096]; /* Copy buffer */
4413 ssize_t bytes; /* Bytes read */
4414 ipp_attribute_t *attr; /* Current attribute */
4415 cups_array_t *ra; /* Attributes to send in response */
4416
4417
4418 /*
4419 * Get the job...
4420 */
4421
4422 if ((job = find_job(client, 0)) == NULL)
4423 {
4424 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
4425 httpFlush(client->http);
4426 return;
4427 }
4428
4429 /*
4430 * See if we already have a document for this job or the job has already
4431 * in a non-pending state...
4432 */
4433
4434 if (job->state > IPP_JSTATE_HELD)
4435 {
4436 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
4437 "Job is not in a pending state.");
4438 httpFlush(client->http);
4439 return;
4440 }
4441 else if (job->filename || job->fd >= 0)
4442 {
4443 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
4444 "Multiple document jobs are not supported.");
4445 httpFlush(client->http);
4446 return;
4447 }
4448
4449 if ((attr = ippFindAttribute(client->request, "last-document",
4450 IPP_TAG_ZERO)) == NULL)
4451 {
4452 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4453 "Missing required last-document attribute.");
4454 httpFlush(client->http);
4455 return;
4456 }
4457 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
4458 !ippGetBoolean(attr, 0))
4459 {
4460 respond_unsupported(client, attr);
4461 httpFlush(client->http);
4462 return;
4463 }
4464
4465 /*
4466 * Validate document attributes...
4467 */
4468
4469 if (!valid_doc_attributes(client))
4470 {
4471 httpFlush(client->http);
4472 return;
4473 }
4474
4475 copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
4476
4477 /*
4478 * Get the document format for the job...
4479 */
4480
4481 _cupsRWLockWrite(&(client->printer->rwlock));
4482
4483 if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
4484 job->format = ippGetString(attr, 0, NULL);
4485 else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
4486 job->format = ippGetString(attr, 0, NULL);
4487 else
4488 job->format = "application/octet-stream";
4489
4490 /*
4491 * Create a file for the request data...
4492 */
4493
4494 create_job_filename(client->printer, job, NULL, filename, sizeof(filename));
4495
4496 if (Verbosity)
4497 fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
4498
4499 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
4500
4501 _cupsRWUnlock(&(client->printer->rwlock));
4502
4503 if (job->fd < 0)
4504 {
4505 job->state = IPP_JSTATE_ABORTED;
4506
4507 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4508 "Unable to create print file: %s", strerror(errno));
4509 return;
4510 }
4511
4512 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
4513 {
4514 if (write(job->fd, buffer, (size_t)bytes) < bytes)
4515 {
4516 int error = errno; /* Write error */
4517
4518 job->state = IPP_JSTATE_ABORTED;
4519
4520 close(job->fd);
4521 job->fd = -1;
4522
4523 unlink(filename);
4524
4525 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4526 "Unable to write print file: %s", strerror(error));
4527 return;
4528 }
4529 }
4530
4531 if (bytes < 0)
4532 {
4533 /*
4534 * Got an error while reading the print data, so abort this job.
4535 */
4536
4537 job->state = IPP_JSTATE_ABORTED;
4538
4539 close(job->fd);
4540 job->fd = -1;
4541
4542 unlink(filename);
4543
4544 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4545 "Unable to read print file.");
4546 return;
4547 }
4548
4549 if (close(job->fd))
4550 {
4551 int error = errno; /* Write error */
4552
4553 job->state = IPP_JSTATE_ABORTED;
4554 job->fd = -1;
4555
4556 unlink(filename);
4557
4558 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4559 "Unable to write print file: %s", strerror(error));
4560 return;
4561 }
4562
4563 _cupsRWLockWrite(&(client->printer->rwlock));
4564
4565 job->fd = -1;
4566 job->filename = strdup(filename);
4567 job->state = IPP_JSTATE_PENDING;
4568
4569 _cupsRWUnlock(&(client->printer->rwlock));
4570
4571 /*
4572 * Process the job, if possible...
4573 */
4574
4575 check_jobs(client->printer);
4576
4577 /*
4578 * Return the job info...
4579 */
4580
4581 respond_ipp(client, IPP_STATUS_OK, NULL);
4582
4583 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4584 cupsArrayAdd(ra, "job-id");
4585 cupsArrayAdd(ra, "job-state");
4586 cupsArrayAdd(ra, "job-state-reasons");
4587 cupsArrayAdd(ra, "job-uri");
4588
4589 copy_job_attributes(client, job, ra);
4590 cupsArrayDelete(ra);
4591 }
4592
4593
4594 /*
4595 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4596 * Create-Job.
4597 */
4598
4599 static void
4600 ipp_send_uri(_ipp_client_t *client) /* I - Client */
4601 {
4602 _ipp_job_t *job; /* Job information */
4603 ipp_attribute_t *uri; /* document-uri */
4604 char scheme[256], /* URI scheme */
4605 userpass[256], /* Username and password info */
4606 hostname[256], /* Hostname */
4607 resource[1024]; /* Resource path */
4608 int port; /* Port number */
4609 http_uri_status_t uri_status; /* URI decode status */
4610 http_encryption_t encryption; /* Encryption to use, if any */
4611 http_t *http; /* Connection for http/https URIs */
4612 http_status_t status; /* Access status for http/https URIs */
4613 int infile; /* Input file for local file URIs */
4614 char filename[1024], /* Filename buffer */
4615 buffer[4096]; /* Copy buffer */
4616 ssize_t bytes; /* Bytes read */
4617 ipp_attribute_t *attr; /* Current attribute */
4618 cups_array_t *ra; /* Attributes to send in response */
4619 static const char * const uri_status_strings[] =
4620 { /* URI decode errors */
4621 "URI too large.",
4622 "Bad arguments to function.",
4623 "Bad resource in URI.",
4624 "Bad port number in URI.",
4625 "Bad hostname in URI.",
4626 "Bad username in URI.",
4627 "Bad scheme in URI.",
4628 "Bad/empty URI."
4629 };
4630
4631
4632 /*
4633 * Get the job...
4634 */
4635
4636 if ((job = find_job(client, 0)) == NULL)
4637 {
4638 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
4639 httpFlush(client->http);
4640 return;
4641 }
4642
4643 /*
4644 * See if we already have a document for this job or the job has already
4645 * in a non-pending state...
4646 */
4647
4648 if (job->state > IPP_JSTATE_HELD)
4649 {
4650 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
4651 "Job is not in a pending state.");
4652 httpFlush(client->http);
4653 return;
4654 }
4655 else if (job->filename || job->fd >= 0)
4656 {
4657 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
4658 "Multiple document jobs are not supported.");
4659 httpFlush(client->http);
4660 return;
4661 }
4662
4663 if ((attr = ippFindAttribute(client->request, "last-document",
4664 IPP_TAG_ZERO)) == NULL)
4665 {
4666 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4667 "Missing required last-document attribute.");
4668 httpFlush(client->http);
4669 return;
4670 }
4671 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
4672 !ippGetBoolean(attr, 0))
4673 {
4674 respond_unsupported(client, attr);
4675 httpFlush(client->http);
4676 return;
4677 }
4678
4679 /*
4680 * Validate document attributes...
4681 */
4682
4683 if (!valid_doc_attributes(client))
4684 {
4685 httpFlush(client->http);
4686 return;
4687 }
4688
4689 /*
4690 * Do we have a file to print?
4691 */
4692
4693 if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
4694 {
4695 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4696 "Unexpected document data following request.");
4697 return;
4698 }
4699
4700 /*
4701 * Do we have a document URI?
4702 */
4703
4704 if ((uri = ippFindAttribute(client->request, "document-uri",
4705 IPP_TAG_URI)) == NULL)
4706 {
4707 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
4708 return;
4709 }
4710
4711 if (ippGetCount(uri) != 1)
4712 {
4713 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4714 "Too many document-uri values.");
4715 return;
4716 }
4717
4718 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
4719 scheme, sizeof(scheme), userpass,
4720 sizeof(userpass), hostname, sizeof(hostname),
4721 &port, resource, sizeof(resource));
4722 if (uri_status < HTTP_URI_STATUS_OK)
4723 {
4724 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
4725 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
4726 return;
4727 }
4728
4729 if (strcmp(scheme, "file") &&
4730 #ifdef HAVE_SSL
4731 strcmp(scheme, "https") &&
4732 #endif /* HAVE_SSL */
4733 strcmp(scheme, "http"))
4734 {
4735 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
4736 "URI scheme \"%s\" not supported.", scheme);
4737 return;
4738 }
4739
4740 if (!strcmp(scheme, "file") && access(resource, R_OK))
4741 {
4742 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4743 "Unable to access URI: %s", strerror(errno));
4744 return;
4745 }
4746
4747 /*
4748 * Get the document format for the job...
4749 */
4750
4751 _cupsRWLockWrite(&(client->printer->rwlock));
4752
4753 if ((attr = ippFindAttribute(job->attrs, "document-format",
4754 IPP_TAG_MIMETYPE)) != NULL)
4755 job->format = ippGetString(attr, 0, NULL);
4756 else
4757 job->format = "application/octet-stream";
4758
4759 /*
4760 * Create a file for the request data...
4761 */
4762
4763 if (!strcasecmp(job->format, "image/jpeg"))
4764 snprintf(filename, sizeof(filename), "%s/%d.jpg",
4765 client->printer->directory, job->id);
4766 else if (!strcasecmp(job->format, "image/png"))
4767 snprintf(filename, sizeof(filename), "%s/%d.png",
4768 client->printer->directory, job->id);
4769 else if (!strcasecmp(job->format, "application/pdf"))
4770 snprintf(filename, sizeof(filename), "%s/%d.pdf",
4771 client->printer->directory, job->id);
4772 else if (!strcasecmp(job->format, "application/postscript"))
4773 snprintf(filename, sizeof(filename), "%s/%d.ps",
4774 client->printer->directory, job->id);
4775 else
4776 snprintf(filename, sizeof(filename), "%s/%d.prn",
4777 client->printer->directory, job->id);
4778
4779 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
4780
4781 _cupsRWUnlock(&(client->printer->rwlock));
4782
4783 if (job->fd < 0)
4784 {
4785 job->state = IPP_JSTATE_ABORTED;
4786
4787 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4788 "Unable to create print file: %s", strerror(errno));
4789 return;
4790 }
4791
4792 if (!strcmp(scheme, "file"))
4793 {
4794 if ((infile = open(resource, O_RDONLY)) < 0)
4795 {
4796 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4797 "Unable to access URI: %s", strerror(errno));
4798 return;
4799 }
4800
4801 do
4802 {
4803 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
4804 (errno == EAGAIN || errno == EINTR))
4805 bytes = 1;
4806 else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
4807 {
4808 int error = errno; /* Write error */
4809
4810 job->state = IPP_JSTATE_ABORTED;
4811
4812 close(job->fd);
4813 job->fd = -1;
4814
4815 unlink(filename);
4816 close(infile);
4817
4818 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4819 "Unable to write print file: %s", strerror(error));
4820 return;
4821 }
4822 }
4823 while (bytes > 0);
4824
4825 close(infile);
4826 }
4827 else
4828 {
4829 #ifdef HAVE_SSL
4830 if (port == 443 || !strcmp(scheme, "https"))
4831 encryption = HTTP_ENCRYPTION_ALWAYS;
4832 else
4833 #endif /* HAVE_SSL */
4834 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
4835
4836 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
4837 1, 30000, NULL)) == NULL)
4838 {
4839 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4840 "Unable to connect to %s: %s", hostname,
4841 cupsLastErrorString());
4842 job->state = IPP_JSTATE_ABORTED;
4843
4844 close(job->fd);
4845 job->fd = -1;
4846
4847 unlink(filename);
4848 return;
4849 }
4850
4851 httpClearFields(http);
4852 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
4853 if (httpGet(http, resource))
4854 {
4855 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4856 "Unable to GET URI: %s", strerror(errno));
4857
4858 job->state = IPP_JSTATE_ABORTED;
4859
4860 close(job->fd);
4861 job->fd = -1;
4862
4863 unlink(filename);
4864 httpClose(http);
4865 return;
4866 }
4867
4868 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
4869
4870 if (status != HTTP_STATUS_OK)
4871 {
4872 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
4873 "Unable to GET URI: %s", httpStatus(status));
4874
4875 job->state = IPP_JSTATE_ABORTED;
4876
4877 close(job->fd);
4878 job->fd = -1;
4879
4880 unlink(filename);
4881 httpClose(http);
4882 return;
4883 }
4884
4885 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
4886 {
4887 if (write(job->fd, buffer, (size_t)bytes) < bytes)
4888 {
4889 int error = errno; /* Write error */
4890
4891 job->state = IPP_JSTATE_ABORTED;
4892
4893 close(job->fd);
4894 job->fd = -1;
4895
4896 unlink(filename);
4897 httpClose(http);
4898
4899 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4900 "Unable to write print file: %s", strerror(error));
4901 return;
4902 }
4903 }
4904
4905 httpClose(http);
4906 }
4907
4908 if (close(job->fd))
4909 {
4910 int error = errno; /* Write error */
4911
4912 job->state = IPP_JSTATE_ABORTED;
4913 job->fd = -1;
4914
4915 unlink(filename);
4916
4917 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
4918 "Unable to write print file: %s", strerror(error));
4919 return;
4920 }
4921
4922 _cupsRWLockWrite(&(client->printer->rwlock));
4923
4924 job->fd = -1;
4925 job->filename = strdup(filename);
4926 job->state = IPP_JSTATE_PENDING;
4927
4928 _cupsRWUnlock(&(client->printer->rwlock));
4929
4930 /*
4931 * Process the job, if possible...
4932 */
4933
4934 check_jobs(client->printer);
4935
4936 /*
4937 * Return the job info...
4938 */
4939
4940 respond_ipp(client, IPP_STATUS_OK, NULL);
4941
4942 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
4943 cupsArrayAdd(ra, "job-id");
4944 cupsArrayAdd(ra, "job-state");
4945 cupsArrayAdd(ra, "job-state-reasons");
4946 cupsArrayAdd(ra, "job-uri");
4947
4948 copy_job_attributes(client, job, ra);
4949 cupsArrayDelete(ra);
4950 }
4951
4952
4953 /*
4954 * 'ipp_update_active_jobs()' - Update the list of active jobs.
4955 */
4956
4957 static void
4958 ipp_update_active_jobs(
4959 _ipp_client_t *client) /* I - Client */
4960 {
4961 _ipp_device_t *device; /* Output device */
4962 _ipp_job_t *job; /* Job */
4963 ipp_attribute_t *job_ids, /* job-ids */
4964 *job_states; /* output-device-job-states */
4965 int i, /* Looping var */
4966 count, /* Number of values */
4967 num_different = 0,
4968 /* Number of jobs with different states */
4969 different[1000],/* Jobs with different states */
4970 num_unsupported = 0,
4971 /* Number of unsupported job-ids */
4972 unsupported[1000];
4973 /* Unsupported job-ids */
4974 ipp_jstate_t states[1000]; /* Different job state values */
4975
4976
4977 /*
4978 * Process the job-ids and output-device-job-states values...
4979 */
4980
4981 if ((device = find_device(client)) == NULL)
4982 {
4983 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Device was not found.");
4984 return;
4985 }
4986
4987 if ((job_ids = ippFindAttribute(client->request, "job-ids", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(job_ids) != IPP_TAG_OPERATION || ippGetValueTag(job_ids) != IPP_TAG_INTEGER)
4988 {
4989 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, job_ids ? "Bad job-ids attribute." : "Missing required job-ids attribute.");
4990 return;
4991 }
4992
4993 if ((job_states = ippFindAttribute(client->request, "output-device-job-states", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(job_states) != IPP_TAG_OPERATION || ippGetValueTag(job_states) != IPP_TAG_ENUM)
4994 {
4995 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, job_ids ? "Bad output-device-job-states attribute." : "Missing required output-device-job-states attribute.");
4996 return;
4997 }
4998
4999 count = ippGetCount(job_ids);
5000 if (count != ippGetCount(job_states))
5001 {
5002 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The job-ids and output-device-job-states attributes do not have the same number of values.");
5003 return;
5004 }
5005
5006 for (i = 0; i < count; i ++)
5007 {
5008 if ((job = find_job(client, ippGetInteger(job_ids, i))) == NULL || !job->dev_uuid || strcmp(job->dev_uuid, device->uuid))
5009 {
5010 if (num_unsupported < 1000)
5011 unsupported[num_unsupported ++] = ippGetInteger(job_ids, i);
5012 }
5013 else
5014 {
5015 ipp_jstate_t state = (ipp_jstate_t)ippGetInteger(job_states, i);
5016
5017 if (job->state >= IPP_JSTATE_STOPPED && state != job->state)
5018 {
5019 if (num_different < 1000)
5020 {
5021 different[num_different] = job->id;
5022 states[num_different ++] = job->state;
5023 }
5024 }
5025 else
5026 job->dev_state = state;
5027 }
5028 }
5029
5030 /*
5031 * Then look for jobs assigned to the device but not listed...
5032 */
5033
5034 for (job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs);
5035 job && num_different < 1000;
5036 job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
5037 {
5038 if (job->dev_uuid && !strcmp(job->dev_uuid, device->uuid) && !ippContainsInteger(job_ids, job->id))
5039 {
5040 different[num_different] = job->id;
5041 states[num_different ++] = job->state;
5042 }
5043 }
5044
5045 respond_ipp(client, IPP_STATUS_OK, NULL);
5046
5047 if (num_different > 0)
5048 {
5049 ippAddIntegers(client->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-ids", num_different, different);
5050 ippAddIntegers(client->response, IPP_TAG_OPERATION, IPP_TAG_ENUM, "output-device-job-states", num_different, (int *)states);
5051 }
5052
5053 if (num_unsupported > 0)
5054 {
5055 ippAddIntegers(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER, "job-ids", num_unsupported, unsupported);
5056 }
5057 }
5058
5059
5060 /*
5061 * 'ipp_update_document_status()' - Update the state of a document.
5062 */
5063
5064 static void
5065 ipp_update_document_status(
5066 _ipp_client_t *client) /* I - Client */
5067 {
5068 _ipp_device_t *device; /* Device */
5069 _ipp_job_t *job; /* Job */
5070 ipp_attribute_t *attr; /* Attribute */
5071
5072
5073 if ((device = find_device(client)) == NULL)
5074 {
5075 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Device was not found.");
5076 return;
5077 }
5078
5079 if ((job = find_job(client, 0)) == NULL)
5080 {
5081 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job was not found.");
5082 return;
5083 }
5084
5085 if (!job->dev_uuid || strcmp(job->dev_uuid, device->uuid))
5086 {
5087 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job not assigned to device.");
5088 return;
5089 }
5090
5091 if ((attr = ippFindAttribute(client->request, "document-number", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(attr) != IPP_TAG_OPERATION || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetCount(attr) != 1 || ippGetInteger(attr, 0) != 1)
5092 {
5093 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, attr ? "Bad document-number attribute." : "Missing document-number attribute.");
5094 return;
5095 }
5096
5097 if ((attr = ippFindAttribute(client->request, "impressions-completed", IPP_TAG_INTEGER)) != NULL)
5098 {
5099 job->impcompleted = ippGetInteger(attr, 0);
5100 add_event(client->printer, job, _IPP_EVENT_JOB_PROGRESS, NULL);
5101 }
5102
5103 respond_ipp(client, IPP_STATUS_OK, NULL);
5104 }
5105
5106
5107 /*
5108 * 'ipp_update_job_status()' - Update the state of a job.
5109 */
5110
5111 static void
5112 ipp_update_job_status(
5113 _ipp_client_t *client) /* I - Client */
5114 {
5115 _ipp_device_t *device; /* Device */
5116 _ipp_job_t *job; /* Job */
5117 ipp_attribute_t *attr; /* Attribute */
5118 _ipp_event_t events = _IPP_EVENT_NONE;
5119 /* Event(s) */
5120
5121
5122 if ((device = find_device(client)) == NULL)
5123 {
5124 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Device was not found.");
5125 return;
5126 }
5127
5128 if ((job = find_job(client, 0)) == NULL)
5129 {
5130 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job was not found.");
5131 return;
5132 }
5133
5134 if (!job->dev_uuid || strcmp(job->dev_uuid, device->uuid))
5135 {
5136 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job not assigned to device.");
5137 return;
5138 }
5139
5140 if ((attr = ippFindAttribute(client->request, "job-impressions-completed", IPP_TAG_INTEGER)) != NULL)
5141 {
5142 job->impcompleted = ippGetInteger(attr, 0);
5143 events |= _IPP_EVENT_JOB_PROGRESS;
5144 }
5145
5146 if ((attr = ippFindAttribute(client->request, "output-device-job-state", IPP_TAG_ENUM)) != NULL)
5147 {
5148 job->dev_state = (ipp_jstate_t)ippGetInteger(attr, 0);
5149 events |= _IPP_EVENT_JOB_STATE_CHANGED;
5150 }
5151
5152 if ((attr = ippFindAttribute(client->request, "output-device-job-state-reasons", IPP_TAG_KEYWORD)) != NULL)
5153 {
5154 job->dev_state_reasons = get_job_state_reasons_bits(attr);
5155 events |= _IPP_EVENT_JOB_STATE_CHANGED;
5156 }
5157
5158 if (events)
5159 add_event(client->printer, job, events, NULL);
5160
5161 respond_ipp(client, IPP_STATUS_OK, NULL);
5162 }
5163
5164
5165 /*
5166 * 'ipp_update_output_device_attributes()' - Update the values for an output device.
5167 */
5168
5169 static void
5170 ipp_update_output_device_attributes(
5171 _ipp_client_t *client) /* I - Client */
5172 {
5173 _ipp_device_t *device; /* Device */
5174 ipp_attribute_t *attr, /* Current attribute */
5175 *dev_attr; /* Device attribute */
5176 _ipp_event_t events = _IPP_EVENT_NONE;
5177 /* Config/state changed? */
5178
5179
5180 if ((device = find_device(client)) == NULL)
5181 {
5182 if ((device = create_device(client)) == NULL)
5183 {
5184 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Unable to add output device.");
5185 return;
5186 }
5187 }
5188
5189 _cupsRWLockWrite(&device->rwlock);
5190
5191 attr = ippFirstAttribute(client->request);
5192 while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
5193 attr = ippNextAttribute(client->request);
5194
5195 for (; attr; attr = ippNextAttribute(client->request))
5196 {
5197 const char *attrname = ippGetName(attr),
5198 /* Attribute name */
5199 *dotptr; /* Pointer to dot in name */
5200
5201 /*
5202 * Skip attributes we don't care about...
5203 */
5204
5205 if (!attrname)
5206 continue;
5207
5208 if (strncmp(attrname, "copies", 6) && strncmp(attrname, "document-format", 15) && strncmp(attrname, "finishings", 10) && strncmp(attrname, "media", 5) && strncmp(attrname, "print-", 6) && strncmp(attrname, "sides", 5) && strncmp(attrname, "printer-alert", 13) && strncmp(attrname, "printer-input", 13) && strncmp(attrname, "printer-output", 14) && strncmp(attrname, "printer-resolution", 18) && strncmp(attrname, "pwg-raster", 10) && strncmp(attrname, "urf-", 4))
5209 continue;
5210
5211 if (strncmp(attrname, "printer-alert", 13) || strncmp(attrname, "printer-state", 13))
5212 events |= _IPP_EVENT_PRINTER_CONFIG_CHANGED;
5213 else
5214 events |= _IPP_EVENT_PRINTER_STATE_CHANGED;
5215
5216 if (!strcmp(attrname, "media-col-ready") || !strcmp(attrname, "media-ready"))
5217 events |= _IPP_EVENT_PRINTER_MEDIA_CHANGED;
5218
5219 if (!strcmp(attrname, "finishings-col-ready") || !strcmp(attrname, "finishings-ready"))
5220 events |= _IPP_EVENT_PRINTER_FINISHINGS_CHANGED;
5221
5222 if ((dotptr = strrchr(attrname, '.')) != NULL && isdigit(dotptr[1] & 255))
5223 {
5224 #if 0
5225 /*
5226 * Sparse representation: name.NNN or name.NNN-NNN
5227 */
5228
5229 char temp[256], /* Temporary name string */
5230 *tempptr; /* Pointer into temporary string */
5231 int low, high; /* Low and high numbers in range */
5232
5233 low = (int)strtol(dotptr + 1, (char **)&dotptr, 10);
5234 if (dotptr && *dotptr == '-')
5235 high = (int)strtol(dotptr + 1, NULL, 10);
5236 else
5237 high = low;
5238
5239 strlcpy(temp, attrname, sizeof(temp));
5240 if ((tempptr = strrchr(temp, '.')) != NULL)
5241 *tempptr = '\0';
5242
5243 if ((dev_attr = ippFindAttribute(device->attrs, temp, IPP_TAG_ZERO)) != NULL)
5244 {
5245 }
5246 else
5247 #endif /* 0 */
5248 respond_unsupported(client, attr);
5249 }
5250 else
5251 {
5252 /*
5253 * Regular representation - replace or delete current attribute,
5254 * if any...
5255 */
5256
5257 if ((dev_attr = ippFindAttribute(device->attrs, attrname, IPP_TAG_ZERO)) != NULL)
5258 ippDeleteAttribute(device->attrs, dev_attr);
5259
5260 if (ippGetValueTag(attr) != IPP_TAG_DELETEATTR)
5261 ippCopyAttribute(device->attrs, attr, 0);
5262 }
5263 }
5264
5265 _cupsRWUnlock(&device->rwlock);
5266
5267 if (events)
5268 {
5269 _cupsRWLockWrite(&client->printer->rwlock);
5270 if (events & _IPP_EVENT_PRINTER_CONFIG_CHANGED)
5271 update_device_attributes_no_lock(client->printer);
5272 if (events & _IPP_EVENT_PRINTER_STATE_CHANGED)
5273 update_device_state_no_lock(client->printer);
5274 _cupsRWUnlock(&client->printer->rwlock);
5275
5276 add_event(client->printer, NULL, events, NULL);
5277 }
5278 }
5279
5280
5281 /*
5282 * 'ipp_validate_document()' - Validate document creation attributes.
5283 */
5284
5285 static void
5286 ipp_validate_document(
5287 _ipp_client_t *client) /* I - Client */
5288 {
5289 if (valid_doc_attributes(client))
5290 respond_ipp(client, IPP_STATUS_OK, NULL);
5291 }
5292
5293
5294 /*
5295 * 'ipp_validate_job()' - Validate job creation attributes.
5296 */
5297
5298 static void
5299 ipp_validate_job(_ipp_client_t *client) /* I - Client */
5300 {
5301 if (valid_job_attributes(client))
5302 respond_ipp(client, IPP_STATUS_OK, NULL);
5303 }
5304
5305
5306 #if 0
5307 /*
5308 * 'parse_options()' - Parse URL options into CUPS options.
5309 *
5310 * The client->options string is destroyed by this function.
5311 */
5312
5313 static int /* O - Number of options */
5314 parse_options(_ipp_client_t *client, /* I - Client */
5315 cups_option_t **options) /* O - Options */
5316 {
5317 char *name, /* Name */
5318 *value, /* Value */
5319 *next; /* Next name=value pair */
5320 int num_options = 0; /* Number of options */
5321
5322
5323 *options = NULL;
5324
5325 for (name = client->options; name && *name; name = next)
5326 {
5327 if ((value = strchr(name, '=')) == NULL)
5328 break;
5329
5330 *value++ = '\0';
5331 if ((next = strchr(value, '&')) != NULL)
5332 *next++ = '\0';
5333
5334 num_options = cupsAddOption(name, value, num_options, options);
5335 }
5336
5337 return (num_options);
5338 }
5339 #endif /* 0 */
5340
5341
5342 /*
5343 * 'process_client()' - Process client requests on a thread.
5344 */
5345
5346 static void * /* O - Exit status */
5347 process_client(_ipp_client_t *client) /* I - Client */
5348 {
5349 /*
5350 * Loop until we are out of requests or timeout (30 seconds)...
5351 */
5352
5353 #ifdef HAVE_SSL
5354 int first_time = 1; /* First time request? */
5355 #endif /* HAVE_SSL */
5356
5357 while (httpWait(client->http, 30000))
5358 {
5359 #ifdef HAVE_SSL
5360 if (first_time)
5361 {
5362 /*
5363 * See if we need to negotiate a TLS connection...
5364 */
5365
5366 char buf[1]; /* First byte from client */
5367
5368 if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
5369 {
5370 fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
5371
5372 if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
5373 {
5374 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5375 break;
5376 }
5377
5378 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5379 }
5380
5381 first_time = 0;
5382 }
5383 #endif /* HAVE_SSL */
5384
5385 if (!process_http(client))
5386 break;
5387 }
5388
5389 /*
5390 * Close the conection to the client and return...
5391 */
5392
5393 delete_client(client);
5394
5395 return (NULL);
5396 }
5397
5398
5399 /*
5400 * 'process_http()' - Process a HTTP request.
5401 */
5402
5403 int /* O - 1 on success, 0 on failure */
5404 process_http(_ipp_client_t *client) /* I - Client connection */
5405 {
5406 char uri[1024]; /* URI */
5407 http_state_t http_state; /* HTTP state */
5408 http_status_t http_status; /* HTTP status */
5409 ipp_state_t ipp_state; /* State of IPP transfer */
5410 char scheme[32], /* Method/scheme */
5411 userpass[128], /* Username:password */
5412 hostname[HTTP_MAX_HOST];
5413 /* Hostname */
5414 int port; /* Port number */
5415 const char *encoding; /* Content-Encoding value */
5416 static const char * const http_states[] =
5417 { /* Strings for logging HTTP method */
5418 "WAITING",
5419 "OPTIONS",
5420 "GET",
5421 "GET_SEND",
5422 "HEAD",
5423 "POST",
5424 "POST_RECV",
5425 "POST_SEND",
5426 "PUT",
5427 "PUT_RECV",
5428 "DELETE",
5429 "TRACE",
5430 "CONNECT",
5431 "STATUS",
5432 "UNKNOWN_METHOD",
5433 "UNKNOWN_VERSION"
5434 };
5435
5436
5437 /*
5438 * Clear state variables...
5439 */
5440
5441 ippDelete(client->request);
5442 ippDelete(client->response);
5443
5444 client->request = NULL;
5445 client->response = NULL;
5446 client->operation = HTTP_STATE_WAITING;
5447
5448 /*
5449 * Read a request from the connection...
5450 */
5451
5452 while ((http_state = httpReadRequest(client->http, uri,
5453 sizeof(uri))) == HTTP_STATE_WAITING)
5454 usleep(1);
5455
5456 /*
5457 * Parse the request line...
5458 */
5459
5460 if (http_state == HTTP_STATE_ERROR)
5461 {
5462 if (httpError(client->http) == EPIPE)
5463 fprintf(stderr, "%s Client closed connection.\n", client->hostname);
5464 else
5465 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname,
5466 strerror(httpError(client->http)));
5467
5468 return (0);
5469 }
5470 else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
5471 {
5472 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
5473 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5474 return (0);
5475 }
5476 else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
5477 {
5478 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
5479 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5480 return (0);
5481 }
5482
5483 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state],
5484 uri);
5485
5486 /*
5487 * Separate the URI into its components...
5488 */
5489
5490 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
5491 userpass, sizeof(userpass),
5492 hostname, sizeof(hostname), &port,
5493 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
5494 (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
5495 {
5496 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
5497 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5498 return (0);
5499 }
5500
5501 if ((client->options = strchr(client->uri, '?')) != NULL)
5502 *(client->options)++ = '\0';
5503
5504 /*
5505 * Process the request...
5506 */
5507
5508 client->start = time(NULL);
5509 client->operation = httpGetState(client->http);
5510
5511 /*
5512 * Parse incoming parameters until the status changes...
5513 */
5514
5515 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
5516
5517 if (http_status != HTTP_STATUS_OK)
5518 {
5519 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5520 return (0);
5521 }
5522
5523 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
5524 httpGetVersion(client->http) >= HTTP_VERSION_1_1)
5525 {
5526 /*
5527 * HTTP/1.1 and higher require the "Host:" field...
5528 */
5529
5530 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5531 return (0);
5532 }
5533
5534 /*
5535 * Handle HTTP Upgrade...
5536 */
5537
5538 if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
5539 "Upgrade"))
5540 {
5541 #ifdef HAVE_SSL
5542 if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
5543 {
5544 if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
5545 return (0);
5546
5547 fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
5548
5549 if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
5550 {
5551 fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5552 return (0);
5553 }
5554
5555 fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5556 }
5557 else
5558 #endif /* HAVE_SSL */
5559
5560 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
5561 return (0);
5562 }
5563
5564 /*
5565 * Handle HTTP Expect...
5566 */
5567
5568 if (httpGetExpect(client->http) &&
5569 (client->operation == HTTP_STATE_POST ||
5570 client->operation == HTTP_STATE_PUT))
5571 {
5572 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
5573 {
5574 /*
5575 * Send 100-continue header...
5576 */
5577
5578 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
5579 return (0);
5580 }
5581 else
5582 {
5583 /*
5584 * Send 417-expectation-failed header...
5585 */
5586
5587 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
5588 return (0);
5589 }
5590 }
5591
5592 /*
5593 * Handle new transfers...
5594 */
5595
5596 encoding = httpGetContentEncoding(client->http);
5597
5598 switch (client->operation)
5599 {
5600 case HTTP_STATE_OPTIONS :
5601 /*
5602 * Do OPTIONS command...
5603 */
5604
5605 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
5606
5607 case HTTP_STATE_HEAD :
5608 #if 0 /* TODO: Work out icon support */
5609 if (!strcmp(client->uri, "/icon.png"))
5610 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
5611 else
5612 #endif /* 0 */
5613 if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
5614 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
5615 else
5616 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5617
5618 case HTTP_STATE_GET :
5619 #if 0 /* TODO: Work out icon support */
5620 if (!strcmp(client->uri, "/icon.png"))
5621 {
5622 /*
5623 * Send PNG icon file.
5624 */
5625
5626 int fd; /* Icon file */
5627 struct stat fileinfo; /* Icon file information */
5628 char buffer[4096]; /* Copy buffer */
5629 ssize_t bytes; /* Bytes */
5630
5631 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
5632
5633 if (!stat(client->printer->icon, &fileinfo) &&
5634 (fd = open(client->printer->icon, O_RDONLY)) >= 0)
5635 {
5636 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png",
5637 (size_t)fileinfo.st_size))
5638 {
5639 close(fd);
5640 return (0);
5641 }
5642
5643 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
5644 httpWrite2(client->http, buffer, (size_t)bytes);
5645
5646 httpFlushWrite(client->http);
5647
5648 close(fd);
5649 }
5650 else
5651 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5652 }
5653 else
5654 #endif /* 0 */
5655 if (!strcmp(client->uri, "/"))
5656 {
5657 /*
5658 * Show web status page...
5659 */
5660
5661 _ipp_job_t *job; /* Current job */
5662 int i; /* Looping var */
5663 _ipp_preason_t reason; /* Current reason */
5664 static const char * const reasons[] =
5665 { /* Reason strings */
5666 "Other",
5667 "Cover Open",
5668 "Input Tray Missing",
5669 "Marker Supply Empty",
5670 "Marker Supply Low",
5671 "Marker Waste Almost Full",
5672 "Marker Waste Full",
5673 "Media Empty",
5674 "Media Jam",
5675 "Media Low",
5676 "Media Needed",
5677 "Moving to Paused",
5678 "Paused",
5679 "Spool Area Full",
5680 "Toner Empty",
5681 "Toner Low"
5682 };
5683
5684 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
5685 return (0);
5686
5687 html_header(client, client->printer->name);
5688 html_printf(client,
5689 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION ")</b></p>\n"
5690 "<p>%s, %d job(s).", client->printer->state == IPP_PSTATE_IDLE ? "Idle" : client->printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(client->printer->jobs));
5691 for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
5692 if (client->printer->state_reasons & reason)
5693 html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
5694 html_printf(client, "</p>\n");
5695
5696 if (cupsArrayCount(client->printer->jobs) > 0)
5697 {
5698 _cupsRWLockRead(&(client->printer->rwlock));
5699
5700 html_printf(client, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>When</th></tr></thead><tbody>\n");
5701 for (job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs); job; job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
5702 {
5703 char when[256], /* When job queued/started/finished */
5704 hhmmss[64]; /* Time HH:MM:SS */
5705
5706 switch (job->state)
5707 {
5708 case IPP_JSTATE_PENDING :
5709 case IPP_JSTATE_HELD :
5710 snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
5711 break;
5712 case IPP_JSTATE_PROCESSING :
5713 case IPP_JSTATE_STOPPED :
5714 snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
5715 break;
5716 case IPP_JSTATE_ABORTED :
5717 snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
5718 break;
5719 case IPP_JSTATE_CANCELED :
5720 snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
5721 break;
5722 case IPP_JSTATE_COMPLETED :
5723 snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
5724 break;
5725 }
5726
5727 html_printf(client, "<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", job->id, job->name, job->username, when);
5728 }
5729 html_printf(client, "</tbody></table>\n");
5730
5731 _cupsRWUnlock(&(client->printer->rwlock));
5732 }
5733 html_footer(client);
5734
5735 return (1);
5736 }
5737 #if 0 /* TODO: Pull media and supply info from device attrs */
5738 else if (!strcmp(client->uri, "/media"))
5739 {
5740 /*
5741 * Show web media page...
5742 */
5743
5744 int i, /* Looping var */
5745 num_options; /* Number of form options */
5746 cups_option_t *options; /* Form options */
5747 static const char * const sizes[] =
5748 { /* Size strings */
5749 "ISO A4",
5750 "ISO A5",
5751 "ISO A6",
5752 "DL Envelope",
5753 "US Legal",
5754 "US Letter",
5755 "#10 Envelope",
5756 "3x5 Photo",
5757 "3.5x5 Photo",
5758 "4x6 Photo",
5759 "5x7 Photo"
5760 };
5761 static const char * const types[] =
5762 /* Type strings */
5763 {
5764 "Auto",
5765 "Cardstock",
5766 "Envelope",
5767 "Labels",
5768 "Other",
5769 "Glossy Photo",
5770 "High-Gloss Photo",
5771 "Matte Photo",
5772 "Satin Photo",
5773 "Semi-Gloss Photo",
5774 "Plain",
5775 "Letterhead",
5776 "Transparency"
5777 };
5778 static const int sheets[] = /* Number of sheets */
5779 {
5780 250,
5781 100,
5782 25,
5783 5,
5784 0
5785 };
5786
5787 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
5788 return (0);
5789
5790 html_header(client, client->printer->name);
5791
5792 if ((num_options = parse_options(client, &options)) > 0)
5793 {
5794 /*
5795 * WARNING: A real printer/server implementation MUST NOT implement
5796 * media updates via a GET request - GET requests are supposed to be
5797 * idempotent (without side-effects) and we obviously are not
5798 * authenticating access here. This form is provided solely to
5799 * enable testing and development!
5800 */
5801
5802 const char *val; /* Form value */
5803
5804 if ((val = cupsGetOption("main_size", num_options, options)) != NULL)
5805 client->printer->main_size = atoi(val);
5806 if ((val = cupsGetOption("main_type", num_options, options)) != NULL)
5807 client->printer->main_type = atoi(val);
5808 if ((val = cupsGetOption("main_level", num_options, options)) != NULL)
5809 client->printer->main_level = atoi(val);
5810
5811 if ((val = cupsGetOption("envelope_size", num_options, options)) != NULL)
5812 client->printer->envelope_size = atoi(val);
5813 if ((val = cupsGetOption("envelope_level", num_options, options)) != NULL)
5814 client->printer->envelope_level = atoi(val);
5815
5816 if ((val = cupsGetOption("photo_size", num_options, options)) != NULL)
5817 client->printer->photo_size = atoi(val);
5818 if ((val = cupsGetOption("photo_type", num_options, options)) != NULL)
5819 client->printer->photo_type = atoi(val);
5820 if ((val = cupsGetOption("photo_level", num_options, options)) != NULL)
5821 client->printer->photo_level = atoi(val);
5822
5823 if ((client->printer->main_level < 100 && client->printer->main_level > 0) || (client->printer->envelope_level < 25 && client->printer->envelope_level > 0) || (client->printer->photo_level < 25 && client->printer->photo_level > 0))
5824 client->printer->state_reasons |= _IPP_PREASON_MEDIA_LOW;
5825 else
5826 client->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_LOW;
5827
5828 if ((client->printer->main_level == 0 && client->printer->main_size > _IPP_MEDIA_SIZE_NONE) || (client->printer->envelope_level == 0 && client->printer->envelope_size > _IPP_MEDIA_SIZE_NONE) || (client->printer->photo_level == 0 && client->printer->photo_size > _IPP_MEDIA_SIZE_NONE))
5829 {
5830 client->printer->state_reasons |= _IPP_PREASON_MEDIA_EMPTY;
5831 if (client->printer->active_job)
5832 client->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
5833 }
5834 else
5835 client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MEDIA_EMPTY | _IPP_PREASON_MEDIA_NEEDED);
5836
5837 html_printf(client, "<blockquote>Media updated.</blockquote>\n");
5838 }
5839
5840 html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
5841
5842 html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
5843 html_printf(client, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5844 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
5845 if (!strstr(sizes[i], "Envelope") && !strstr(sizes[i], "Photo"))
5846 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_size ? " selected" : "", sizes[i]);
5847 html_printf(client, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5848 for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
5849 if (!strstr(types[i], "Photo"))
5850 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_type ? " selected" : "", types[i]);
5851 html_printf(client, "</select> <select name=\"main_level\">");
5852 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
5853 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->main_level ? " selected" : "", sheets[i]);
5854 html_printf(client, "</select></td></tr>\n");
5855
5856 html_printf(client,
5857 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5858 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
5859 if (strstr(sizes[i], "Envelope"))
5860 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->envelope_size ? " selected" : "", sizes[i]);
5861 html_printf(client, "</select> <select name=\"envelope_level\">");
5862 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
5863 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->envelope_level ? " selected" : "", sheets[i]);
5864 html_printf(client, "</select></td></tr>\n");
5865
5866 html_printf(client,
5867 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5868 for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
5869 if (strstr(sizes[i], "Photo"))
5870 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_size ? " selected" : "", sizes[i]);
5871 html_printf(client, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5872 for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
5873 if (strstr(types[i], "Photo"))
5874 html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_type ? " selected" : "", types[i]);
5875 html_printf(client, "</select> <select name=\"photo_level\">");
5876 for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
5877 html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->photo_level ? " selected" : "", sheets[i]);
5878 html_printf(client, "</select></td></tr>\n");
5879
5880 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5881 html_footer(client);
5882
5883 return (1);
5884 }
5885 else if (!strcmp(client->uri, "/supplies"))
5886 {
5887 /*
5888 * Show web supplies page...
5889 */
5890
5891 int i, j, /* Looping vars */
5892 num_options; /* Number of form options */
5893 cups_option_t *options; /* Form options */
5894 static const int levels[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5895
5896 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
5897 return (0);
5898
5899 html_header(client, client->printer->name);
5900
5901 if ((num_options = parse_options(client, &options)) > 0)
5902 {
5903 /*
5904 * WARNING: A real printer/server implementation MUST NOT implement
5905 * supply updates via a GET request - GET requests are supposed to be
5906 * idempotent (without side-effects) and we obviously are not
5907 * authenticating access here. This form is provided solely to
5908 * enable testing and development!
5909 */
5910
5911 char name[64]; /* Form field */
5912 const char *val; /* Form value */
5913
5914 client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MARKER_SUPPLY_EMPTY | _IPP_PREASON_MARKER_SUPPLY_LOW | _IPP_PREASON_MARKER_WASTE_ALMOST_FULL | _IPP_PREASON_MARKER_WASTE_FULL | _IPP_PREASON_TONER_EMPTY | _IPP_PREASON_TONER_LOW);
5915
5916 for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
5917 {
5918 snprintf(name, sizeof(name), "supply_%d", i);
5919 if ((val = cupsGetOption(name, num_options, options)) != NULL)
5920 {
5921 int level = client->printer->supplies[i] = atoi(val);
5922 /* New level */
5923
5924 if (i < 4)
5925 {
5926 if (level == 0)
5927 client->printer->state_reasons |= _IPP_PREASON_TONER_EMPTY;
5928 else if (level < 10)
5929 client->printer->state_reasons |= _IPP_PREASON_TONER_LOW;
5930 }
5931 else
5932 {
5933 if (level == 100)
5934 client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_FULL;
5935 else if (level > 90)
5936 client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL;
5937 }
5938 }
5939 }
5940
5941 html_printf(client, "<blockquote>Supplies updated.</blockquote>\n");
5942 }
5943
5944 html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
5945
5946 html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
5947 for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
5948 {
5949 html_printf(client, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies[i], i);
5950 for (j = 0; j < (int)(sizeof(levels) / sizeof(levels[0])); j ++)
5951 html_printf(client, "<option value=\"%d\"%s>%d%%</option>", levels[j], levels[j] == client->printer->supplies[i] ? " selected" : "", levels[j]);
5952 html_printf(client, "</select></td></tr>\n");
5953 }
5954 html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5955 html_footer(client);
5956
5957 return (1);
5958 }
5959 #endif /* 0 */
5960 else
5961 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
5962 break;
5963
5964 case HTTP_STATE_POST :
5965 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
5966 "application/ipp"))
5967 {
5968 /*
5969 * Not an IPP request...
5970 */
5971
5972 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
5973 }
5974
5975 /*
5976 * Read the IPP request...
5977 */
5978
5979 client->request = ippNew();
5980
5981 while ((ipp_state = ippRead(client->http,
5982 client->request)) != IPP_STATE_DATA)
5983 {
5984 if (ipp_state == IPP_STATE_ERROR)
5985 {
5986 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname,
5987 cupsLastErrorString());
5988 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5989 return (0);
5990 }
5991 }
5992
5993 /*
5994 * Now that we have the IPP request, process the request...
5995 */
5996
5997 return (process_ipp(client));
5998
5999 default :
6000 break; /* Anti-compiler-warning-code */
6001 }
6002
6003 return (1);
6004 }
6005
6006
6007 /*
6008 * 'process_ipp()' - Process an IPP request.
6009 */
6010
6011 static int /* O - 1 on success, 0 on error */
6012 process_ipp(_ipp_client_t *client) /* I - Client */
6013 {
6014 ipp_tag_t group; /* Current group tag */
6015 ipp_attribute_t *attr; /* Current attribute */
6016 ipp_attribute_t *charset; /* Character set attribute */
6017 ipp_attribute_t *language; /* Language attribute */
6018 ipp_attribute_t *uri; /* Printer URI attribute */
6019 int major, minor; /* Version number */
6020 const char *name; /* Name of attribute */
6021
6022
6023 debug_attributes("Request", client->request, 1);
6024
6025 /*
6026 * First build an empty response message for this request...
6027 */
6028
6029 client->operation_id = ippGetOperation(client->request);
6030 client->response = ippNewResponse(client->request);
6031
6032 /*
6033 * Then validate the request header and required attributes...
6034 */
6035
6036 major = ippGetVersion(client->request, &minor);
6037
6038 if (major < 1 || major > 2)
6039 {
6040 /*
6041 * Return an error, since we only support IPP 1.x and 2.x.
6042 */
6043
6044 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED,
6045 "Bad request version number %d.%d.", major, minor);
6046 }
6047 else if (ippGetRequestId(client->request) <= 0)
6048 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.",
6049 ippGetRequestId(client->request));
6050 else if (!ippFirstAttribute(client->request))
6051 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6052 "No attributes in request.");
6053 else
6054 {
6055 /*
6056 * Make sure that the attributes are provided in the correct order and
6057 * don't repeat groups...
6058 */
6059
6060 for (attr = ippFirstAttribute(client->request),
6061 group = ippGetGroupTag(attr);
6062 attr;
6063 attr = ippNextAttribute(client->request))
6064 {
6065 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
6066 {
6067 /*
6068 * Out of order; return an error...
6069 */
6070
6071 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6072 "Attribute groups are out of order (%x < %x).",
6073 ippGetGroupTag(attr), group);
6074 break;
6075 }
6076 else
6077 group = ippGetGroupTag(attr);
6078 }
6079
6080 if (!attr)
6081 {
6082 /*
6083 * Then make sure that the first three attributes are:
6084 *
6085 * attributes-charset
6086 * attributes-natural-language
6087 * printer-uri/job-uri
6088 */
6089
6090 attr = ippFirstAttribute(client->request);
6091 name = ippGetName(attr);
6092 if (attr && name && !strcmp(name, "attributes-charset") &&
6093 ippGetValueTag(attr) == IPP_TAG_CHARSET)
6094 charset = attr;
6095 else
6096 charset = NULL;
6097
6098 attr = ippNextAttribute(client->request);
6099 name = ippGetName(attr);
6100
6101 if (attr && name && !strcmp(name, "attributes-natural-language") &&
6102 ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
6103 language = attr;
6104 else
6105 language = NULL;
6106
6107 if ((attr = ippFindAttribute(client->request, "printer-uri",
6108 IPP_TAG_URI)) != NULL)
6109 uri = attr;
6110 else if ((attr = ippFindAttribute(client->request, "job-uri",
6111 IPP_TAG_URI)) != NULL)
6112 uri = attr;
6113 else
6114 uri = NULL;
6115
6116 if (charset &&
6117 strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
6118 strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
6119 {
6120 /*
6121 * Bad character set...
6122 */
6123
6124 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6125 "Unsupported character set \"%s\".",
6126 ippGetString(charset, 0, NULL));
6127 }
6128 else if (!charset || !language || !uri)
6129 {
6130 /*
6131 * Return an error, since attributes-charset,
6132 * attributes-natural-language, and printer-uri/job-uri are required
6133 * for all operations.
6134 */
6135
6136 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6137 "Missing required attributes.");
6138 }
6139 else
6140 {
6141 char scheme[32], /* URI scheme */
6142 userpass[32], /* Username/password in URI */
6143 host[256], /* Host name in URI */
6144 resource[256]; /* Resource path in URI */
6145 int port; /* Port number in URI */
6146
6147 name = ippGetName(uri);
6148
6149 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
6150 scheme, sizeof(scheme),
6151 userpass, sizeof(userpass),
6152 host, sizeof(host), &port,
6153 resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
6154 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
6155 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
6156 else if ((!strcmp(name, "job-uri") && strncmp(resource, "/ipp/print/", 11)) ||
6157 (!strcmp(name, "printer-uri") && strcmp(resource, "/ipp/print")))
6158 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
6159 name, ippGetString(uri, 0, NULL));
6160 else
6161 {
6162 /*
6163 * Try processing the operation...
6164 */
6165
6166 switch ((int)ippGetOperation(client->request))
6167 {
6168 case IPP_OP_PRINT_JOB :
6169 ipp_print_job(client);
6170 break;
6171
6172 case IPP_OP_PRINT_URI :
6173 ipp_print_uri(client);
6174 break;
6175
6176 case IPP_OP_VALIDATE_JOB :
6177 ipp_validate_job(client);
6178 break;
6179
6180 case IPP_OP_CREATE_JOB :
6181 ipp_create_job(client);
6182 break;
6183
6184 case IPP_OP_SEND_DOCUMENT :
6185 ipp_send_document(client);
6186 break;
6187
6188 case IPP_OP_SEND_URI :
6189 ipp_send_uri(client);
6190 break;
6191
6192 case IPP_OP_CANCEL_JOB :
6193 ipp_cancel_job(client);
6194 break;
6195
6196 case IPP_OP_CANCEL_MY_JOBS :
6197 ipp_cancel_my_jobs(client);
6198 break;
6199
6200 case IPP_OP_GET_JOB_ATTRIBUTES :
6201 ipp_get_job_attributes(client);
6202 break;
6203
6204 case IPP_OP_GET_JOBS :
6205 ipp_get_jobs(client);
6206 break;
6207
6208 case IPP_OP_GET_PRINTER_ATTRIBUTES :
6209 ipp_get_printer_attributes(client);
6210 break;
6211
6212 case IPP_OP_GET_PRINTER_SUPPORTED_VALUES :
6213 ipp_get_printer_supported_values(client);
6214 break;
6215
6216 case IPP_OP_CLOSE_JOB :
6217 ipp_close_job(client);
6218 break;
6219
6220 case IPP_OP_IDENTIFY_PRINTER :
6221 ipp_identify_printer(client);
6222 break;
6223
6224 case IPP_OP_CANCEL_SUBSCRIPTION :
6225 ipp_cancel_subscription(client);
6226 break;
6227
6228 case IPP_OP_CREATE_JOB_SUBSCRIPTIONS :
6229 case IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS :
6230 ipp_create_xxx_subscriptions(client);
6231 break;
6232
6233 case IPP_OP_GET_NOTIFICATIONS :
6234 ipp_get_notifications(client);
6235 break;
6236
6237 case IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES :
6238 ipp_get_subscription_attributes(client);
6239 break;
6240
6241 case IPP_OP_GET_SUBSCRIPTIONS :
6242 ipp_get_subscriptions(client);
6243 break;
6244
6245 case IPP_OP_RENEW_SUBSCRIPTION :
6246 ipp_renew_subscription(client);
6247 break;
6248
6249 case IPP_OP_GET_DOCUMENT_ATTRIBUTES :
6250 ipp_get_document_attributes(client);
6251 break;
6252
6253 case IPP_OP_GET_DOCUMENTS :
6254 ipp_get_documents(client);
6255 break;
6256
6257 case IPP_OP_VALIDATE_DOCUMENT :
6258 ipp_validate_document(client);
6259 break;
6260
6261 case _IPP_OP_ACKNOWLEDGE_DOCUMENT :
6262 ipp_acknowledge_document(client);
6263 break;
6264
6265 case _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER :
6266 ipp_acknowledge_identify_printer(client);
6267 break;
6268
6269 case _IPP_OP_ACKNOWLEDGE_JOB :
6270 ipp_acknowledge_job(client);
6271 break;
6272
6273 case _IPP_OP_FETCH_DOCUMENT :
6274 ipp_fetch_document(client);
6275 break;
6276
6277 case _IPP_OP_FETCH_JOB :
6278 ipp_fetch_job(client);
6279 break;
6280
6281 case _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES :
6282 ipp_get_output_device_attributes(client);
6283 break;
6284
6285 case _IPP_OP_UPDATE_ACTIVE_JOBS :
6286 ipp_update_active_jobs(client);
6287 break;
6288
6289 case _IPP_OP_UPDATE_DOCUMENT_STATUS :
6290 ipp_update_document_status(client);
6291 break;
6292
6293 case _IPP_OP_UPDATE_JOB_STATUS :
6294 ipp_update_job_status(client);
6295 break;
6296
6297 case _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES :
6298 ipp_update_output_device_attributes(client);
6299 break;
6300
6301 case _IPP_OP_DEREGISTER_OUTPUT_DEVICE :
6302 ipp_deregister_output_device(client);
6303 break;
6304
6305 default :
6306 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
6307 "Operation not supported.");
6308 break;
6309 }
6310 }
6311 }
6312 }
6313 }
6314
6315 /*
6316 * Send the HTTP header and return...
6317 */
6318
6319 if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
6320 httpFlush(client->http); /* Flush trailing (junk) data */
6321
6322 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
6323 client->fetch_file >= 0 ? 0 : ippLength(client->response)));
6324 }
6325
6326
6327 /*
6328 * 'process_job()' - Process a print job.
6329 */
6330
6331 static void * /* O - Thread exit status */
6332 process_job(_ipp_job_t *job) /* I - Job */
6333 {
6334 job->state = IPP_JSTATE_PROCESSING;
6335 job->printer->state = IPP_PSTATE_PROCESSING;
6336 job->processing = time(NULL);
6337 job->printer->processing_job = job;
6338
6339 add_event(job->printer, job, _IPP_EVENT_JOB_STATE_CHANGED, "Job processing.");
6340
6341 /*
6342 * TODO: Perform any preprocessing needed...
6343 */
6344
6345 // job->state_reasons |= _IPP_JREASON_JOB_TRANSFORMING;
6346 // job->state_reasons &= ~_IPP_JREASON_JOB_TRANSFORMING;
6347
6348 /*
6349 * Set the state to processing-stopped, fetchable, then send a
6350 * notification.
6351 */
6352
6353 job->state = IPP_JSTATE_STOPPED;
6354 job->state_reasons |= _IPP_JREASON_JOB_FETCHABLE;
6355
6356 add_event(job->printer, job, _IPP_EVENT_JOB_STATE_CHANGED, "Job fetchable.");
6357
6358 return (NULL);
6359 }
6360
6361
6362 /*
6363 * 'respond_http()' - Send a HTTP response.
6364 */
6365
6366 int /* O - 1 on success, 0 on failure */
6367 respond_http(
6368 _ipp_client_t *client, /* I - Client */
6369 http_status_t code, /* I - HTTP status of response */
6370 const char *content_encoding, /* I - Content-Encoding of response */
6371 const char *type, /* I - MIME media type of response */
6372 size_t length) /* I - Length of response */
6373 {
6374 char message[1024]; /* Text message */
6375
6376
6377 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
6378
6379 if (code == HTTP_STATUS_CONTINUE)
6380 {
6381 /*
6382 * 100-continue doesn't send any headers...
6383 */
6384
6385 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
6386 }
6387
6388 /*
6389 * Format an error message...
6390 */
6391
6392 if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
6393 {
6394 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
6395
6396 type = "text/plain";
6397 length = strlen(message);
6398 }
6399 else
6400 message[0] = '\0';
6401
6402 /*
6403 * Send the HTTP response header...
6404 */
6405
6406 httpClearFields(client->http);
6407
6408 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
6409 client->operation == HTTP_STATE_OPTIONS)
6410 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
6411
6412 if (type)
6413 {
6414 if (!strcmp(type, "text/html"))
6415 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
6416 "text/html; charset=utf-8");
6417 else
6418 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
6419
6420 if (content_encoding)
6421 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
6422 }
6423
6424 httpSetLength(client->http, length);
6425
6426 if (httpWriteResponse(client->http, code) < 0)
6427 return (0);
6428
6429 /*
6430 * Send the response data...
6431 */
6432
6433 if (message[0])
6434 {
6435 /*
6436 * Send a plain text message.
6437 */
6438
6439 if (httpPrintf(client->http, "%s", message) < 0)
6440 return (0);
6441
6442 if (httpWrite2(client->http, "", 0) < 0)
6443 return (0);
6444 }
6445 else if (client->response)
6446 {
6447 /*
6448 * Send an IPP response...
6449 */
6450
6451 debug_attributes("Response", client->response, 2);
6452
6453 ippSetState(client->response, IPP_STATE_IDLE);
6454
6455 if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
6456 return (0);
6457
6458 if (client->fetch_file >= 0)
6459 {
6460 ssize_t bytes; /* Bytes read */
6461 char buffer[32768]; /* Buffer */
6462
6463 if (client->fetch_compression)
6464 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, "gzip");
6465
6466 while ((bytes = read(client->fetch_file, buffer, sizeof(buffer))) > 0)
6467 httpWrite2(client->http, buffer, (size_t)bytes);
6468
6469 httpWrite2(client->http, "", 0);
6470 close(client->fetch_file);
6471 client->fetch_file = -1;
6472 }
6473 }
6474
6475 return (1);
6476 }
6477
6478
6479 /*
6480 * 'respond_ipp()' - Send an IPP response.
6481 */
6482
6483 static void
6484 respond_ipp(_ipp_client_t *client, /* I - Client */
6485 ipp_status_t status, /* I - status-code */
6486 const char *message, /* I - printf-style status-message */
6487 ...) /* I - Additional args as needed */
6488 {
6489 const char *formatted = NULL; /* Formatted message */
6490
6491
6492 ippSetStatusCode(client->response, status);
6493
6494 if (message)
6495 {
6496 va_list ap; /* Pointer to additional args */
6497 ipp_attribute_t *attr; /* New status-message attribute */
6498
6499 va_start(ap, message);
6500 if ((attr = ippFindAttribute(client->response, "status-message",
6501 IPP_TAG_TEXT)) != NULL)
6502 ippSetStringfv(client->response, &attr, 0, message, ap);
6503 else
6504 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
6505 "status-message", NULL, message, ap);
6506 va_end(ap);
6507
6508 formatted = ippGetString(attr, 0, NULL);
6509 }
6510
6511 if (formatted)
6512 fprintf(stderr, "%s %s %s (%s)\n", client->hostname,
6513 ippOpString(client->operation_id), ippErrorString(status),
6514 formatted);
6515 else
6516 fprintf(stderr, "%s %s %s\n", client->hostname,
6517 ippOpString(client->operation_id), ippErrorString(status));
6518 }
6519
6520
6521 /*
6522 * 'respond_unsupported()' - Respond with an unsupported attribute.
6523 */
6524
6525 static void
6526 respond_unsupported(
6527 _ipp_client_t *client, /* I - Client */
6528 ipp_attribute_t *attr) /* I - Atribute */
6529 {
6530 ipp_attribute_t *temp; /* Copy of attribute */
6531
6532
6533 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
6534 "Unsupported %s %s%s value.", ippGetName(attr),
6535 ippGetCount(attr) > 1 ? "1setOf " : "",
6536 ippTagString(ippGetValueTag(attr)));
6537
6538 temp = ippCopyAttribute(client->response, attr, 0);
6539 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
6540 }
6541
6542
6543 /*
6544 * 'run_printer()' - Run the printer service.
6545 */
6546
6547 static void
6548 run_printer(_ipp_printer_t *printer) /* I - Printer */
6549 {
6550 int num_fds; /* Number of file descriptors */
6551 struct pollfd polldata[3]; /* poll() data */
6552 int timeout; /* Timeout for poll() */
6553 _ipp_client_t *client; /* New client */
6554
6555
6556 /*
6557 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6558 */
6559
6560 polldata[0].fd = printer->ipv4;
6561 polldata[0].events = POLLIN;
6562
6563 polldata[1].fd = printer->ipv6;
6564 polldata[1].events = POLLIN;
6565
6566 num_fds = 2;
6567
6568 /*
6569 * Loop until we are killed or have a hard error...
6570 */
6571
6572 for (;;)
6573 {
6574 if (cupsArrayCount(printer->jobs))
6575 timeout = 10;
6576 else
6577 timeout = -1;
6578
6579 if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
6580 {
6581 perror("poll() failed");
6582 break;
6583 }
6584
6585 if (polldata[0].revents & POLLIN)
6586 {
6587 if ((client = create_client(printer, printer->ipv4)) != NULL)
6588 {
6589 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
6590 {
6591 perror("Unable to create client thread");
6592 delete_client(client);
6593 }
6594 }
6595 }
6596
6597 if (polldata[1].revents & POLLIN)
6598 {
6599 if ((client = create_client(printer, printer->ipv6)) != NULL)
6600 {
6601 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
6602 {
6603 perror("Unable to create client thread");
6604 delete_client(client);
6605 }
6606 }
6607 }
6608
6609 /*
6610 * Clean out old jobs...
6611 */
6612
6613 clean_jobs(printer);
6614 }
6615 }
6616
6617
6618 /*
6619 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6620 */
6621
6622 static char *
6623 time_string(time_t tv, /* I - Time value */
6624 char *buffer, /* I - Buffer */
6625 size_t bufsize) /* I - Size of buffer */
6626 {
6627 struct tm *curtime = localtime(&tv);
6628 /* Local time */
6629
6630 strftime(buffer, bufsize, "%X", curtime);
6631 return (buffer);
6632 }
6633
6634
6635 /*
6636 * 'update_device_attributes_no_lock()' - Update the composite device attributes.
6637 *
6638 * Note: Caller MUST lock the printer object for writing before using.
6639 */
6640
6641 static void
6642 update_device_attributes_no_lock(
6643 _ipp_printer_t *printer) /* I - Printer */
6644 {
6645 _ipp_device_t *device; /* Current device */
6646 ipp_t *dev_attrs; /* Device attributes */
6647
6648
6649 /* TODO: Support multiple output devices, icons, etc... */
6650 device = (_ipp_device_t *)cupsArrayFirst(printer->devices);
6651 dev_attrs = ippNew();
6652
6653 if (device)
6654 copy_attributes(dev_attrs, device->attrs, NULL, IPP_TAG_PRINTER, 0);
6655
6656 ippDelete(printer->dev_attrs);
6657 printer->dev_attrs = dev_attrs;
6658
6659 printer->config_time = time(NULL);
6660 }
6661
6662
6663 /*
6664 * 'update_device_status_no_lock()' - Update the composite device state.
6665 *
6666 * Note: Caller MUST lock the printer object for writing before using.
6667 */
6668
6669 static void
6670 update_device_state_no_lock(
6671 _ipp_printer_t *printer) /* I - Printer */
6672 {
6673 _ipp_device_t *device; /* Current device */
6674 ipp_attribute_t *attr; /* Current attribute */
6675
6676
6677 /* TODO: Support multiple output devices, icons, etc... */
6678 device = (_ipp_device_t *)cupsArrayFirst(printer->devices);
6679
6680 if ((attr = ippFindAttribute(device->attrs, "printer-state", IPP_TAG_ENUM)) != NULL)
6681 printer->dev_state = (ipp_pstate_t)ippGetInteger(attr, 0);
6682 else
6683 printer->dev_state = IPP_PSTATE_STOPPED;
6684
6685 if ((attr = ippFindAttribute(device->attrs, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL)
6686 printer->dev_reasons = get_printer_state_reasons_bits(attr);
6687 else
6688 printer->dev_reasons = _IPP_PREASON_PAUSED;
6689
6690 printer->state_time = time(NULL);
6691 }
6692
6693
6694 /*
6695 * 'usage()' - Show program usage.
6696 */
6697
6698 static void
6699 usage(int status) /* O - Exit status */
6700 {
6701 if (!status)
6702 {
6703 puts(CUPS_SVERSION " - Copyright 2010-2014 by Apple Inc. All rights reserved.");
6704 puts("");
6705 }
6706
6707 puts("Usage: ippinfra [options] \"name\"");
6708 puts("");
6709 puts("Options:");
6710 printf("-d spool-directory Spool directory "
6711 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6712 puts("-h Show program help");
6713 puts("-k Keep job spool files");
6714 puts("-n hostname Hostname for printer");
6715 puts("-p port Port number (default=auto)");
6716 puts("-u user:pass Set proxy username and password");
6717 puts("-v[vvv] Be (very) verbose");
6718
6719 exit(status);
6720 }
6721
6722
6723 /*
6724 * 'valid_doc_attributes()' - Determine whether the document attributes are
6725 * valid.
6726 *
6727 * When one or more document attributes are invalid, this function adds a
6728 * suitable response and attributes to the unsupported group.
6729 */
6730
6731 static int /* O - 1 if valid, 0 if not */
6732 valid_doc_attributes(
6733 _ipp_client_t *client) /* I - Client */
6734 {
6735 int valid = 1; /* Valid attributes? */
6736 ipp_op_t op = ippGetOperation(client->request);
6737 /* IPP operation */
6738 const char *op_name = ippOpString(op);
6739 /* IPP operation name */
6740 ipp_attribute_t *attr, /* Current attribute */
6741 *supported; /* xxx-supported attribute */
6742 const char *compression = NULL,
6743 /* compression value */
6744 *format = NULL; /* document-format value */
6745
6746
6747 /*
6748 * Check operation attributes...
6749 */
6750
6751 if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
6752 {
6753 /*
6754 * If compression is specified, only accept a supported value in a Print-Job
6755 * or Send-Document request...
6756 */
6757
6758 compression = ippGetString(attr, 0, NULL);
6759 supported = ippFindAttribute(client->printer->attrs,
6760 "compression-supported", IPP_TAG_KEYWORD);
6761
6762 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
6763 ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
6764 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
6765 op != IPP_OP_VALIDATE_JOB) ||
6766 !ippContainsString(supported, compression))
6767 {
6768 respond_unsupported(client, attr);
6769 valid = 0;
6770 }
6771 else
6772 {
6773 fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
6774
6775 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
6776
6777 if (strcmp(compression, "none"))
6778 {
6779 if (Verbosity)
6780 fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
6781 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
6782 }
6783 }
6784 }
6785
6786 /*
6787 * Is it a format we support?
6788 */
6789
6790 if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
6791 {
6792 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
6793 ippGetGroupTag(attr) != IPP_TAG_OPERATION)
6794 {
6795 respond_unsupported(client, attr);
6796 valid = 0;
6797 }
6798 else
6799 {
6800 format = ippGetString(attr, 0, NULL);
6801
6802 fprintf(stderr, "%s %s document-format=\"%s\"\n",
6803 client->hostname, op_name, format);
6804
6805 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
6806 }
6807 }
6808 else
6809 {
6810 format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
6811 if (!format)
6812 format = "application/octet-stream"; /* Should never happen */
6813
6814 attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
6815 }
6816
6817 if (!strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
6818 {
6819 /*
6820 * Auto-type the file using the first 8 bytes of the file...
6821 */
6822
6823 unsigned char header[8]; /* First 8 bytes of file */
6824
6825 memset(header, 0, sizeof(header));
6826 httpPeek(client->http, (char *)header, sizeof(header));
6827
6828 if (!memcmp(header, "%PDF", 4))
6829 format = "application/pdf";
6830 else if (!memcmp(header, "%!", 2))
6831 format = "application/postscript";
6832 else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
6833 format = "image/jpeg";
6834 else if (!memcmp(header, "\211PNG", 4))
6835 format = "image/png";
6836 else if (!memcmp(header, "RAS2", 4))
6837 format = "image/pwg-raster";
6838 else if (!memcmp(header, "UNIRAST", 8))
6839 format = "image/urf";
6840 else
6841 format = NULL;
6842
6843 if (format)
6844 {
6845 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
6846 client->hostname, op_name, format);
6847
6848 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
6849 }
6850 }
6851
6852 if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
6853 {
6854 respond_unsupported(client, attr);
6855 valid = 0;
6856 }
6857
6858 /*
6859 * document-name
6860 */
6861
6862 if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
6863 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
6864
6865 return (valid);
6866 }
6867
6868
6869 /*
6870 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6871 *
6872 * When one or more job attributes are invalid, this function adds a suitable
6873 * response and attributes to the unsupported group.
6874 */
6875
6876 static int /* O - 1 if valid, 0 if not */
6877 valid_job_attributes(
6878 _ipp_client_t *client) /* I - Client */
6879 {
6880 int i, /* Looping var */
6881 valid = 1; /* Valid attributes? */
6882 ipp_attribute_t *attr, /* Current attribute */
6883 *supported; /* xxx-supported attribute */
6884
6885
6886 /*
6887 * Check operation attributes...
6888 */
6889
6890 valid = valid_doc_attributes(client);
6891
6892 /*
6893 * Check the various job template attributes...
6894 */
6895
6896 if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
6897 {
6898 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
6899 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
6900 {
6901 respond_unsupported(client, attr);
6902 valid = 0;
6903 }
6904 }
6905
6906 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
6907 {
6908 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
6909 {
6910 respond_unsupported(client, attr);
6911 valid = 0;
6912 }
6913 }
6914
6915 if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
6916 {
6917 if (ippGetCount(attr) != 1 ||
6918 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6919 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6920 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
6921 strcmp(ippGetString(attr, 0, NULL), "no-hold"))
6922 {
6923 respond_unsupported(client, attr);
6924 valid = 0;
6925 }
6926 }
6927
6928 if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
6929 {
6930 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
6931 {
6932 respond_unsupported(client, attr);
6933 valid = 0;
6934 }
6935 }
6936
6937 if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
6938 {
6939 if (ippGetCount(attr) != 1 ||
6940 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6941 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
6942 {
6943 respond_unsupported(client, attr);
6944 valid = 0;
6945 }
6946
6947 ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
6948 }
6949 else
6950 ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
6951
6952 if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
6953 {
6954 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
6955 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
6956 {
6957 respond_unsupported(client, attr);
6958 valid = 0;
6959 }
6960 }
6961
6962 if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
6963 {
6964 if (ippGetCount(attr) != 1 ||
6965 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6966 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6967 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
6968 strcmp(ippGetString(attr, 0, NULL), "none"))
6969 {
6970 respond_unsupported(client, attr);
6971 valid = 0;
6972 }
6973 }
6974
6975 if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
6976 {
6977 if (ippGetCount(attr) != 1 ||
6978 (ippGetValueTag(attr) != IPP_TAG_NAME &&
6979 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
6980 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
6981 {
6982 respond_unsupported(client, attr);
6983 valid = 0;
6984 }
6985 else
6986 {
6987 #if 0 /* TODO: Validate media */
6988 for (i = 0;
6989 i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
6990 i ++)
6991 if (!strcmp(ippGetString(attr, 0, NULL), media_supported[i]))
6992 break;
6993
6994 if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
6995 {
6996 respond_unsupported(client, attr);
6997 valid = 0;
6998 }
6999 #endif /* 0 */
7000 }
7001 }
7002
7003 if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
7004 {
7005 if (ippGetCount(attr) != 1 ||
7006 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
7007 {
7008 respond_unsupported(client, attr);
7009 valid = 0;
7010 }
7011 /* TODO: check for valid media-col */
7012 }
7013
7014 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
7015 {
7016 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
7017 (strcmp(ippGetString(attr, 0, NULL),
7018 "separate-documents-uncollated-copies") &&
7019 strcmp(ippGetString(attr, 0, NULL),
7020 "separate-documents-collated-copies")))
7021 {
7022 respond_unsupported(client, attr);
7023 valid = 0;
7024 }
7025 }
7026
7027 if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
7028 {
7029 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7030 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
7031 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
7032 {
7033 respond_unsupported(client, attr);
7034 valid = 0;
7035 }
7036 }
7037
7038 if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
7039 {
7040 if (ippGetValueTag(attr) != IPP_TAG_RANGE)
7041 {
7042 respond_unsupported(client, attr);
7043 valid = 0;
7044 }
7045 }
7046
7047 if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
7048 {
7049 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
7050 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
7051 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
7052 {
7053 respond_unsupported(client, attr);
7054 valid = 0;
7055 }
7056 }
7057
7058 if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
7059 {
7060 supported = ippFindAttribute(client->printer->dev_attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
7061
7062 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
7063 !supported)
7064 {
7065 respond_unsupported(client, attr);
7066 valid = 0;
7067 }
7068 else
7069 {
7070 int count, /* Number of supported values */
7071 xdpi, /* Horizontal resolution for job template attribute */
7072 ydpi, /* Vertical resolution for job template attribute */
7073 sydpi; /* Vertical resolution for supported value */
7074 ipp_res_t units, /* Units for job template attribute */
7075 sunits; /* Units for supported value */
7076
7077 xdpi = ippGetResolution(attr, 0, &ydpi, &units);
7078 count = ippGetCount(supported);
7079
7080 for (i = 0; i < count; i ++)
7081 {
7082 if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
7083 break;
7084 }
7085
7086 if (i >= count)
7087 {
7088 respond_unsupported(client, attr);
7089 valid = 0;
7090 }
7091 }
7092 }
7093
7094 if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
7095 {
7096 const char *sides = ippGetString(attr, 0, NULL);
7097 /* "sides" value... */
7098
7099 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
7100 {
7101 respond_unsupported(client, attr);
7102 valid = 0;
7103 }
7104 else if ((supported = ippFindAttribute(client->printer->dev_attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
7105 {
7106 if (!ippContainsString(supported, sides))
7107 {
7108 respond_unsupported(client, attr);
7109 valid = 0;
7110 }
7111 }
7112 else if (strcmp(sides, "one-sided"))
7113 {
7114 respond_unsupported(client, attr);
7115 valid = 0;
7116 }
7117 }
7118
7119 return (valid);
7120 }
7121
7122
7123 /*
7124 * End of "$Id$".
7125 */