2 * Sample IPP INFRA server for CUPS.
4 * Copyright 2010-2014 by Apple Inc.
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * which should have been included with this file. If this file is
10 * file is missing or damaged, see the license at "http://www.cups.org/".
12 * This file is subject to the Apple OS-Developed Software exception.
16 * Disable private and deprecated stuff so we can verify that the public API
17 * is sufficient to implement a server.
20 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
21 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
25 * Include necessary headers...
28 #include <config.h> /* CUPS configuration header */
29 #include <cups/cups.h> /* Public API */
30 #include <cups/string-private.h> /* CUPS string functions */
31 #include <cups/thread-private.h> /* For multithreading functions */
44 # define WEXITSTATUS(s) (s)
45 # include <winsock2.h>
49 extern char **environ
;
51 # include <sys/fcntl.h>
52 # include <sys/wait.h>
56 #ifdef HAVE_SYS_MOUNT_H
57 # include <sys/mount.h>
58 #endif /* HAVE_SYS_MOUNT_H */
59 #ifdef HAVE_SYS_STATFS_H
60 # include <sys/statfs.h>
61 #endif /* HAVE_SYS_STATFS_H */
62 #ifdef HAVE_SYS_STATVFS_H
63 # include <sys/statvfs.h>
64 #endif /* HAVE_SYS_STATVFS_H */
67 #endif /* HAVE_SYS_VFS_H */
70 typedef pthread_cond_t _cups_cond_t
;
71 # define _CUPS_COND_INITIALIZER PTHREAD_COND_INITIALIZER
72 # define _cupsCondBroadcast(c) pthread_cond_broadcast(c)
73 # define _cupsCondDeinit(c) pthread_cond_destroy(c)
74 # define _cupsCondInit(c) pthread_cond_init((c), NULL)
75 # define _cupsCondWait(c,m) pthread_cond_wait((c),(m))
76 # define _cupsMutexDeinit(m) pthread_mutex_destroy(m)
77 # define _cupsRWDeinit(rw) pthread_rwlock_destroy(rw)
79 typedef char _cups_cond_t
;
80 # define _CUPS_COND_INITIALIZER 0
81 # define _cupsCondBroadcast(c)
82 # define _cupsCondDeinit(c)
83 # define _cupsCondInit(c) *(c)=0
84 # define _cupsCondWait(c,m) 0
85 # define _cupsMutexDeinit(m)
86 # define _cupsRWDeinit(rw)
87 #endif /* HAVE_PTHREAD_H */
94 /* New IPP operation codes from IPP INFRA */
95 # define _IPP_OP_ACKNOWLEDGE_DOCUMENT (ipp_op_t)0x003f
96 # define _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER (ipp_op_t)0x0040
97 # define _IPP_OP_ACKNOWLEDGE_JOB (ipp_op_t)0x0041
98 # define _IPP_OP_FETCH_DOCUMENT (ipp_op_t)0x0042
99 # define _IPP_OP_FETCH_JOB (ipp_op_t)0x0043
100 # define _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES (ipp_op_t)0x0044
101 # define _IPP_OP_UPDATE_ACTIVE_JOBS (ipp_op_t)0x0045
102 # define _IPP_OP_UPDATE_DOCUMENT_STATUS (ipp_op_t)0x0047
103 # define _IPP_OP_UPDATE_JOB_STATUS (ipp_op_t)0x0048
104 # define _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES (ipp_op_t)0x0049
105 # define _IPP_OP_DEREGISTER_OUTPUT_DEVICE (ipp_op_t)0x204b
107 /* New IPP status code from IPP INFRA */
108 # define _IPP_STATUS_ERROR_NOT_FETCHABLE (ipp_status_t)0x0420
110 /* Maximum lease duration value from RFC 3995 - 2^26-1 or ~2 years */
111 # define _IPP_NOTIFY_LEASE_DURATION_MAX 67108863
112 /* But a value of 0 means "never expires"... */
113 # define _IPP_NOTIFY_LEASE_DURATION_FOREVER 0
114 /* Default duration is 1 day */
115 # define _IPP_NOTIFY_LEASE_DURATION_DEFAULT 86400
119 * Event mask enumeration...
122 enum _ipp_event_e
/* notify-events bit values */
124 _IPP_EVENT_DOCUMENT_COMPLETED
= 0x00000001,
125 _IPP_EVENT_DOCUMENT_CONFIG_CHANGED
= 0x00000002,
126 _IPP_EVENT_DOCUMENT_CREATED
= 0x00000004,
127 _IPP_EVENT_DOCUMENT_FETCHABLE
= 0x00000008,
128 _IPP_EVENT_DOCUMENT_STATE_CHANGED
= 0x00000010,
129 _IPP_EVENT_DOCUMENT_STOPPED
= 0x00000020,
130 _IPP_EVENT_JOB_COMPLETED
= 0x00000040,
131 _IPP_EVENT_JOB_CONFIG_CHANGED
= 0x00000080,
132 _IPP_EVENT_JOB_CREATED
= 0x00000100,
133 _IPP_EVENT_JOB_FETCHABLE
= 0x00000200,
134 _IPP_EVENT_JOB_PROGRESS
= 0x00000400,
135 _IPP_EVENT_JOB_STATE_CHANGED
= 0x00000800,
136 _IPP_EVENT_JOB_STOPPED
= 0x00001000,
137 _IPP_EVENT_PRINTER_CONFIG_CHANGED
= 0x00002000,
138 _IPP_EVENT_PRINTER_FINISHINGS_CHANGED
= 0x00004000,
139 _IPP_EVENT_PRINTER_MEDIA_CHANGED
= 0x00008000,
140 _IPP_EVENT_PRINTER_QUEUE_ORDER_CHANGED
= 0x00010000,
141 _IPP_EVENT_PRINTER_RESTARTED
= 0x00020000,
142 _IPP_EVENT_PRINTER_SHUTDOWN
= 0x00040000,
143 _IPP_EVENT_PRINTER_STATE_CHANGED
= 0x00080000,
144 _IPP_EVENT_PRINTER_STOPPED
= 0x00100000,
146 /* "Wildcard" values... */
147 _IPP_EVENT_NONE
= 0x00000000, /* Nothing */
148 _IPP_EVENT_DOCUMENT_ALL
= 0x0000003f,
149 _IPP_EVENT_DOCUMENT_STATE_ALL
= 0x00000037,
150 _IPP_EVENT_JOB_ALL
= 0x00001fc0,
151 _IPP_EVENT_JOB_STATE_ALL
= 0x00001940,
152 _IPP_EVENT_PRINTER_ALL
= 0x001fe000,
153 _IPP_EVENT_PRINTER_CONFIG_ALL
= 0x0000e000,
154 _IPP_EVENT_PRINTER_STATE_ALL
= 0x001e0000,
155 _IPP_EVENT_ALL
= 0x001fffff /* Everything */
157 typedef unsigned int _ipp_event_t
; /* Bitfield for notify-events */
158 #define _IPP_EVENT_DEFAULT _IPP_EVENT_JOB_COMPLETED
159 #define _IPP_EVENT_DEFAULT_STRING "job-completed"
160 static const char * const _ipp_events
[] =
161 { /* Strings for bits */
162 "document-completed",
163 "document-config-changed",
165 "document-fetchable",
166 "document-state-changed",
169 "job-config-changed",
175 "printer-config-changed",
176 "printer-finishings-changed",
177 "printer-media-changed",
178 "printer-queue-order-changed",
181 "printer-state-changed",
185 enum _ipp_jreason_e
/* job-state-reasons bit values */
187 _IPP_JREASON_NONE
= 0x00000000, /* none */
188 _IPP_JREASON_ABORTED_BY_SYSTEM
= 0x00000001,
189 _IPP_JREASON_COMPRESSION_ERROR
= 0x00000002,
190 _IPP_JREASON_DOCUMENT_ACCESS_ERROR
= 0x00000004,
191 _IPP_JREASON_DOCUMENT_FORMAT_ERROR
= 0x00000008,
192 _IPP_JREASON_DOCUMENT_PASSWORD_ERROR
= 0x00000010,
193 _IPP_JREASON_DOCUMENT_PERMISSION_ERROR
= 0x00000020,
194 _IPP_JREASON_DOCUMENT_SECURITY_ERROR
= 0x00000040,
195 _IPP_JREASON_DOCUMENT_UNPRINTABLE_ERROR
= 0x00000080,
196 _IPP_JREASON_ERRORS_DETECTED
= 0x00000100,
197 _IPP_JREASON_JOB_CANCELED_AT_DEVICE
= 0x00000200,
198 _IPP_JREASON_JOB_CANCELED_BY_USER
= 0x00000400,
199 _IPP_JREASON_JOB_COMPLETED_SUCCESSFULLY
= 0x00000800,
200 _IPP_JREASON_JOB_COMPLETED_WITH_ERRORS
= 0x00001000,
201 _IPP_JREASON_JOB_COMPLETED_WITH_WARNINGS
= 0x00002000,
202 _IPP_JREASON_JOB_DATA_INSUFFICIENT
= 0x00004000,
203 _IPP_JREASON_JOB_FETCHABLE
= 0x00008000,
204 _IPP_JREASON_JOB_INCOMING
= 0x00010000,
205 _IPP_JREASON_JOB_PASSWORD_WAIT
= 0x00020000,
206 _IPP_JREASON_JOB_PRINTING
= 0x00040000,
207 _IPP_JREASON_JOB_QUEUED
= 0x00080000,
208 _IPP_JREASON_JOB_SPOOLING
= 0x00100000,
209 _IPP_JREASON_JOB_STOPPED
= 0x00200000,
210 _IPP_JREASON_JOB_TRANSFORMING
= 0x00400000,
211 _IPP_JREASON_PRINTER_STOPPED
= 0x00800000,
212 _IPP_JREASON_PRINTER_STOPPED_PARTLY
= 0x01000000,
213 _IPP_JREASON_PROCESSING_TO_STOP_POINT
= 0x02000000,
214 _IPP_JREASON_QUEUED_IN_DEVICE
= 0x04000000,
215 _IPP_JREASON_WARNINGS_DETECTED
= 0x08000000
217 typedef unsigned int _ipp_jreason_t
; /* Bitfield for job-state-reasons */
218 static const char * const _ipp_jreasons
[] =
219 { /* Strings for bits */
222 "document-access-error",
223 "document-format-error",
224 "document-password-error",
225 "document-permission-error",
226 "document-security-error",
227 "document-unprintable-error",
229 "job-canceled-at-device",
230 "job-canceled-by-user",
231 "job-completed-successfully",
232 "job-completed-with-errors",
233 "job-completed-with-warnings",
234 "job-data-insufficient",
244 "printer-stopped-partly",
245 "processing-to-stop-point",
250 enum _ipp_preason_e
/* printer-state-reasons bit values */
252 _IPP_PREASON_NONE
= 0x0000, /* none */
253 _IPP_PREASON_OTHER
= 0x0001, /* other */
254 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
255 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
256 /* input-tray-missing */
257 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
258 /* marker-supply-empty */
259 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
260 /* marker-supply-low */
261 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
262 /* marker-waste-almost-full */
263 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
264 /* marker-waste-full */
265 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
266 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
267 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
268 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
269 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
270 /* moving-to-paused */
271 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
272 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
273 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
274 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
276 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
277 static const char * const _ipp_preasons
[] =
278 { /* Strings for bits */
281 "input-tray-missing",
282 "marker-supply-empty",
284 "marker-waste-almost-full",
302 typedef struct _ipp_filter_s
/**** Attribute filter ****/
304 cups_array_t
*ra
; /* Requested attributes */
305 ipp_tag_t group_tag
; /* Group to copy */
308 typedef struct _ipp_job_s _ipp_job_t
;
310 typedef struct _ipp_device_s
/**** Output Device data ****/
312 _cups_rwlock_t rwlock
; /* Printer lock */
313 char *name
, /* printer-name (mapped to output-device) */
314 *uuid
; /* output-device-uuid */
315 ipp_t
*attrs
; /* All printer attributes */
316 ipp_pstate_t state
; /* printer-state value */
317 _ipp_preason_t reasons
; /* printer-state-reasons values */
320 typedef struct _ipp_printer_s
/**** Printer data ****/
322 _cups_rwlock_t rwlock
; /* Printer lock */
323 int ipv4
, /* IPv4 listener */
324 ipv6
; /* IPv6 listener */
325 char *name
, /* printer-name */
326 *directory
, /* Spool directory */
327 *hostname
, /* Hostname */
328 *uri
, /* printer-uri-supported */
329 *proxy_user
, /* Proxy username */
330 *proxy_pass
; /* Proxy password */
332 size_t urilen
; /* Length of printer URI */
333 cups_array_t
*devices
; /* Associated devices */
334 ipp_t
*attrs
; /* Static attributes */
335 ipp_t
*dev_attrs
; /* Current device attributes */
336 time_t start_time
; /* Startup time */
337 time_t config_time
; /* printer-config-change-time */
338 ipp_pstate_t state
, /* printer-state value */
339 dev_state
; /* Current device printer-state value */
340 _ipp_preason_t state_reasons
, /* printer-state-reasons values */
341 dev_reasons
; /* Current device printer-state-reasons values */
342 time_t state_time
; /* printer-state-change-time */
343 cups_array_t
*jobs
, /* Jobs */
344 *active_jobs
, /* Active jobs */
345 *completed_jobs
;/* Completed jobs */
346 _ipp_job_t
*processing_job
;/* Current processing job */
347 int next_job_id
; /* Next job-id value */
348 cups_array_t
*subscriptions
; /* Subscriptions */
349 int next_sub_id
; /* Next notify-subscription-id value */
352 struct _ipp_job_s
/**** Job data ****/
355 _cups_rwlock_t rwlock
; /* Job lock */
356 const char *name
, /* job-name */
357 *username
, /* job-originating-user-name */
358 *format
; /* document-format */
359 int priority
; /* job-priority */
360 char *dev_uuid
; /* output-device-uuid-assigned */
361 ipp_jstate_t state
, /* job-state value */
362 dev_state
; /* output-device-job-state value */
363 _ipp_jreason_t state_reasons
, /* job-state-reasons values */
365 /* output-device-job-state-reasons values */
366 char *dev_state_message
;
367 /* output-device-job-state-message value */
368 time_t created
, /* time-at-creation value */
369 processing
, /* time-at-processing value */
370 completed
; /* time-at-completed value */
371 int impressions
, /* job-impressions value */
372 impcompleted
; /* job-impressions-completed value */
373 ipp_t
*attrs
; /* Attributes */
374 int cancel
; /* Non-zero when job canceled */
375 char *filename
; /* Print file name */
376 int fd
; /* Print file descriptor */
377 _ipp_printer_t
*printer
; /* Printer */
380 typedef struct _ipp_subscription_s
/**** Subscription data ****/
382 int id
; /* notify-subscription-id */
383 const char *uuid
; /* notify-subscription-uuid */
384 _cups_rwlock_t rwlock
; /* Subscription lock */
385 _ipp_event_t mask
; /* Event mask */
386 _ipp_printer_t
*printer
; /* Printer */
387 _ipp_job_t
*job
; /* Job, if any */
388 ipp_t
*attrs
; /* Attributes */
389 const char *username
; /* notify-subscriber-user-name */
390 int lease
; /* notify-lease-duration */
391 int interval
; /* notify-time-interval */
392 time_t expire
; /* Lease expiration time */
393 int first_sequence
, /* First notify-sequence-number in cache */
394 last_sequence
; /* Last notify-sequence-number used */
395 cups_array_t
*events
; /* Events (ipp_t *'s) */
396 int pending_delete
; /* Non-zero when the subscription is about to be deleted/canceled */
397 } _ipp_subscription_t
;
399 typedef struct _ipp_client_s
/**** Client data ****/
401 http_t
*http
; /* HTTP connection */
402 ipp_t
*request
, /* IPP request */
403 *response
; /* IPP response */
404 time_t start
; /* Request start time */
405 http_state_t operation
; /* Request operation */
406 ipp_op_t operation_id
; /* IPP operation-id */
407 char uri
[1024], /* Request URI */
408 *options
; /* URI options */
409 http_addr_t addr
; /* Client address */
410 char hostname
[256], /* Client hostname */
411 username
[32]; /* Client authenticated username */
412 _ipp_printer_t
*printer
; /* Printer */
413 _ipp_job_t
*job
; /* Current job, if any */
414 int fetch_compression
,
416 fetch_file
; /* File to fetch */
424 static void add_event(_ipp_printer_t
*printer
, _ipp_job_t
*job
, _ipp_event_t event
, const char *message
, ...) __attribute__((__format__(__printf__
, 4, 5)));
425 static void check_jobs(_ipp_printer_t
*printer
);
426 static void clean_jobs(_ipp_printer_t
*printer
);
427 static int compare_active_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
428 static int compare_completed_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
429 static int compare_devices(_ipp_device_t
*a
, _ipp_device_t
*b
);
430 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
431 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
432 ipp_tag_t group_tag
, int quickcopy
);
433 static void copy_job_attributes(_ipp_client_t
*client
,
434 _ipp_job_t
*job
, cups_array_t
*ra
);
435 static void copy_job_state_reasons(ipp_t
*ipp
, ipp_tag_t group_tag
, _ipp_job_t
*job
);
436 static void copy_printer_state_reasons(ipp_t
*ipp
, ipp_tag_t group_tag
, _ipp_printer_t
*printer
);
437 static void copy_subscription_attributes(_ipp_client_t
*client
, _ipp_subscription_t
*sub
, cups_array_t
*ra
);
438 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
439 static _ipp_device_t
*create_device(_ipp_client_t
*client
);
440 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
441 static void create_job_filename(_ipp_printer_t
*printer
, _ipp_job_t
*job
, const char *format
, char *fname
, size_t fnamesize
);
442 static int create_listener(int family
, int port
);
443 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
);
444 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
);
445 static void debug_attributes(const char *title
, ipp_t
*ipp
,
447 static void delete_client(_ipp_client_t
*client
);
448 static void delete_device(_ipp_device_t
*device
);
449 static void delete_job(_ipp_job_t
*job
);
450 static void delete_printer(_ipp_printer_t
*printer
);
451 static void delete_subscription(_ipp_subscription_t
*sub
);
452 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
453 static _ipp_device_t
*find_device(_ipp_client_t
*client
);
454 static _ipp_job_t
*find_job(_ipp_client_t
*client
, int job_id
);
455 static _ipp_subscription_t
*find_subscription(_ipp_client_t
*client
, int sub_id
);
456 static _ipp_jreason_t
get_job_state_reasons_bits(ipp_attribute_t
*attr
);
457 static _ipp_event_t
get_notify_events_bits(ipp_attribute_t
*attr
);
458 static const char *get_notify_subscribed_event(_ipp_event_t event
);
459 static _ipp_preason_t
get_printer_state_reasons_bits(ipp_attribute_t
*attr
);
460 static void html_escape(_ipp_client_t
*client
, const char *s
,
462 static void html_footer(_ipp_client_t
*client
);
463 static void html_header(_ipp_client_t
*client
, const char *title
);
464 static void html_printf(_ipp_client_t
*client
, const char *format
, ...) __attribute__((__format__(__printf__
, 2, 3)));
465 static void ipp_acknowledge_document(_ipp_client_t
*client
);
466 static void ipp_acknowledge_identify_printer(_ipp_client_t
*client
);
467 static void ipp_acknowledge_job(_ipp_client_t
*client
);
468 static void ipp_cancel_job(_ipp_client_t
*client
);
469 static void ipp_cancel_my_jobs(_ipp_client_t
*client
);
470 static void ipp_cancel_subscription(_ipp_client_t
*client
);
471 static void ipp_close_job(_ipp_client_t
*client
);
472 static void ipp_create_job(_ipp_client_t
*client
);
473 static void ipp_create_xxx_subscriptions(_ipp_client_t
*client
);
474 static void ipp_deregister_output_device(_ipp_client_t
*client
);
475 static void ipp_fetch_document(_ipp_client_t
*client
);
476 static void ipp_fetch_job(_ipp_client_t
*client
);
477 static void ipp_get_document_attributes(_ipp_client_t
*client
);
478 static void ipp_get_documents(_ipp_client_t
*client
);
479 static void ipp_get_job_attributes(_ipp_client_t
*client
);
480 static void ipp_get_jobs(_ipp_client_t
*client
);
481 static void ipp_get_notifications(_ipp_client_t
*client
);
482 static void ipp_get_output_device_attributes(_ipp_client_t
*client
);
483 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
484 static void ipp_get_printer_supported_values(_ipp_client_t
*client
);
485 static void ipp_get_subscription_attributes(_ipp_client_t
*client
);
486 static void ipp_get_subscriptions(_ipp_client_t
*client
);
487 static void ipp_identify_printer(_ipp_client_t
*client
);
488 static void ipp_print_job(_ipp_client_t
*client
);
489 static void ipp_print_uri(_ipp_client_t
*client
);
490 static void ipp_renew_subscription(_ipp_client_t
*client
);
491 static void ipp_send_document(_ipp_client_t
*client
);
492 static void ipp_send_uri(_ipp_client_t
*client
);
493 static void ipp_update_active_jobs(_ipp_client_t
*client
);
494 static void ipp_update_document_status(_ipp_client_t
*client
);
495 static void ipp_update_job_status(_ipp_client_t
*client
);
496 static void ipp_update_output_device_attributes(_ipp_client_t
*client
);
497 static void ipp_validate_document(_ipp_client_t
*client
);
498 static void ipp_validate_job(_ipp_client_t
*client
);
499 //static int parse_options(_ipp_client_t *client, cups_option_t **options);
500 static void *process_client(_ipp_client_t
*client
);
501 static int process_http(_ipp_client_t
*client
);
502 static int process_ipp(_ipp_client_t
*client
);
503 static void *process_job(_ipp_job_t
*job
);
504 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
505 const char *content_coding
,
506 const char *type
, size_t length
);
507 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
508 const char *message
, ...)
509 __attribute__ ((__format__ (__printf__
, 3, 4)));
510 static void respond_unsupported(_ipp_client_t
*client
,
511 ipp_attribute_t
*attr
);
512 static void run_printer(_ipp_printer_t
*printer
);
513 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
514 static void update_device_attributes_no_lock(_ipp_printer_t
*printer
);
515 static void update_device_state_no_lock(_ipp_printer_t
*printer
);
516 static void usage(int status
) __attribute__((noreturn
));
517 static int valid_doc_attributes(_ipp_client_t
*client
);
518 static int valid_job_attributes(_ipp_client_t
*client
);
525 static int KeepFiles
= 0,
527 //static _cups_mutex_t SubscriptionMutex = _CUPS_MUTEX_INITIALIZER;
528 static _cups_cond_t SubscriptionCondition
= _CUPS_COND_INITIALIZER
;
532 * 'main()' - Main entry to the sample infrastructure server.
535 int /* O - Exit status */
536 main(int argc
, /* I - Number of command-line args */
537 char *argv
[]) /* I - Command-line arguments */
539 int i
; /* Looping var */
540 const char *opt
, /* Current option character */
541 *servername
= NULL
, /* Server host name */
542 *name
= NULL
; /* Printer name */
544 const char *keypath
= NULL
; /* Keychain path */
545 #endif /* HAVE_SSL */
546 int port
= 0; /* Port number (0 = auto) */
547 char directory
[1024] = "", /* Spool directory */
548 hostname
[1024], /* Auto-detected hostname */
549 proxy_user
[256] = "", /* Proxy username */
550 *proxy_pass
= NULL
; /* Proxy password */
551 _ipp_printer_t
*printer
; /* Printer object */
555 * Parse command-line arguments...
558 for (i
= 1; i
< argc
; i
++)
559 if (argv
[i
][0] == '-')
561 for (opt
= argv
[i
] + 1; *opt
; opt
++)
566 case 'K' : /* -K keypath */
572 #endif /* HAVE_SSL */
574 case 'd' : /* -d spool-directory */
578 strlcpy(directory
, argv
[i
], sizeof(directory
));
581 case 'h' : /* -h (show help) */
584 case 'k' : /* -k (keep files) */
588 case 'n' : /* -n hostname */
592 servername
= argv
[i
];
595 case 'p' : /* -p port */
597 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
599 port
= atoi(argv
[i
]);
602 case 'u' : /* -u user:pass */
606 strlcpy(proxy_user
, argv
[i
], sizeof(proxy_user
));
607 if ((proxy_pass
= strchr(proxy_user
, ':')) != NULL
)
608 *proxy_pass
++ = '\0';
611 case 'v' : /* -v (be verbose) */
615 default : /* Unknown */
616 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
627 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
635 * Apply defaults as needed...
639 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
645 * Windows is almost always used as a single user system, so use a default port
653 * Use 8000 + UID mod 1000 for the default port number...
656 port
= 8000 + ((int)getuid() % 1000);
659 fprintf(stderr
, "Listening on port %d.\n", port
);
664 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
666 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
668 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
669 directory
, strerror(errno
));
674 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
679 strlcpy(proxy_user
, "test", sizeof(proxy_user
));
682 fputs("Using proxy username \"test\".\n", stderr
);
687 proxy_pass
= "test123";
690 fputs("Using proxy password \"test123\".\n", stderr
);
694 cupsSetServerCredentials(keypath
, servername
, 1);
695 #endif /* HAVE_SSL */
698 * Create the printer...
701 if ((printer
= create_printer(servername
, port
, name
, directory
, proxy_user
, proxy_pass
)) == NULL
)
705 * Run the print service...
708 run_printer(printer
);
711 * Destroy the printer and exit...
714 delete_printer(printer
);
721 * 'add_event()' - Add an event to a subscription.
725 add_event(_ipp_printer_t
*printer
, /* I - Printer */
726 _ipp_job_t
*job
, /* I - Job, if any */
727 _ipp_event_t event
, /* I - Event */
728 const char *message
, /* I - Printf-style notify-text message */
729 ...) /* I - Additional printf arguments */
731 _ipp_subscription_t
*sub
; /* Current subscription */
732 ipp_t
*n
; /* Notify attributes */
733 char text
[1024]; /* notify-text value */
734 va_list ap
; /* Argument pointer */
737 va_start(ap
, message
);
738 vsnprintf(text
, sizeof(text
), message
, ap
);
741 for (sub
= (_ipp_subscription_t
*)cupsArrayFirst(printer
->subscriptions
);
743 sub
= (_ipp_subscription_t
*)cupsArrayNext(printer
->subscriptions
))
745 if (sub
->mask
& event
&& (!sub
->job
|| job
== sub
->job
))
747 _cupsRWLockWrite(&sub
->rwlock
);
750 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_CHARSET
, "notify-charset", NULL
, "utf-8");
751 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_LANGUAGE
, "notify-natural-language", NULL
, "en");
752 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-printer-up-time", time(NULL
) - printer
->start_time
);
753 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
, "notify-printer-uri", NULL
, printer
->uri
);
755 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-job-id", job
->id
);
756 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-subcription-id", sub
->id
);
757 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
, "notify-subscription-uuid", NULL
, sub
->uuid
);
758 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-sequence-number", ++ sub
->last_sequence
);
759 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "notify-subscribed-event", NULL
, get_notify_subscribed_event(event
));
760 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_TEXT
, "notify-text", NULL
, text
);
761 if (event
& _IPP_EVENT_PRINTER_ALL
)
763 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
, "printer-state", printer
->state
);
764 copy_printer_state_reasons(n
, IPP_TAG_EVENT_NOTIFICATION
, printer
);
766 if (event
& _IPP_EVENT_JOB_ALL
)
768 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
, "job-state", job
->state
);
769 copy_job_state_reasons(n
, IPP_TAG_EVENT_NOTIFICATION
, job
);
770 if (event
== _IPP_EVENT_JOB_CREATED
)
772 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
, "job-name", NULL
, job
->name
);
773 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
777 cupsArrayAdd(sub
->events
, n
);
778 if (cupsArrayCount(sub
->events
) > 100)
780 n
= (ipp_t
*)cupsArrayFirst(sub
->events
);
781 cupsArrayRemove(sub
->events
, n
);
783 sub
->first_sequence
++;
786 _cupsRWUnlock(&sub
->rwlock
);
787 _cupsCondBroadcast(&SubscriptionCondition
);
794 * 'check_jobs()' - Check for new jobs to process.
798 check_jobs(_ipp_printer_t
*printer
) /* I - Printer */
800 _ipp_job_t
*job
; /* Current job */
803 if (printer
->processing_job
)
806 _cupsRWLockWrite(&(printer
->rwlock
));
807 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->active_jobs
);
809 job
= (_ipp_job_t
*)cupsArrayNext(printer
->active_jobs
))
811 if (job
->state
== IPP_JSTATE_PENDING
)
813 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
815 job
->state
= IPP_JSTATE_ABORTED
;
816 job
->completed
= time(NULL
);
818 add_event(printer
, job
, _IPP_EVENT_JOB_COMPLETED
, "Job aborted because creation of processing thread failed.");
823 _cupsRWUnlock(&(printer
->rwlock
));
828 * 'clean_jobs()' - Clean out old (completed) jobs.
832 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
834 _ipp_job_t
*job
; /* Current job */
835 time_t cleantime
; /* Clean time */
838 if (cupsArrayCount(printer
->jobs
) == 0)
841 cleantime
= time(NULL
) - 60;
843 _cupsRWLockWrite(&(printer
->rwlock
));
844 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
846 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
847 if (job
->completed
&& job
->completed
< cleantime
)
849 cupsArrayRemove(printer
->jobs
, job
);
854 _cupsRWUnlock(&(printer
->rwlock
));
859 * 'compare_active_jobs()' - Compare two active jobs.
862 static int /* O - Result of comparison */
863 compare_active_jobs(_ipp_job_t
*a
, /* I - First job */
864 _ipp_job_t
*b
) /* I - Second job */
866 int diff
; /* Difference */
869 if ((diff
= b
->priority
- a
->priority
) == 0)
870 diff
= b
->id
- a
->id
;
877 * 'compare_completed_jobs()' - Compare two completed jobs.
880 static int /* O - Result of comparison */
881 compare_completed_jobs(_ipp_job_t
*a
, /* I - First job */
882 _ipp_job_t
*b
) /* I - Second job */
884 int diff
; /* Difference */
887 if ((diff
= a
->completed
- b
->completed
) == 0)
888 diff
= b
->id
- a
->id
;
895 * 'compare_devices()' - Compare two devices...
898 static int /* O - Result of comparison */
899 compare_devices(_ipp_device_t
*a
, /* I - First device */
900 _ipp_device_t
*b
) /* I - Second device */
902 return (strcmp(a
->uuid
, b
->uuid
));
907 * 'compare_jobs()' - Compare two jobs.
910 static int /* O - Result of comparison */
911 compare_jobs(_ipp_job_t
*a
, /* I - First job */
912 _ipp_job_t
*b
) /* I - Second job */
914 return (b
->id
- a
->id
);
919 * 'copy_attributes()' - Copy attributes from one request to another.
923 copy_attributes(ipp_t
*to
, /* I - Destination request */
924 ipp_t
*from
, /* I - Source request */
925 cups_array_t
*ra
, /* I - Requested attributes */
926 ipp_tag_t group_tag
, /* I - Group to copy */
927 int quickcopy
) /* I - Do a quick copy? */
929 _ipp_filter_t filter
; /* Filter data */
933 filter
.group_tag
= group_tag
;
935 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
940 * 'copy_job_attrs()' - Copy job attributes to the response.
945 _ipp_client_t
*client
, /* I - Client */
946 _ipp_job_t
*job
, /* I - Job */
947 cups_array_t
*ra
) /* I - requested-attributes */
949 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
951 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
954 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
956 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
959 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
962 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
964 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
967 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
968 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
970 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
971 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
973 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
974 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
976 if (!ra
|| cupsArrayFind(ra
, "job-state"))
977 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
978 "job-state", job
->state
);
980 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
982 if (job
->dev_state_message
)
984 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_TAG_TEXT
, "job-state-message", NULL
, job
->dev_state_message
);
988 const char *message
= ""; /* Message string */
992 case IPP_JSTATE_PENDING
:
993 message
= "Job pending.";
996 case IPP_JSTATE_HELD
:
997 if (job
->state_reasons
& _IPP_JREASON_JOB_INCOMING
)
998 message
= "Job incoming.";
999 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
1000 message
= "Job held.";
1002 message
= "Job created.";
1005 case IPP_JSTATE_PROCESSING
:
1006 if (job
->state_reasons
& _IPP_JREASON_PROCESSING_TO_STOP_POINT
)
1009 message
= "Cancel in progress.";
1011 message
= "Abort in progress.";
1014 message
= "Job printing.";
1017 case IPP_JSTATE_STOPPED
:
1018 message
= "Job stopped.";
1021 case IPP_JSTATE_CANCELED
:
1022 message
= "Job canceled.";
1025 case IPP_JSTATE_ABORTED
:
1026 message
= "Job aborted.";
1029 case IPP_JSTATE_COMPLETED
:
1030 message
= "Job completed.";
1034 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, message
);
1038 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
1039 copy_job_state_reasons(client
->response
, IPP_TAG_JOB
, job
);
1043 case IPP_JSTATE_PENDING :
1044 ippAddString(client->response, IPP_TAG_JOB,
1045 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1049 case IPP_JSTATE_HELD :
1051 ippAddString(client->response, IPP_TAG_JOB,
1052 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1053 "job-state-reasons", NULL, "job-incoming");
1054 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
1055 ippAddString(client->response, IPP_TAG_JOB,
1056 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1057 "job-state-reasons", NULL, "job-hold-until-specified");
1059 ippAddString(client->response, IPP_TAG_JOB,
1060 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1061 "job-state-reasons", NULL, "job-data-insufficient");
1064 case IPP_JSTATE_PROCESSING :
1066 ippAddString(client->response, IPP_TAG_JOB,
1067 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1068 "job-state-reasons", NULL, "processing-to-stop-point");
1070 ippAddString(client->response, IPP_TAG_JOB,
1071 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1072 "job-state-reasons", NULL, "job-printing");
1075 case IPP_JSTATE_STOPPED :
1076 ippAddString(client->response, IPP_TAG_JOB,
1077 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1078 NULL, "job-stopped");
1081 case IPP_JSTATE_CANCELED :
1082 ippAddString(client->response, IPP_TAG_JOB,
1083 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1084 NULL, "job-canceled-by-user");
1087 case IPP_JSTATE_ABORTED :
1088 ippAddString(client->response, IPP_TAG_JOB,
1089 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1090 NULL, "aborted-by-system");
1093 case IPP_JSTATE_COMPLETED :
1094 ippAddString(client->response, IPP_TAG_JOB,
1095 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1096 NULL, "job-completed-successfully");
1101 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
1102 ippAddInteger(client
->response
, IPP_TAG_JOB
,
1103 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
1104 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
1106 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
1107 ippAddInteger(client
->response
, IPP_TAG_JOB
,
1108 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
1109 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
1114 * 'copy_job_state_reasons()' - Copy printer-state-reasons values.
1118 copy_job_state_reasons(
1119 ipp_t
*ipp
, /* I - Attributes */
1120 ipp_tag_t group_tag
, /* I - Group */
1121 _ipp_job_t
*job
) /* I - Printer */
1123 _ipp_jreason_t creasons
; /* Combined job-state-reasons */
1126 creasons
= job
->state_reasons
| job
->dev_state_reasons
;
1130 ippAddString(ipp
, group_tag
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons", NULL
, "none");
1134 int i
, /* Looping var */
1135 num_reasons
= 0;/* Number of reasons */
1136 _ipp_jreason_t reason
; /* Current reason */
1137 const char *reasons
[32]; /* Reason strings */
1139 for (i
= 0, reason
= 1; i
< (int)(sizeof(_ipp_jreasons
) / sizeof(_ipp_jreasons
[0])); i
++, reason
<<= 1)
1141 if (creasons
& reason
)
1142 reasons
[num_reasons
++] = _ipp_jreasons
[i
];
1145 ippAddStrings(ipp
, group_tag
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons", num_reasons
, NULL
, reasons
);
1151 * 'copy_printer_state_reasons()' - Copy printer-state-reasons values.
1155 copy_printer_state_reasons(
1156 ipp_t
*ipp
, /* I - Attributes */
1157 ipp_tag_t group_tag
, /* I - Group */
1158 _ipp_printer_t
*printer
) /* I - Printer */
1160 _ipp_preason_t creasons
= printer
->state_reasons
| printer
->dev_reasons
;
1161 /* Combined reasons */
1164 if (creasons
== _IPP_PREASON_NONE
)
1166 ippAddString(ipp
, group_tag
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", NULL
, "none");
1170 int i
, /* Looping var */ num_reasons
= 0;/* Number of reasons */
1171 _ipp_preason_t reason
; /* Current reason */
1172 const char *reasons
[32]; /* Reason strings */
1174 for (i
= 0, reason
= 1; i
< (int)(sizeof(_ipp_preasons
) / sizeof(_ipp_preasons
[0])); i
++, reason
<<= 1)
1176 if (creasons
& reason
)
1177 reasons
[num_reasons
++] = _ipp_preasons
[i
];
1180 ippAddStrings(ipp
, group_tag
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", num_reasons
, NULL
, reasons
);
1186 * 'copy_sub_attrs()' - Copy job attributes to the response.
1190 copy_subscription_attributes(
1191 _ipp_client_t
*client
, /* I - Client */
1192 _ipp_subscription_t
*sub
, /* I - Subscription */
1193 cups_array_t
*ra
) /* I - requested-attributes */
1195 copy_attributes(client
->response
, sub
->attrs
, ra
, IPP_TAG_SUBSCRIPTION
, 0);
1197 if (!ra
|| cupsArrayFind(ra
, "notify-lease-expiration-time"))
1198 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-lease-expiration-time", (int)(sub
->expire
- client
->printer
->start_time
));
1200 if (!ra
|| cupsArrayFind(ra
, "notify-printer-up-time"))
1201 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
1203 if (!ra
|| cupsArrayFind(ra
, "notify-sequence-number"))
1204 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-sequence-number", sub
->last_sequence
);
1209 * 'create_client()' - Accept a new network connection and create a client
1213 static _ipp_client_t
* /* O - Client */
1214 create_client(_ipp_printer_t
*printer
, /* I - Printer */
1215 int sock
) /* I - Listen socket */
1217 _ipp_client_t
*client
; /* Client */
1220 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
1222 perror("Unable to allocate memory for client");
1226 client
->printer
= printer
;
1227 client
->fetch_file
= -1;
1230 * Accept the client and get the remote address...
1233 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
1235 perror("Unable to accept client connection");
1242 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
1245 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
1252 * 'create_device()' - Create an output device tracking object.
1255 static _ipp_device_t
* /* O - Device */
1256 create_device(_ipp_client_t
*client
) /* I - Client */
1258 _ipp_device_t
*device
; /* Device */
1259 ipp_attribute_t
*uuid
; /* output-device-uuid */
1262 if ((uuid
= ippFindAttribute(client
->request
, "output-device-uuid", IPP_TAG_URI
)) == NULL
)
1265 if ((device
= calloc(1, sizeof(_ipp_device_t
))) == NULL
)
1268 _cupsRWInit(&device
->rwlock
);
1270 device
->uuid
= strdup(ippGetString(uuid
, 0, NULL
));
1271 device
->state
= IPP_PSTATE_STOPPED
;
1273 _cupsRWLockWrite(&client
->printer
->rwlock
);
1274 cupsArrayAdd(client
->printer
->devices
, device
);
1275 _cupsRWUnlock(&client
->printer
->rwlock
);
1282 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1286 static _ipp_job_t
* /* O - Job */
1287 create_job(_ipp_client_t
*client
) /* I - Client */
1289 _ipp_job_t
*job
; /* Job */
1290 ipp_attribute_t
*attr
; /* Job attribute */
1291 char uri
[1024], /* job-uri value */
1292 uuid
[64]; /* job-uuid value */
1295 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1298 * Allocate and initialize the job object...
1301 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
1303 perror("Unable to allocate memory for job");
1307 job
->printer
= client
->printer
;
1308 job
->attrs
= ippNew();
1309 job
->state
= IPP_JSTATE_HELD
;
1313 * Copy all of the job attributes...
1316 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1319 * Get the requesting-user-name, document format, and priority...
1322 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
1323 job
->priority
= ippGetInteger(attr
, 0);
1327 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1328 job
->username
= ippGetString(attr
, 0, NULL
);
1330 job
->username
= "anonymous";
1332 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1334 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1336 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1337 job
->format
= ippGetString(attr
, 0, NULL
);
1338 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1339 job
->format
= ippGetString(attr
, 0, NULL
);
1341 job
->format
= "application/octet-stream";
1344 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1345 job
->impressions
= ippGetInteger(attr
, 0);
1347 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1348 job
->name
= ippGetString(attr
, 0, NULL
);
1351 * Add job description attributes and add to the jobs array...
1354 job
->id
= client
->printer
->next_job_id
++;
1356 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1357 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1359 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1360 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1361 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1362 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1363 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1364 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1366 cupsArrayAdd(client
->printer
->jobs
, job
);
1367 cupsArrayAdd(client
->printer
->active_jobs
, job
);
1369 _cupsRWUnlock(&(client
->printer
->rwlock
));
1376 * 'create_job_filename()' - Create the filename for a document in a job.
1379 static void create_job_filename(
1380 _ipp_printer_t
*printer
, /* I - Printer */
1381 _ipp_job_t
*job
, /* I - Job */
1382 const char *format
, /* I - Format or NULL */
1383 char *fname
, /* I - Filename buffer */
1384 size_t fnamesize
) /* I - Size of filename buffer */
1386 char name
[256], /* "Safe" filename */
1387 *nameptr
; /* Pointer into filename */
1388 const char *ext
, /* Filename extension */
1389 *job_name
; /* job-name value */
1390 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1394 * Make a name from the job-name attribute...
1397 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1398 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1400 job_name
= "untitled";
1402 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1403 if (isalnum(*job_name
& 255) || *job_name
== '-')
1404 *nameptr
++ = (char)tolower(*job_name
& 255);
1411 * Figure out the extension...
1415 format
= job
->format
;
1417 if (!strcasecmp(format
, "image/jpeg"))
1419 else if (!strcasecmp(format
, "image/png"))
1421 else if (!strcasecmp(format
, "image/pwg-raster"))
1423 else if (!strcasecmp(format
, "image/urf"))
1425 else if (!strcasecmp(format
, "application/pdf"))
1427 else if (!strcasecmp(format
, "application/postscript"))
1433 * Create a filename with the job-id, job-name, and document-format (extension)...
1436 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1441 * 'create_listener()' - Create a listener socket.
1444 static int /* O - Listener socket or -1 on error */
1445 create_listener(int family
, /* I - Address family */
1446 int port
) /* I - Port number */
1448 int sock
; /* Listener socket */
1449 http_addrlist_t
*addrlist
; /* Listen address */
1450 char service
[255]; /* Service port */
1453 snprintf(service
, sizeof(service
), "%d", port
);
1454 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1457 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1459 httpAddrFreeList(addrlist
);
1466 * 'create_printer()' - Create, register, and listen for connections to a
1470 static _ipp_printer_t
* /* O - Printer */
1471 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1472 int port
, /* I - Port number */
1473 const char *name
, /* I - printer-name */
1474 const char *directory
, /* I - Spool directory */
1475 const char *proxy_user
, /* I - Proxy account username */
1476 const char *proxy_pass
) /* I - Proxy account password */
1478 _ipp_printer_t
*printer
; /* Printer */
1479 char uri
[1024], /* Printer URI */
1480 adminurl
[1024], /* printer-more-info URI */
1481 supplyurl
[1024],/* printer-supply-info-uri URI */
1482 uuid
[128]; /* printer-uuid */
1483 int k_supported
; /* Maximum file size supported */
1485 struct statvfs spoolinfo
; /* FS info for spool directory */
1486 double spoolsize
; /* FS size */
1487 #elif defined(HAVE_STATFS)
1488 struct statfs spoolinfo
; /* FS info for spool directory */
1489 double spoolsize
; /* FS size */
1490 #endif /* HAVE_STATVFS */
1491 static const char * const versions
[] =/* ipp-versions-supported values */
1497 static const char * const features
[] =/* ipp-features-supported values */
1501 "infrastructure-printer",
1504 static const int ops
[] = /* operations-supported values */
1508 IPP_OP_VALIDATE_JOB
,
1510 IPP_OP_SEND_DOCUMENT
,
1513 IPP_OP_GET_JOB_ATTRIBUTES
,
1515 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1516 IPP_OP_GET_PRINTER_SUPPORTED_VALUES
,
1517 IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
,
1518 IPP_OP_CREATE_JOB_SUBSCRIPTIONS
,
1519 IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES
,
1520 IPP_OP_GET_SUBSCRIPTIONS
,
1521 IPP_OP_RENEW_SUBSCRIPTION
,
1522 IPP_OP_CANCEL_SUBSCRIPTION
,
1523 IPP_OP_GET_NOTIFICATIONS
,
1524 IPP_OP_GET_DOCUMENT_ATTRIBUTES
,
1525 IPP_OP_GET_DOCUMENTS
,
1526 IPP_OP_CANCEL_MY_JOBS
,
1528 IPP_OP_IDENTIFY_PRINTER
,
1529 IPP_OP_VALIDATE_DOCUMENT
,
1530 _IPP_OP_ACKNOWLEDGE_DOCUMENT
,
1531 _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER
,
1532 _IPP_OP_ACKNOWLEDGE_JOB
,
1533 _IPP_OP_FETCH_DOCUMENT
,
1535 _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES
,
1536 _IPP_OP_UPDATE_ACTIVE_JOBS
,
1537 _IPP_OP_UPDATE_DOCUMENT_STATUS
,
1538 _IPP_OP_UPDATE_JOB_STATUS
,
1539 _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES
,
1540 _IPP_OP_DEREGISTER_OUTPUT_DEVICE
1542 static const char * const charsets
[] =/* charset-supported values */
1547 static const char * const compressions
[] =/* compression-supported values */
1552 #endif /* HAVE_LIBZ */
1555 static const char * const notify_attributes
[] =
1556 { /* notify-attributes-supported */
1557 "printer-state-change-time",
1558 "notify-lease-expiration-time",
1559 "notify-subscriber-user-name"
1561 static const char * const reference_uri_schemes_supported
[] =
1562 { /* reference-uri-schemes-supported */
1568 #endif /* HAVE_SSL */
1570 static const char * const which_jobs
[] =
1571 { /* which-jobs-supported values */
1580 "processing-stopped"
1585 * Allocate memory for the printer...
1588 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1590 perror("ippserver: Unable to allocate memory for printer");
1596 printer
->name
= strdup(name
);
1597 printer
->directory
= strdup(directory
);
1598 printer
->hostname
= strdup(servername
);
1599 printer
->port
= port
;
1600 printer
->start_time
= time(NULL
);
1601 printer
->config_time
= printer
->start_time
;
1602 printer
->state
= IPP_PSTATE_IDLE
;
1603 printer
->state_reasons
= _IPP_PREASON_NONE
;
1604 printer
->state_time
= printer
->start_time
;
1605 printer
->jobs
= cupsArrayNew3((cups_array_func_t
)compare_jobs
, NULL
, NULL
, 0, NULL
, (cups_afree_func_t
)delete_job
);
1606 printer
->active_jobs
= cupsArrayNew((cups_array_func_t
)compare_active_jobs
, NULL
);
1607 printer
->completed_jobs
= cupsArrayNew((cups_array_func_t
)compare_completed_jobs
, NULL
);
1608 printer
->next_job_id
= 1;
1610 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1611 printer
->hostname
, printer
->port
, "/ipp/print");
1612 printer
->uri
= strdup(uri
);
1613 printer
->urilen
= strlen(uri
);
1616 printer
->proxy_user
= strdup(proxy_user
);
1618 printer
->proxy_pass
= strdup(proxy_pass
);
1620 printer
->devices
= cupsArrayNew((cups_array_func_t
)compare_devices
, NULL
);
1622 _cupsRWInit(&(printer
->rwlock
));
1625 * Create the listener sockets...
1628 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1630 perror("Unable to create IPv4 listener");
1634 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1636 perror("Unable to create IPv6 listener");
1641 * Prepare values for the printer attributes...
1644 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1645 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1649 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1650 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1651 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1655 * Get the maximum spool size based on the size of the filesystem used for
1656 * the spool directory. If the host OS doesn't support the statfs call
1657 * or the filesystem is larger than 2TiB, always report INT_MAX.
1661 if (statvfs(printer
->directory
, &spoolinfo
))
1662 k_supported
= INT_MAX
;
1663 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1664 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1665 k_supported
= INT_MAX
;
1667 k_supported
= (int)spoolsize
;
1669 #elif defined(HAVE_STATFS)
1670 if (statfs(printer
->directory
, &spoolinfo
))
1671 k_supported
= INT_MAX
;
1672 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1673 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1674 k_supported
= INT_MAX
;
1676 k_supported
= (int)spoolsize
;
1679 k_supported
= INT_MAX
;
1680 #endif /* HAVE_STATVFS */
1683 * Create the printer attributes. This list of attributes is sorted to improve
1684 * performance when the client provides a requested-attributes attribute...
1687 printer
->attrs
= ippNew();
1689 /* charset-configured */
1690 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1691 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1692 "charset-configured", NULL
, "utf-8");
1694 /* charset-supported */
1695 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1696 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1697 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1700 /* compression-supported */
1701 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1702 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1703 "compression-supported",
1704 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1707 /* generated-natural-language-supported */
1708 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1709 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1710 "generated-natural-language-supported", NULL
, "en");
1712 /* ipp-features-supported */
1713 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1715 /* ipp-versions-supported */
1716 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1718 /* ippget-event-life */
1719 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "ippget-event-life", 300);
1721 /* job-ids-supported */
1722 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1724 /* job-k-octets-supported */
1725 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1728 /* job-priority-default */
1729 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1730 "job-priority-default", 50);
1732 /* job-priority-supported */
1733 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1734 "job-priority-supported", 100);
1736 /* multiple-document-jobs-supported */
1737 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1739 /* multiple-operation-time-out */
1740 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1742 /* multiple-operation-time-out-action */
1743 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1745 /* natural-language-configured */
1746 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1747 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1748 "natural-language-configured", NULL
, "en");
1750 /* notify-attributes-supported */
1751 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
);
1753 /* notify-events-default */
1754 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-events-default", NULL
, "job-completed");
1756 /* notify-events-supported */
1757 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
);
1759 /* notify-lease-duration-default */
1760 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "notify-lease-duration-default", 86400);
1762 /* notify-lease-duration-supported */
1763 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "notify-lease-duration-supported", 0, _IPP_NOTIFY_LEASE_DURATION_MAX
);
1765 /* notify-max-events-supported */
1766 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "notify-lease-duration-default", (int)(sizeof(_ipp_events
) / sizeof(_ipp_events
[0])));
1768 /* notify-pull-method-supported */
1769 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-pull-method-supported", NULL
, "ippget");
1771 /* operations-supported */
1772 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1773 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1775 /* printer-get-attributes-supported */
1776 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1778 /* printer-is-accepting-jobs */
1779 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1782 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
1784 /* printer-more-info */
1785 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1788 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
1790 /* printer-supply-info-uri */
1791 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1793 /* printer-uri-supported */
1794 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1797 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
1798 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1800 /* reference-uri-scheme-supported */
1801 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1802 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1803 "reference-uri-schemes-supported",
1804 (int)(sizeof(reference_uri_schemes_supported
) /
1805 sizeof(reference_uri_schemes_supported
[0])),
1806 NULL
, reference_uri_schemes_supported
);
1808 /* uri-authentication-supported */
1809 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1810 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1811 "uri-authentication-supported", NULL
, "basic");
1813 /* uri-security-supported */
1814 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1815 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1816 "uri-security-supported", NULL
, "tls");
1818 /* which-jobs-supported */
1819 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1820 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1821 "which-jobs-supported",
1822 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1824 debug_attributes("Printer", printer
->attrs
, 0);
1834 * If we get here we were unable to create the printer...
1839 delete_printer(printer
);
1845 * 'create_subscription()' - Create a new subscription object from a
1846 * Print-Job, Create-Job, or Create-xxx-Subscription
1850 static _ipp_subscription_t
* /* O - Subscription object */
1851 create_subscription(
1852 _ipp_printer_t
*printer
, /* I - Printer */
1853 _ipp_job_t
*job
, /* I - Job, if any */
1854 int interval
, /* I - Interval for progress events */
1855 int lease
, /* I - Lease duration */
1856 const char *username
, /* I - User creating the subscription */
1857 ipp_attribute_t
*notify_events
, /* I - Events to monitor */
1858 ipp_attribute_t
*notify_attributes
, /* I - Attributes to report */
1859 ipp_attribute_t
*notify_user_data
) /* I - User data, if any */
1861 _ipp_subscription_t
*sub
; /* Subscription */
1862 ipp_attribute_t
*attr
; /* Subscription attribute */
1863 char uuid
[64]; /* notify-subscription-uuid value */
1867 * Allocate and initialize the subscription object...
1870 if ((sub
= calloc(1, sizeof(_ipp_subscription_t
))) == NULL
)
1872 perror("Unable to allocate memory for subscription");
1876 _cupsRWLockWrite(&(printer
->rwlock
));
1878 sub
->id
= printer
->next_sub_id
++;
1879 sub
->mask
= notify_events
? get_notify_events_bits(notify_events
) : _IPP_EVENT_DEFAULT
;
1880 sub
->printer
= printer
;
1882 sub
->interval
= interval
;
1884 sub
->attrs
= ippNew();
1887 sub
->expire
= time(NULL
) + sub
->lease
;
1889 sub
->expire
= INT_MAX
;
1891 _cupsRWInit(&(sub
->rwlock
));
1894 * Add subscription description attributes and add to the subscriptions
1898 ippAddInteger(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-subscription-id", sub
->id
);
1900 httpAssembleUUID(printer
->hostname
, printer
->port
, printer
->name
, -sub
->id
, uuid
, sizeof(uuid
));
1901 attr
= ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
, "notify-subscription-uuid", NULL
, uuid
);
1902 sub
->uuid
= ippGetString(attr
, 0, NULL
);
1904 ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
, "notify-printer-uri", NULL
, printer
->uri
);
1907 ippAddInteger(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-job-id", job
->id
);
1909 ippAddInteger(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-lease-duration", sub
->lease
);
1911 attr
= ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_NAME
, "notify-subscriber-user-name", NULL
, username
);
1912 sub
->username
= ippGetString(attr
, 0, NULL
);
1915 ippCopyAttribute(sub
->attrs
, notify_events
, 0);
1917 ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-events", NULL
, _IPP_EVENT_DEFAULT_STRING
);
1919 ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-pull-method", NULL
, "ippget");
1921 if (notify_attributes
)
1922 ippCopyAttribute(sub
->attrs
, notify_attributes
, 0);
1924 if (notify_user_data
)
1925 ippCopyAttribute(sub
->attrs
, notify_user_data
, 0);
1927 sub
->events
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
, (cups_afree_func_t
)ippDelete
);
1929 cupsArrayAdd(printer
->subscriptions
, sub
);
1931 _cupsRWUnlock(&(printer
->rwlock
));
1938 * 'debug_attributes()' - Print attributes in a request or response.
1942 debug_attributes(const char *title
, /* I - Title */
1943 ipp_t
*ipp
, /* I - Request/response */
1944 int type
) /* I - 0 = object, 1 = request, 2 = response */
1946 ipp_tag_t group_tag
; /* Current group */
1947 ipp_attribute_t
*attr
; /* Current attribute */
1948 char buffer
[2048]; /* String buffer for value */
1949 int major
, minor
; /* Version */
1955 fprintf(stderr
, "%s:\n", title
);
1956 major
= ippGetVersion(ipp
, &minor
);
1957 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1959 fprintf(stderr
, " operation-id=%s(%04x)\n",
1960 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1962 fprintf(stderr
, " status-code=%s(%04x)\n",
1963 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1964 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1966 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1968 attr
= ippNextAttribute(ipp
))
1970 if (ippGetGroupTag(attr
) != group_tag
)
1972 group_tag
= ippGetGroupTag(attr
);
1973 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1976 if (ippGetName(attr
))
1978 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1979 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1980 ippGetCount(attr
) > 1 ? "1setOf " : "",
1981 ippTagString(ippGetValueTag(attr
)), buffer
);
1988 * 'delete_client()' - Close the socket and free all memory used by a client
1993 delete_client(_ipp_client_t
*client
) /* I - Client */
1996 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1999 * Flush pending writes before closing...
2002 httpFlushWrite(client
->http
);
2008 httpClose(client
->http
);
2010 ippDelete(client
->request
);
2011 ippDelete(client
->response
);
2018 * 'delete_device()' - Remove a device from a printer.
2020 * Note: Caller is responsible for locking the printer object.
2024 delete_device(_ipp_device_t
*device
) /* I - Device */
2027 * Free memory used for the device...
2030 _cupsRWDeinit(&device
->rwlock
);
2037 ippDelete(device
->attrs
);
2044 * 'delete_job()' - Remove from the printer and free all memory used by a job
2049 delete_job(_ipp_job_t
*job
) /* I - Job */
2052 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2054 _cupsRWLockWrite(&job
->rwlock
);
2056 ippDelete(job
->attrs
);
2061 unlink(job
->filename
);
2063 free(job
->filename
);
2066 _cupsRWDeinit(&job
->rwlock
);
2073 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2074 * used by a printer object.
2078 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2080 _cupsRWLockWrite(&printer
->rwlock
);
2082 if (printer
->ipv4
>= 0)
2083 close(printer
->ipv4
);
2085 if (printer
->ipv6
>= 0)
2086 close(printer
->ipv6
);
2089 free(printer
->name
);
2090 if (printer
->directory
)
2091 free(printer
->directory
);
2092 if (printer
->hostname
)
2093 free(printer
->hostname
);
2096 if (printer
->proxy_user
)
2097 free(printer
->proxy_user
);
2098 if (printer
->proxy_pass
)
2099 free(printer
->proxy_pass
);
2102 ippDelete(printer
->attrs
);
2103 ippDelete(printer
->dev_attrs
);
2105 cupsArrayDelete(printer
->active_jobs
);
2106 cupsArrayDelete(printer
->completed_jobs
);
2107 cupsArrayDelete(printer
->jobs
);
2108 cupsArrayDelete(printer
->subscriptions
);
2110 _cupsRWDeinit(&printer
->rwlock
);
2117 * 'delete_subscription()' - Delete a subscription.
2121 delete_subscription(
2122 _ipp_subscription_t
*sub
) /* I - Subscription */
2124 sub
->pending_delete
= 1;
2126 _cupsCondBroadcast(&SubscriptionCondition
);
2128 _cupsRWLockWrite(&sub
->rwlock
);
2130 ippDelete(sub
->attrs
);
2131 cupsArrayDelete(sub
->events
);
2133 _cupsRWDeinit(&sub
->rwlock
);
2140 * 'filter_cb()' - Filter printer attributes based on the requested array.
2143 static int /* O - 1 to copy, 0 to ignore */
2144 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2145 ipp_t
*dst
, /* I - Destination (unused) */
2146 ipp_attribute_t
*attr
) /* I - Source attribute */
2149 * Filter attributes as needed...
2154 ipp_tag_t group
= ippGetGroupTag(attr
);
2155 const char *name
= ippGetName(attr
);
2157 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 (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2165 * 'find_device()' - Find a device.
2168 static _ipp_device_t
* /* I - Device */
2169 find_device(_ipp_client_t
*client
) /* I - Client */
2171 ipp_attribute_t
*uuid
; /* output-device-uuid */
2172 _ipp_device_t key
, /* Search key */
2173 *device
; /* Matching device */
2176 if ((uuid
= ippFindAttribute(client
->request
, "output-device-uuid", IPP_TAG_URI
)) == NULL
)
2179 key
.uuid
= (char *)ippGetString(uuid
, 0, NULL
);
2181 _cupsRWLockRead(&client
->printer
->rwlock
);
2182 device
= (_ipp_device_t
*)cupsArrayFind(client
->printer
->devices
, &key
);
2183 _cupsRWUnlock(&client
->printer
->rwlock
);
2190 * 'find_job()' - Find a job specified in a request.
2193 static _ipp_job_t
* /* O - Job or NULL */
2194 find_job(_ipp_client_t
*client
, /* I - Client */
2195 int job_id
) /* I - Job ID to find or 0 to lookup */
2197 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2198 _ipp_job_t key
, /* Job search key */
2199 *job
; /* Matching job, if any */
2206 else if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2208 const char *uri
= ippGetString(attr
, 0, NULL
);
2210 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2211 uri
[client
->printer
->urilen
] == '/')
2212 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2216 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2218 key
.id
= ippGetInteger(attr
, 0);
2221 _cupsRWLockRead(&(client
->printer
->rwlock
));
2222 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2223 _cupsRWUnlock(&(client
->printer
->rwlock
));
2230 * 'find_subscription()' - Find a subcription.
2233 static _ipp_subscription_t
* /* O - Subscription */
2234 find_subscription(_ipp_client_t
*client
,/* I - Client */
2235 int sub_id
) /* I - Subscription ID or 0 */
2237 ipp_attribute_t
*notify_subscription_id
;
2238 /* notify-subscription-id */
2239 _ipp_subscription_t key
, /* Search key */
2240 *sub
; /* Matching subscription */
2245 else if ((notify_subscription_id
= ippFindAttribute(client
->request
, "notify-subscription-id", IPP_TAG_INTEGER
)) == NULL
)
2248 key
.id
= ippGetInteger(notify_subscription_id
, 0);
2250 _cupsRWLockRead(&client
->printer
->rwlock
);
2251 sub
= (_ipp_subscription_t
*)cupsArrayFind(client
->printer
->subscriptions
, &key
);
2252 _cupsRWUnlock(&client
->printer
->rwlock
);
2259 * 'get_job_state_reasons_bits()' - Get the bits associates with "job-state-reasons" values.
2262 static _ipp_jreason_t
/* O - Bits */
2263 get_job_state_reasons_bits(
2264 ipp_attribute_t
*attr
) /* I - "job-state-reasons" attribute */
2266 int i
, j
, /* Looping vars */
2267 count
; /* Number of "job-state-reasons" values */
2268 const char *keyword
; /* "job-state-reasons" value */
2269 _ipp_jreason_t jreasons
= _IPP_JREASON_NONE
;
2270 /* Bits for "job-state-reasons" values */
2273 count
= ippGetCount(attr
);
2274 for (i
= 0; i
< count
; i
++)
2276 keyword
= ippGetString(attr
, i
, NULL
);
2278 for (j
= 0; j
< (int)(sizeof(_ipp_jreasons
) / sizeof(_ipp_jreasons
[0])); j
++)
2280 if (!strcmp(keyword
, _ipp_jreasons
[j
]))
2293 * 'get_notify_event_bits()' - Get the bits associated with "notify-events" values.
2296 static _ipp_event_t
/* O - Bits */
2297 get_notify_events_bits(
2298 ipp_attribute_t
*attr
) /* I - "notify-events" attribute */
2300 int i
, j
, /* Looping vars */
2301 count
; /* Number of "notify-events" values */
2302 const char *keyword
; /* "notify-events" value */
2303 _ipp_event_t events
= _IPP_EVENT_NONE
;
2304 /* Bits for "notify-events" values */
2307 count
= ippGetCount(attr
);
2308 for (i
= 0; i
< count
; i
++)
2310 keyword
= ippGetString(attr
, i
, NULL
);
2312 for (j
= 0; j
< (int)(sizeof(_ipp_events
) / sizeof(_ipp_events
[0])); j
++)
2314 if (!strcmp(keyword
, _ipp_jreasons
[j
]))
2327 * 'get_notify_subscribed_event()' - Get the event name.
2330 static const char * /* O - Event name */
2331 get_notify_subscribed_event(
2332 _ipp_event_t event
) /* I - Event bit */
2334 int i
; /* Looping var */
2335 _ipp_event_t mask
; /* Current mask */
2337 for (i
= 0, mask
= 1; i
< (int)(sizeof(_ipp_events
) / sizeof(_ipp_events
[0])); i
++, mask
<<= 1)
2339 return (_ipp_events
[i
]);
2346 * 'get_printer_state_reasons_bits()' - Get the bits associated with "printer-state-reasons" values.
2349 static _ipp_preason_t
/* O - Bits */
2350 get_printer_state_reasons_bits(
2351 ipp_attribute_t
*attr
) /* I - "printer-state-reasons" bits */
2353 int i
, j
, /* Looping vars */
2354 count
; /* Number of "printer-state-reasons" values */
2355 const char *keyword
; /* "printer-state-reasons" value */
2356 _ipp_preason_t preasons
= _IPP_PREASON_NONE
;
2357 /* Bits for "printer-state-reasons" values */
2360 count
= ippGetCount(attr
);
2361 for (i
= 0; i
< count
; i
++)
2363 keyword
= ippGetString(attr
, i
, NULL
);
2365 for (j
= 0; j
< (int)(sizeof(_ipp_preasons
) / sizeof(_ipp_preasons
[0])); j
++)
2367 if (!strcmp(keyword
, _ipp_preasons
[j
]))
2380 * 'html_escape()' - Write a HTML-safe string.
2384 html_escape(_ipp_client_t
*client
, /* I - Client */
2385 const char *s
, /* I - String to write */
2386 size_t slen
) /* I - Number of characters to write */
2388 const char *start
, /* Start of segment */
2389 *end
; /* End of string */
2393 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2395 while (*s
&& s
< end
)
2397 if (*s
== '&' || *s
== '<')
2400 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2403 httpWrite2(client
->http
, "&", 5);
2405 httpWrite2(client
->http
, "<", 4);
2414 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2419 * 'html_footer()' - Show the web interface footer.
2421 * This function also writes the trailing 0-length chunk.
2425 html_footer(_ipp_client_t
*client
) /* I - Client */
2431 httpWrite2(client
->http
, "", 0);
2436 * 'html_header()' - Show the web interface header and title.
2440 html_header(_ipp_client_t
*client
, /* I - Client */
2441 const char *title
) /* I - Title */
2447 "<title>%s</title>\n"
2448 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2449 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2450 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2451 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2453 "body { font-family: sans-serif; margin: 0; }\n"
2454 "div.body { padding: 0px 10px 10px; }\n"
2455 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2456 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2457 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2458 "table.form th { text-align: right; }\n"
2459 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2460 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2461 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2462 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2463 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2464 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2465 "table.nav td { margin: 0; text-align: center; }\n"
2466 "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"
2467 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2468 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2469 "td.nav:hover { background: #666; color: #fff; }\n"
2470 "td.nav:active { background: #000; color: #ff0; }\n"
2474 "<table class=\"nav\"><tr>"
2475 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2476 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2477 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2479 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2484 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2488 html_printf(_ipp_client_t
*client
, /* I - Client */
2489 const char *format
, /* I - Printf-style format string */
2490 ...) /* I - Additional arguments as needed */
2492 va_list ap
; /* Pointer to arguments */
2493 const char *start
; /* Start of string */
2494 char size
, /* Size character (h, l, L) */
2495 type
; /* Format type character */
2496 int width
, /* Width of field */
2497 prec
; /* Number of characters of precision */
2498 char tformat
[100], /* Temporary format string for sprintf() */
2499 *tptr
, /* Pointer into temporary format */
2500 temp
[1024]; /* Buffer for formatted numbers */
2501 char *s
; /* Pointer to string */
2505 * Loop through the format string, formatting as needed...
2508 va_start(ap
, format
);
2516 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2519 *tptr
++ = *format
++;
2523 httpWrite2(client
->http
, "%", 1);
2528 else if (strchr(" -+#\'", *format
))
2529 *tptr
++ = *format
++;
2534 * Get width from argument...
2538 width
= va_arg(ap
, int);
2540 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2541 tptr
+= strlen(tptr
);
2547 while (isdigit(*format
& 255))
2549 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2552 width
= width
* 10 + *format
++ - '0';
2558 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2566 * Get precision from argument...
2570 prec
= va_arg(ap
, int);
2572 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2573 tptr
+= strlen(tptr
);
2579 while (isdigit(*format
& 255))
2581 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2584 prec
= prec
* 10 + *format
++ - '0';
2589 if (*format
== 'l' && format
[1] == 'l')
2593 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2601 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2603 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2618 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2627 case 'E' : /* Floating point formats */
2632 if ((size_t)(width
+ 2) > sizeof(temp
))
2635 sprintf(temp
, tformat
, va_arg(ap
, double));
2637 httpWrite2(client
->http
, temp
, strlen(temp
));
2640 case 'B' : /* Integer formats */
2648 if ((size_t)(width
+ 2) > sizeof(temp
))
2651 # ifdef HAVE_LONG_LONG
2653 sprintf(temp
, tformat
, va_arg(ap
, long long));
2655 # endif /* HAVE_LONG_LONG */
2657 sprintf(temp
, tformat
, va_arg(ap
, long));
2659 sprintf(temp
, tformat
, va_arg(ap
, int));
2661 httpWrite2(client
->http
, temp
, strlen(temp
));
2664 case 'p' : /* Pointer value */
2665 if ((size_t)(width
+ 2) > sizeof(temp
))
2668 sprintf(temp
, tformat
, va_arg(ap
, void *));
2670 httpWrite2(client
->http
, temp
, strlen(temp
));
2673 case 'c' : /* Character or character array */
2676 temp
[0] = (char)va_arg(ap
, int);
2678 html_escape(client
, temp
, 1);
2681 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2684 case 's' : /* String */
2685 if ((s
= va_arg(ap
, char *)) == NULL
)
2688 html_escape(client
, s
, strlen(s
));
2697 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2704 * 'ipp_acknowledge_document()' - Acknowledge receipt of a document.
2708 ipp_acknowledge_document(
2709 _ipp_client_t
*client
) /* I - Client */
2711 _ipp_device_t
*device
; /* Device */
2712 _ipp_job_t
*job
; /* Job */
2713 ipp_attribute_t
*attr
; /* Attribute */
2716 if ((device
= find_device(client
)) == NULL
)
2718 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
2722 if ((job
= find_job(client
, 0)) == NULL
)
2724 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
2728 if (!job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
2730 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
2734 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)
2736 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, attr
? "Bad document-number attribute." : "Missing document-number attribute.");
2740 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2745 * 'ipp_acknowledge_identify_printer()' - Acknowledge an identify command.
2749 ipp_acknowledge_identify_printer(
2750 _ipp_client_t
*client
) /* I - Client */
2752 // TODO: Implement this!
2753 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
2758 * 'ipp_acknowledge_job()' - Acknowledge receipt of a job.
2762 ipp_acknowledge_job(
2763 _ipp_client_t
*client
) /* I - Client */
2765 _ipp_device_t
*device
; /* Device */
2766 _ipp_job_t
*job
; /* Job */
2769 if ((device
= find_device(client
)) == NULL
)
2771 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
2775 if ((job
= find_job(client
, 0)) == NULL
)
2777 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
2781 if (job
->dev_uuid
&& strcmp(job
->dev_uuid
, device
->uuid
))
2783 respond_ipp(client
, IPP_STATUS_ERROR_NOT_AUTHORIZED
, "Job not assigned to device.");
2787 if (!(job
->state_reasons
& _IPP_JREASON_JOB_FETCHABLE
))
2789 respond_ipp(client
, _IPP_STATUS_ERROR_NOT_FETCHABLE
, "Job not fetchable.");
2794 job
->dev_uuid
= strdup(device
->uuid
);
2796 job
->state_reasons
&= (_ipp_jreason_t
)~_IPP_JREASON_JOB_FETCHABLE
;
2798 add_event(client
->printer
, job
, _IPP_EVENT_JOB_STATE_CHANGED
, "Job acknowledged.");
2800 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2805 * 'ipp_cancel_job()' - Cancel a job.
2809 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2811 _ipp_job_t
*job
; /* Job information */
2818 if ((job
= find_job(client
, 0)) == NULL
)
2820 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2825 * See if the job is already completed, canceled, or aborted; if so,
2826 * we can't cancel...
2831 case IPP_JSTATE_CANCELED
:
2832 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2833 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2836 case IPP_JSTATE_ABORTED
:
2837 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2838 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2841 case IPP_JSTATE_COMPLETED
:
2842 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2843 "Job #%d is already completed - can\'t cancel.", job
->id
);
2851 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2853 if (job
->state
== IPP_JSTATE_PROCESSING
||
2854 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2858 job
->state
= IPP_JSTATE_CANCELED
;
2859 job
->completed
= time(NULL
);
2862 _cupsRWUnlock(&(client
->printer
->rwlock
));
2864 add_event(client
->printer
, job
, _IPP_EVENT_JOB_COMPLETED
, NULL
);
2866 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2873 * 'ipp_cancel_my_jobs()' - Cancel a user's jobs.
2878 _ipp_client_t
*client
) /* I - Client */
2880 // TODO: Implement this!
2881 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
2886 * 'ipp_cancel_subscription()' - Cancel a subscription.
2890 ipp_cancel_subscription(
2891 _ipp_client_t
*client
) /* I - Client */
2893 _ipp_subscription_t
*sub
; /* Subscription */
2896 if ((sub
= find_subscription(client
, 0)) == NULL
)
2898 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Subscription was not found.");
2902 _cupsRWLockWrite(&client
->printer
->rwlock
);
2903 cupsArrayRemove(client
->printer
->subscriptions
, sub
);
2904 delete_subscription(sub
);
2905 _cupsRWUnlock(&client
->printer
->rwlock
);
2906 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2911 * 'ipp_close_job()' - Close an open job.
2915 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2917 _ipp_job_t
*job
; /* Job information */
2924 if ((job
= find_job(client
, 0)) == NULL
)
2926 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2931 * See if the job is already completed, canceled, or aborted; if so,
2932 * we can't cancel...
2937 case IPP_JSTATE_CANCELED
:
2938 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2939 "Job #%d is canceled - can\'t close.", job
->id
);
2942 case IPP_JSTATE_ABORTED
:
2943 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2944 "Job #%d is aborted - can\'t close.", job
->id
);
2947 case IPP_JSTATE_COMPLETED
:
2948 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2949 "Job #%d is completed - can\'t close.", job
->id
);
2952 case IPP_JSTATE_PROCESSING
:
2953 case IPP_JSTATE_STOPPED
:
2954 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2955 "Job #%d is already closed.", job
->id
);
2959 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2966 * 'ipp_create_job()' - Create a job object.
2970 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2972 _ipp_job_t
*job
; /* New job */
2973 cups_array_t
*ra
; /* Attributes to send in response */
2977 * Validate print job attributes...
2980 if (!valid_job_attributes(client
))
2982 httpFlush(client
->http
);
2987 * Do we have a file to print?
2990 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2992 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2993 "Unexpected document data following request.");
3001 if ((job
= create_job(client
)) == NULL
)
3003 respond_ipp(client
, IPP_STATUS_ERROR_TOO_MANY_JOBS
, "Too many jobs are queued.");
3008 * Return the job info...
3011 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3013 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3014 cupsArrayAdd(ra
, "job-id");
3015 cupsArrayAdd(ra
, "job-state");
3016 cupsArrayAdd(ra
, "job-state-message");
3017 cupsArrayAdd(ra
, "job-state-reasons");
3018 cupsArrayAdd(ra
, "job-uri");
3020 copy_job_attributes(client
, job
, ra
);
3021 cupsArrayDelete(ra
);
3024 * Add any subscriptions...
3028 ipp_create_xxx_subscriptions(client
);
3033 * 'ipp_create_xxx_subscriptions()' - Create job and printer subscriptions.
3037 ipp_create_xxx_subscriptions(
3038 _ipp_client_t
*client
)
3040 _ipp_subscription_t
*sub
; /* Subscription */
3041 ipp_attribute_t
*attr
; /* Subscription attribute */
3042 const char *username
; /* requesting-user-name or
3043 authenticated username */
3044 int num_subs
= 0, /* Number of subscriptions */
3045 ok_subs
= 0; /* Number of good subscriptions */
3049 * For the Create-xxx-Subscriptions operations, queue up a successful-ok
3053 if (ippGetOperation(client
->request
) == IPP_OP_CREATE_JOB_SUBSCRIPTIONS
|| ippGetOperation(client
->request
) == IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
)
3054 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3057 * Get the authenticated user name, if any...
3060 if (client
->username
[0])
3061 username
= client
->username
;
3062 else if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
&& ippGetGroupTag(attr
) == IPP_TAG_OPERATION
&& ippGetCount(attr
) == 1)
3063 username
= ippGetString(attr
, 0, NULL
);
3068 * Skip past the initial attributes to the first subscription group.
3071 attr
= ippFirstAttribute(client
->request
);
3072 while (attr
&& ippGetGroupTag(attr
) != IPP_TAG_SUBSCRIPTION
)
3073 attr
= ippNextAttribute(client
->request
);
3077 _ipp_job_t
*job
= NULL
; /* Job */
3078 const char *attrname
, /* Attribute name */
3080 /* notify-pull-method */
3081 ipp_attribute_t
*notify_attributes
= NULL
,
3082 /* notify-attributes */
3083 *notify_events
= NULL
,
3085 *notify_user_data
= NULL
;
3086 /* notify-user-data */
3087 int interval
= 0, /* notify-time-interval */
3088 lease
= _IPP_NOTIFY_LEASE_DURATION_DEFAULT
;
3089 /* notify-lease-duration */
3090 ipp_status_t status
= IPP_STATUS_OK
;
3091 /* notify-status-code */
3097 if ((attrname
= ippGetName(attr
)) == NULL
)
3100 if (!strcmp(attrname
, "notify-recipient-uri"))
3103 * Push notifications not supported.
3106 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3107 ippCopyAttribute(client
->response
, attr
, 0);
3109 else if (!strcmp(attrname
, "notify-pull-method"))
3111 pullmethod
= ippGetString(attr
, 0, NULL
);
3113 if (ippGetValueTag(attr
) != IPP_TAG_KEYWORD
|| ippGetCount(attr
) != 1 || !pullmethod
|| strcmp(pullmethod
, "ippget"))
3115 ippCopyAttribute(client
->response
, attr
, 0);
3117 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3120 else if (!strcmp(attrname
, "notify-attributes"))
3122 if (ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
3124 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3125 ippCopyAttribute(client
->response
, attr
, 0);
3128 notify_attributes
= attr
;
3130 else if (!strcmp(attrname
, "notify-charset"))
3132 if (ippGetValueTag(attr
) != IPP_TAG_CHARSET
|| ippGetCount(attr
) != 1 ||
3133 (strcmp(ippGetString(attr
, 0, NULL
), "us-ascii") && strcmp(ippGetString(attr
, 0, NULL
), "utf-8")))
3135 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3136 ippCopyAttribute(client
->response
, attr
, 0);
3139 else if (!strcmp(attrname
, "notify-natural-language"))
3141 if (ippGetValueTag(attr
) != IPP_TAG_LANGUAGE
|| ippGetCount(attr
) != 1 || strcmp(ippGetString(attr
, 0, NULL
), "en"))
3143 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3144 ippCopyAttribute(client
->response
, attr
, 0);
3147 else if (!strcmp(attrname
, "notify-user-data"))
3149 int datalen
; /* Length of data */
3151 if (ippGetValueTag(attr
) != IPP_TAG_STRING
|| ippGetCount(attr
) != 1 || !ippGetOctetString(attr
, 0, &datalen
) || datalen
> 63)
3153 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3154 ippCopyAttribute(client
->response
, attr
, 0);
3157 notify_user_data
= attr
;
3159 else if (!strcmp(attrname
, "notify-events"))
3161 if (ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
3163 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3164 ippCopyAttribute(client
->response
, attr
, 0);
3167 notify_events
= attr
;
3169 else if (!strcmp(attrname
, "notify-lease-duration"))
3171 if (ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) < 0)
3173 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3174 ippCopyAttribute(client
->response
, attr
, 0);
3177 lease
= ippGetInteger(attr
, 0);
3179 else if (!strcmp(attrname
, "notify-time-interval"))
3181 if (ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) < 0)
3183 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3184 ippCopyAttribute(client
->response
, attr
, 0);
3187 interval
= ippGetInteger(attr
, 0);
3189 else if (!strcmp(attrname
, "notify-job-id"))
3191 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB_SUBSCRIPTIONS
|| ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 1)
3193 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3194 ippCopyAttribute(client
->response
, attr
, 0);
3196 else if ((job
= find_job(client
, ippGetInteger(attr
, 0))) == NULL
)
3198 status
= IPP_STATUS_ERROR_NOT_FOUND
;
3199 ippCopyAttribute(client
->response
, attr
, 0);
3203 attr
= ippNextAttribute(client
->request
);
3208 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
, "notify-status-code", status
);
3210 else if (!pullmethod
)
3212 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
, "notify-status-code", IPP_STATUS_ERROR_BAD_REQUEST
);
3216 switch (ippGetOperation(client
->request
))
3218 case IPP_OP_PRINT_JOB
:
3219 case IPP_OP_PRINT_URI
:
3220 case IPP_OP_CREATE_JOB
:
3228 if ((sub
= create_subscription(client
->printer
, job
, interval
, lease
, username
, notify_events
, notify_attributes
, notify_user_data
)) == NULL
)
3230 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-subscription-id", sub
->id
);
3234 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
, "notify-status-code", IPP_STATUS_ERROR_INTERNAL
);
3239 ippSetStatusCode(client
->response
, IPP_STATUS_ERROR_IGNORED_ALL_SUBSCRIPTIONS
);
3240 else if (ok_subs
!= num_subs
)
3241 ippSetStatusCode(client
->response
, IPP_STATUS_OK_IGNORED_SUBSCRIPTIONS
);
3246 * 'ipp_deregister_output_device()' - Unregister an output device.
3250 ipp_deregister_output_device(
3251 _ipp_client_t
*client
) /* I - Client */
3253 _ipp_device_t
*device
; /* Device */
3257 * Find the device...
3260 if ((device
= find_device(client
)) == NULL
)
3262 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Output device not found.");
3267 * Remove the device from the printer...
3270 _cupsRWLockWrite(&client
->printer
->rwlock
);
3272 cupsArrayRemove(client
->printer
->devices
, device
);
3274 update_device_attributes_no_lock(client
->printer
);
3275 update_device_state_no_lock(client
->printer
);
3277 _cupsRWUnlock(&client
->printer
->rwlock
);
3280 * Delete the device...
3283 delete_device(device
);
3285 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3290 * 'ipp_fetch_document()' - Download a document.
3295 _ipp_client_t
*client
) /* I - Client */
3297 _ipp_device_t
*device
; /* Device */
3298 _ipp_job_t
*job
; /* Job */
3299 ipp_attribute_t
*attr
; /* Attribute */
3300 int compression
; /* compression */
3301 char filename
[1024]; /* Job filename */
3302 const char *format
; /* document-format */
3305 if ((device
= find_device(client
)) == NULL
)
3307 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
3311 if ((job
= find_job(client
, 0)) == NULL
)
3313 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
3317 if (!job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
3319 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
3323 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)
3325 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, attr
? "Bad document-number attribute." : "Missing document-number attribute.");
3329 if ((attr
= ippFindAttribute(client
->request
, "compression-accepted", IPP_TAG_KEYWORD
)) != NULL
)
3330 compression
= !strcmp(ippGetString(attr
, 0, NULL
), "gzip");
3334 if ((attr
= ippFindAttribute(client
->request
, "document-format-accepted", IPP_TAG_MIMETYPE
)) != NULL
)
3336 int i
, /* Looping var */
3337 count
= ippGetCount(attr
); /* Number of values */
3340 for (i
= 0; i
< count
; i
++)
3342 format
= ippGetString(attr
, i
, NULL
);
3344 create_job_filename(client
->printer
, job
, NULL
, filename
, sizeof(filename
));
3346 if (!access(filename
, R_OK
))
3352 respond_ipp(client
, _IPP_STATUS_ERROR_NOT_FETCHABLE
, "Document not available in requested format.");
3356 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
3357 format
= ippGetString(attr
, 0, NULL
);
3360 respond_ipp(client
, _IPP_STATUS_ERROR_NOT_FETCHABLE
, "Document format unknown.");
3364 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3365 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
3366 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "compression", NULL
, compression
? "gzip" : "none");
3368 client
->fetch_file
= open(filename
, O_RDONLY
);
3373 * 'ipp_fetch_job()' - Download a job.
3377 ipp_fetch_job(_ipp_client_t
*client
) /* I - Client */
3379 _ipp_device_t
*device
; /* Device */
3380 _ipp_job_t
*job
; /* Job */
3383 if ((device
= find_device(client
)) == NULL
)
3385 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
3389 if ((job
= find_job(client
, 0)) == NULL
)
3391 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
3395 if (job
->dev_uuid
&& strcmp(job
->dev_uuid
, device
->uuid
))
3397 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
3401 if (!(job
->state_reasons
& _IPP_JREASON_JOB_FETCHABLE
))
3403 respond_ipp(client
, _IPP_STATUS_ERROR_NOT_FETCHABLE
, "Job not fetchable.");
3407 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3408 copy_attributes(client
->response
, job
->attrs
, NULL
, IPP_TAG_JOB
, 0);
3413 * 'ipp_get_document_attributes()' - Get the attributes for a document object.
3415 * Note: This implementation only supports single document jobs so we
3416 * synthesize the information for a single document from the job.
3420 ipp_get_document_attributes(
3421 _ipp_client_t
*client
) /* I - Client */
3423 // TODO: Implement this!
3424 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
3429 * 'ipp_get_documents()' - Get the list of documents in a job.
3431 * Note: This implementation only supports single document jobs so we
3432 * synthesize the information for a single document from the job.
3436 ipp_get_documents(_ipp_client_t
*client
)/* I - Client */
3438 // TODO: Implement this!
3439 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
3444 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3448 ipp_get_job_attributes(
3449 _ipp_client_t
*client
) /* I - Client */
3451 _ipp_job_t
*job
; /* Job */
3452 cups_array_t
*ra
; /* requested-attributes */
3455 if ((job
= find_job(client
, 0)) == NULL
)
3457 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3461 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3463 ra
= ippCreateRequestedArray(client
->request
);
3464 copy_job_attributes(client
, job
, ra
);
3465 cupsArrayDelete(ra
);
3470 * 'ipp_get_jobs()' - Get a list of job objects.
3474 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3476 ipp_attribute_t
*attr
; /* Current attribute */
3477 const char *which_jobs
= NULL
;
3478 /* which-jobs values */
3479 int job_comparison
; /* Job comparison */
3480 ipp_jstate_t job_state
; /* job-state value */
3481 int first_job_id
, /* First job ID */
3482 limit
, /* Maximum number of jobs to return */
3483 count
; /* Number of jobs that match */
3484 const char *username
; /* Username */
3485 _ipp_job_t
*job
; /* Current job pointer */
3486 cups_array_t
*ra
; /* Requested attributes array */
3490 * See if the "which-jobs" attribute have been specified...
3493 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3494 IPP_TAG_KEYWORD
)) != NULL
)
3496 which_jobs
= ippGetString(attr
, 0, NULL
);
3497 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3500 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3502 job_comparison
= -1;
3503 job_state
= IPP_JSTATE_STOPPED
;
3505 else if (!strcmp(which_jobs
, "completed"))
3508 job_state
= IPP_JSTATE_CANCELED
;
3510 else if (!strcmp(which_jobs
, "aborted"))
3513 job_state
= IPP_JSTATE_ABORTED
;
3515 else if (!strcmp(which_jobs
, "all"))
3518 job_state
= IPP_JSTATE_PENDING
;
3520 else if (!strcmp(which_jobs
, "canceled"))
3523 job_state
= IPP_JSTATE_CANCELED
;
3525 else if (!strcmp(which_jobs
, "pending"))
3528 job_state
= IPP_JSTATE_PENDING
;
3530 else if (!strcmp(which_jobs
, "pending-held"))
3533 job_state
= IPP_JSTATE_HELD
;
3535 else if (!strcmp(which_jobs
, "processing"))
3538 job_state
= IPP_JSTATE_PROCESSING
;
3540 else if (!strcmp(which_jobs
, "processing-stopped"))
3543 job_state
= IPP_JSTATE_STOPPED
;
3547 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3548 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3549 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3550 "which-jobs", NULL
, which_jobs
);
3555 * See if they want to limit the number of jobs reported...
3558 if ((attr
= ippFindAttribute(client
->request
, "limit",
3559 IPP_TAG_INTEGER
)) != NULL
)
3561 limit
= ippGetInteger(attr
, 0);
3563 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3568 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3569 IPP_TAG_INTEGER
)) != NULL
)
3571 first_job_id
= ippGetInteger(attr
, 0);
3573 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3580 * See if we only want to see jobs for a specific user...
3585 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3586 IPP_TAG_BOOLEAN
)) != NULL
)
3588 int my_jobs
= ippGetBoolean(attr
, 0);
3590 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3591 my_jobs
? "true" : "false");
3595 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3596 IPP_TAG_NAME
)) == NULL
)
3598 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3599 "Need requesting-user-name with my-jobs.");
3603 username
= ippGetString(attr
, 0, NULL
);
3605 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3606 client
->hostname
, username
);
3611 * OK, build a list of jobs for this printer...
3614 ra
= ippCreateRequestedArray(client
->request
);
3616 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3618 _cupsRWLockRead(&(client
->printer
->rwlock
));
3620 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3621 (limit
<= 0 || count
< limit
) && job
;
3622 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3625 * Filter out jobs that don't match...
3628 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3629 (job_comparison
== 0 && job
->state
!= job_state
) ||
3630 (job_comparison
> 0 && job
->state
< job_state
) ||
3631 job
->id
< first_job_id
||
3632 (username
&& job
->username
&&
3633 strcasecmp(username
, job
->username
)))
3637 ippAddSeparator(client
->response
);
3640 copy_job_attributes(client
, job
, ra
);
3643 cupsArrayDelete(ra
);
3645 _cupsRWUnlock(&(client
->printer
->rwlock
));
3650 * 'ipp_get_notifications()' - Get notification events for one or more subscriptions.
3654 ipp_get_notifications(
3655 _ipp_client_t
*client
) /* I - Client */
3657 ipp_attribute_t
*sub_ids
, /* notify-subscription-ids */
3658 *seq_nums
, /* notify-sequence-numbers */
3659 *notify_wait
; /* Wait for events? */
3660 int i
, /* Looping vars */
3661 count
, /* Number of IDs */
3662 first
= 1, /* First event? */
3663 seq_num
; /* Sequence number */
3664 _ipp_subscription_t
*sub
; /* Current subscription */
3665 ipp_t
*event
; /* Current event */
3668 if ((sub_ids
= ippFindAttribute(client
->request
, "notify-subscription-ids", IPP_TAG_INTEGER
)) == NULL
)
3670 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing notify-subscription-ids attribute.");
3674 count
= ippGetCount(sub_ids
);
3675 seq_nums
= ippFindAttribute(client
->request
, "notify-sequence-numbers", IPP_TAG_INTEGER
);
3676 notify_wait
= ippFindAttribute(client
->request
, "notify-wait", IPP_TAG_BOOLEAN
);
3678 if (seq_nums
&& count
!= ippGetCount(seq_nums
))
3680 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The notify-subscription-ids and notify-sequence-numbers attributes are different lengths.");
3684 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3685 ippAddInteger(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "notify-get-interval", 30);
3687 for (i
= 0; i
< count
; i
++)
3689 if ((sub
= find_subscription(client
, ippGetInteger(sub_ids
, i
))) == NULL
)
3692 seq_num
= ippGetInteger(seq_nums
, i
);
3693 if (seq_num
< sub
->first_sequence
)
3694 seq_num
= sub
->first_sequence
;
3696 if (seq_num
> sub
->last_sequence
)
3699 for (event
= (ipp_t
*)cupsArrayIndex(sub
->events
, seq_num
- sub
->first_sequence
);
3701 event
= (ipp_t
*)cupsArrayNext(sub
->events
))
3706 ippAddSeparator(client
->response
);
3708 ippCopyAttributes(client
->response
, event
, 0, NULL
, NULL
);
3715 * 'ipp_get_output_device_attributes()' - Get attributes for an output device.
3719 ipp_get_output_device_attributes(
3720 _ipp_client_t
*client
) /* I - Client */
3722 // TODO: Implement this!
3723 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
3728 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3732 ipp_get_printer_attributes(
3733 _ipp_client_t
*client
) /* I - Client */
3735 cups_array_t
*ra
; /* Requested attributes array */
3736 _ipp_printer_t
*printer
; /* Printer */
3740 * Send the attributes...
3743 ra
= ippCreateRequestedArray(client
->request
);
3744 printer
= client
->printer
;
3746 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3748 _cupsRWLockRead(&(printer
->rwlock
));
3750 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3751 IPP_TAG_CUPS_CONST
);
3752 copy_attributes(client
->response
, printer
->dev_attrs
, ra
, IPP_TAG_ZERO
, IPP_TAG_ZERO
);
3754 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3755 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3757 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3758 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3760 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3761 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3764 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3765 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3766 "printer-state", printer
->state
> printer
->dev_state
? printer
->state
: printer
->dev_state
);
3768 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3769 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3771 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3772 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3774 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3776 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3778 if (printer
->state
> printer
->dev_state
)
3779 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3781 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->dev_state
- IPP_PSTATE_IDLE
]);
3784 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3785 copy_printer_state_reasons(client
->response
, IPP_TAG_PRINTER
, printer
);
3787 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3788 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3790 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3791 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "queued-job-count", cupsArrayCount(printer
->active_jobs
));
3793 _cupsRWUnlock(&(printer
->rwlock
));
3795 cupsArrayDelete(ra
);
3800 * 'ipp_get_printer_supported_values()' - Return the supported values for
3801 * the infrastructure printer.
3805 ipp_get_printer_supported_values(
3806 _ipp_client_t
*client
) /* I - Client */
3808 cups_array_t
*ra
= ippCreateRequestedArray(client
->request
);
3809 /* Requested attributes */
3812 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3814 copy_attributes(client
->response
, client
->printer
->attrs
, ra
, IPP_TAG_PRINTER
, 1);
3816 cupsArrayDelete(ra
);
3821 * 'ipp_get_subscription_attributes()' - Get attributes for a subscription.
3825 ipp_get_subscription_attributes(
3826 _ipp_client_t
*client
) /* I - Client */
3828 _ipp_subscription_t
*sub
; /* Subscription */
3829 cups_array_t
*ra
= ippCreateRequestedArray(client
->request
);
3830 /* Requested attributes */
3833 if ((sub
= find_subscription(client
, 0)) == NULL
)
3835 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Subscription was not found.");
3839 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3840 copy_subscription_attributes(client
, sub
, ra
);
3843 cupsArrayDelete(ra
);
3848 * 'ipp_get_subscriptions()' - Get attributes for all subscriptions.
3852 ipp_get_subscriptions(
3853 _ipp_client_t
*client
) /* I - Client */
3855 _ipp_subscription_t
*sub
; /* Current subscription */
3856 cups_array_t
*ra
= ippCreateRequestedArray(client
->request
);
3857 /* Requested attributes */
3858 int first
= 1; /* First time? */
3861 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3862 _cupsRWLockRead(&client
->printer
->rwlock
);
3863 for (sub
= (_ipp_subscription_t
*)cupsArrayFirst(client
->printer
->subscriptions
);
3865 sub
= (_ipp_subscription_t
*)cupsArrayNext(client
->printer
->subscriptions
))
3870 ippAddSeparator(client
->response
);
3872 copy_subscription_attributes(client
, sub
, ra
);
3875 cupsArrayDelete(ra
);
3880 * 'ipp_identify_printer()' - Beep or display a message.
3884 ipp_identify_printer(
3885 _ipp_client_t
*client
) /* I - Client */
3887 /* TODO: Do something */
3889 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3894 * 'ipp_print_job()' - Create a job object with an attached document.
3898 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3900 _ipp_job_t
*job
; /* New job */
3901 char filename
[1024], /* Filename buffer */
3902 buffer
[4096]; /* Copy buffer */
3903 ssize_t bytes
; /* Bytes read */
3904 cups_array_t
*ra
; /* Attributes to send in response */
3908 * Validate print job attributes...
3911 if (!valid_job_attributes(client
))
3913 httpFlush(client
->http
);
3918 * Do we have a file to print?
3921 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3923 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3931 if ((job
= create_job(client
)) == NULL
)
3933 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3934 "Currently printing another job.");
3939 * Create a file for the request data...
3942 create_job_filename(client
->printer
, job
, NULL
, filename
, sizeof(filename
));
3945 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3947 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3949 job
->state
= IPP_JSTATE_ABORTED
;
3951 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3952 "Unable to create print file: %s", strerror(errno
));
3956 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3958 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3960 int error
= errno
; /* Write error */
3962 job
->state
= IPP_JSTATE_ABORTED
;
3969 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3970 "Unable to write print file: %s", strerror(error
));
3978 * Got an error while reading the print data, so abort this job.
3981 job
->state
= IPP_JSTATE_ABORTED
;
3988 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3989 "Unable to read print file.");
3995 int error
= errno
; /* Write error */
3997 job
->state
= IPP_JSTATE_ABORTED
;
4002 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4003 "Unable to write print file: %s", strerror(error
));
4008 job
->filename
= strdup(filename
);
4009 job
->state
= IPP_JSTATE_PENDING
;
4012 * Process the job, if possible...
4015 check_jobs(client
->printer
);
4018 * Return the job info...
4021 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4023 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4024 cupsArrayAdd(ra
, "job-id");
4025 cupsArrayAdd(ra
, "job-state");
4026 cupsArrayAdd(ra
, "job-state-message");
4027 cupsArrayAdd(ra
, "job-state-reasons");
4028 cupsArrayAdd(ra
, "job-uri");
4030 copy_job_attributes(client
, job
, ra
);
4031 cupsArrayDelete(ra
);
4034 * Process any pending subscriptions...
4038 ipp_create_xxx_subscriptions(client
);
4043 * 'ipp_print_uri()' - Create a job object with a referenced document.
4047 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
4049 _ipp_job_t
*job
; /* New job */
4050 ipp_attribute_t
*uri
; /* document-uri */
4051 char scheme
[256], /* URI scheme */
4052 userpass
[256], /* Username and password info */
4053 hostname
[256], /* Hostname */
4054 resource
[1024]; /* Resource path */
4055 int port
; /* Port number */
4056 http_uri_status_t uri_status
; /* URI decode status */
4057 http_encryption_t encryption
; /* Encryption to use, if any */
4058 http_t
*http
; /* Connection for http/https URIs */
4059 http_status_t status
; /* Access status for http/https URIs */
4060 int infile
; /* Input file for local file URIs */
4061 char filename
[1024], /* Filename buffer */
4062 buffer
[4096]; /* Copy buffer */
4063 ssize_t bytes
; /* Bytes read */
4064 cups_array_t
*ra
; /* Attributes to send in response */
4065 static const char * const uri_status_strings
[] =
4066 { /* URI decode errors */
4068 "Bad arguments to function.",
4069 "Bad resource in URI.",
4070 "Bad port number in URI.",
4071 "Bad hostname in URI.",
4072 "Bad username in URI.",
4073 "Bad scheme in URI.",
4079 * Validate print job attributes...
4082 if (!valid_job_attributes(client
))
4084 httpFlush(client
->http
);
4089 * Do we have a file to print?
4092 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4094 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4095 "Unexpected document data following request.");
4100 * Do we have a document URI?
4103 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4104 IPP_TAG_URI
)) == NULL
)
4106 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4110 if (ippGetCount(uri
) != 1)
4112 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4113 "Too many document-uri values.");
4117 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4118 scheme
, sizeof(scheme
), userpass
,
4119 sizeof(userpass
), hostname
, sizeof(hostname
),
4120 &port
, resource
, sizeof(resource
));
4121 if (uri_status
< HTTP_URI_STATUS_OK
)
4123 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4124 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4128 if (strcmp(scheme
, "file") &&
4130 strcmp(scheme
, "https") &&
4131 #endif /* HAVE_SSL */
4132 strcmp(scheme
, "http"))
4134 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4135 "URI scheme \"%s\" not supported.", scheme
);
4139 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4141 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4142 "Unable to access URI: %s", strerror(errno
));
4150 if ((job
= create_job(client
)) == NULL
)
4152 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
4153 "Currently printing another job.");
4158 * Create a file for the request data...
4161 if (!strcasecmp(job
->format
, "image/jpeg"))
4162 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4163 client
->printer
->directory
, job
->id
);
4164 else if (!strcasecmp(job
->format
, "image/png"))
4165 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4166 client
->printer
->directory
, job
->id
);
4167 else if (!strcasecmp(job
->format
, "application/pdf"))
4168 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4169 client
->printer
->directory
, job
->id
);
4170 else if (!strcasecmp(job
->format
, "application/postscript"))
4171 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4172 client
->printer
->directory
, job
->id
);
4174 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4175 client
->printer
->directory
, job
->id
);
4177 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4179 job
->state
= IPP_JSTATE_ABORTED
;
4181 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4182 "Unable to create print file: %s", strerror(errno
));
4186 if (!strcmp(scheme
, "file"))
4188 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4190 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4191 "Unable to access URI: %s", strerror(errno
));
4197 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4198 (errno
== EAGAIN
|| errno
== EINTR
))
4200 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4202 int error
= errno
; /* Write error */
4204 job
->state
= IPP_JSTATE_ABORTED
;
4212 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4213 "Unable to write print file: %s", strerror(error
));
4224 if (port
== 443 || !strcmp(scheme
, "https"))
4225 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4227 #endif /* HAVE_SSL */
4228 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4230 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4231 1, 30000, NULL
)) == NULL
)
4233 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4234 "Unable to connect to %s: %s", hostname
,
4235 cupsLastErrorString());
4236 job
->state
= IPP_JSTATE_ABORTED
;
4245 httpClearFields(http
);
4246 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4247 if (httpGet(http
, resource
))
4249 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4250 "Unable to GET URI: %s", strerror(errno
));
4252 job
->state
= IPP_JSTATE_ABORTED
;
4262 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4264 if (status
!= HTTP_STATUS_OK
)
4266 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4267 "Unable to GET URI: %s", httpStatus(status
));
4269 job
->state
= IPP_JSTATE_ABORTED
;
4279 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4281 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4283 int error
= errno
; /* Write error */
4285 job
->state
= IPP_JSTATE_ABORTED
;
4293 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4294 "Unable to write print file: %s", strerror(error
));
4304 int error
= errno
; /* Write error */
4306 job
->state
= IPP_JSTATE_ABORTED
;
4311 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4312 "Unable to write print file: %s", strerror(error
));
4317 job
->filename
= strdup(filename
);
4318 job
->state
= IPP_JSTATE_PENDING
;
4320 /* TODO: Do something different here - only process if the printer is idle */
4322 * Process the job...
4325 check_jobs(client
->printer
);
4328 * Return the job info...
4331 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4333 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4334 cupsArrayAdd(ra
, "job-id");
4335 cupsArrayAdd(ra
, "job-state");
4336 cupsArrayAdd(ra
, "job-state-reasons");
4337 cupsArrayAdd(ra
, "job-uri");
4339 copy_job_attributes(client
, job
, ra
);
4340 cupsArrayDelete(ra
);
4343 * Process any pending subscriptions...
4347 ipp_create_xxx_subscriptions(client
);
4352 * 'ipp_renew_subscription()' - Renew a subscription.
4356 ipp_renew_subscription(
4357 _ipp_client_t
*client
) /* I - Client */
4359 _ipp_subscription_t
*sub
; /* Subscription */
4360 ipp_attribute_t
*attr
; /* notify-lease-duration */
4361 int lease
; /* Lease duration in seconds */
4364 if ((sub
= find_subscription(client
, 0)) == NULL
)
4366 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Subscription was not found.");
4372 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Per-job subscriptions cannot be renewed.");
4376 if ((attr
= ippFindAttribute(client
->request
, "notify-lease-duration", IPP_TAG_ZERO
)) != NULL
)
4378 if (ippGetGroupTag(attr
) != IPP_TAG_SUBSCRIPTION
|| ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) < 0)
4380 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, "Bad notify-lease-duration.");
4384 lease
= ippGetInteger(attr
, 0);
4387 lease
= _IPP_NOTIFY_LEASE_DURATION_DEFAULT
;
4392 sub
->expire
= time(NULL
) + sub
->lease
;
4394 sub
->expire
= INT_MAX
;
4396 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4401 * 'ipp_send_document()' - Add an attached document to a job object created with
4406 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
4408 _ipp_job_t
*job
; /* Job information */
4409 char filename
[1024], /* Filename buffer */
4410 buffer
[4096]; /* Copy buffer */
4411 ssize_t bytes
; /* Bytes read */
4412 ipp_attribute_t
*attr
; /* Current attribute */
4413 cups_array_t
*ra
; /* Attributes to send in response */
4420 if ((job
= find_job(client
, 0)) == NULL
)
4422 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4423 httpFlush(client
->http
);
4428 * See if we already have a document for this job or the job has already
4429 * in a non-pending state...
4432 if (job
->state
> IPP_JSTATE_HELD
)
4434 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4435 "Job is not in a pending state.");
4436 httpFlush(client
->http
);
4439 else if (job
->filename
|| job
->fd
>= 0)
4441 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4442 "Multiple document jobs are not supported.");
4443 httpFlush(client
->http
);
4447 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4448 IPP_TAG_ZERO
)) == NULL
)
4450 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4451 "Missing required last-document attribute.");
4452 httpFlush(client
->http
);
4455 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4456 !ippGetBoolean(attr
, 0))
4458 respond_unsupported(client
, attr
);
4459 httpFlush(client
->http
);
4464 * Validate document attributes...
4467 if (!valid_doc_attributes(client
))
4469 httpFlush(client
->http
);
4473 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4476 * Get the document format for the job...
4479 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4481 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4482 job
->format
= ippGetString(attr
, 0, NULL
);
4483 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4484 job
->format
= ippGetString(attr
, 0, NULL
);
4486 job
->format
= "application/octet-stream";
4489 * Create a file for the request data...
4492 create_job_filename(client
->printer
, job
, NULL
, filename
, sizeof(filename
));
4495 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4497 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4499 _cupsRWUnlock(&(client
->printer
->rwlock
));
4503 job
->state
= IPP_JSTATE_ABORTED
;
4505 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4506 "Unable to create print file: %s", strerror(errno
));
4510 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4512 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4514 int error
= errno
; /* Write error */
4516 job
->state
= IPP_JSTATE_ABORTED
;
4523 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4524 "Unable to write print file: %s", strerror(error
));
4532 * Got an error while reading the print data, so abort this job.
4535 job
->state
= IPP_JSTATE_ABORTED
;
4542 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4543 "Unable to read print file.");
4549 int error
= errno
; /* Write error */
4551 job
->state
= IPP_JSTATE_ABORTED
;
4556 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4557 "Unable to write print file: %s", strerror(error
));
4561 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4564 job
->filename
= strdup(filename
);
4565 job
->state
= IPP_JSTATE_PENDING
;
4567 _cupsRWUnlock(&(client
->printer
->rwlock
));
4570 * Process the job, if possible...
4573 check_jobs(client
->printer
);
4576 * Return the job info...
4579 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4581 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4582 cupsArrayAdd(ra
, "job-id");
4583 cupsArrayAdd(ra
, "job-state");
4584 cupsArrayAdd(ra
, "job-state-reasons");
4585 cupsArrayAdd(ra
, "job-uri");
4587 copy_job_attributes(client
, job
, ra
);
4588 cupsArrayDelete(ra
);
4593 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4598 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4600 _ipp_job_t
*job
; /* Job information */
4601 ipp_attribute_t
*uri
; /* document-uri */
4602 char scheme
[256], /* URI scheme */
4603 userpass
[256], /* Username and password info */
4604 hostname
[256], /* Hostname */
4605 resource
[1024]; /* Resource path */
4606 int port
; /* Port number */
4607 http_uri_status_t uri_status
; /* URI decode status */
4608 http_encryption_t encryption
; /* Encryption to use, if any */
4609 http_t
*http
; /* Connection for http/https URIs */
4610 http_status_t status
; /* Access status for http/https URIs */
4611 int infile
; /* Input file for local file URIs */
4612 char filename
[1024], /* Filename buffer */
4613 buffer
[4096]; /* Copy buffer */
4614 ssize_t bytes
; /* Bytes read */
4615 ipp_attribute_t
*attr
; /* Current attribute */
4616 cups_array_t
*ra
; /* Attributes to send in response */
4617 static const char * const uri_status_strings
[] =
4618 { /* URI decode errors */
4620 "Bad arguments to function.",
4621 "Bad resource in URI.",
4622 "Bad port number in URI.",
4623 "Bad hostname in URI.",
4624 "Bad username in URI.",
4625 "Bad scheme in URI.",
4634 if ((job
= find_job(client
, 0)) == NULL
)
4636 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4637 httpFlush(client
->http
);
4642 * See if we already have a document for this job or the job has already
4643 * in a non-pending state...
4646 if (job
->state
> IPP_JSTATE_HELD
)
4648 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4649 "Job is not in a pending state.");
4650 httpFlush(client
->http
);
4653 else if (job
->filename
|| job
->fd
>= 0)
4655 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4656 "Multiple document jobs are not supported.");
4657 httpFlush(client
->http
);
4661 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4662 IPP_TAG_ZERO
)) == NULL
)
4664 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4665 "Missing required last-document attribute.");
4666 httpFlush(client
->http
);
4669 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4670 !ippGetBoolean(attr
, 0))
4672 respond_unsupported(client
, attr
);
4673 httpFlush(client
->http
);
4678 * Validate document attributes...
4681 if (!valid_doc_attributes(client
))
4683 httpFlush(client
->http
);
4688 * Do we have a file to print?
4691 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4693 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4694 "Unexpected document data following request.");
4699 * Do we have a document URI?
4702 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4703 IPP_TAG_URI
)) == NULL
)
4705 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4709 if (ippGetCount(uri
) != 1)
4711 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4712 "Too many document-uri values.");
4716 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4717 scheme
, sizeof(scheme
), userpass
,
4718 sizeof(userpass
), hostname
, sizeof(hostname
),
4719 &port
, resource
, sizeof(resource
));
4720 if (uri_status
< HTTP_URI_STATUS_OK
)
4722 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4723 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4727 if (strcmp(scheme
, "file") &&
4729 strcmp(scheme
, "https") &&
4730 #endif /* HAVE_SSL */
4731 strcmp(scheme
, "http"))
4733 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4734 "URI scheme \"%s\" not supported.", scheme
);
4738 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4740 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4741 "Unable to access URI: %s", strerror(errno
));
4746 * Get the document format for the job...
4749 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4751 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4752 IPP_TAG_MIMETYPE
)) != NULL
)
4753 job
->format
= ippGetString(attr
, 0, NULL
);
4755 job
->format
= "application/octet-stream";
4758 * Create a file for the request data...
4761 if (!strcasecmp(job
->format
, "image/jpeg"))
4762 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4763 client
->printer
->directory
, job
->id
);
4764 else if (!strcasecmp(job
->format
, "image/png"))
4765 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4766 client
->printer
->directory
, job
->id
);
4767 else if (!strcasecmp(job
->format
, "application/pdf"))
4768 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4769 client
->printer
->directory
, job
->id
);
4770 else if (!strcasecmp(job
->format
, "application/postscript"))
4771 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4772 client
->printer
->directory
, job
->id
);
4774 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4775 client
->printer
->directory
, job
->id
);
4777 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4779 _cupsRWUnlock(&(client
->printer
->rwlock
));
4783 job
->state
= IPP_JSTATE_ABORTED
;
4785 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4786 "Unable to create print file: %s", strerror(errno
));
4790 if (!strcmp(scheme
, "file"))
4792 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4794 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4795 "Unable to access URI: %s", strerror(errno
));
4801 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4802 (errno
== EAGAIN
|| errno
== EINTR
))
4804 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4806 int error
= errno
; /* Write error */
4808 job
->state
= IPP_JSTATE_ABORTED
;
4816 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4817 "Unable to write print file: %s", strerror(error
));
4828 if (port
== 443 || !strcmp(scheme
, "https"))
4829 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4831 #endif /* HAVE_SSL */
4832 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4834 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4835 1, 30000, NULL
)) == NULL
)
4837 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4838 "Unable to connect to %s: %s", hostname
,
4839 cupsLastErrorString());
4840 job
->state
= IPP_JSTATE_ABORTED
;
4849 httpClearFields(http
);
4850 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4851 if (httpGet(http
, resource
))
4853 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4854 "Unable to GET URI: %s", strerror(errno
));
4856 job
->state
= IPP_JSTATE_ABORTED
;
4866 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4868 if (status
!= HTTP_STATUS_OK
)
4870 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4871 "Unable to GET URI: %s", httpStatus(status
));
4873 job
->state
= IPP_JSTATE_ABORTED
;
4883 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4885 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4887 int error
= errno
; /* Write error */
4889 job
->state
= IPP_JSTATE_ABORTED
;
4897 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4898 "Unable to write print file: %s", strerror(error
));
4908 int error
= errno
; /* Write error */
4910 job
->state
= IPP_JSTATE_ABORTED
;
4915 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4916 "Unable to write print file: %s", strerror(error
));
4920 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4923 job
->filename
= strdup(filename
);
4924 job
->state
= IPP_JSTATE_PENDING
;
4926 _cupsRWUnlock(&(client
->printer
->rwlock
));
4929 * Process the job, if possible...
4932 check_jobs(client
->printer
);
4935 * Return the job info...
4938 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4940 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4941 cupsArrayAdd(ra
, "job-id");
4942 cupsArrayAdd(ra
, "job-state");
4943 cupsArrayAdd(ra
, "job-state-reasons");
4944 cupsArrayAdd(ra
, "job-uri");
4946 copy_job_attributes(client
, job
, ra
);
4947 cupsArrayDelete(ra
);
4952 * 'ipp_update_active_jobs()' - Update the list of active jobs.
4956 ipp_update_active_jobs(
4957 _ipp_client_t
*client
) /* I - Client */
4959 _ipp_device_t
*device
; /* Output device */
4960 _ipp_job_t
*job
; /* Job */
4961 ipp_attribute_t
*job_ids
, /* job-ids */
4962 *job_states
; /* output-device-job-states */
4963 int i
, /* Looping var */
4964 count
, /* Number of values */
4966 /* Number of jobs with different states */
4967 different
[1000],/* Jobs with different states */
4968 num_unsupported
= 0,
4969 /* Number of unsupported job-ids */
4971 /* Unsupported job-ids */
4972 ipp_jstate_t states
[1000]; /* Different job state values */
4976 * Process the job-ids and output-device-job-states values...
4979 if ((device
= find_device(client
)) == NULL
)
4981 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
4985 if ((job_ids
= ippFindAttribute(client
->request
, "job-ids", IPP_TAG_ZERO
)) == NULL
|| ippGetGroupTag(job_ids
) != IPP_TAG_OPERATION
|| ippGetValueTag(job_ids
) != IPP_TAG_INTEGER
)
4987 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, job_ids
? "Bad job-ids attribute." : "Missing required job-ids attribute.");
4991 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
)
4993 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, job_ids
? "Bad output-device-job-states attribute." : "Missing required output-device-job-states attribute.");
4997 count
= ippGetCount(job_ids
);
4998 if (count
!= ippGetCount(job_states
))
5000 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.");
5004 for (i
= 0; i
< count
; i
++)
5006 if ((job
= find_job(client
, ippGetInteger(job_ids
, i
))) == NULL
|| !job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
5008 if (num_unsupported
< 1000)
5009 unsupported
[num_unsupported
++] = ippGetInteger(job_ids
, i
);
5013 ipp_jstate_t state
= (ipp_jstate_t
)ippGetInteger(job_states
, i
);
5015 if (job
->state
>= IPP_JSTATE_STOPPED
&& state
!= job
->state
)
5017 if (num_different
< 1000)
5019 different
[num_different
] = job
->id
;
5020 states
[num_different
++] = job
->state
;
5024 job
->dev_state
= state
;
5029 * Then look for jobs assigned to the device but not listed...
5032 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
5033 job
&& num_different
< 1000;
5034 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5036 if (job
->dev_uuid
&& !strcmp(job
->dev_uuid
, device
->uuid
) && !ippContainsInteger(job_ids
, job
->id
))
5038 different
[num_different
] = job
->id
;
5039 states
[num_different
++] = job
->state
;
5043 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5045 if (num_different
> 0)
5047 ippAddIntegers(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-ids", num_different
, different
);
5048 ippAddIntegers(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "output-device-job-states", num_different
, (int *)states
);
5051 if (num_unsupported
> 0)
5053 ippAddIntegers(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_INTEGER
, "job-ids", num_unsupported
, unsupported
);
5059 * 'ipp_update_document_status()' - Update the state of a document.
5063 ipp_update_document_status(
5064 _ipp_client_t
*client
) /* I - Client */
5066 _ipp_device_t
*device
; /* Device */
5067 _ipp_job_t
*job
; /* Job */
5068 ipp_attribute_t
*attr
; /* Attribute */
5071 if ((device
= find_device(client
)) == NULL
)
5073 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
5077 if ((job
= find_job(client
, 0)) == NULL
)
5079 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
5083 if (!job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
5085 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
5089 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)
5091 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, attr
? "Bad document-number attribute." : "Missing document-number attribute.");
5095 if ((attr
= ippFindAttribute(client
->request
, "impressions-completed", IPP_TAG_INTEGER
)) != NULL
)
5097 job
->impcompleted
= ippGetInteger(attr
, 0);
5098 add_event(client
->printer
, job
, _IPP_EVENT_JOB_PROGRESS
, NULL
);
5101 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5106 * 'ipp_update_job_status()' - Update the state of a job.
5110 ipp_update_job_status(
5111 _ipp_client_t
*client
) /* I - Client */
5113 _ipp_device_t
*device
; /* Device */
5114 _ipp_job_t
*job
; /* Job */
5115 ipp_attribute_t
*attr
; /* Attribute */
5116 _ipp_event_t events
= _IPP_EVENT_NONE
;
5120 if ((device
= find_device(client
)) == NULL
)
5122 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
5126 if ((job
= find_job(client
, 0)) == NULL
)
5128 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
5132 if (!job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
5134 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
5138 if ((attr
= ippFindAttribute(client
->request
, "job-impressions-completed", IPP_TAG_INTEGER
)) != NULL
)
5140 job
->impcompleted
= ippGetInteger(attr
, 0);
5141 events
|= _IPP_EVENT_JOB_PROGRESS
;
5144 if ((attr
= ippFindAttribute(client
->request
, "output-device-job-state", IPP_TAG_ENUM
)) != NULL
)
5146 job
->dev_state
= (ipp_jstate_t
)ippGetInteger(attr
, 0);
5147 events
|= _IPP_EVENT_JOB_STATE_CHANGED
;
5150 if ((attr
= ippFindAttribute(client
->request
, "output-device-job-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
5152 job
->dev_state_reasons
= get_job_state_reasons_bits(attr
);
5153 events
|= _IPP_EVENT_JOB_STATE_CHANGED
;
5157 add_event(client
->printer
, job
, events
, NULL
);
5159 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5164 * 'ipp_update_output_device_attributes()' - Update the values for an output device.
5168 ipp_update_output_device_attributes(
5169 _ipp_client_t
*client
) /* I - Client */
5171 _ipp_device_t
*device
; /* Device */
5172 ipp_attribute_t
*attr
, /* Current attribute */
5173 *dev_attr
; /* Device attribute */
5174 _ipp_event_t events
= _IPP_EVENT_NONE
;
5175 /* Config/state changed? */
5178 if ((device
= find_device(client
)) == NULL
)
5180 if ((device
= create_device(client
)) == NULL
)
5182 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Unable to add output device.");
5187 _cupsRWLockWrite(&device
->rwlock
);
5189 attr
= ippFirstAttribute(client
->request
);
5190 while (attr
&& ippGetGroupTag(attr
) != IPP_TAG_PRINTER
)
5191 attr
= ippNextAttribute(client
->request
);
5193 for (; attr
; attr
= ippNextAttribute(client
->request
))
5195 const char *attrname
= ippGetName(attr
),
5196 /* Attribute name */
5197 *dotptr
; /* Pointer to dot in name */
5200 * Skip attributes we don't care about...
5206 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 if (strncmp(attrname
, "printer-alert", 13) || strncmp(attrname
, "printer-state", 13))
5210 events
|= _IPP_EVENT_PRINTER_CONFIG_CHANGED
;
5212 events
|= _IPP_EVENT_PRINTER_STATE_CHANGED
;
5214 if (!strcmp(attrname
, "media-col-ready") || !strcmp(attrname
, "media-ready"))
5215 events
|= _IPP_EVENT_PRINTER_MEDIA_CHANGED
;
5217 if (!strcmp(attrname
, "finishings-col-ready") || !strcmp(attrname
, "finishings-ready"))
5218 events
|= _IPP_EVENT_PRINTER_FINISHINGS_CHANGED
;
5220 if ((dotptr
= strrchr(attrname
, '.')) != NULL
&& isdigit(dotptr
[1] & 255))
5224 * Sparse representation: name.NNN or name.NNN-NNN
5227 char temp
[256], /* Temporary name string */
5228 *tempptr
; /* Pointer into temporary string */
5229 int low
, high
; /* Low and high numbers in range */
5231 low
= (int)strtol(dotptr
+ 1, (char **)&dotptr
, 10);
5232 if (dotptr
&& *dotptr
== '-')
5233 high
= (int)strtol(dotptr
+ 1, NULL
, 10);
5237 strlcpy(temp
, attrname
, sizeof(temp
));
5238 if ((tempptr
= strrchr(temp
, '.')) != NULL
)
5241 if ((dev_attr
= ippFindAttribute(device
->attrs
, temp
, IPP_TAG_ZERO
)) != NULL
)
5246 respond_unsupported(client
, attr
);
5251 * Regular representation - replace or delete current attribute,
5255 if ((dev_attr
= ippFindAttribute(device
->attrs
, attrname
, IPP_TAG_ZERO
)) != NULL
)
5256 ippDeleteAttribute(device
->attrs
, dev_attr
);
5258 if (ippGetValueTag(attr
) != IPP_TAG_DELETEATTR
)
5259 ippCopyAttribute(device
->attrs
, attr
, 0);
5263 _cupsRWUnlock(&device
->rwlock
);
5267 _cupsRWLockWrite(&client
->printer
->rwlock
);
5268 if (events
& _IPP_EVENT_PRINTER_CONFIG_CHANGED
)
5269 update_device_attributes_no_lock(client
->printer
);
5270 if (events
& _IPP_EVENT_PRINTER_STATE_CHANGED
)
5271 update_device_state_no_lock(client
->printer
);
5272 _cupsRWUnlock(&client
->printer
->rwlock
);
5274 add_event(client
->printer
, NULL
, events
, NULL
);
5280 * 'ipp_validate_document()' - Validate document creation attributes.
5284 ipp_validate_document(
5285 _ipp_client_t
*client
) /* I - Client */
5287 if (valid_doc_attributes(client
))
5288 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5293 * 'ipp_validate_job()' - Validate job creation attributes.
5297 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
5299 if (valid_job_attributes(client
))
5300 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5306 * 'parse_options()' - Parse URL options into CUPS options.
5308 * The client->options string is destroyed by this function.
5311 static int /* O - Number of options */
5312 parse_options(_ipp_client_t
*client
, /* I - Client */
5313 cups_option_t
**options
) /* O - Options */
5315 char *name
, /* Name */
5317 *next
; /* Next name=value pair */
5318 int num_options
= 0; /* Number of options */
5323 for (name
= client
->options
; name
&& *name
; name
= next
)
5325 if ((value
= strchr(name
, '=')) == NULL
)
5329 if ((next
= strchr(value
, '&')) != NULL
)
5332 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5335 return (num_options
);
5341 * 'process_client()' - Process client requests on a thread.
5344 static void * /* O - Exit status */
5345 process_client(_ipp_client_t
*client
) /* I - Client */
5348 * Loop until we are out of requests or timeout (30 seconds)...
5352 int first_time
= 1; /* First time request? */
5353 #endif /* HAVE_SSL */
5355 while (httpWait(client
->http
, 30000))
5361 * See if we need to negotiate a TLS connection...
5364 char buf
[1]; /* First byte from client */
5366 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5368 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5370 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5372 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5376 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5381 #endif /* HAVE_SSL */
5383 if (!process_http(client
))
5388 * Close the conection to the client and return...
5391 delete_client(client
);
5398 * 'process_http()' - Process a HTTP request.
5401 int /* O - 1 on success, 0 on failure */
5402 process_http(_ipp_client_t
*client
) /* I - Client connection */
5404 char uri
[1024]; /* URI */
5405 http_state_t http_state
; /* HTTP state */
5406 http_status_t http_status
; /* HTTP status */
5407 ipp_state_t ipp_state
; /* State of IPP transfer */
5408 char scheme
[32], /* Method/scheme */
5409 userpass
[128], /* Username:password */
5410 hostname
[HTTP_MAX_HOST
];
5412 int port
; /* Port number */
5413 const char *encoding
; /* Content-Encoding value */
5414 static const char * const http_states
[] =
5415 { /* Strings for logging HTTP method */
5436 * Clear state variables...
5439 ippDelete(client
->request
);
5440 ippDelete(client
->response
);
5442 client
->request
= NULL
;
5443 client
->response
= NULL
;
5444 client
->operation
= HTTP_STATE_WAITING
;
5447 * Read a request from the connection...
5450 while ((http_state
= httpReadRequest(client
->http
, uri
,
5451 sizeof(uri
))) == HTTP_STATE_WAITING
)
5455 * Parse the request line...
5458 if (http_state
== HTTP_STATE_ERROR
)
5460 if (httpError(client
->http
) == EPIPE
)
5461 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5463 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5464 strerror(httpError(client
->http
)));
5468 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5470 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5471 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5474 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5476 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5477 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5481 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5485 * Separate the URI into its components...
5488 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5489 userpass
, sizeof(userpass
),
5490 hostname
, sizeof(hostname
), &port
,
5491 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5492 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5494 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5495 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5499 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5500 *(client
->options
)++ = '\0';
5503 * Process the request...
5506 client
->start
= time(NULL
);
5507 client
->operation
= httpGetState(client
->http
);
5510 * Parse incoming parameters until the status changes...
5513 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5515 if (http_status
!= HTTP_STATUS_OK
)
5517 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5521 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5522 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5525 * HTTP/1.1 and higher require the "Host:" field...
5528 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5533 * Handle HTTP Upgrade...
5536 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5540 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5542 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5545 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5547 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5549 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5553 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5556 #endif /* HAVE_SSL */
5558 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5563 * Handle HTTP Expect...
5566 if (httpGetExpect(client
->http
) &&
5567 (client
->operation
== HTTP_STATE_POST
||
5568 client
->operation
== HTTP_STATE_PUT
))
5570 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5573 * Send 100-continue header...
5576 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5582 * Send 417-expectation-failed header...
5585 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5591 * Handle new transfers...
5594 encoding
= httpGetContentEncoding(client
->http
);
5596 switch (client
->operation
)
5598 case HTTP_STATE_OPTIONS
:
5600 * Do OPTIONS command...
5603 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5605 case HTTP_STATE_HEAD
:
5606 #if 0 /* TODO: Work out icon support */
5607 if (!strcmp(client
->uri
, "/icon.png"))
5608 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5611 if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5612 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5614 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5616 case HTTP_STATE_GET
:
5617 #if 0 /* TODO: Work out icon support */
5618 if (!strcmp(client
->uri
, "/icon.png"))
5621 * Send PNG icon file.
5624 int fd
; /* Icon file */
5625 struct stat fileinfo
; /* Icon file information */
5626 char buffer
[4096]; /* Copy buffer */
5627 ssize_t bytes
; /* Bytes */
5629 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5631 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5632 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5634 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5635 (size_t)fileinfo
.st_size
))
5641 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5642 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5644 httpFlushWrite(client
->http
);
5649 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5653 if (!strcmp(client
->uri
, "/"))
5656 * Show web status page...
5659 _ipp_job_t
*job
; /* Current job */
5660 int i
; /* Looping var */
5661 _ipp_preason_t reason
; /* Current reason */
5662 static const char * const reasons
[] =
5663 { /* Reason strings */
5666 "Input Tray Missing",
5667 "Marker Supply Empty",
5668 "Marker Supply Low",
5669 "Marker Waste Almost Full",
5670 "Marker Waste Full",
5682 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5685 html_header(client
, client
->printer
->name
);
5687 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5688 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5689 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5690 if (client
->printer
->state_reasons
& reason
)
5691 html_printf(client
, "\n<br> %s", reasons
[i
]);
5692 html_printf(client
, "</p>\n");
5694 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5696 _cupsRWLockRead(&(client
->printer
->rwlock
));
5698 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");
5699 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5701 char when
[256], /* When job queued/started/finished */
5702 hhmmss
[64]; /* Time HH:MM:SS */
5706 case IPP_JSTATE_PENDING
:
5707 case IPP_JSTATE_HELD
:
5708 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5710 case IPP_JSTATE_PROCESSING
:
5711 case IPP_JSTATE_STOPPED
:
5712 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5714 case IPP_JSTATE_ABORTED
:
5715 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5717 case IPP_JSTATE_CANCELED
:
5718 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5720 case IPP_JSTATE_COMPLETED
:
5721 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5725 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
);
5727 html_printf(client
, "</tbody></table>\n");
5729 _cupsRWUnlock(&(client
->printer
->rwlock
));
5731 html_footer(client
);
5735 #if 0 /* TODO: Pull media and supply info from device attrs */
5736 else if (!strcmp(client
->uri
, "/media"))
5739 * Show web media page...
5742 int i
, /* Looping var */
5743 num_options
; /* Number of form options */
5744 cups_option_t
*options
; /* Form options */
5745 static const char * const sizes
[] =
5746 { /* Size strings */
5759 static const char * const types
[] =
5776 static const int sheets
[] = /* Number of sheets */
5785 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5788 html_header(client
, client
->printer
->name
);
5790 if ((num_options
= parse_options(client
, &options
)) > 0)
5793 * WARNING: A real printer/server implementation MUST NOT implement
5794 * media updates via a GET request - GET requests are supposed to be
5795 * idempotent (without side-effects) and we obviously are not
5796 * authenticating access here. This form is provided solely to
5797 * enable testing and development!
5800 const char *val
; /* Form value */
5802 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5803 client
->printer
->main_size
= atoi(val
);
5804 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5805 client
->printer
->main_type
= atoi(val
);
5806 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5807 client
->printer
->main_level
= atoi(val
);
5809 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5810 client
->printer
->envelope_size
= atoi(val
);
5811 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5812 client
->printer
->envelope_level
= atoi(val
);
5814 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5815 client
->printer
->photo_size
= atoi(val
);
5816 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5817 client
->printer
->photo_type
= atoi(val
);
5818 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5819 client
->printer
->photo_level
= atoi(val
);
5821 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))
5822 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
5824 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
5826 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
))
5828 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
5829 if (client
->printer
->active_job
)
5830 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5833 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
5835 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5838 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5840 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5841 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5842 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5843 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5844 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5845 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5846 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5847 if (!strstr(types
[i
], "Photo"))
5848 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5849 html_printf(client
, "</select> <select name=\"main_level\">");
5850 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5851 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5852 html_printf(client
, "</select></td></tr>\n");
5855 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5856 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5857 if (strstr(sizes
[i
], "Envelope"))
5858 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5859 html_printf(client
, "</select> <select name=\"envelope_level\">");
5860 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5861 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5862 html_printf(client
, "</select></td></tr>\n");
5865 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5866 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5867 if (strstr(sizes
[i
], "Photo"))
5868 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5869 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5870 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5871 if (strstr(types
[i
], "Photo"))
5872 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5873 html_printf(client
, "</select> <select name=\"photo_level\">");
5874 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5875 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5876 html_printf(client
, "</select></td></tr>\n");
5878 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5879 html_footer(client
);
5883 else if (!strcmp(client
->uri
, "/supplies"))
5886 * Show web supplies page...
5889 int i
, j
, /* Looping vars */
5890 num_options
; /* Number of form options */
5891 cups_option_t
*options
; /* Form options */
5892 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5894 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5897 html_header(client
, client
->printer
->name
);
5899 if ((num_options
= parse_options(client
, &options
)) > 0)
5902 * WARNING: A real printer/server implementation MUST NOT implement
5903 * supply updates via a GET request - GET requests are supposed to be
5904 * idempotent (without side-effects) and we obviously are not
5905 * authenticating access here. This form is provided solely to
5906 * enable testing and development!
5909 char name
[64]; /* Form field */
5910 const char *val
; /* Form value */
5912 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
);
5914 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5916 snprintf(name
, sizeof(name
), "supply_%d", i
);
5917 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5919 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5925 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5926 else if (level
< 10)
5927 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5932 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5933 else if (level
> 90)
5934 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5939 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5942 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5944 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5945 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5947 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5948 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5949 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5950 html_printf(client
, "</select></td></tr>\n");
5952 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5953 html_footer(client
);
5959 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5962 case HTTP_STATE_POST
:
5963 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5967 * Not an IPP request...
5970 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5974 * Read the IPP request...
5977 client
->request
= ippNew();
5979 while ((ipp_state
= ippRead(client
->http
,
5980 client
->request
)) != IPP_STATE_DATA
)
5982 if (ipp_state
== IPP_STATE_ERROR
)
5984 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5985 cupsLastErrorString());
5986 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5992 * Now that we have the IPP request, process the request...
5995 return (process_ipp(client
));
5998 break; /* Anti-compiler-warning-code */
6006 * 'process_ipp()' - Process an IPP request.
6009 static int /* O - 1 on success, 0 on error */
6010 process_ipp(_ipp_client_t
*client
) /* I - Client */
6012 ipp_tag_t group
; /* Current group tag */
6013 ipp_attribute_t
*attr
; /* Current attribute */
6014 ipp_attribute_t
*charset
; /* Character set attribute */
6015 ipp_attribute_t
*language
; /* Language attribute */
6016 ipp_attribute_t
*uri
; /* Printer URI attribute */
6017 int major
, minor
; /* Version number */
6018 const char *name
; /* Name of attribute */
6021 debug_attributes("Request", client
->request
, 1);
6024 * First build an empty response message for this request...
6027 client
->operation_id
= ippGetOperation(client
->request
);
6028 client
->response
= ippNewResponse(client
->request
);
6031 * Then validate the request header and required attributes...
6034 major
= ippGetVersion(client
->request
, &minor
);
6036 if (major
< 1 || major
> 2)
6039 * Return an error, since we only support IPP 1.x and 2.x.
6042 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
6043 "Bad request version number %d.%d.", major
, minor
);
6045 else if (ippGetRequestId(client
->request
) <= 0)
6046 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
6047 ippGetRequestId(client
->request
));
6048 else if (!ippFirstAttribute(client
->request
))
6049 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6050 "No attributes in request.");
6054 * Make sure that the attributes are provided in the correct order and
6055 * don't repeat groups...
6058 for (attr
= ippFirstAttribute(client
->request
),
6059 group
= ippGetGroupTag(attr
);
6061 attr
= ippNextAttribute(client
->request
))
6063 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
6066 * Out of order; return an error...
6069 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6070 "Attribute groups are out of order (%x < %x).",
6071 ippGetGroupTag(attr
), group
);
6075 group
= ippGetGroupTag(attr
);
6081 * Then make sure that the first three attributes are:
6083 * attributes-charset
6084 * attributes-natural-language
6085 * printer-uri/job-uri
6088 attr
= ippFirstAttribute(client
->request
);
6089 name
= ippGetName(attr
);
6090 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
6091 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
6096 attr
= ippNextAttribute(client
->request
);
6097 name
= ippGetName(attr
);
6099 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
6100 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
6105 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
6106 IPP_TAG_URI
)) != NULL
)
6108 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
6109 IPP_TAG_URI
)) != NULL
)
6115 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
6116 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
6119 * Bad character set...
6122 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6123 "Unsupported character set \"%s\".",
6124 ippGetString(charset
, 0, NULL
));
6126 else if (!charset
|| !language
|| !uri
)
6129 * Return an error, since attributes-charset,
6130 * attributes-natural-language, and printer-uri/job-uri are required
6131 * for all operations.
6134 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6135 "Missing required attributes.");
6139 char scheme
[32], /* URI scheme */
6140 userpass
[32], /* Username/password in URI */
6141 host
[256], /* Host name in URI */
6142 resource
[256]; /* Resource path in URI */
6143 int port
; /* Port number in URI */
6145 name
= ippGetName(uri
);
6147 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
6148 scheme
, sizeof(scheme
),
6149 userpass
, sizeof(userpass
),
6150 host
, sizeof(host
), &port
,
6151 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
6152 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6153 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
6154 else if ((!strcmp(name
, "job-uri") && strncmp(resource
, "/ipp/print/", 11)) ||
6155 (!strcmp(name
, "printer-uri") && strcmp(resource
, "/ipp/print")))
6156 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
6157 name
, ippGetString(uri
, 0, NULL
));
6161 * Try processing the operation...
6164 switch ((int)ippGetOperation(client
->request
))
6166 case IPP_OP_PRINT_JOB
:
6167 ipp_print_job(client
);
6170 case IPP_OP_PRINT_URI
:
6171 ipp_print_uri(client
);
6174 case IPP_OP_VALIDATE_JOB
:
6175 ipp_validate_job(client
);
6178 case IPP_OP_CREATE_JOB
:
6179 ipp_create_job(client
);
6182 case IPP_OP_SEND_DOCUMENT
:
6183 ipp_send_document(client
);
6186 case IPP_OP_SEND_URI
:
6187 ipp_send_uri(client
);
6190 case IPP_OP_CANCEL_JOB
:
6191 ipp_cancel_job(client
);
6194 case IPP_OP_CANCEL_MY_JOBS
:
6195 ipp_cancel_my_jobs(client
);
6198 case IPP_OP_GET_JOB_ATTRIBUTES
:
6199 ipp_get_job_attributes(client
);
6202 case IPP_OP_GET_JOBS
:
6203 ipp_get_jobs(client
);
6206 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
6207 ipp_get_printer_attributes(client
);
6210 case IPP_OP_GET_PRINTER_SUPPORTED_VALUES
:
6211 ipp_get_printer_supported_values(client
);
6214 case IPP_OP_CLOSE_JOB
:
6215 ipp_close_job(client
);
6218 case IPP_OP_IDENTIFY_PRINTER
:
6219 ipp_identify_printer(client
);
6222 case IPP_OP_CANCEL_SUBSCRIPTION
:
6223 ipp_cancel_subscription(client
);
6226 case IPP_OP_CREATE_JOB_SUBSCRIPTIONS
:
6227 case IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
:
6228 ipp_create_xxx_subscriptions(client
);
6231 case IPP_OP_GET_NOTIFICATIONS
:
6232 ipp_get_notifications(client
);
6235 case IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES
:
6236 ipp_get_subscription_attributes(client
);
6239 case IPP_OP_GET_SUBSCRIPTIONS
:
6240 ipp_get_subscriptions(client
);
6243 case IPP_OP_RENEW_SUBSCRIPTION
:
6244 ipp_renew_subscription(client
);
6247 case IPP_OP_GET_DOCUMENT_ATTRIBUTES
:
6248 ipp_get_document_attributes(client
);
6251 case IPP_OP_GET_DOCUMENTS
:
6252 ipp_get_documents(client
);
6255 case IPP_OP_VALIDATE_DOCUMENT
:
6256 ipp_validate_document(client
);
6259 case _IPP_OP_ACKNOWLEDGE_DOCUMENT
:
6260 ipp_acknowledge_document(client
);
6263 case _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER
:
6264 ipp_acknowledge_identify_printer(client
);
6267 case _IPP_OP_ACKNOWLEDGE_JOB
:
6268 ipp_acknowledge_job(client
);
6271 case _IPP_OP_FETCH_DOCUMENT
:
6272 ipp_fetch_document(client
);
6275 case _IPP_OP_FETCH_JOB
:
6276 ipp_fetch_job(client
);
6279 case _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES
:
6280 ipp_get_output_device_attributes(client
);
6283 case _IPP_OP_UPDATE_ACTIVE_JOBS
:
6284 ipp_update_active_jobs(client
);
6287 case _IPP_OP_UPDATE_DOCUMENT_STATUS
:
6288 ipp_update_document_status(client
);
6291 case _IPP_OP_UPDATE_JOB_STATUS
:
6292 ipp_update_job_status(client
);
6295 case _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES
:
6296 ipp_update_output_device_attributes(client
);
6299 case _IPP_OP_DEREGISTER_OUTPUT_DEVICE
:
6300 ipp_deregister_output_device(client
);
6304 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
6305 "Operation not supported.");
6314 * Send the HTTP header and return...
6317 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6318 httpFlush(client
->http
); /* Flush trailing (junk) data */
6320 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6321 client
->fetch_file
>= 0 ? 0 : ippLength(client
->response
)));
6326 * 'process_job()' - Process a print job.
6329 static void * /* O - Thread exit status */
6330 process_job(_ipp_job_t
*job
) /* I - Job */
6332 job
->state
= IPP_JSTATE_PROCESSING
;
6333 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6334 job
->processing
= time(NULL
);
6335 job
->printer
->processing_job
= job
;
6337 add_event(job
->printer
, job
, _IPP_EVENT_JOB_STATE_CHANGED
, "Job processing.");
6340 * TODO: Perform any preprocessing needed...
6343 // job->state_reasons |= _IPP_JREASON_JOB_TRANSFORMING;
6344 // job->state_reasons &= ~_IPP_JREASON_JOB_TRANSFORMING;
6347 * Set the state to processing-stopped, fetchable, then send a
6351 job
->state
= IPP_JSTATE_STOPPED
;
6352 job
->state_reasons
|= _IPP_JREASON_JOB_FETCHABLE
;
6354 add_event(job
->printer
, job
, _IPP_EVENT_JOB_STATE_CHANGED
, "Job fetchable.");
6361 * 'respond_http()' - Send a HTTP response.
6364 int /* O - 1 on success, 0 on failure */
6366 _ipp_client_t
*client
, /* I - Client */
6367 http_status_t code
, /* I - HTTP status of response */
6368 const char *content_encoding
, /* I - Content-Encoding of response */
6369 const char *type
, /* I - MIME media type of response */
6370 size_t length
) /* I - Length of response */
6372 char message
[1024]; /* Text message */
6375 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6377 if (code
== HTTP_STATUS_CONTINUE
)
6380 * 100-continue doesn't send any headers...
6383 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6387 * Format an error message...
6390 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6392 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6394 type
= "text/plain";
6395 length
= strlen(message
);
6401 * Send the HTTP response header...
6404 httpClearFields(client
->http
);
6406 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6407 client
->operation
== HTTP_STATE_OPTIONS
)
6408 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6412 if (!strcmp(type
, "text/html"))
6413 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6414 "text/html; charset=utf-8");
6416 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6418 if (content_encoding
)
6419 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6422 httpSetLength(client
->http
, length
);
6424 if (httpWriteResponse(client
->http
, code
) < 0)
6428 * Send the response data...
6434 * Send a plain text message.
6437 if (httpPrintf(client
->http
, "%s", message
) < 0)
6440 if (httpWrite2(client
->http
, "", 0) < 0)
6443 else if (client
->response
)
6446 * Send an IPP response...
6449 debug_attributes("Response", client
->response
, 2);
6451 ippSetState(client
->response
, IPP_STATE_IDLE
);
6453 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6456 if (client
->fetch_file
>= 0)
6458 ssize_t bytes
; /* Bytes read */
6459 char buffer
[32768]; /* Buffer */
6461 if (client
->fetch_compression
)
6462 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, "gzip");
6464 while ((bytes
= read(client
->fetch_file
, buffer
, sizeof(buffer
))) > 0)
6465 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
6467 httpWrite2(client
->http
, "", 0);
6468 close(client
->fetch_file
);
6469 client
->fetch_file
= -1;
6478 * 'respond_ipp()' - Send an IPP response.
6482 respond_ipp(_ipp_client_t
*client
, /* I - Client */
6483 ipp_status_t status
, /* I - status-code */
6484 const char *message
, /* I - printf-style status-message */
6485 ...) /* I - Additional args as needed */
6487 const char *formatted
= NULL
; /* Formatted message */
6490 ippSetStatusCode(client
->response
, status
);
6494 va_list ap
; /* Pointer to additional args */
6495 ipp_attribute_t
*attr
; /* New status-message attribute */
6497 va_start(ap
, message
);
6498 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6499 IPP_TAG_TEXT
)) != NULL
)
6500 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6502 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6503 "status-message", NULL
, message
, ap
);
6506 formatted
= ippGetString(attr
, 0, NULL
);
6510 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6511 ippOpString(client
->operation_id
), ippErrorString(status
),
6514 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6515 ippOpString(client
->operation_id
), ippErrorString(status
));
6520 * 'respond_unsupported()' - Respond with an unsupported attribute.
6524 respond_unsupported(
6525 _ipp_client_t
*client
, /* I - Client */
6526 ipp_attribute_t
*attr
) /* I - Atribute */
6528 ipp_attribute_t
*temp
; /* Copy of attribute */
6531 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6532 "Unsupported %s %s%s value.", ippGetName(attr
),
6533 ippGetCount(attr
) > 1 ? "1setOf " : "",
6534 ippTagString(ippGetValueTag(attr
)));
6536 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6537 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6542 * 'run_printer()' - Run the printer service.
6546 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
6548 int num_fds
; /* Number of file descriptors */
6549 struct pollfd polldata
[3]; /* poll() data */
6550 int timeout
; /* Timeout for poll() */
6551 _ipp_client_t
*client
; /* New client */
6555 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6558 polldata
[0].fd
= printer
->ipv4
;
6559 polldata
[0].events
= POLLIN
;
6561 polldata
[1].fd
= printer
->ipv6
;
6562 polldata
[1].events
= POLLIN
;
6567 * Loop until we are killed or have a hard error...
6572 if (cupsArrayCount(printer
->jobs
))
6577 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6579 perror("poll() failed");
6583 if (polldata
[0].revents
& POLLIN
)
6585 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6587 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6589 perror("Unable to create client thread");
6590 delete_client(client
);
6595 if (polldata
[1].revents
& POLLIN
)
6597 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6599 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6601 perror("Unable to create client thread");
6602 delete_client(client
);
6608 * Clean out old jobs...
6611 clean_jobs(printer
);
6617 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6621 time_string(time_t tv
, /* I - Time value */
6622 char *buffer
, /* I - Buffer */
6623 size_t bufsize
) /* I - Size of buffer */
6625 struct tm
*curtime
= localtime(&tv
);
6628 strftime(buffer
, bufsize
, "%X", curtime
);
6634 * 'update_device_attributes_no_lock()' - Update the composite device attributes.
6636 * Note: Caller MUST lock the printer object for writing before using.
6640 update_device_attributes_no_lock(
6641 _ipp_printer_t
*printer
) /* I - Printer */
6643 _ipp_device_t
*device
; /* Current device */
6644 ipp_t
*dev_attrs
; /* Device attributes */
6647 /* TODO: Support multiple output devices, icons, etc... */
6648 device
= (_ipp_device_t
*)cupsArrayFirst(printer
->devices
);
6649 dev_attrs
= ippNew();
6652 copy_attributes(dev_attrs
, device
->attrs
, NULL
, IPP_TAG_PRINTER
, 0);
6654 ippDelete(printer
->dev_attrs
);
6655 printer
->dev_attrs
= dev_attrs
;
6657 printer
->config_time
= time(NULL
);
6662 * 'update_device_status_no_lock()' - Update the composite device state.
6664 * Note: Caller MUST lock the printer object for writing before using.
6668 update_device_state_no_lock(
6669 _ipp_printer_t
*printer
) /* I - Printer */
6671 _ipp_device_t
*device
; /* Current device */
6672 ipp_attribute_t
*attr
; /* Current attribute */
6675 /* TODO: Support multiple output devices, icons, etc... */
6676 device
= (_ipp_device_t
*)cupsArrayFirst(printer
->devices
);
6678 if ((attr
= ippFindAttribute(device
->attrs
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
6679 printer
->dev_state
= (ipp_pstate_t
)ippGetInteger(attr
, 0);
6681 printer
->dev_state
= IPP_PSTATE_STOPPED
;
6683 if ((attr
= ippFindAttribute(device
->attrs
, "printer-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
6684 printer
->dev_reasons
= get_printer_state_reasons_bits(attr
);
6686 printer
->dev_reasons
= _IPP_PREASON_PAUSED
;
6688 printer
->state_time
= time(NULL
);
6693 * 'usage()' - Show program usage.
6697 usage(int status
) /* O - Exit status */
6701 puts(CUPS_SVERSION
" - Copyright 2010-2014 by Apple Inc. All rights reserved.");
6705 puts("Usage: ippinfra [options] \"name\"");
6708 printf("-d spool-directory Spool directory "
6709 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6710 puts("-h Show program help");
6711 puts("-k Keep job spool files");
6712 puts("-n hostname Hostname for printer");
6713 puts("-p port Port number (default=auto)");
6714 puts("-u user:pass Set proxy username and password");
6715 puts("-v[vvv] Be (very) verbose");
6722 * 'valid_doc_attributes()' - Determine whether the document attributes are
6725 * When one or more document attributes are invalid, this function adds a
6726 * suitable response and attributes to the unsupported group.
6729 static int /* O - 1 if valid, 0 if not */
6730 valid_doc_attributes(
6731 _ipp_client_t
*client
) /* I - Client */
6733 int valid
= 1; /* Valid attributes? */
6734 ipp_op_t op
= ippGetOperation(client
->request
);
6736 const char *op_name
= ippOpString(op
);
6737 /* IPP operation name */
6738 ipp_attribute_t
*attr
, /* Current attribute */
6739 *supported
; /* xxx-supported attribute */
6740 const char *compression
= NULL
,
6741 /* compression value */
6742 *format
= NULL
; /* document-format value */
6746 * Check operation attributes...
6749 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6752 * If compression is specified, only accept a supported value in a Print-Job
6753 * or Send-Document request...
6756 compression
= ippGetString(attr
, 0, NULL
);
6757 supported
= ippFindAttribute(client
->printer
->attrs
,
6758 "compression-supported", IPP_TAG_KEYWORD
);
6760 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6761 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6762 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6763 op
!= IPP_OP_VALIDATE_JOB
) ||
6764 !ippContainsString(supported
, compression
))
6766 respond_unsupported(client
, attr
);
6771 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6773 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6775 if (strcmp(compression
, "none"))
6778 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6779 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6785 * Is it a format we support?
6788 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6790 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6791 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6793 respond_unsupported(client
, attr
);
6798 format
= ippGetString(attr
, 0, NULL
);
6800 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6801 client
->hostname
, op_name
, format
);
6803 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6808 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6810 format
= "application/octet-stream"; /* Should never happen */
6812 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6815 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6818 * Auto-type the file using the first 8 bytes of the file...
6821 unsigned char header
[8]; /* First 8 bytes of file */
6823 memset(header
, 0, sizeof(header
));
6824 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6826 if (!memcmp(header
, "%PDF", 4))
6827 format
= "application/pdf";
6828 else if (!memcmp(header
, "%!", 2))
6829 format
= "application/postscript";
6830 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6831 format
= "image/jpeg";
6832 else if (!memcmp(header
, "\211PNG", 4))
6833 format
= "image/png";
6834 else if (!memcmp(header
, "RAS2", 4))
6835 format
= "image/pwg-raster";
6836 else if (!memcmp(header
, "UNIRAST", 8))
6837 format
= "image/urf";
6843 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6844 client
->hostname
, op_name
, format
);
6846 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6850 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6852 respond_unsupported(client
, attr
);
6860 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6861 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6868 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6870 * When one or more job attributes are invalid, this function adds a suitable
6871 * response and attributes to the unsupported group.
6874 static int /* O - 1 if valid, 0 if not */
6875 valid_job_attributes(
6876 _ipp_client_t
*client
) /* I - Client */
6878 int i
, /* Looping var */
6879 valid
= 1; /* Valid attributes? */
6880 ipp_attribute_t
*attr
, /* Current attribute */
6881 *supported
; /* xxx-supported attribute */
6885 * Check operation attributes...
6888 valid
= valid_doc_attributes(client
);
6891 * Check the various job template attributes...
6894 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6896 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6897 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6899 respond_unsupported(client
, attr
);
6904 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6906 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6908 respond_unsupported(client
, attr
);
6913 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6915 if (ippGetCount(attr
) != 1 ||
6916 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6917 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6918 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6919 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6921 respond_unsupported(client
, attr
);
6926 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6928 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6930 respond_unsupported(client
, attr
);
6935 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6937 if (ippGetCount(attr
) != 1 ||
6938 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6939 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6941 respond_unsupported(client
, attr
);
6945 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6948 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6950 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6952 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6953 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6955 respond_unsupported(client
, attr
);
6960 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6962 if (ippGetCount(attr
) != 1 ||
6963 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6964 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6965 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6966 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6968 respond_unsupported(client
, attr
);
6973 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6975 if (ippGetCount(attr
) != 1 ||
6976 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6977 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6978 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6980 respond_unsupported(client
, attr
);
6985 #if 0 /* TODO: Validate media */
6987 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6989 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6992 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6994 respond_unsupported(client
, attr
);
7001 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7003 if (ippGetCount(attr
) != 1 ||
7004 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7006 respond_unsupported(client
, attr
);
7009 /* TODO: check for valid media-col */
7012 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7014 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7015 (strcmp(ippGetString(attr
, 0, NULL
),
7016 "separate-documents-uncollated-copies") &&
7017 strcmp(ippGetString(attr
, 0, NULL
),
7018 "separate-documents-collated-copies")))
7020 respond_unsupported(client
, attr
);
7025 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7027 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7028 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7029 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7031 respond_unsupported(client
, attr
);
7036 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7038 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7040 respond_unsupported(client
, attr
);
7045 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7047 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7048 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7049 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7051 respond_unsupported(client
, attr
);
7056 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7058 supported
= ippFindAttribute(client
->printer
->dev_attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7060 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7063 respond_unsupported(client
, attr
);
7068 int count
, /* Number of supported values */
7069 xdpi
, /* Horizontal resolution for job template attribute */
7070 ydpi
, /* Vertical resolution for job template attribute */
7071 sydpi
; /* Vertical resolution for supported value */
7072 ipp_res_t units
, /* Units for job template attribute */
7073 sunits
; /* Units for supported value */
7075 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7076 count
= ippGetCount(supported
);
7078 for (i
= 0; i
< count
; i
++)
7080 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7086 respond_unsupported(client
, attr
);
7092 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7094 const char *sides
= ippGetString(attr
, 0, NULL
);
7095 /* "sides" value... */
7097 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7099 respond_unsupported(client
, attr
);
7102 else if ((supported
= ippFindAttribute(client
->printer
->dev_attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7104 if (!ippContainsString(supported
, sides
))
7106 respond_unsupported(client
, attr
);
7110 else if (strcmp(sides
, "one-sided"))
7112 respond_unsupported(client
, attr
);