4 * Sample IPP INFRA server for CUPS.
6 * Copyright 2010-2014 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
27 * Include necessary headers...
30 #include <config.h> /* CUPS configuration header */
31 #include <cups/cups.h> /* Public API */
32 #include <cups/string-private.h> /* CUPS string functions */
33 #include <cups/thread-private.h> /* For multithreading functions */
46 # define WEXITSTATUS(s) (s)
47 # include <winsock2.h>
51 extern char **environ
;
53 # include <sys/fcntl.h>
54 # include <sys/wait.h>
58 #ifdef HAVE_SYS_MOUNT_H
59 # include <sys/mount.h>
60 #endif /* HAVE_SYS_MOUNT_H */
61 #ifdef HAVE_SYS_STATFS_H
62 # include <sys/statfs.h>
63 #endif /* HAVE_SYS_STATFS_H */
64 #ifdef HAVE_SYS_STATVFS_H
65 # include <sys/statvfs.h>
66 #endif /* HAVE_SYS_STATVFS_H */
69 #endif /* HAVE_SYS_VFS_H */
72 typedef pthread_cond_t _cups_cond_t
;
73 # define _CUPS_COND_INITIALIZER PTHREAD_COND_INITIALIZER
74 # define _cupsCondBroadcast(c) pthread_cond_broadcast(c)
75 # define _cupsCondDeinit(c) pthread_cond_destroy(c)
76 # define _cupsCondInit(c) pthread_cond_init((c), NULL)
77 # define _cupsCondWait(c,m) pthread_cond_wait((c),(m))
78 # define _cupsMutexDeinit(m) pthread_mutex_destroy(m)
79 # define _cupsRWDeinit(rw) pthread_rwlock_destroy(rw)
81 typedef char _cups_cond_t
;
82 # define _CUPS_COND_INITIALIZER 0
83 # define _cupsCondBroadcast(c)
84 # define _cupsCondDeinit(c)
85 # define _cupsCondInit(c) *(c)=0
86 # define _cupsCondWait(c,m) 0
87 # define _cupsMutexDeinit(m)
88 # define _cupsRWDeinit(rw)
89 #endif /* HAVE_PTHREAD_H */
96 /* New IPP operation codes from IPP INFRA */
97 # define _IPP_OP_ACKNOWLEDGE_DOCUMENT (ipp_op_t)0x003f
98 # define _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER (ipp_op_t)0x0040
99 # define _IPP_OP_ACKNOWLEDGE_JOB (ipp_op_t)0x0041
100 # define _IPP_OP_FETCH_DOCUMENT (ipp_op_t)0x0042
101 # define _IPP_OP_FETCH_JOB (ipp_op_t)0x0043
102 # define _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES (ipp_op_t)0x0044
103 # define _IPP_OP_UPDATE_ACTIVE_JOBS (ipp_op_t)0x0045
104 # define _IPP_OP_UPDATE_DOCUMENT_STATUS (ipp_op_t)0x0047
105 # define _IPP_OP_UPDATE_JOB_STATUS (ipp_op_t)0x0048
106 # define _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES (ipp_op_t)0x0049
107 # define _IPP_OP_DEREGISTER_OUTPUT_DEVICE (ipp_op_t)0x204b
109 /* New IPP status code from IPP INFRA */
110 # define _IPP_STATUS_ERROR_NOT_FETCHABLE (ipp_status_t)0x0420
112 /* Maximum lease duration value from RFC 3995 - 2^26-1 or ~2 years */
113 # define _IPP_NOTIFY_LEASE_DURATION_MAX 67108863
114 /* But a value of 0 means "never expires"... */
115 # define _IPP_NOTIFY_LEASE_DURATION_FOREVER 0
116 /* Default duration is 1 day */
117 # define _IPP_NOTIFY_LEASE_DURATION_DEFAULT 86400
121 * Event mask enumeration...
124 enum _ipp_event_e
/* notify-events bit values */
126 _IPP_EVENT_DOCUMENT_COMPLETED
= 0x00000001,
127 _IPP_EVENT_DOCUMENT_CONFIG_CHANGED
= 0x00000002,
128 _IPP_EVENT_DOCUMENT_CREATED
= 0x00000004,
129 _IPP_EVENT_DOCUMENT_FETCHABLE
= 0x00000008,
130 _IPP_EVENT_DOCUMENT_STATE_CHANGED
= 0x00000010,
131 _IPP_EVENT_DOCUMENT_STOPPED
= 0x00000020,
132 _IPP_EVENT_JOB_COMPLETED
= 0x00000040,
133 _IPP_EVENT_JOB_CONFIG_CHANGED
= 0x00000080,
134 _IPP_EVENT_JOB_CREATED
= 0x00000100,
135 _IPP_EVENT_JOB_FETCHABLE
= 0x00000200,
136 _IPP_EVENT_JOB_PROGRESS
= 0x00000400,
137 _IPP_EVENT_JOB_STATE_CHANGED
= 0x00000800,
138 _IPP_EVENT_JOB_STOPPED
= 0x00001000,
139 _IPP_EVENT_PRINTER_CONFIG_CHANGED
= 0x00002000,
140 _IPP_EVENT_PRINTER_FINISHINGS_CHANGED
= 0x00004000,
141 _IPP_EVENT_PRINTER_MEDIA_CHANGED
= 0x00008000,
142 _IPP_EVENT_PRINTER_QUEUE_ORDER_CHANGED
= 0x00010000,
143 _IPP_EVENT_PRINTER_RESTARTED
= 0x00020000,
144 _IPP_EVENT_PRINTER_SHUTDOWN
= 0x00040000,
145 _IPP_EVENT_PRINTER_STATE_CHANGED
= 0x00080000,
146 _IPP_EVENT_PRINTER_STOPPED
= 0x00100000,
148 /* "Wildcard" values... */
149 _IPP_EVENT_NONE
= 0x00000000, /* Nothing */
150 _IPP_EVENT_DOCUMENT_ALL
= 0x0000003f,
151 _IPP_EVENT_DOCUMENT_STATE_ALL
= 0x00000037,
152 _IPP_EVENT_JOB_ALL
= 0x00001fc0,
153 _IPP_EVENT_JOB_STATE_ALL
= 0x00001940,
154 _IPP_EVENT_PRINTER_ALL
= 0x001fe000,
155 _IPP_EVENT_PRINTER_CONFIG_ALL
= 0x0000e000,
156 _IPP_EVENT_PRINTER_STATE_ALL
= 0x001e0000,
157 _IPP_EVENT_ALL
= 0x001fffff /* Everything */
159 typedef unsigned int _ipp_event_t
; /* Bitfield for notify-events */
160 #define _IPP_EVENT_DEFAULT _IPP_EVENT_JOB_COMPLETED
161 #define _IPP_EVENT_DEFAULT_STRING "job-completed"
162 static const char * const _ipp_events
[] =
163 { /* Strings for bits */
164 "document-completed",
165 "document-config-changed",
167 "document-fetchable",
168 "document-state-changed",
171 "job-config-changed",
177 "printer-config-changed",
178 "printer-finishings-changed",
179 "printer-media-changed",
180 "printer-queue-order-changed",
183 "printer-state-changed",
187 enum _ipp_jreason_e
/* job-state-reasons bit values */
189 _IPP_JREASON_NONE
= 0x00000000, /* none */
190 _IPP_JREASON_ABORTED_BY_SYSTEM
= 0x00000001,
191 _IPP_JREASON_COMPRESSION_ERROR
= 0x00000002,
192 _IPP_JREASON_DOCUMENT_ACCESS_ERROR
= 0x00000004,
193 _IPP_JREASON_DOCUMENT_FORMAT_ERROR
= 0x00000008,
194 _IPP_JREASON_DOCUMENT_PASSWORD_ERROR
= 0x00000010,
195 _IPP_JREASON_DOCUMENT_PERMISSION_ERROR
= 0x00000020,
196 _IPP_JREASON_DOCUMENT_SECURITY_ERROR
= 0x00000040,
197 _IPP_JREASON_DOCUMENT_UNPRINTABLE_ERROR
= 0x00000080,
198 _IPP_JREASON_ERRORS_DETECTED
= 0x00000100,
199 _IPP_JREASON_JOB_CANCELED_AT_DEVICE
= 0x00000200,
200 _IPP_JREASON_JOB_CANCELED_BY_USER
= 0x00000400,
201 _IPP_JREASON_JOB_COMPLETED_SUCCESSFULLY
= 0x00000800,
202 _IPP_JREASON_JOB_COMPLETED_WITH_ERRORS
= 0x00001000,
203 _IPP_JREASON_JOB_COMPLETED_WITH_WARNINGS
= 0x00002000,
204 _IPP_JREASON_JOB_DATA_INSUFFICIENT
= 0x00004000,
205 _IPP_JREASON_JOB_FETCHABLE
= 0x00008000,
206 _IPP_JREASON_JOB_INCOMING
= 0x00010000,
207 _IPP_JREASON_JOB_PASSWORD_WAIT
= 0x00020000,
208 _IPP_JREASON_JOB_PRINTING
= 0x00040000,
209 _IPP_JREASON_JOB_QUEUED
= 0x00080000,
210 _IPP_JREASON_JOB_SPOOLING
= 0x00100000,
211 _IPP_JREASON_JOB_STOPPED
= 0x00200000,
212 _IPP_JREASON_JOB_TRANSFORMING
= 0x00400000,
213 _IPP_JREASON_PRINTER_STOPPED
= 0x00800000,
214 _IPP_JREASON_PRINTER_STOPPED_PARTLY
= 0x01000000,
215 _IPP_JREASON_PROCESSING_TO_STOP_POINT
= 0x02000000,
216 _IPP_JREASON_QUEUED_IN_DEVICE
= 0x04000000,
217 _IPP_JREASON_WARNINGS_DETECTED
= 0x08000000
219 typedef unsigned int _ipp_jreason_t
; /* Bitfield for job-state-reasons */
220 static const char * const _ipp_jreasons
[] =
221 { /* Strings for bits */
224 "document-access-error",
225 "document-format-error",
226 "document-password-error",
227 "document-permission-error",
228 "document-security-error",
229 "document-unprintable-error",
231 "job-canceled-at-device",
232 "job-canceled-by-user",
233 "job-completed-successfully",
234 "job-completed-with-errors",
235 "job-completed-with-warnings",
236 "job-data-insufficient",
246 "printer-stopped-partly",
247 "processing-to-stop-point",
252 enum _ipp_preason_e
/* printer-state-reasons bit values */
254 _IPP_PREASON_NONE
= 0x0000, /* none */
255 _IPP_PREASON_OTHER
= 0x0001, /* other */
256 _IPP_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
257 _IPP_PREASON_INPUT_TRAY_MISSING
= 0x0004,
258 /* input-tray-missing */
259 _IPP_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
260 /* marker-supply-empty */
261 _IPP_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
262 /* marker-supply-low */
263 _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
264 /* marker-waste-almost-full */
265 _IPP_PREASON_MARKER_WASTE_FULL
= 0x0040,
266 /* marker-waste-full */
267 _IPP_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
268 _IPP_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
269 _IPP_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
270 _IPP_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
271 _IPP_PREASON_MOVING_TO_PAUSED
= 0x0800,
272 /* moving-to-paused */
273 _IPP_PREASON_PAUSED
= 0x1000, /* paused */
274 _IPP_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
275 _IPP_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
276 _IPP_PREASON_TONER_LOW
= 0x8000 /* toner-low */
278 typedef unsigned int _ipp_preason_t
; /* Bitfield for printer-state-reasons */
279 static const char * const _ipp_preasons
[] =
280 { /* Strings for bits */
283 "input-tray-missing",
284 "marker-supply-empty",
286 "marker-waste-almost-full",
304 typedef struct _ipp_filter_s
/**** Attribute filter ****/
306 cups_array_t
*ra
; /* Requested attributes */
307 ipp_tag_t group_tag
; /* Group to copy */
310 typedef struct _ipp_job_s _ipp_job_t
;
312 typedef struct _ipp_device_s
/**** Output Device data ****/
314 _cups_rwlock_t rwlock
; /* Printer lock */
315 char *name
, /* printer-name (mapped to output-device) */
316 *uuid
; /* output-device-uuid */
317 ipp_t
*attrs
; /* All printer attributes */
318 ipp_pstate_t state
; /* printer-state value */
319 _ipp_preason_t reasons
; /* printer-state-reasons values */
322 typedef struct _ipp_printer_s
/**** Printer data ****/
324 _cups_rwlock_t rwlock
; /* Printer lock */
325 int ipv4
, /* IPv4 listener */
326 ipv6
; /* IPv6 listener */
327 char *name
, /* printer-name */
328 *directory
, /* Spool directory */
329 *hostname
, /* Hostname */
330 *uri
, /* printer-uri-supported */
331 *proxy_user
, /* Proxy username */
332 *proxy_pass
; /* Proxy password */
334 size_t urilen
; /* Length of printer URI */
335 cups_array_t
*devices
; /* Associated devices */
336 ipp_t
*attrs
; /* Static attributes */
337 ipp_t
*dev_attrs
; /* Current device attributes */
338 time_t start_time
; /* Startup time */
339 time_t config_time
; /* printer-config-change-time */
340 ipp_pstate_t state
, /* printer-state value */
341 dev_state
; /* Current device printer-state value */
342 _ipp_preason_t state_reasons
, /* printer-state-reasons values */
343 dev_reasons
; /* Current device printer-state-reasons values */
344 time_t state_time
; /* printer-state-change-time */
345 cups_array_t
*jobs
, /* Jobs */
346 *active_jobs
, /* Active jobs */
347 *completed_jobs
;/* Completed jobs */
348 _ipp_job_t
*processing_job
;/* Current processing job */
349 int next_job_id
; /* Next job-id value */
350 cups_array_t
*subscriptions
; /* Subscriptions */
351 int next_sub_id
; /* Next notify-subscription-id value */
354 struct _ipp_job_s
/**** Job data ****/
357 _cups_rwlock_t rwlock
; /* Job lock */
358 const char *name
, /* job-name */
359 *username
, /* job-originating-user-name */
360 *format
; /* document-format */
361 int priority
; /* job-priority */
362 char *dev_uuid
; /* output-device-uuid-assigned */
363 ipp_jstate_t state
, /* job-state value */
364 dev_state
; /* output-device-job-state value */
365 _ipp_jreason_t state_reasons
, /* job-state-reasons values */
367 /* output-device-job-state-reasons values */
368 char *dev_state_message
;
369 /* output-device-job-state-message value */
370 time_t created
, /* time-at-creation value */
371 processing
, /* time-at-processing value */
372 completed
; /* time-at-completed value */
373 int impressions
, /* job-impressions value */
374 impcompleted
; /* job-impressions-completed value */
375 ipp_t
*attrs
; /* Attributes */
376 int cancel
; /* Non-zero when job canceled */
377 char *filename
; /* Print file name */
378 int fd
; /* Print file descriptor */
379 _ipp_printer_t
*printer
; /* Printer */
382 typedef struct _ipp_subscription_s
/**** Subscription data ****/
384 int id
; /* notify-subscription-id */
385 const char *uuid
; /* notify-subscription-uuid */
386 _cups_rwlock_t rwlock
; /* Subscription lock */
387 _ipp_event_t mask
; /* Event mask */
388 _ipp_printer_t
*printer
; /* Printer */
389 _ipp_job_t
*job
; /* Job, if any */
390 ipp_t
*attrs
; /* Attributes */
391 const char *username
; /* notify-subscriber-user-name */
392 int lease
; /* notify-lease-duration */
393 int interval
; /* notify-time-interval */
394 time_t expire
; /* Lease expiration time */
395 int first_sequence
, /* First notify-sequence-number in cache */
396 last_sequence
; /* Last notify-sequence-number used */
397 cups_array_t
*events
; /* Events (ipp_t *'s) */
398 int pending_delete
; /* Non-zero when the subscription is about to be deleted/canceled */
399 } _ipp_subscription_t
;
401 typedef struct _ipp_client_s
/**** Client data ****/
403 http_t
*http
; /* HTTP connection */
404 ipp_t
*request
, /* IPP request */
405 *response
; /* IPP response */
406 time_t start
; /* Request start time */
407 http_state_t operation
; /* Request operation */
408 ipp_op_t operation_id
; /* IPP operation-id */
409 char uri
[1024], /* Request URI */
410 *options
; /* URI options */
411 http_addr_t addr
; /* Client address */
412 char hostname
[256], /* Client hostname */
413 username
[32]; /* Client authenticated username */
414 _ipp_printer_t
*printer
; /* Printer */
415 _ipp_job_t
*job
; /* Current job, if any */
416 int fetch_compression
,
418 fetch_file
; /* File to fetch */
426 static void add_event(_ipp_printer_t
*printer
, _ipp_job_t
*job
, _ipp_event_t event
, const char *message
, ...) __attribute__((__format__(__printf__
, 4, 5)));
427 static void check_jobs(_ipp_printer_t
*printer
);
428 static void clean_jobs(_ipp_printer_t
*printer
);
429 static int compare_active_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
430 static int compare_completed_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
431 static int compare_devices(_ipp_device_t
*a
, _ipp_device_t
*b
);
432 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
433 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
434 ipp_tag_t group_tag
, int quickcopy
);
435 static void copy_job_attributes(_ipp_client_t
*client
,
436 _ipp_job_t
*job
, cups_array_t
*ra
);
437 static void copy_job_state_reasons(ipp_t
*ipp
, ipp_tag_t group_tag
, _ipp_job_t
*job
);
438 static void copy_printer_state_reasons(ipp_t
*ipp
, ipp_tag_t group_tag
, _ipp_printer_t
*printer
);
439 static void copy_subscription_attributes(_ipp_client_t
*client
, _ipp_subscription_t
*sub
, cups_array_t
*ra
);
440 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
441 static _ipp_device_t
*create_device(_ipp_client_t
*client
);
442 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
443 static void create_job_filename(_ipp_printer_t
*printer
, _ipp_job_t
*job
, const char *format
, char *fname
, size_t fnamesize
);
444 static int create_listener(int family
, int port
);
445 static _ipp_subscription_t
*create_subscription(_ipp_printer_t
*printer
, _ipp_job_t
*job
, int interval
, int lease
, const char *username
, ipp_attribute_t
*notify_events
, ipp_attribute_t
*notify_attributes
, ipp_attribute_t
*notify_user_data
);
446 static _ipp_printer_t
*create_printer(const char *servername
, int port
, const char *name
, const char *directory
, const char *proxy_user
, const char *proxy_pass
);
447 static void debug_attributes(const char *title
, ipp_t
*ipp
,
449 static void delete_client(_ipp_client_t
*client
);
450 static void delete_device(_ipp_device_t
*device
);
451 static void delete_job(_ipp_job_t
*job
);
452 static void delete_printer(_ipp_printer_t
*printer
);
453 static void delete_subscription(_ipp_subscription_t
*sub
);
454 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
455 static _ipp_device_t
*find_device(_ipp_client_t
*client
);
456 static _ipp_job_t
*find_job(_ipp_client_t
*client
, int job_id
);
457 static _ipp_subscription_t
*find_subscription(_ipp_client_t
*client
, int sub_id
);
458 static _ipp_jreason_t
get_job_state_reasons_bits(ipp_attribute_t
*attr
);
459 static _ipp_event_t
get_notify_events_bits(ipp_attribute_t
*attr
);
460 static const char *get_notify_subscribed_event(_ipp_event_t event
);
461 static _ipp_preason_t
get_printer_state_reasons_bits(ipp_attribute_t
*attr
);
462 static void html_escape(_ipp_client_t
*client
, const char *s
,
464 static void html_footer(_ipp_client_t
*client
);
465 static void html_header(_ipp_client_t
*client
, const char *title
);
466 static void html_printf(_ipp_client_t
*client
, const char *format
, ...) __attribute__((__format__(__printf__
, 2, 3)));
467 static void ipp_acknowledge_document(_ipp_client_t
*client
);
468 static void ipp_acknowledge_identify_printer(_ipp_client_t
*client
);
469 static void ipp_acknowledge_job(_ipp_client_t
*client
);
470 static void ipp_cancel_job(_ipp_client_t
*client
);
471 static void ipp_cancel_my_jobs(_ipp_client_t
*client
);
472 static void ipp_cancel_subscription(_ipp_client_t
*client
);
473 static void ipp_close_job(_ipp_client_t
*client
);
474 static void ipp_create_job(_ipp_client_t
*client
);
475 static void ipp_create_xxx_subscriptions(_ipp_client_t
*client
);
476 static void ipp_deregister_output_device(_ipp_client_t
*client
);
477 static void ipp_fetch_document(_ipp_client_t
*client
);
478 static void ipp_fetch_job(_ipp_client_t
*client
);
479 static void ipp_get_document_attributes(_ipp_client_t
*client
);
480 static void ipp_get_documents(_ipp_client_t
*client
);
481 static void ipp_get_job_attributes(_ipp_client_t
*client
);
482 static void ipp_get_jobs(_ipp_client_t
*client
);
483 static void ipp_get_notifications(_ipp_client_t
*client
);
484 static void ipp_get_output_device_attributes(_ipp_client_t
*client
);
485 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
486 static void ipp_get_printer_supported_values(_ipp_client_t
*client
);
487 static void ipp_get_subscription_attributes(_ipp_client_t
*client
);
488 static void ipp_get_subscriptions(_ipp_client_t
*client
);
489 static void ipp_identify_printer(_ipp_client_t
*client
);
490 static void ipp_print_job(_ipp_client_t
*client
);
491 static void ipp_print_uri(_ipp_client_t
*client
);
492 static void ipp_renew_subscription(_ipp_client_t
*client
);
493 static void ipp_send_document(_ipp_client_t
*client
);
494 static void ipp_send_uri(_ipp_client_t
*client
);
495 static void ipp_update_active_jobs(_ipp_client_t
*client
);
496 static void ipp_update_document_status(_ipp_client_t
*client
);
497 static void ipp_update_job_status(_ipp_client_t
*client
);
498 static void ipp_update_output_device_attributes(_ipp_client_t
*client
);
499 static void ipp_validate_document(_ipp_client_t
*client
);
500 static void ipp_validate_job(_ipp_client_t
*client
);
501 //static int parse_options(_ipp_client_t *client, cups_option_t **options);
502 static void *process_client(_ipp_client_t
*client
);
503 static int process_http(_ipp_client_t
*client
);
504 static int process_ipp(_ipp_client_t
*client
);
505 static void *process_job(_ipp_job_t
*job
);
506 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
507 const char *content_coding
,
508 const char *type
, size_t length
);
509 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
510 const char *message
, ...)
511 __attribute__ ((__format__ (__printf__
, 3, 4)));
512 static void respond_unsupported(_ipp_client_t
*client
,
513 ipp_attribute_t
*attr
);
514 static void run_printer(_ipp_printer_t
*printer
);
515 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
516 static void update_device_attributes_no_lock(_ipp_printer_t
*printer
);
517 static void update_device_state_no_lock(_ipp_printer_t
*printer
);
518 static void usage(int status
) __attribute__((noreturn
));
519 static int valid_doc_attributes(_ipp_client_t
*client
);
520 static int valid_job_attributes(_ipp_client_t
*client
);
527 static int KeepFiles
= 0,
529 //static _cups_mutex_t SubscriptionMutex = _CUPS_MUTEX_INITIALIZER;
530 static _cups_cond_t SubscriptionCondition
= _CUPS_COND_INITIALIZER
;
534 * 'main()' - Main entry to the sample infrastructure server.
537 int /* O - Exit status */
538 main(int argc
, /* I - Number of command-line args */
539 char *argv
[]) /* I - Command-line arguments */
541 int i
; /* Looping var */
542 const char *opt
, /* Current option character */
543 *servername
= NULL
, /* Server host name */
544 *name
= NULL
; /* Printer name */
546 const char *keypath
= NULL
; /* Keychain path */
547 #endif /* HAVE_SSL */
548 int port
= 0; /* Port number (0 = auto) */
549 char directory
[1024] = "", /* Spool directory */
550 hostname
[1024], /* Auto-detected hostname */
551 proxy_user
[256] = "", /* Proxy username */
552 *proxy_pass
= NULL
; /* Proxy password */
553 _ipp_printer_t
*printer
; /* Printer object */
557 * Parse command-line arguments...
560 for (i
= 1; i
< argc
; i
++)
561 if (argv
[i
][0] == '-')
563 for (opt
= argv
[i
] + 1; *opt
; opt
++)
568 case 'K' : /* -K keypath */
574 #endif /* HAVE_SSL */
576 case 'd' : /* -d spool-directory */
580 strlcpy(directory
, argv
[i
], sizeof(directory
));
583 case 'h' : /* -h (show help) */
586 case 'k' : /* -k (keep files) */
590 case 'n' : /* -n hostname */
594 servername
= argv
[i
];
597 case 'p' : /* -p port */
599 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
601 port
= atoi(argv
[i
]);
604 case 'u' : /* -u user:pass */
608 strlcpy(proxy_user
, argv
[i
], sizeof(proxy_user
));
609 if ((proxy_pass
= strchr(proxy_user
, ':')) != NULL
)
610 *proxy_pass
++ = '\0';
613 case 'v' : /* -v (be verbose) */
617 default : /* Unknown */
618 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
629 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
637 * Apply defaults as needed...
641 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
647 * Windows is almost always used as a single user system, so use a default port
655 * Use 8000 + UID mod 1000 for the default port number...
658 port
= 8000 + ((int)getuid() % 1000);
661 fprintf(stderr
, "Listening on port %d.\n", port
);
666 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
668 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
670 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
671 directory
, strerror(errno
));
676 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
681 strlcpy(proxy_user
, "test", sizeof(proxy_user
));
684 fputs("Using proxy username \"test\".\n", stderr
);
689 proxy_pass
= "test123";
692 fputs("Using proxy password \"test123\".\n", stderr
);
696 cupsSetServerCredentials(keypath
, servername
, 1);
697 #endif /* HAVE_SSL */
700 * Create the printer...
703 if ((printer
= create_printer(servername
, port
, name
, directory
, proxy_user
, proxy_pass
)) == NULL
)
707 * Run the print service...
710 run_printer(printer
);
713 * Destroy the printer and exit...
716 delete_printer(printer
);
723 * 'add_event()' - Add an event to a subscription.
727 add_event(_ipp_printer_t
*printer
, /* I - Printer */
728 _ipp_job_t
*job
, /* I - Job, if any */
729 _ipp_event_t event
, /* I - Event */
730 const char *message
, /* I - Printf-style notify-text message */
731 ...) /* I - Additional printf arguments */
733 _ipp_subscription_t
*sub
; /* Current subscription */
734 ipp_t
*n
; /* Notify attributes */
735 char text
[1024]; /* notify-text value */
736 va_list ap
; /* Argument pointer */
739 va_start(ap
, message
);
740 vsnprintf(text
, sizeof(text
), message
, ap
);
743 for (sub
= (_ipp_subscription_t
*)cupsArrayFirst(printer
->subscriptions
);
745 sub
= (_ipp_subscription_t
*)cupsArrayNext(printer
->subscriptions
))
747 if (sub
->mask
& event
&& (!sub
->job
|| job
== sub
->job
))
749 _cupsRWLockWrite(&sub
->rwlock
);
752 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_CHARSET
, "notify-charset", NULL
, "utf-8");
753 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_LANGUAGE
, "notify-natural-language", NULL
, "en");
754 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-printer-up-time", time(NULL
) - printer
->start_time
);
755 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
, "notify-printer-uri", NULL
, printer
->uri
);
757 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-job-id", job
->id
);
758 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-subcription-id", sub
->id
);
759 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
, "notify-subscription-uuid", NULL
, sub
->uuid
);
760 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-sequence-number", ++ sub
->last_sequence
);
761 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "notify-subscribed-event", NULL
, get_notify_subscribed_event(event
));
762 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_TEXT
, "notify-text", NULL
, text
);
763 if (event
& _IPP_EVENT_PRINTER_ALL
)
765 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
, "printer-state", printer
->state
);
766 copy_printer_state_reasons(n
, IPP_TAG_EVENT_NOTIFICATION
, printer
);
768 if (event
& _IPP_EVENT_JOB_ALL
)
770 ippAddInteger(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
, "job-state", job
->state
);
771 copy_job_state_reasons(n
, IPP_TAG_EVENT_NOTIFICATION
, job
);
772 if (event
== _IPP_EVENT_JOB_CREATED
)
774 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
, "job-name", NULL
, job
->name
);
775 ippAddString(n
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
779 cupsArrayAdd(sub
->events
, n
);
780 if (cupsArrayCount(sub
->events
) > 100)
782 n
= (ipp_t
*)cupsArrayFirst(sub
->events
);
783 cupsArrayRemove(sub
->events
, n
);
785 sub
->first_sequence
++;
788 _cupsRWUnlock(&sub
->rwlock
);
789 _cupsCondBroadcast(&SubscriptionCondition
);
796 * 'check_jobs()' - Check for new jobs to process.
800 check_jobs(_ipp_printer_t
*printer
) /* I - Printer */
802 _ipp_job_t
*job
; /* Current job */
805 if (printer
->processing_job
)
808 _cupsRWLockWrite(&(printer
->rwlock
));
809 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->active_jobs
);
811 job
= (_ipp_job_t
*)cupsArrayNext(printer
->active_jobs
))
813 if (job
->state
== IPP_JSTATE_PENDING
)
815 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
817 job
->state
= IPP_JSTATE_ABORTED
;
818 job
->completed
= time(NULL
);
820 add_event(printer
, job
, _IPP_EVENT_JOB_COMPLETED
, "Job aborted because creation of processing thread failed.");
825 _cupsRWUnlock(&(printer
->rwlock
));
830 * 'clean_jobs()' - Clean out old (completed) jobs.
834 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
836 _ipp_job_t
*job
; /* Current job */
837 time_t cleantime
; /* Clean time */
840 if (cupsArrayCount(printer
->jobs
) == 0)
843 cleantime
= time(NULL
) - 60;
845 _cupsRWLockWrite(&(printer
->rwlock
));
846 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
848 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
849 if (job
->completed
&& job
->completed
< cleantime
)
851 cupsArrayRemove(printer
->jobs
, job
);
856 _cupsRWUnlock(&(printer
->rwlock
));
861 * 'compare_active_jobs()' - Compare two active jobs.
864 static int /* O - Result of comparison */
865 compare_active_jobs(_ipp_job_t
*a
, /* I - First job */
866 _ipp_job_t
*b
) /* I - Second job */
868 int diff
; /* Difference */
871 if ((diff
= b
->priority
- a
->priority
) == 0)
872 diff
= b
->id
- a
->id
;
879 * 'compare_completed_jobs()' - Compare two completed jobs.
882 static int /* O - Result of comparison */
883 compare_completed_jobs(_ipp_job_t
*a
, /* I - First job */
884 _ipp_job_t
*b
) /* I - Second job */
886 int diff
; /* Difference */
889 if ((diff
= a
->completed
- b
->completed
) == 0)
890 diff
= b
->id
- a
->id
;
897 * 'compare_devices()' - Compare two devices...
900 static int /* O - Result of comparison */
901 compare_devices(_ipp_device_t
*a
, /* I - First device */
902 _ipp_device_t
*b
) /* I - Second device */
904 return (strcmp(a
->uuid
, b
->uuid
));
909 * 'compare_jobs()' - Compare two jobs.
912 static int /* O - Result of comparison */
913 compare_jobs(_ipp_job_t
*a
, /* I - First job */
914 _ipp_job_t
*b
) /* I - Second job */
916 return (b
->id
- a
->id
);
921 * 'copy_attributes()' - Copy attributes from one request to another.
925 copy_attributes(ipp_t
*to
, /* I - Destination request */
926 ipp_t
*from
, /* I - Source request */
927 cups_array_t
*ra
, /* I - Requested attributes */
928 ipp_tag_t group_tag
, /* I - Group to copy */
929 int quickcopy
) /* I - Do a quick copy? */
931 _ipp_filter_t filter
; /* Filter data */
935 filter
.group_tag
= group_tag
;
937 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
942 * 'copy_job_attrs()' - Copy job attributes to the response.
947 _ipp_client_t
*client
, /* I - Client */
948 _ipp_job_t
*job
, /* I - Job */
949 cups_array_t
*ra
) /* I - requested-attributes */
951 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
953 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
956 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
958 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
961 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
964 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
966 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
969 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
970 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
972 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
973 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
975 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
976 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
978 if (!ra
|| cupsArrayFind(ra
, "job-state"))
979 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
980 "job-state", job
->state
);
982 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
984 if (job
->dev_state_message
)
986 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_TAG_TEXT
, "job-state-message", NULL
, job
->dev_state_message
);
990 const char *message
= ""; /* Message string */
994 case IPP_JSTATE_PENDING
:
995 message
= "Job pending.";
998 case IPP_JSTATE_HELD
:
999 if (job
->state_reasons
& _IPP_JREASON_JOB_INCOMING
)
1000 message
= "Job incoming.";
1001 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
1002 message
= "Job held.";
1004 message
= "Job created.";
1007 case IPP_JSTATE_PROCESSING
:
1008 if (job
->state_reasons
& _IPP_JREASON_PROCESSING_TO_STOP_POINT
)
1011 message
= "Cancel in progress.";
1013 message
= "Abort in progress.";
1016 message
= "Job printing.";
1019 case IPP_JSTATE_STOPPED
:
1020 message
= "Job stopped.";
1023 case IPP_JSTATE_CANCELED
:
1024 message
= "Job canceled.";
1027 case IPP_JSTATE_ABORTED
:
1028 message
= "Job aborted.";
1031 case IPP_JSTATE_COMPLETED
:
1032 message
= "Job completed.";
1036 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, message
);
1040 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
1041 copy_job_state_reasons(client
->response
, IPP_TAG_JOB
, job
);
1045 case IPP_JSTATE_PENDING :
1046 ippAddString(client->response, IPP_TAG_JOB,
1047 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1051 case IPP_JSTATE_HELD :
1053 ippAddString(client->response, IPP_TAG_JOB,
1054 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1055 "job-state-reasons", NULL, "job-incoming");
1056 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
1057 ippAddString(client->response, IPP_TAG_JOB,
1058 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1059 "job-state-reasons", NULL, "job-hold-until-specified");
1061 ippAddString(client->response, IPP_TAG_JOB,
1062 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1063 "job-state-reasons", NULL, "job-data-insufficient");
1066 case IPP_JSTATE_PROCESSING :
1068 ippAddString(client->response, IPP_TAG_JOB,
1069 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1070 "job-state-reasons", NULL, "processing-to-stop-point");
1072 ippAddString(client->response, IPP_TAG_JOB,
1073 IPP_CONST_TAG(IPP_TAG_KEYWORD),
1074 "job-state-reasons", NULL, "job-printing");
1077 case IPP_JSTATE_STOPPED :
1078 ippAddString(client->response, IPP_TAG_JOB,
1079 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1080 NULL, "job-stopped");
1083 case IPP_JSTATE_CANCELED :
1084 ippAddString(client->response, IPP_TAG_JOB,
1085 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1086 NULL, "job-canceled-by-user");
1089 case IPP_JSTATE_ABORTED :
1090 ippAddString(client->response, IPP_TAG_JOB,
1091 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1092 NULL, "aborted-by-system");
1095 case IPP_JSTATE_COMPLETED :
1096 ippAddString(client->response, IPP_TAG_JOB,
1097 IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1098 NULL, "job-completed-successfully");
1103 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
1104 ippAddInteger(client
->response
, IPP_TAG_JOB
,
1105 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
1106 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
1108 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
1109 ippAddInteger(client
->response
, IPP_TAG_JOB
,
1110 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
1111 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
1116 * 'copy_job_state_reasons()' - Copy printer-state-reasons values.
1120 copy_job_state_reasons(
1121 ipp_t
*ipp
, /* I - Attributes */
1122 ipp_tag_t group_tag
, /* I - Group */
1123 _ipp_job_t
*job
) /* I - Printer */
1125 _ipp_jreason_t creasons
; /* Combined job-state-reasons */
1128 creasons
= job
->state_reasons
| job
->dev_state_reasons
;
1132 ippAddString(ipp
, group_tag
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons", NULL
, "none");
1136 int i
, /* Looping var */
1137 num_reasons
= 0;/* Number of reasons */
1138 _ipp_jreason_t reason
; /* Current reason */
1139 const char *reasons
[32]; /* Reason strings */
1141 for (i
= 0, reason
= 1; i
< (int)(sizeof(_ipp_jreasons
) / sizeof(_ipp_jreasons
[0])); i
++, reason
<<= 1)
1143 if (creasons
& reason
)
1144 reasons
[num_reasons
++] = _ipp_jreasons
[i
];
1147 ippAddStrings(ipp
, group_tag
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons", num_reasons
, NULL
, reasons
);
1153 * 'copy_printer_state_reasons()' - Copy printer-state-reasons values.
1157 copy_printer_state_reasons(
1158 ipp_t
*ipp
, /* I - Attributes */
1159 ipp_tag_t group_tag
, /* I - Group */
1160 _ipp_printer_t
*printer
) /* I - Printer */
1162 _ipp_preason_t creasons
= printer
->state_reasons
| printer
->dev_reasons
;
1163 /* Combined reasons */
1166 if (creasons
== _IPP_PREASON_NONE
)
1168 ippAddString(ipp
, group_tag
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", NULL
, "none");
1172 int i
, /* Looping var */ num_reasons
= 0;/* Number of reasons */
1173 _ipp_preason_t reason
; /* Current reason */
1174 const char *reasons
[32]; /* Reason strings */
1176 for (i
= 0, reason
= 1; i
< (int)(sizeof(_ipp_preasons
) / sizeof(_ipp_preasons
[0])); i
++, reason
<<= 1)
1178 if (creasons
& reason
)
1179 reasons
[num_reasons
++] = _ipp_preasons
[i
];
1182 ippAddStrings(ipp
, group_tag
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", num_reasons
, NULL
, reasons
);
1188 * 'copy_sub_attrs()' - Copy job attributes to the response.
1192 copy_subscription_attributes(
1193 _ipp_client_t
*client
, /* I - Client */
1194 _ipp_subscription_t
*sub
, /* I - Subscription */
1195 cups_array_t
*ra
) /* I - requested-attributes */
1197 copy_attributes(client
->response
, sub
->attrs
, ra
, IPP_TAG_SUBSCRIPTION
, 0);
1199 if (!ra
|| cupsArrayFind(ra
, "notify-lease-expiration-time"))
1200 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-lease-expiration-time", (int)(sub
->expire
- client
->printer
->start_time
));
1202 if (!ra
|| cupsArrayFind(ra
, "notify-printer-up-time"))
1203 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
1205 if (!ra
|| cupsArrayFind(ra
, "notify-sequence-number"))
1206 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-sequence-number", sub
->last_sequence
);
1211 * 'create_client()' - Accept a new network connection and create a client
1215 static _ipp_client_t
* /* O - Client */
1216 create_client(_ipp_printer_t
*printer
, /* I - Printer */
1217 int sock
) /* I - Listen socket */
1219 _ipp_client_t
*client
; /* Client */
1222 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
1224 perror("Unable to allocate memory for client");
1228 client
->printer
= printer
;
1229 client
->fetch_file
= -1;
1232 * Accept the client and get the remote address...
1235 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
1237 perror("Unable to accept client connection");
1244 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
1247 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
1254 * 'create_device()' - Create an output device tracking object.
1257 static _ipp_device_t
* /* O - Device */
1258 create_device(_ipp_client_t
*client
) /* I - Client */
1260 _ipp_device_t
*device
; /* Device */
1261 ipp_attribute_t
*uuid
; /* output-device-uuid */
1264 if ((uuid
= ippFindAttribute(client
->request
, "output-device-uuid", IPP_TAG_URI
)) == NULL
)
1267 if ((device
= calloc(1, sizeof(_ipp_device_t
))) == NULL
)
1270 _cupsRWInit(&device
->rwlock
);
1272 device
->uuid
= strdup(ippGetString(uuid
, 0, NULL
));
1273 device
->state
= IPP_PSTATE_STOPPED
;
1275 _cupsRWLockWrite(&client
->printer
->rwlock
);
1276 cupsArrayAdd(client
->printer
->devices
, device
);
1277 _cupsRWUnlock(&client
->printer
->rwlock
);
1284 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1288 static _ipp_job_t
* /* O - Job */
1289 create_job(_ipp_client_t
*client
) /* I - Client */
1291 _ipp_job_t
*job
; /* Job */
1292 ipp_attribute_t
*attr
; /* Job attribute */
1293 char uri
[1024], /* job-uri value */
1294 uuid
[64]; /* job-uuid value */
1297 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1300 * Allocate and initialize the job object...
1303 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
1305 perror("Unable to allocate memory for job");
1309 job
->printer
= client
->printer
;
1310 job
->attrs
= ippNew();
1311 job
->state
= IPP_JSTATE_HELD
;
1315 * Copy all of the job attributes...
1318 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1321 * Get the requesting-user-name, document format, and priority...
1324 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
1325 job
->priority
= ippGetInteger(attr
, 0);
1329 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1330 job
->username
= ippGetString(attr
, 0, NULL
);
1332 job
->username
= "anonymous";
1334 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1336 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1338 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1339 job
->format
= ippGetString(attr
, 0, NULL
);
1340 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1341 job
->format
= ippGetString(attr
, 0, NULL
);
1343 job
->format
= "application/octet-stream";
1346 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1347 job
->impressions
= ippGetInteger(attr
, 0);
1349 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1350 job
->name
= ippGetString(attr
, 0, NULL
);
1353 * Add job description attributes and add to the jobs array...
1356 job
->id
= client
->printer
->next_job_id
++;
1358 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1359 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1361 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1362 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1363 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1364 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1365 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1366 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1368 cupsArrayAdd(client
->printer
->jobs
, job
);
1369 cupsArrayAdd(client
->printer
->active_jobs
, job
);
1371 _cupsRWUnlock(&(client
->printer
->rwlock
));
1378 * 'create_job_filename()' - Create the filename for a document in a job.
1381 static void create_job_filename(
1382 _ipp_printer_t
*printer
, /* I - Printer */
1383 _ipp_job_t
*job
, /* I - Job */
1384 const char *format
, /* I - Format or NULL */
1385 char *fname
, /* I - Filename buffer */
1386 size_t fnamesize
) /* I - Size of filename buffer */
1388 char name
[256], /* "Safe" filename */
1389 *nameptr
; /* Pointer into filename */
1390 const char *ext
, /* Filename extension */
1391 *job_name
; /* job-name value */
1392 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1396 * Make a name from the job-name attribute...
1399 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1400 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1402 job_name
= "untitled";
1404 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1405 if (isalnum(*job_name
& 255) || *job_name
== '-')
1406 *nameptr
++ = (char)tolower(*job_name
& 255);
1413 * Figure out the extension...
1417 format
= job
->format
;
1419 if (!strcasecmp(format
, "image/jpeg"))
1421 else if (!strcasecmp(format
, "image/png"))
1423 else if (!strcasecmp(format
, "image/pwg-raster"))
1425 else if (!strcasecmp(format
, "image/urf"))
1427 else if (!strcasecmp(format
, "application/pdf"))
1429 else if (!strcasecmp(format
, "application/postscript"))
1435 * Create a filename with the job-id, job-name, and document-format (extension)...
1438 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1443 * 'create_listener()' - Create a listener socket.
1446 static int /* O - Listener socket or -1 on error */
1447 create_listener(int family
, /* I - Address family */
1448 int port
) /* I - Port number */
1450 int sock
; /* Listener socket */
1451 http_addrlist_t
*addrlist
; /* Listen address */
1452 char service
[255]; /* Service port */
1455 snprintf(service
, sizeof(service
), "%d", port
);
1456 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1459 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1461 httpAddrFreeList(addrlist
);
1468 * 'create_printer()' - Create, register, and listen for connections to a
1472 static _ipp_printer_t
* /* O - Printer */
1473 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1474 int port
, /* I - Port number */
1475 const char *name
, /* I - printer-name */
1476 const char *directory
, /* I - Spool directory */
1477 const char *proxy_user
, /* I - Proxy account username */
1478 const char *proxy_pass
) /* I - Proxy account password */
1480 _ipp_printer_t
*printer
; /* Printer */
1481 char uri
[1024], /* Printer URI */
1482 adminurl
[1024], /* printer-more-info URI */
1483 supplyurl
[1024],/* printer-supply-info-uri URI */
1484 uuid
[128]; /* printer-uuid */
1485 int k_supported
; /* Maximum file size supported */
1487 struct statvfs spoolinfo
; /* FS info for spool directory */
1488 double spoolsize
; /* FS size */
1489 #elif defined(HAVE_STATFS)
1490 struct statfs spoolinfo
; /* FS info for spool directory */
1491 double spoolsize
; /* FS size */
1492 #endif /* HAVE_STATVFS */
1493 static const char * const versions
[] =/* ipp-versions-supported values */
1499 static const char * const features
[] =/* ipp-features-supported values */
1503 "infrastructure-printer",
1506 static const int ops
[] = /* operations-supported values */
1510 IPP_OP_VALIDATE_JOB
,
1512 IPP_OP_SEND_DOCUMENT
,
1515 IPP_OP_GET_JOB_ATTRIBUTES
,
1517 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1518 IPP_OP_GET_PRINTER_SUPPORTED_VALUES
,
1519 IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
,
1520 IPP_OP_CREATE_JOB_SUBSCRIPTIONS
,
1521 IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES
,
1522 IPP_OP_GET_SUBSCRIPTIONS
,
1523 IPP_OP_RENEW_SUBSCRIPTION
,
1524 IPP_OP_CANCEL_SUBSCRIPTION
,
1525 IPP_OP_GET_NOTIFICATIONS
,
1526 IPP_OP_GET_DOCUMENT_ATTRIBUTES
,
1527 IPP_OP_GET_DOCUMENTS
,
1528 IPP_OP_CANCEL_MY_JOBS
,
1530 IPP_OP_IDENTIFY_PRINTER
,
1531 IPP_OP_VALIDATE_DOCUMENT
,
1532 _IPP_OP_ACKNOWLEDGE_DOCUMENT
,
1533 _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER
,
1534 _IPP_OP_ACKNOWLEDGE_JOB
,
1535 _IPP_OP_FETCH_DOCUMENT
,
1537 _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES
,
1538 _IPP_OP_UPDATE_ACTIVE_JOBS
,
1539 _IPP_OP_UPDATE_DOCUMENT_STATUS
,
1540 _IPP_OP_UPDATE_JOB_STATUS
,
1541 _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES
,
1542 _IPP_OP_DEREGISTER_OUTPUT_DEVICE
1544 static const char * const charsets
[] =/* charset-supported values */
1549 static const char * const compressions
[] =/* compression-supported values */
1554 #endif /* HAVE_LIBZ */
1557 static const char * const notify_attributes
[] =
1558 { /* notify-attributes-supported */
1559 "printer-state-change-time",
1560 "notify-lease-expiration-time",
1561 "notify-subscriber-user-name"
1563 static const char * const reference_uri_schemes_supported
[] =
1564 { /* reference-uri-schemes-supported */
1570 #endif /* HAVE_SSL */
1572 static const char * const which_jobs
[] =
1573 { /* which-jobs-supported values */
1582 "processing-stopped"
1587 * Allocate memory for the printer...
1590 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1592 perror("ippserver: Unable to allocate memory for printer");
1598 printer
->name
= strdup(name
);
1599 printer
->directory
= strdup(directory
);
1600 printer
->hostname
= strdup(servername
);
1601 printer
->port
= port
;
1602 printer
->start_time
= time(NULL
);
1603 printer
->config_time
= printer
->start_time
;
1604 printer
->state
= IPP_PSTATE_IDLE
;
1605 printer
->state_reasons
= _IPP_PREASON_NONE
;
1606 printer
->state_time
= printer
->start_time
;
1607 printer
->jobs
= cupsArrayNew3((cups_array_func_t
)compare_jobs
, NULL
, NULL
, 0, NULL
, (cups_afree_func_t
)delete_job
);
1608 printer
->active_jobs
= cupsArrayNew((cups_array_func_t
)compare_active_jobs
, NULL
);
1609 printer
->completed_jobs
= cupsArrayNew((cups_array_func_t
)compare_completed_jobs
, NULL
);
1610 printer
->next_job_id
= 1;
1612 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1613 printer
->hostname
, printer
->port
, "/ipp/print");
1614 printer
->uri
= strdup(uri
);
1615 printer
->urilen
= strlen(uri
);
1618 printer
->proxy_user
= strdup(proxy_user
);
1620 printer
->proxy_pass
= strdup(proxy_pass
);
1622 printer
->devices
= cupsArrayNew((cups_array_func_t
)compare_devices
, NULL
);
1624 _cupsRWInit(&(printer
->rwlock
));
1627 * Create the listener sockets...
1630 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1632 perror("Unable to create IPv4 listener");
1636 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1638 perror("Unable to create IPv6 listener");
1643 * Prepare values for the printer attributes...
1646 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/");
1647 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), "http", NULL
, printer
->hostname
, printer
->port
, "/supplies");
1651 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1652 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1653 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1657 * Get the maximum spool size based on the size of the filesystem used for
1658 * the spool directory. If the host OS doesn't support the statfs call
1659 * or the filesystem is larger than 2TiB, always report INT_MAX.
1663 if (statvfs(printer
->directory
, &spoolinfo
))
1664 k_supported
= INT_MAX
;
1665 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1666 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1667 k_supported
= INT_MAX
;
1669 k_supported
= (int)spoolsize
;
1671 #elif defined(HAVE_STATFS)
1672 if (statfs(printer
->directory
, &spoolinfo
))
1673 k_supported
= INT_MAX
;
1674 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1675 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1676 k_supported
= INT_MAX
;
1678 k_supported
= (int)spoolsize
;
1681 k_supported
= INT_MAX
;
1682 #endif /* HAVE_STATVFS */
1685 * Create the printer attributes. This list of attributes is sorted to improve
1686 * performance when the client provides a requested-attributes attribute...
1689 printer
->attrs
= ippNew();
1691 /* charset-configured */
1692 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1693 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1694 "charset-configured", NULL
, "utf-8");
1696 /* charset-supported */
1697 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1698 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1699 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1702 /* compression-supported */
1703 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1704 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1705 "compression-supported",
1706 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1709 /* generated-natural-language-supported */
1710 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1711 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1712 "generated-natural-language-supported", NULL
, "en");
1714 /* ipp-features-supported */
1715 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1717 /* ipp-versions-supported */
1718 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1720 /* ippget-event-life */
1721 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "ippget-event-life", 300);
1723 /* job-ids-supported */
1724 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1726 /* job-k-octets-supported */
1727 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1730 /* job-priority-default */
1731 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1732 "job-priority-default", 50);
1734 /* job-priority-supported */
1735 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1736 "job-priority-supported", 100);
1738 /* multiple-document-jobs-supported */
1739 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1741 /* multiple-operation-time-out */
1742 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1744 /* multiple-operation-time-out-action */
1745 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1747 /* natural-language-configured */
1748 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1749 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1750 "natural-language-configured", NULL
, "en");
1752 /* notify-attributes-supported */
1753 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-attributes-supported", sizeof(notify_attributes
) / sizeof(notify_attributes
[0]), NULL
, notify_attributes
);
1755 /* notify-events-default */
1756 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-events-default", NULL
, "job-completed");
1758 /* notify-events-supported */
1759 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-events-supported", sizeof(_ipp_events
) / sizeof(_ipp_events
[0]), NULL
, _ipp_events
);
1761 /* notify-lease-duration-default */
1762 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "notify-lease-duration-default", 86400);
1764 /* notify-lease-duration-supported */
1765 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "notify-lease-duration-supported", 0, _IPP_NOTIFY_LEASE_DURATION_MAX
);
1767 /* notify-max-events-supported */
1768 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "notify-lease-duration-default", (int)(sizeof(_ipp_events
) / sizeof(_ipp_events
[0])));
1770 /* notify-pull-method-supported */
1771 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-pull-method-supported", NULL
, "ippget");
1773 /* operations-supported */
1774 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1775 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1777 /* printer-get-attributes-supported */
1778 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1780 /* printer-is-accepting-jobs */
1781 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1784 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
1786 /* printer-more-info */
1787 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1790 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
1792 /* printer-supply-info-uri */
1793 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1795 /* printer-uri-supported */
1796 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1799 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
1800 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1802 /* reference-uri-scheme-supported */
1803 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1804 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1805 "reference-uri-schemes-supported",
1806 (int)(sizeof(reference_uri_schemes_supported
) /
1807 sizeof(reference_uri_schemes_supported
[0])),
1808 NULL
, reference_uri_schemes_supported
);
1810 /* uri-authentication-supported */
1811 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1812 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1813 "uri-authentication-supported", NULL
, "basic");
1815 /* uri-security-supported */
1816 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1817 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1818 "uri-security-supported", NULL
, "tls");
1820 /* which-jobs-supported */
1821 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1822 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1823 "which-jobs-supported",
1824 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1826 debug_attributes("Printer", printer
->attrs
, 0);
1836 * If we get here we were unable to create the printer...
1841 delete_printer(printer
);
1847 * 'create_subscription()' - Create a new subscription object from a
1848 * Print-Job, Create-Job, or Create-xxx-Subscription
1852 static _ipp_subscription_t
* /* O - Subscription object */
1853 create_subscription(
1854 _ipp_printer_t
*printer
, /* I - Printer */
1855 _ipp_job_t
*job
, /* I - Job, if any */
1856 int interval
, /* I - Interval for progress events */
1857 int lease
, /* I - Lease duration */
1858 const char *username
, /* I - User creating the subscription */
1859 ipp_attribute_t
*notify_events
, /* I - Events to monitor */
1860 ipp_attribute_t
*notify_attributes
, /* I - Attributes to report */
1861 ipp_attribute_t
*notify_user_data
) /* I - User data, if any */
1863 _ipp_subscription_t
*sub
; /* Subscription */
1864 ipp_attribute_t
*attr
; /* Subscription attribute */
1865 char uuid
[64]; /* notify-subscription-uuid value */
1869 * Allocate and initialize the subscription object...
1872 if ((sub
= calloc(1, sizeof(_ipp_subscription_t
))) == NULL
)
1874 perror("Unable to allocate memory for subscription");
1878 _cupsRWLockWrite(&(printer
->rwlock
));
1880 sub
->id
= printer
->next_sub_id
++;
1881 sub
->mask
= notify_events
? get_notify_events_bits(notify_events
) : _IPP_EVENT_DEFAULT
;
1882 sub
->printer
= printer
;
1884 sub
->interval
= interval
;
1886 sub
->attrs
= ippNew();
1889 sub
->expire
= time(NULL
) + sub
->lease
;
1891 sub
->expire
= INT_MAX
;
1893 _cupsRWInit(&(sub
->rwlock
));
1896 * Add subscription description attributes and add to the subscriptions
1900 ippAddInteger(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-subscription-id", sub
->id
);
1902 httpAssembleUUID(printer
->hostname
, printer
->port
, printer
->name
, -sub
->id
, uuid
, sizeof(uuid
));
1903 attr
= ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
, "notify-subscription-uuid", NULL
, uuid
);
1904 sub
->uuid
= ippGetString(attr
, 0, NULL
);
1906 ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
, "notify-printer-uri", NULL
, printer
->uri
);
1909 ippAddInteger(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-job-id", job
->id
);
1911 ippAddInteger(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-lease-duration", sub
->lease
);
1913 attr
= ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_NAME
, "notify-subscriber-user-name", NULL
, username
);
1914 sub
->username
= ippGetString(attr
, 0, NULL
);
1917 ippCopyAttribute(sub
->attrs
, notify_events
, 0);
1919 ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-events", NULL
, _IPP_EVENT_DEFAULT_STRING
);
1921 ippAddString(sub
->attrs
, IPP_TAG_SUBSCRIPTION
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "notify-pull-method", NULL
, "ippget");
1923 if (notify_attributes
)
1924 ippCopyAttribute(sub
->attrs
, notify_attributes
, 0);
1926 if (notify_user_data
)
1927 ippCopyAttribute(sub
->attrs
, notify_user_data
, 0);
1929 sub
->events
= cupsArrayNew3(NULL
, NULL
, NULL
, 0, NULL
, (cups_afree_func_t
)ippDelete
);
1931 cupsArrayAdd(printer
->subscriptions
, sub
);
1933 _cupsRWUnlock(&(printer
->rwlock
));
1940 * 'debug_attributes()' - Print attributes in a request or response.
1944 debug_attributes(const char *title
, /* I - Title */
1945 ipp_t
*ipp
, /* I - Request/response */
1946 int type
) /* I - 0 = object, 1 = request, 2 = response */
1948 ipp_tag_t group_tag
; /* Current group */
1949 ipp_attribute_t
*attr
; /* Current attribute */
1950 char buffer
[2048]; /* String buffer for value */
1951 int major
, minor
; /* Version */
1957 fprintf(stderr
, "%s:\n", title
);
1958 major
= ippGetVersion(ipp
, &minor
);
1959 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1961 fprintf(stderr
, " operation-id=%s(%04x)\n",
1962 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1964 fprintf(stderr
, " status-code=%s(%04x)\n",
1965 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1966 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1968 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1970 attr
= ippNextAttribute(ipp
))
1972 if (ippGetGroupTag(attr
) != group_tag
)
1974 group_tag
= ippGetGroupTag(attr
);
1975 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1978 if (ippGetName(attr
))
1980 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1981 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1982 ippGetCount(attr
) > 1 ? "1setOf " : "",
1983 ippTagString(ippGetValueTag(attr
)), buffer
);
1990 * 'delete_client()' - Close the socket and free all memory used by a client
1995 delete_client(_ipp_client_t
*client
) /* I - Client */
1998 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2001 * Flush pending writes before closing...
2004 httpFlushWrite(client
->http
);
2010 httpClose(client
->http
);
2012 ippDelete(client
->request
);
2013 ippDelete(client
->response
);
2020 * 'delete_device()' - Remove a device from a printer.
2022 * Note: Caller is responsible for locking the printer object.
2026 delete_device(_ipp_device_t
*device
) /* I - Device */
2029 * Free memory used for the device...
2032 _cupsRWDeinit(&device
->rwlock
);
2039 ippDelete(device
->attrs
);
2046 * 'delete_job()' - Remove from the printer and free all memory used by a job
2051 delete_job(_ipp_job_t
*job
) /* I - Job */
2054 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2056 _cupsRWLockWrite(&job
->rwlock
);
2058 ippDelete(job
->attrs
);
2063 unlink(job
->filename
);
2065 free(job
->filename
);
2068 _cupsRWDeinit(&job
->rwlock
);
2075 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2076 * used by a printer object.
2080 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2082 _cupsRWLockWrite(&printer
->rwlock
);
2084 if (printer
->ipv4
>= 0)
2085 close(printer
->ipv4
);
2087 if (printer
->ipv6
>= 0)
2088 close(printer
->ipv6
);
2091 free(printer
->name
);
2092 if (printer
->directory
)
2093 free(printer
->directory
);
2094 if (printer
->hostname
)
2095 free(printer
->hostname
);
2098 if (printer
->proxy_user
)
2099 free(printer
->proxy_user
);
2100 if (printer
->proxy_pass
)
2101 free(printer
->proxy_pass
);
2104 ippDelete(printer
->attrs
);
2105 ippDelete(printer
->dev_attrs
);
2107 cupsArrayDelete(printer
->active_jobs
);
2108 cupsArrayDelete(printer
->completed_jobs
);
2109 cupsArrayDelete(printer
->jobs
);
2110 cupsArrayDelete(printer
->subscriptions
);
2112 _cupsRWDeinit(&printer
->rwlock
);
2119 * 'delete_subscription()' - Delete a subscription.
2123 delete_subscription(
2124 _ipp_subscription_t
*sub
) /* I - Subscription */
2126 sub
->pending_delete
= 1;
2128 _cupsCondBroadcast(&SubscriptionCondition
);
2130 _cupsRWLockWrite(&sub
->rwlock
);
2132 ippDelete(sub
->attrs
);
2133 cupsArrayDelete(sub
->events
);
2135 _cupsRWDeinit(&sub
->rwlock
);
2142 * 'filter_cb()' - Filter printer attributes based on the requested array.
2145 static int /* O - 1 to copy, 0 to ignore */
2146 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2147 ipp_t
*dst
, /* I - Destination (unused) */
2148 ipp_attribute_t
*attr
) /* I - Source attribute */
2151 * Filter attributes as needed...
2156 ipp_tag_t group
= ippGetGroupTag(attr
);
2157 const char *name
= ippGetName(attr
);
2159 if ((filter
->group_tag
!= IPP_TAG_ZERO
&& group
!= filter
->group_tag
&& group
!= IPP_TAG_ZERO
) || !name
|| (!strcmp(name
, "media-col-database") && !cupsArrayFind(filter
->ra
, (void *)name
)))
2162 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2167 * 'find_device()' - Find a device.
2170 static _ipp_device_t
* /* I - Device */
2171 find_device(_ipp_client_t
*client
) /* I - Client */
2173 ipp_attribute_t
*uuid
; /* output-device-uuid */
2174 _ipp_device_t key
, /* Search key */
2175 *device
; /* Matching device */
2178 if ((uuid
= ippFindAttribute(client
->request
, "output-device-uuid", IPP_TAG_URI
)) == NULL
)
2181 key
.uuid
= (char *)ippGetString(uuid
, 0, NULL
);
2183 _cupsRWLockRead(&client
->printer
->rwlock
);
2184 device
= (_ipp_device_t
*)cupsArrayFind(client
->printer
->devices
, &key
);
2185 _cupsRWUnlock(&client
->printer
->rwlock
);
2192 * 'find_job()' - Find a job specified in a request.
2195 static _ipp_job_t
* /* O - Job or NULL */
2196 find_job(_ipp_client_t
*client
, /* I - Client */
2197 int job_id
) /* I - Job ID to find or 0 to lookup */
2199 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2200 _ipp_job_t key
, /* Job search key */
2201 *job
; /* Matching job, if any */
2208 else if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2210 const char *uri
= ippGetString(attr
, 0, NULL
);
2212 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2213 uri
[client
->printer
->urilen
] == '/')
2214 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2218 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2220 key
.id
= ippGetInteger(attr
, 0);
2223 _cupsRWLockRead(&(client
->printer
->rwlock
));
2224 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2225 _cupsRWUnlock(&(client
->printer
->rwlock
));
2232 * 'find_subscription()' - Find a subcription.
2235 static _ipp_subscription_t
* /* O - Subscription */
2236 find_subscription(_ipp_client_t
*client
,/* I - Client */
2237 int sub_id
) /* I - Subscription ID or 0 */
2239 ipp_attribute_t
*notify_subscription_id
;
2240 /* notify-subscription-id */
2241 _ipp_subscription_t key
, /* Search key */
2242 *sub
; /* Matching subscription */
2247 else if ((notify_subscription_id
= ippFindAttribute(client
->request
, "notify-subscription-id", IPP_TAG_INTEGER
)) == NULL
)
2250 key
.id
= ippGetInteger(notify_subscription_id
, 0);
2252 _cupsRWLockRead(&client
->printer
->rwlock
);
2253 sub
= (_ipp_subscription_t
*)cupsArrayFind(client
->printer
->subscriptions
, &key
);
2254 _cupsRWUnlock(&client
->printer
->rwlock
);
2261 * 'get_job_state_reasons_bits()' - Get the bits associates with "job-state-reasons" values.
2264 static _ipp_jreason_t
/* O - Bits */
2265 get_job_state_reasons_bits(
2266 ipp_attribute_t
*attr
) /* I - "job-state-reasons" attribute */
2268 int i
, j
, /* Looping vars */
2269 count
; /* Number of "job-state-reasons" values */
2270 const char *keyword
; /* "job-state-reasons" value */
2271 _ipp_jreason_t jreasons
= _IPP_JREASON_NONE
;
2272 /* Bits for "job-state-reasons" values */
2275 count
= ippGetCount(attr
);
2276 for (i
= 0; i
< count
; i
++)
2278 keyword
= ippGetString(attr
, i
, NULL
);
2280 for (j
= 0; j
< (int)(sizeof(_ipp_jreasons
) / sizeof(_ipp_jreasons
[0])); j
++)
2282 if (!strcmp(keyword
, _ipp_jreasons
[j
]))
2295 * 'get_notify_event_bits()' - Get the bits associated with "notify-events" values.
2298 static _ipp_event_t
/* O - Bits */
2299 get_notify_events_bits(
2300 ipp_attribute_t
*attr
) /* I - "notify-events" attribute */
2302 int i
, j
, /* Looping vars */
2303 count
; /* Number of "notify-events" values */
2304 const char *keyword
; /* "notify-events" value */
2305 _ipp_event_t events
= _IPP_EVENT_NONE
;
2306 /* Bits for "notify-events" values */
2309 count
= ippGetCount(attr
);
2310 for (i
= 0; i
< count
; i
++)
2312 keyword
= ippGetString(attr
, i
, NULL
);
2314 for (j
= 0; j
< (int)(sizeof(_ipp_events
) / sizeof(_ipp_events
[0])); j
++)
2316 if (!strcmp(keyword
, _ipp_jreasons
[j
]))
2329 * 'get_notify_subscribed_event()' - Get the event name.
2332 static const char * /* O - Event name */
2333 get_notify_subscribed_event(
2334 _ipp_event_t event
) /* I - Event bit */
2336 int i
; /* Looping var */
2337 _ipp_event_t mask
; /* Current mask */
2339 for (i
= 0, mask
= 1; i
< (int)(sizeof(_ipp_events
) / sizeof(_ipp_events
[0])); i
++, mask
<<= 1)
2341 return (_ipp_events
[i
]);
2348 * 'get_printer_state_reasons_bits()' - Get the bits associated with "printer-state-reasons" values.
2351 static _ipp_preason_t
/* O - Bits */
2352 get_printer_state_reasons_bits(
2353 ipp_attribute_t
*attr
) /* I - "printer-state-reasons" bits */
2355 int i
, j
, /* Looping vars */
2356 count
; /* Number of "printer-state-reasons" values */
2357 const char *keyword
; /* "printer-state-reasons" value */
2358 _ipp_preason_t preasons
= _IPP_PREASON_NONE
;
2359 /* Bits for "printer-state-reasons" values */
2362 count
= ippGetCount(attr
);
2363 for (i
= 0; i
< count
; i
++)
2365 keyword
= ippGetString(attr
, i
, NULL
);
2367 for (j
= 0; j
< (int)(sizeof(_ipp_preasons
) / sizeof(_ipp_preasons
[0])); j
++)
2369 if (!strcmp(keyword
, _ipp_preasons
[j
]))
2382 * 'html_escape()' - Write a HTML-safe string.
2386 html_escape(_ipp_client_t
*client
, /* I - Client */
2387 const char *s
, /* I - String to write */
2388 size_t slen
) /* I - Number of characters to write */
2390 const char *start
, /* Start of segment */
2391 *end
; /* End of string */
2395 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2397 while (*s
&& s
< end
)
2399 if (*s
== '&' || *s
== '<')
2402 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2405 httpWrite2(client
->http
, "&", 5);
2407 httpWrite2(client
->http
, "<", 4);
2416 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2421 * 'html_footer()' - Show the web interface footer.
2423 * This function also writes the trailing 0-length chunk.
2427 html_footer(_ipp_client_t
*client
) /* I - Client */
2433 httpWrite2(client
->http
, "", 0);
2438 * 'html_header()' - Show the web interface header and title.
2442 html_header(_ipp_client_t
*client
, /* I - Client */
2443 const char *title
) /* I - Title */
2449 "<title>%s</title>\n"
2450 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2451 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2452 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2453 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2455 "body { font-family: sans-serif; margin: 0; }\n"
2456 "div.body { padding: 0px 10px 10px; }\n"
2457 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2458 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2459 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2460 "table.form th { text-align: right; }\n"
2461 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2462 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2463 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2464 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2465 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2466 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2467 "table.nav td { margin: 0; text-align: center; }\n"
2468 "td.nav a, td.nav a:active, td.nav a:hover, td.nav a:hover:link, td.nav a:hover:link:visited, td.nav a:link, td.nav a:link:visited, td.nav a:visited { background: inherit; color: inherit; font-size: 80%%; text-decoration: none; }\n"
2469 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2470 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2471 "td.nav:hover { background: #666; color: #fff; }\n"
2472 "td.nav:active { background: #000; color: #ff0; }\n"
2476 "<table class=\"nav\"><tr>"
2477 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2478 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2479 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2481 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2486 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2490 html_printf(_ipp_client_t
*client
, /* I - Client */
2491 const char *format
, /* I - Printf-style format string */
2492 ...) /* I - Additional arguments as needed */
2494 va_list ap
; /* Pointer to arguments */
2495 const char *start
; /* Start of string */
2496 char size
, /* Size character (h, l, L) */
2497 type
; /* Format type character */
2498 int width
, /* Width of field */
2499 prec
; /* Number of characters of precision */
2500 char tformat
[100], /* Temporary format string for sprintf() */
2501 *tptr
, /* Pointer into temporary format */
2502 temp
[1024]; /* Buffer for formatted numbers */
2503 char *s
; /* Pointer to string */
2507 * Loop through the format string, formatting as needed...
2510 va_start(ap
, format
);
2518 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2521 *tptr
++ = *format
++;
2525 httpWrite2(client
->http
, "%", 1);
2530 else if (strchr(" -+#\'", *format
))
2531 *tptr
++ = *format
++;
2536 * Get width from argument...
2540 width
= va_arg(ap
, int);
2542 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2543 tptr
+= strlen(tptr
);
2549 while (isdigit(*format
& 255))
2551 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2554 width
= width
* 10 + *format
++ - '0';
2560 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2568 * Get precision from argument...
2572 prec
= va_arg(ap
, int);
2574 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2575 tptr
+= strlen(tptr
);
2581 while (isdigit(*format
& 255))
2583 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2586 prec
= prec
* 10 + *format
++ - '0';
2591 if (*format
== 'l' && format
[1] == 'l')
2595 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2603 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2605 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2620 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2629 case 'E' : /* Floating point formats */
2634 if ((size_t)(width
+ 2) > sizeof(temp
))
2637 sprintf(temp
, tformat
, va_arg(ap
, double));
2639 httpWrite2(client
->http
, temp
, strlen(temp
));
2642 case 'B' : /* Integer formats */
2650 if ((size_t)(width
+ 2) > sizeof(temp
))
2653 # ifdef HAVE_LONG_LONG
2655 sprintf(temp
, tformat
, va_arg(ap
, long long));
2657 # endif /* HAVE_LONG_LONG */
2659 sprintf(temp
, tformat
, va_arg(ap
, long));
2661 sprintf(temp
, tformat
, va_arg(ap
, int));
2663 httpWrite2(client
->http
, temp
, strlen(temp
));
2666 case 'p' : /* Pointer value */
2667 if ((size_t)(width
+ 2) > sizeof(temp
))
2670 sprintf(temp
, tformat
, va_arg(ap
, void *));
2672 httpWrite2(client
->http
, temp
, strlen(temp
));
2675 case 'c' : /* Character or character array */
2678 temp
[0] = (char)va_arg(ap
, int);
2680 html_escape(client
, temp
, 1);
2683 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2686 case 's' : /* String */
2687 if ((s
= va_arg(ap
, char *)) == NULL
)
2690 html_escape(client
, s
, strlen(s
));
2699 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2706 * 'ipp_acknowledge_document()' - Acknowledge receipt of a document.
2710 ipp_acknowledge_document(
2711 _ipp_client_t
*client
) /* I - Client */
2713 _ipp_device_t
*device
; /* Device */
2714 _ipp_job_t
*job
; /* Job */
2715 ipp_attribute_t
*attr
; /* Attribute */
2718 if ((device
= find_device(client
)) == NULL
)
2720 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
2724 if ((job
= find_job(client
, 0)) == NULL
)
2726 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
2730 if (!job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
2732 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
2736 if ((attr
= ippFindAttribute(client
->request
, "document-number", IPP_TAG_ZERO
)) == NULL
|| ippGetGroupTag(attr
) != IPP_TAG_OPERATION
|| ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) != 1)
2738 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, attr
? "Bad document-number attribute." : "Missing document-number attribute.");
2742 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2747 * 'ipp_acknowledge_identify_printer()' - Acknowledge an identify command.
2751 ipp_acknowledge_identify_printer(
2752 _ipp_client_t
*client
) /* I - Client */
2754 // TODO: Implement this!
2755 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
2760 * 'ipp_acknowledge_job()' - Acknowledge receipt of a job.
2764 ipp_acknowledge_job(
2765 _ipp_client_t
*client
) /* I - Client */
2767 _ipp_device_t
*device
; /* Device */
2768 _ipp_job_t
*job
; /* Job */
2771 if ((device
= find_device(client
)) == NULL
)
2773 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
2777 if ((job
= find_job(client
, 0)) == NULL
)
2779 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
2783 if (job
->dev_uuid
&& strcmp(job
->dev_uuid
, device
->uuid
))
2785 respond_ipp(client
, IPP_STATUS_ERROR_NOT_AUTHORIZED
, "Job not assigned to device.");
2789 if (!(job
->state_reasons
& _IPP_JREASON_JOB_FETCHABLE
))
2791 respond_ipp(client
, _IPP_STATUS_ERROR_NOT_FETCHABLE
, "Job not fetchable.");
2796 job
->dev_uuid
= strdup(device
->uuid
);
2798 job
->state_reasons
&= (_ipp_jreason_t
)~_IPP_JREASON_JOB_FETCHABLE
;
2800 add_event(client
->printer
, job
, _IPP_EVENT_JOB_STATE_CHANGED
, "Job acknowledged.");
2802 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2807 * 'ipp_cancel_job()' - Cancel a job.
2811 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2813 _ipp_job_t
*job
; /* Job information */
2820 if ((job
= find_job(client
, 0)) == NULL
)
2822 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2827 * See if the job is already completed, canceled, or aborted; if so,
2828 * we can't cancel...
2833 case IPP_JSTATE_CANCELED
:
2834 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2835 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2838 case IPP_JSTATE_ABORTED
:
2839 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2840 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2843 case IPP_JSTATE_COMPLETED
:
2844 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2845 "Job #%d is already completed - can\'t cancel.", job
->id
);
2853 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2855 if (job
->state
== IPP_JSTATE_PROCESSING
||
2856 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2860 job
->state
= IPP_JSTATE_CANCELED
;
2861 job
->completed
= time(NULL
);
2864 _cupsRWUnlock(&(client
->printer
->rwlock
));
2866 add_event(client
->printer
, job
, _IPP_EVENT_JOB_COMPLETED
, NULL
);
2868 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2875 * 'ipp_cancel_my_jobs()' - Cancel a user's jobs.
2880 _ipp_client_t
*client
) /* I - Client */
2882 // TODO: Implement this!
2883 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
2888 * 'ipp_cancel_subscription()' - Cancel a subscription.
2892 ipp_cancel_subscription(
2893 _ipp_client_t
*client
) /* I - Client */
2895 _ipp_subscription_t
*sub
; /* Subscription */
2898 if ((sub
= find_subscription(client
, 0)) == NULL
)
2900 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Subscription was not found.");
2904 _cupsRWLockWrite(&client
->printer
->rwlock
);
2905 cupsArrayRemove(client
->printer
->subscriptions
, sub
);
2906 delete_subscription(sub
);
2907 _cupsRWUnlock(&client
->printer
->rwlock
);
2908 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2913 * 'ipp_close_job()' - Close an open job.
2917 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2919 _ipp_job_t
*job
; /* Job information */
2926 if ((job
= find_job(client
, 0)) == NULL
)
2928 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2933 * See if the job is already completed, canceled, or aborted; if so,
2934 * we can't cancel...
2939 case IPP_JSTATE_CANCELED
:
2940 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2941 "Job #%d is canceled - can\'t close.", job
->id
);
2944 case IPP_JSTATE_ABORTED
:
2945 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2946 "Job #%d is aborted - can\'t close.", job
->id
);
2949 case IPP_JSTATE_COMPLETED
:
2950 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2951 "Job #%d is completed - can\'t close.", job
->id
);
2954 case IPP_JSTATE_PROCESSING
:
2955 case IPP_JSTATE_STOPPED
:
2956 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2957 "Job #%d is already closed.", job
->id
);
2961 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2968 * 'ipp_create_job()' - Create a job object.
2972 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2974 _ipp_job_t
*job
; /* New job */
2975 cups_array_t
*ra
; /* Attributes to send in response */
2979 * Validate print job attributes...
2982 if (!valid_job_attributes(client
))
2984 httpFlush(client
->http
);
2989 * Do we have a file to print?
2992 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2994 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2995 "Unexpected document data following request.");
3003 if ((job
= create_job(client
)) == NULL
)
3005 respond_ipp(client
, IPP_STATUS_ERROR_TOO_MANY_JOBS
, "Too many jobs are queued.");
3010 * Return the job info...
3013 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3015 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3016 cupsArrayAdd(ra
, "job-id");
3017 cupsArrayAdd(ra
, "job-state");
3018 cupsArrayAdd(ra
, "job-state-message");
3019 cupsArrayAdd(ra
, "job-state-reasons");
3020 cupsArrayAdd(ra
, "job-uri");
3022 copy_job_attributes(client
, job
, ra
);
3023 cupsArrayDelete(ra
);
3026 * Add any subscriptions...
3030 ipp_create_xxx_subscriptions(client
);
3035 * 'ipp_create_xxx_subscriptions()' - Create job and printer subscriptions.
3039 ipp_create_xxx_subscriptions(
3040 _ipp_client_t
*client
)
3042 _ipp_subscription_t
*sub
; /* Subscription */
3043 ipp_attribute_t
*attr
; /* Subscription attribute */
3044 const char *username
; /* requesting-user-name or
3045 authenticated username */
3046 int num_subs
= 0, /* Number of subscriptions */
3047 ok_subs
= 0; /* Number of good subscriptions */
3051 * For the Create-xxx-Subscriptions operations, queue up a successful-ok
3055 if (ippGetOperation(client
->request
) == IPP_OP_CREATE_JOB_SUBSCRIPTIONS
|| ippGetOperation(client
->request
) == IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
)
3056 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3059 * Get the authenticated user name, if any...
3062 if (client
->username
[0])
3063 username
= client
->username
;
3064 else if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
&& ippGetGroupTag(attr
) == IPP_TAG_OPERATION
&& ippGetCount(attr
) == 1)
3065 username
= ippGetString(attr
, 0, NULL
);
3070 * Skip past the initial attributes to the first subscription group.
3073 attr
= ippFirstAttribute(client
->request
);
3074 while (attr
&& ippGetGroupTag(attr
) != IPP_TAG_SUBSCRIPTION
)
3075 attr
= ippNextAttribute(client
->request
);
3079 _ipp_job_t
*job
= NULL
; /* Job */
3080 const char *attrname
, /* Attribute name */
3082 /* notify-pull-method */
3083 ipp_attribute_t
*notify_attributes
= NULL
,
3084 /* notify-attributes */
3085 *notify_events
= NULL
,
3087 *notify_user_data
= NULL
;
3088 /* notify-user-data */
3089 int interval
= 0, /* notify-time-interval */
3090 lease
= _IPP_NOTIFY_LEASE_DURATION_DEFAULT
;
3091 /* notify-lease-duration */
3092 ipp_status_t status
= IPP_STATUS_OK
;
3093 /* notify-status-code */
3099 if ((attrname
= ippGetName(attr
)) == NULL
)
3102 if (!strcmp(attrname
, "notify-recipient-uri"))
3105 * Push notifications not supported.
3108 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3109 ippCopyAttribute(client
->response
, attr
, 0);
3111 else if (!strcmp(attrname
, "notify-pull-method"))
3113 pullmethod
= ippGetString(attr
, 0, NULL
);
3115 if (ippGetValueTag(attr
) != IPP_TAG_KEYWORD
|| ippGetCount(attr
) != 1 || !pullmethod
|| strcmp(pullmethod
, "ippget"))
3117 ippCopyAttribute(client
->response
, attr
, 0);
3119 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3122 else if (!strcmp(attrname
, "notify-attributes"))
3124 if (ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
3126 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3127 ippCopyAttribute(client
->response
, attr
, 0);
3130 notify_attributes
= attr
;
3132 else if (!strcmp(attrname
, "notify-charset"))
3134 if (ippGetValueTag(attr
) != IPP_TAG_CHARSET
|| ippGetCount(attr
) != 1 ||
3135 (strcmp(ippGetString(attr
, 0, NULL
), "us-ascii") && strcmp(ippGetString(attr
, 0, NULL
), "utf-8")))
3137 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3138 ippCopyAttribute(client
->response
, attr
, 0);
3141 else if (!strcmp(attrname
, "notify-natural-language"))
3143 if (ippGetValueTag(attr
) != IPP_TAG_LANGUAGE
|| ippGetCount(attr
) != 1 || strcmp(ippGetString(attr
, 0, NULL
), "en"))
3145 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3146 ippCopyAttribute(client
->response
, attr
, 0);
3149 else if (!strcmp(attrname
, "notify-user-data"))
3151 int datalen
; /* Length of data */
3153 if (ippGetValueTag(attr
) != IPP_TAG_STRING
|| ippGetCount(attr
) != 1 || !ippGetOctetString(attr
, 0, &datalen
) || datalen
> 63)
3155 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3156 ippCopyAttribute(client
->response
, attr
, 0);
3159 notify_user_data
= attr
;
3161 else if (!strcmp(attrname
, "notify-events"))
3163 if (ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
3165 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3166 ippCopyAttribute(client
->response
, attr
, 0);
3169 notify_events
= attr
;
3171 else if (!strcmp(attrname
, "notify-lease-duration"))
3173 if (ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) < 0)
3175 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3176 ippCopyAttribute(client
->response
, attr
, 0);
3179 lease
= ippGetInteger(attr
, 0);
3181 else if (!strcmp(attrname
, "notify-time-interval"))
3183 if (ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) < 0)
3185 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3186 ippCopyAttribute(client
->response
, attr
, 0);
3189 interval
= ippGetInteger(attr
, 0);
3191 else if (!strcmp(attrname
, "notify-job-id"))
3193 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB_SUBSCRIPTIONS
|| ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 1)
3195 status
= IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
;
3196 ippCopyAttribute(client
->response
, attr
, 0);
3198 else if ((job
= find_job(client
, ippGetInteger(attr
, 0))) == NULL
)
3200 status
= IPP_STATUS_ERROR_NOT_FOUND
;
3201 ippCopyAttribute(client
->response
, attr
, 0);
3205 attr
= ippNextAttribute(client
->request
);
3210 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
, "notify-status-code", status
);
3212 else if (!pullmethod
)
3214 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
, "notify-status-code", IPP_STATUS_ERROR_BAD_REQUEST
);
3218 switch (ippGetOperation(client
->request
))
3220 case IPP_OP_PRINT_JOB
:
3221 case IPP_OP_PRINT_URI
:
3222 case IPP_OP_CREATE_JOB
:
3230 if ((sub
= create_subscription(client
->printer
, job
, interval
, lease
, username
, notify_events
, notify_attributes
, notify_user_data
)) == NULL
)
3232 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
, "notify-subscription-id", sub
->id
);
3236 ippAddInteger(client
->response
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_ENUM
, "notify-status-code", IPP_STATUS_ERROR_INTERNAL
);
3241 ippSetStatusCode(client
->response
, IPP_STATUS_ERROR_IGNORED_ALL_SUBSCRIPTIONS
);
3242 else if (ok_subs
!= num_subs
)
3243 ippSetStatusCode(client
->response
, IPP_STATUS_OK_IGNORED_SUBSCRIPTIONS
);
3248 * 'ipp_deregister_output_device()' - Unregister an output device.
3252 ipp_deregister_output_device(
3253 _ipp_client_t
*client
) /* I - Client */
3255 _ipp_device_t
*device
; /* Device */
3259 * Find the device...
3262 if ((device
= find_device(client
)) == NULL
)
3264 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Output device not found.");
3269 * Remove the device from the printer...
3272 _cupsRWLockWrite(&client
->printer
->rwlock
);
3274 cupsArrayRemove(client
->printer
->devices
, device
);
3276 update_device_attributes_no_lock(client
->printer
);
3277 update_device_state_no_lock(client
->printer
);
3279 _cupsRWUnlock(&client
->printer
->rwlock
);
3282 * Delete the device...
3285 delete_device(device
);
3287 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3292 * 'ipp_fetch_document()' - Download a document.
3297 _ipp_client_t
*client
) /* I - Client */
3299 _ipp_device_t
*device
; /* Device */
3300 _ipp_job_t
*job
; /* Job */
3301 ipp_attribute_t
*attr
; /* Attribute */
3302 int compression
; /* compression */
3303 char filename
[1024]; /* Job filename */
3304 const char *format
; /* document-format */
3307 if ((device
= find_device(client
)) == NULL
)
3309 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
3313 if ((job
= find_job(client
, 0)) == NULL
)
3315 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
3319 if (!job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
3321 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
3325 if ((attr
= ippFindAttribute(client
->request
, "document-number", IPP_TAG_ZERO
)) == NULL
|| ippGetGroupTag(attr
) != IPP_TAG_OPERATION
|| ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) != 1)
3327 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, attr
? "Bad document-number attribute." : "Missing document-number attribute.");
3331 if ((attr
= ippFindAttribute(client
->request
, "compression-accepted", IPP_TAG_KEYWORD
)) != NULL
)
3332 compression
= !strcmp(ippGetString(attr
, 0, NULL
), "gzip");
3336 if ((attr
= ippFindAttribute(client
->request
, "document-format-accepted", IPP_TAG_MIMETYPE
)) != NULL
)
3338 int i
, /* Looping var */
3339 count
= ippGetCount(attr
); /* Number of values */
3342 for (i
= 0; i
< count
; i
++)
3344 format
= ippGetString(attr
, i
, NULL
);
3346 create_job_filename(client
->printer
, job
, NULL
, filename
, sizeof(filename
));
3348 if (!access(filename
, R_OK
))
3354 respond_ipp(client
, _IPP_STATUS_ERROR_NOT_FETCHABLE
, "Document not available in requested format.");
3358 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
3359 format
= ippGetString(attr
, 0, NULL
);
3362 respond_ipp(client
, _IPP_STATUS_ERROR_NOT_FETCHABLE
, "Document format unknown.");
3366 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3367 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
3368 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "compression", NULL
, compression
? "gzip" : "none");
3370 client
->fetch_file
= open(filename
, O_RDONLY
);
3375 * 'ipp_fetch_job()' - Download a job.
3379 ipp_fetch_job(_ipp_client_t
*client
) /* I - Client */
3381 _ipp_device_t
*device
; /* Device */
3382 _ipp_job_t
*job
; /* Job */
3385 if ((device
= find_device(client
)) == NULL
)
3387 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
3391 if ((job
= find_job(client
, 0)) == NULL
)
3393 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
3397 if (job
->dev_uuid
&& strcmp(job
->dev_uuid
, device
->uuid
))
3399 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
3403 if (!(job
->state_reasons
& _IPP_JREASON_JOB_FETCHABLE
))
3405 respond_ipp(client
, _IPP_STATUS_ERROR_NOT_FETCHABLE
, "Job not fetchable.");
3409 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3410 copy_attributes(client
->response
, job
->attrs
, NULL
, IPP_TAG_JOB
, 0);
3415 * 'ipp_get_document_attributes()' - Get the attributes for a document object.
3417 * Note: This implementation only supports single document jobs so we
3418 * synthesize the information for a single document from the job.
3422 ipp_get_document_attributes(
3423 _ipp_client_t
*client
) /* I - Client */
3425 // TODO: Implement this!
3426 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
3431 * 'ipp_get_documents()' - Get the list of documents in a job.
3433 * Note: This implementation only supports single document jobs so we
3434 * synthesize the information for a single document from the job.
3438 ipp_get_documents(_ipp_client_t
*client
)/* I - Client */
3440 // TODO: Implement this!
3441 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
3446 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3450 ipp_get_job_attributes(
3451 _ipp_client_t
*client
) /* I - Client */
3453 _ipp_job_t
*job
; /* Job */
3454 cups_array_t
*ra
; /* requested-attributes */
3457 if ((job
= find_job(client
, 0)) == NULL
)
3459 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3463 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3465 ra
= ippCreateRequestedArray(client
->request
);
3466 copy_job_attributes(client
, job
, ra
);
3467 cupsArrayDelete(ra
);
3472 * 'ipp_get_jobs()' - Get a list of job objects.
3476 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
3478 ipp_attribute_t
*attr
; /* Current attribute */
3479 const char *which_jobs
= NULL
;
3480 /* which-jobs values */
3481 int job_comparison
; /* Job comparison */
3482 ipp_jstate_t job_state
; /* job-state value */
3483 int first_job_id
, /* First job ID */
3484 limit
, /* Maximum number of jobs to return */
3485 count
; /* Number of jobs that match */
3486 const char *username
; /* Username */
3487 _ipp_job_t
*job
; /* Current job pointer */
3488 cups_array_t
*ra
; /* Requested attributes array */
3492 * See if the "which-jobs" attribute have been specified...
3495 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3496 IPP_TAG_KEYWORD
)) != NULL
)
3498 which_jobs
= ippGetString(attr
, 0, NULL
);
3499 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3502 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3504 job_comparison
= -1;
3505 job_state
= IPP_JSTATE_STOPPED
;
3507 else if (!strcmp(which_jobs
, "completed"))
3510 job_state
= IPP_JSTATE_CANCELED
;
3512 else if (!strcmp(which_jobs
, "aborted"))
3515 job_state
= IPP_JSTATE_ABORTED
;
3517 else if (!strcmp(which_jobs
, "all"))
3520 job_state
= IPP_JSTATE_PENDING
;
3522 else if (!strcmp(which_jobs
, "canceled"))
3525 job_state
= IPP_JSTATE_CANCELED
;
3527 else if (!strcmp(which_jobs
, "pending"))
3530 job_state
= IPP_JSTATE_PENDING
;
3532 else if (!strcmp(which_jobs
, "pending-held"))
3535 job_state
= IPP_JSTATE_HELD
;
3537 else if (!strcmp(which_jobs
, "processing"))
3540 job_state
= IPP_JSTATE_PROCESSING
;
3542 else if (!strcmp(which_jobs
, "processing-stopped"))
3545 job_state
= IPP_JSTATE_STOPPED
;
3549 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3550 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3551 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3552 "which-jobs", NULL
, which_jobs
);
3557 * See if they want to limit the number of jobs reported...
3560 if ((attr
= ippFindAttribute(client
->request
, "limit",
3561 IPP_TAG_INTEGER
)) != NULL
)
3563 limit
= ippGetInteger(attr
, 0);
3565 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3570 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3571 IPP_TAG_INTEGER
)) != NULL
)
3573 first_job_id
= ippGetInteger(attr
, 0);
3575 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3582 * See if we only want to see jobs for a specific user...
3587 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3588 IPP_TAG_BOOLEAN
)) != NULL
)
3590 int my_jobs
= ippGetBoolean(attr
, 0);
3592 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3593 my_jobs
? "true" : "false");
3597 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3598 IPP_TAG_NAME
)) == NULL
)
3600 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3601 "Need requesting-user-name with my-jobs.");
3605 username
= ippGetString(attr
, 0, NULL
);
3607 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3608 client
->hostname
, username
);
3613 * OK, build a list of jobs for this printer...
3616 ra
= ippCreateRequestedArray(client
->request
);
3618 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3620 _cupsRWLockRead(&(client
->printer
->rwlock
));
3622 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3623 (limit
<= 0 || count
< limit
) && job
;
3624 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3627 * Filter out jobs that don't match...
3630 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3631 (job_comparison
== 0 && job
->state
!= job_state
) ||
3632 (job_comparison
> 0 && job
->state
< job_state
) ||
3633 job
->id
< first_job_id
||
3634 (username
&& job
->username
&&
3635 strcasecmp(username
, job
->username
)))
3639 ippAddSeparator(client
->response
);
3642 copy_job_attributes(client
, job
, ra
);
3645 cupsArrayDelete(ra
);
3647 _cupsRWUnlock(&(client
->printer
->rwlock
));
3652 * 'ipp_get_notifications()' - Get notification events for one or more subscriptions.
3656 ipp_get_notifications(
3657 _ipp_client_t
*client
) /* I - Client */
3659 ipp_attribute_t
*sub_ids
, /* notify-subscription-ids */
3660 *seq_nums
, /* notify-sequence-numbers */
3661 *notify_wait
; /* Wait for events? */
3662 int i
, /* Looping vars */
3663 count
, /* Number of IDs */
3664 first
= 1, /* First event? */
3665 seq_num
; /* Sequence number */
3666 _ipp_subscription_t
*sub
; /* Current subscription */
3667 ipp_t
*event
; /* Current event */
3670 if ((sub_ids
= ippFindAttribute(client
->request
, "notify-subscription-ids", IPP_TAG_INTEGER
)) == NULL
)
3672 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing notify-subscription-ids attribute.");
3676 count
= ippGetCount(sub_ids
);
3677 seq_nums
= ippFindAttribute(client
->request
, "notify-sequence-numbers", IPP_TAG_INTEGER
);
3678 notify_wait
= ippFindAttribute(client
->request
, "notify-wait", IPP_TAG_BOOLEAN
);
3680 if (seq_nums
&& count
!= ippGetCount(seq_nums
))
3682 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The notify-subscription-ids and notify-sequence-numbers attributes are different lengths.");
3686 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3687 ippAddInteger(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "notify-get-interval", 30);
3689 for (i
= 0; i
< count
; i
++)
3691 if ((sub
= find_subscription(client
, ippGetInteger(sub_ids
, i
))) == NULL
)
3694 seq_num
= ippGetInteger(seq_nums
, i
);
3695 if (seq_num
< sub
->first_sequence
)
3696 seq_num
= sub
->first_sequence
;
3698 if (seq_num
> sub
->last_sequence
)
3701 for (event
= (ipp_t
*)cupsArrayIndex(sub
->events
, seq_num
- sub
->first_sequence
);
3703 event
= (ipp_t
*)cupsArrayNext(sub
->events
))
3708 ippAddSeparator(client
->response
);
3710 ippCopyAttributes(client
->response
, event
, 0, NULL
, NULL
);
3717 * 'ipp_get_output_device_attributes()' - Get attributes for an output device.
3721 ipp_get_output_device_attributes(
3722 _ipp_client_t
*client
) /* I - Client */
3724 // TODO: Implement this!
3725 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Need to implement this.");
3730 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3734 ipp_get_printer_attributes(
3735 _ipp_client_t
*client
) /* I - Client */
3737 cups_array_t
*ra
; /* Requested attributes array */
3738 _ipp_printer_t
*printer
; /* Printer */
3742 * Send the attributes...
3745 ra
= ippCreateRequestedArray(client
->request
);
3746 printer
= client
->printer
;
3748 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3750 _cupsRWLockRead(&(printer
->rwlock
));
3752 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3753 IPP_TAG_CUPS_CONST
);
3754 copy_attributes(client
->response
, printer
->dev_attrs
, ra
, IPP_TAG_ZERO
, IPP_TAG_ZERO
);
3756 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3757 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3759 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3760 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3762 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3763 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3766 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3767 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3768 "printer-state", printer
->state
> printer
->dev_state
? printer
->state
: printer
->dev_state
);
3770 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3771 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3773 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3774 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3776 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3778 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3780 if (printer
->state
> printer
->dev_state
)
3781 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3783 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->dev_state
- IPP_PSTATE_IDLE
]);
3786 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3787 copy_printer_state_reasons(client
->response
, IPP_TAG_PRINTER
, printer
);
3789 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3790 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3792 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3793 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "queued-job-count", cupsArrayCount(printer
->active_jobs
));
3795 _cupsRWUnlock(&(printer
->rwlock
));
3797 cupsArrayDelete(ra
);
3802 * 'ipp_get_printer_supported_values()' - Return the supported values for
3803 * the infrastructure printer.
3807 ipp_get_printer_supported_values(
3808 _ipp_client_t
*client
) /* I - Client */
3810 cups_array_t
*ra
= ippCreateRequestedArray(client
->request
);
3811 /* Requested attributes */
3814 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3816 copy_attributes(client
->response
, client
->printer
->attrs
, ra
, IPP_TAG_PRINTER
, 1);
3818 cupsArrayDelete(ra
);
3823 * 'ipp_get_subscription_attributes()' - Get attributes for a subscription.
3827 ipp_get_subscription_attributes(
3828 _ipp_client_t
*client
) /* I - Client */
3830 _ipp_subscription_t
*sub
; /* Subscription */
3831 cups_array_t
*ra
= ippCreateRequestedArray(client
->request
);
3832 /* Requested attributes */
3835 if ((sub
= find_subscription(client
, 0)) == NULL
)
3837 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Subscription was not found.");
3841 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3842 copy_subscription_attributes(client
, sub
, ra
);
3845 cupsArrayDelete(ra
);
3850 * 'ipp_get_subscriptions()' - Get attributes for all subscriptions.
3854 ipp_get_subscriptions(
3855 _ipp_client_t
*client
) /* I - Client */
3857 _ipp_subscription_t
*sub
; /* Current subscription */
3858 cups_array_t
*ra
= ippCreateRequestedArray(client
->request
);
3859 /* Requested attributes */
3860 int first
= 1; /* First time? */
3863 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3864 _cupsRWLockRead(&client
->printer
->rwlock
);
3865 for (sub
= (_ipp_subscription_t
*)cupsArrayFirst(client
->printer
->subscriptions
);
3867 sub
= (_ipp_subscription_t
*)cupsArrayNext(client
->printer
->subscriptions
))
3872 ippAddSeparator(client
->response
);
3874 copy_subscription_attributes(client
, sub
, ra
);
3877 cupsArrayDelete(ra
);
3882 * 'ipp_identify_printer()' - Beep or display a message.
3886 ipp_identify_printer(
3887 _ipp_client_t
*client
) /* I - Client */
3889 /* TODO: Do something */
3891 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3896 * 'ipp_print_job()' - Create a job object with an attached document.
3900 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
3902 _ipp_job_t
*job
; /* New job */
3903 char filename
[1024], /* Filename buffer */
3904 buffer
[4096]; /* Copy buffer */
3905 ssize_t bytes
; /* Bytes read */
3906 cups_array_t
*ra
; /* Attributes to send in response */
3910 * Validate print job attributes...
3913 if (!valid_job_attributes(client
))
3915 httpFlush(client
->http
);
3920 * Do we have a file to print?
3923 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3925 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3933 if ((job
= create_job(client
)) == NULL
)
3935 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3936 "Currently printing another job.");
3941 * Create a file for the request data...
3944 create_job_filename(client
->printer
, job
, NULL
, filename
, sizeof(filename
));
3947 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3949 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3951 job
->state
= IPP_JSTATE_ABORTED
;
3953 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3954 "Unable to create print file: %s", strerror(errno
));
3958 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3960 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3962 int error
= errno
; /* Write error */
3964 job
->state
= IPP_JSTATE_ABORTED
;
3971 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3972 "Unable to write print file: %s", strerror(error
));
3980 * Got an error while reading the print data, so abort this job.
3983 job
->state
= IPP_JSTATE_ABORTED
;
3990 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3991 "Unable to read print file.");
3997 int error
= errno
; /* Write error */
3999 job
->state
= IPP_JSTATE_ABORTED
;
4004 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4005 "Unable to write print file: %s", strerror(error
));
4010 job
->filename
= strdup(filename
);
4011 job
->state
= IPP_JSTATE_PENDING
;
4014 * Process the job, if possible...
4017 check_jobs(client
->printer
);
4020 * Return the job info...
4023 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4025 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4026 cupsArrayAdd(ra
, "job-id");
4027 cupsArrayAdd(ra
, "job-state");
4028 cupsArrayAdd(ra
, "job-state-message");
4029 cupsArrayAdd(ra
, "job-state-reasons");
4030 cupsArrayAdd(ra
, "job-uri");
4032 copy_job_attributes(client
, job
, ra
);
4033 cupsArrayDelete(ra
);
4036 * Process any pending subscriptions...
4040 ipp_create_xxx_subscriptions(client
);
4045 * 'ipp_print_uri()' - Create a job object with a referenced document.
4049 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
4051 _ipp_job_t
*job
; /* New job */
4052 ipp_attribute_t
*uri
; /* document-uri */
4053 char scheme
[256], /* URI scheme */
4054 userpass
[256], /* Username and password info */
4055 hostname
[256], /* Hostname */
4056 resource
[1024]; /* Resource path */
4057 int port
; /* Port number */
4058 http_uri_status_t uri_status
; /* URI decode status */
4059 http_encryption_t encryption
; /* Encryption to use, if any */
4060 http_t
*http
; /* Connection for http/https URIs */
4061 http_status_t status
; /* Access status for http/https URIs */
4062 int infile
; /* Input file for local file URIs */
4063 char filename
[1024], /* Filename buffer */
4064 buffer
[4096]; /* Copy buffer */
4065 ssize_t bytes
; /* Bytes read */
4066 cups_array_t
*ra
; /* Attributes to send in response */
4067 static const char * const uri_status_strings
[] =
4068 { /* URI decode errors */
4070 "Bad arguments to function.",
4071 "Bad resource in URI.",
4072 "Bad port number in URI.",
4073 "Bad hostname in URI.",
4074 "Bad username in URI.",
4075 "Bad scheme in URI.",
4081 * Validate print job attributes...
4084 if (!valid_job_attributes(client
))
4086 httpFlush(client
->http
);
4091 * Do we have a file to print?
4094 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4096 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4097 "Unexpected document data following request.");
4102 * Do we have a document URI?
4105 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4106 IPP_TAG_URI
)) == NULL
)
4108 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4112 if (ippGetCount(uri
) != 1)
4114 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4115 "Too many document-uri values.");
4119 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4120 scheme
, sizeof(scheme
), userpass
,
4121 sizeof(userpass
), hostname
, sizeof(hostname
),
4122 &port
, resource
, sizeof(resource
));
4123 if (uri_status
< HTTP_URI_STATUS_OK
)
4125 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4126 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4130 if (strcmp(scheme
, "file") &&
4132 strcmp(scheme
, "https") &&
4133 #endif /* HAVE_SSL */
4134 strcmp(scheme
, "http"))
4136 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4137 "URI scheme \"%s\" not supported.", scheme
);
4141 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4143 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4144 "Unable to access URI: %s", strerror(errno
));
4152 if ((job
= create_job(client
)) == NULL
)
4154 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
4155 "Currently printing another job.");
4160 * Create a file for the request data...
4163 if (!strcasecmp(job
->format
, "image/jpeg"))
4164 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4165 client
->printer
->directory
, job
->id
);
4166 else if (!strcasecmp(job
->format
, "image/png"))
4167 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4168 client
->printer
->directory
, job
->id
);
4169 else if (!strcasecmp(job
->format
, "application/pdf"))
4170 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4171 client
->printer
->directory
, job
->id
);
4172 else if (!strcasecmp(job
->format
, "application/postscript"))
4173 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4174 client
->printer
->directory
, job
->id
);
4176 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4177 client
->printer
->directory
, job
->id
);
4179 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4181 job
->state
= IPP_JSTATE_ABORTED
;
4183 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4184 "Unable to create print file: %s", strerror(errno
));
4188 if (!strcmp(scheme
, "file"))
4190 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4192 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4193 "Unable to access URI: %s", strerror(errno
));
4199 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4200 (errno
== EAGAIN
|| errno
== EINTR
))
4202 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4204 int error
= errno
; /* Write error */
4206 job
->state
= IPP_JSTATE_ABORTED
;
4214 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4215 "Unable to write print file: %s", strerror(error
));
4226 if (port
== 443 || !strcmp(scheme
, "https"))
4227 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4229 #endif /* HAVE_SSL */
4230 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4232 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4233 1, 30000, NULL
)) == NULL
)
4235 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4236 "Unable to connect to %s: %s", hostname
,
4237 cupsLastErrorString());
4238 job
->state
= IPP_JSTATE_ABORTED
;
4247 httpClearFields(http
);
4248 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4249 if (httpGet(http
, resource
))
4251 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4252 "Unable to GET URI: %s", strerror(errno
));
4254 job
->state
= IPP_JSTATE_ABORTED
;
4264 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4266 if (status
!= HTTP_STATUS_OK
)
4268 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4269 "Unable to GET URI: %s", httpStatus(status
));
4271 job
->state
= IPP_JSTATE_ABORTED
;
4281 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4283 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4285 int error
= errno
; /* Write error */
4287 job
->state
= IPP_JSTATE_ABORTED
;
4295 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4296 "Unable to write print file: %s", strerror(error
));
4306 int error
= errno
; /* Write error */
4308 job
->state
= IPP_JSTATE_ABORTED
;
4313 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4314 "Unable to write print file: %s", strerror(error
));
4319 job
->filename
= strdup(filename
);
4320 job
->state
= IPP_JSTATE_PENDING
;
4322 /* TODO: Do something different here - only process if the printer is idle */
4324 * Process the job...
4327 check_jobs(client
->printer
);
4330 * Return the job info...
4333 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4335 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4336 cupsArrayAdd(ra
, "job-id");
4337 cupsArrayAdd(ra
, "job-state");
4338 cupsArrayAdd(ra
, "job-state-reasons");
4339 cupsArrayAdd(ra
, "job-uri");
4341 copy_job_attributes(client
, job
, ra
);
4342 cupsArrayDelete(ra
);
4345 * Process any pending subscriptions...
4349 ipp_create_xxx_subscriptions(client
);
4354 * 'ipp_renew_subscription()' - Renew a subscription.
4358 ipp_renew_subscription(
4359 _ipp_client_t
*client
) /* I - Client */
4361 _ipp_subscription_t
*sub
; /* Subscription */
4362 ipp_attribute_t
*attr
; /* notify-lease-duration */
4363 int lease
; /* Lease duration in seconds */
4366 if ((sub
= find_subscription(client
, 0)) == NULL
)
4368 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Subscription was not found.");
4374 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Per-job subscriptions cannot be renewed.");
4378 if ((attr
= ippFindAttribute(client
->request
, "notify-lease-duration", IPP_TAG_ZERO
)) != NULL
)
4380 if (ippGetGroupTag(attr
) != IPP_TAG_SUBSCRIPTION
|| ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) < 0)
4382 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, "Bad notify-lease-duration.");
4386 lease
= ippGetInteger(attr
, 0);
4389 lease
= _IPP_NOTIFY_LEASE_DURATION_DEFAULT
;
4394 sub
->expire
= time(NULL
) + sub
->lease
;
4396 sub
->expire
= INT_MAX
;
4398 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4403 * 'ipp_send_document()' - Add an attached document to a job object created with
4408 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
4410 _ipp_job_t
*job
; /* Job information */
4411 char filename
[1024], /* Filename buffer */
4412 buffer
[4096]; /* Copy buffer */
4413 ssize_t bytes
; /* Bytes read */
4414 ipp_attribute_t
*attr
; /* Current attribute */
4415 cups_array_t
*ra
; /* Attributes to send in response */
4422 if ((job
= find_job(client
, 0)) == NULL
)
4424 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4425 httpFlush(client
->http
);
4430 * See if we already have a document for this job or the job has already
4431 * in a non-pending state...
4434 if (job
->state
> IPP_JSTATE_HELD
)
4436 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4437 "Job is not in a pending state.");
4438 httpFlush(client
->http
);
4441 else if (job
->filename
|| job
->fd
>= 0)
4443 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4444 "Multiple document jobs are not supported.");
4445 httpFlush(client
->http
);
4449 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4450 IPP_TAG_ZERO
)) == NULL
)
4452 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4453 "Missing required last-document attribute.");
4454 httpFlush(client
->http
);
4457 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4458 !ippGetBoolean(attr
, 0))
4460 respond_unsupported(client
, attr
);
4461 httpFlush(client
->http
);
4466 * Validate document attributes...
4469 if (!valid_doc_attributes(client
))
4471 httpFlush(client
->http
);
4475 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4478 * Get the document format for the job...
4481 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4483 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4484 job
->format
= ippGetString(attr
, 0, NULL
);
4485 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4486 job
->format
= ippGetString(attr
, 0, NULL
);
4488 job
->format
= "application/octet-stream";
4491 * Create a file for the request data...
4494 create_job_filename(client
->printer
, job
, NULL
, filename
, sizeof(filename
));
4497 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4499 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4501 _cupsRWUnlock(&(client
->printer
->rwlock
));
4505 job
->state
= IPP_JSTATE_ABORTED
;
4507 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4508 "Unable to create print file: %s", strerror(errno
));
4512 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4514 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4516 int error
= errno
; /* Write error */
4518 job
->state
= IPP_JSTATE_ABORTED
;
4525 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4526 "Unable to write print file: %s", strerror(error
));
4534 * Got an error while reading the print data, so abort this job.
4537 job
->state
= IPP_JSTATE_ABORTED
;
4544 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4545 "Unable to read print file.");
4551 int error
= errno
; /* Write error */
4553 job
->state
= IPP_JSTATE_ABORTED
;
4558 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4559 "Unable to write print file: %s", strerror(error
));
4563 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4566 job
->filename
= strdup(filename
);
4567 job
->state
= IPP_JSTATE_PENDING
;
4569 _cupsRWUnlock(&(client
->printer
->rwlock
));
4572 * Process the job, if possible...
4575 check_jobs(client
->printer
);
4578 * Return the job info...
4581 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4583 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4584 cupsArrayAdd(ra
, "job-id");
4585 cupsArrayAdd(ra
, "job-state");
4586 cupsArrayAdd(ra
, "job-state-reasons");
4587 cupsArrayAdd(ra
, "job-uri");
4589 copy_job_attributes(client
, job
, ra
);
4590 cupsArrayDelete(ra
);
4595 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4600 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
4602 _ipp_job_t
*job
; /* Job information */
4603 ipp_attribute_t
*uri
; /* document-uri */
4604 char scheme
[256], /* URI scheme */
4605 userpass
[256], /* Username and password info */
4606 hostname
[256], /* Hostname */
4607 resource
[1024]; /* Resource path */
4608 int port
; /* Port number */
4609 http_uri_status_t uri_status
; /* URI decode status */
4610 http_encryption_t encryption
; /* Encryption to use, if any */
4611 http_t
*http
; /* Connection for http/https URIs */
4612 http_status_t status
; /* Access status for http/https URIs */
4613 int infile
; /* Input file for local file URIs */
4614 char filename
[1024], /* Filename buffer */
4615 buffer
[4096]; /* Copy buffer */
4616 ssize_t bytes
; /* Bytes read */
4617 ipp_attribute_t
*attr
; /* Current attribute */
4618 cups_array_t
*ra
; /* Attributes to send in response */
4619 static const char * const uri_status_strings
[] =
4620 { /* URI decode errors */
4622 "Bad arguments to function.",
4623 "Bad resource in URI.",
4624 "Bad port number in URI.",
4625 "Bad hostname in URI.",
4626 "Bad username in URI.",
4627 "Bad scheme in URI.",
4636 if ((job
= find_job(client
, 0)) == NULL
)
4638 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4639 httpFlush(client
->http
);
4644 * See if we already have a document for this job or the job has already
4645 * in a non-pending state...
4648 if (job
->state
> IPP_JSTATE_HELD
)
4650 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4651 "Job is not in a pending state.");
4652 httpFlush(client
->http
);
4655 else if (job
->filename
|| job
->fd
>= 0)
4657 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4658 "Multiple document jobs are not supported.");
4659 httpFlush(client
->http
);
4663 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4664 IPP_TAG_ZERO
)) == NULL
)
4666 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4667 "Missing required last-document attribute.");
4668 httpFlush(client
->http
);
4671 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4672 !ippGetBoolean(attr
, 0))
4674 respond_unsupported(client
, attr
);
4675 httpFlush(client
->http
);
4680 * Validate document attributes...
4683 if (!valid_doc_attributes(client
))
4685 httpFlush(client
->http
);
4690 * Do we have a file to print?
4693 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4695 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4696 "Unexpected document data following request.");
4701 * Do we have a document URI?
4704 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4705 IPP_TAG_URI
)) == NULL
)
4707 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4711 if (ippGetCount(uri
) != 1)
4713 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4714 "Too many document-uri values.");
4718 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4719 scheme
, sizeof(scheme
), userpass
,
4720 sizeof(userpass
), hostname
, sizeof(hostname
),
4721 &port
, resource
, sizeof(resource
));
4722 if (uri_status
< HTTP_URI_STATUS_OK
)
4724 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4725 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4729 if (strcmp(scheme
, "file") &&
4731 strcmp(scheme
, "https") &&
4732 #endif /* HAVE_SSL */
4733 strcmp(scheme
, "http"))
4735 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4736 "URI scheme \"%s\" not supported.", scheme
);
4740 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4742 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4743 "Unable to access URI: %s", strerror(errno
));
4748 * Get the document format for the job...
4751 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4753 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4754 IPP_TAG_MIMETYPE
)) != NULL
)
4755 job
->format
= ippGetString(attr
, 0, NULL
);
4757 job
->format
= "application/octet-stream";
4760 * Create a file for the request data...
4763 if (!strcasecmp(job
->format
, "image/jpeg"))
4764 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4765 client
->printer
->directory
, job
->id
);
4766 else if (!strcasecmp(job
->format
, "image/png"))
4767 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4768 client
->printer
->directory
, job
->id
);
4769 else if (!strcasecmp(job
->format
, "application/pdf"))
4770 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4771 client
->printer
->directory
, job
->id
);
4772 else if (!strcasecmp(job
->format
, "application/postscript"))
4773 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4774 client
->printer
->directory
, job
->id
);
4776 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4777 client
->printer
->directory
, job
->id
);
4779 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4781 _cupsRWUnlock(&(client
->printer
->rwlock
));
4785 job
->state
= IPP_JSTATE_ABORTED
;
4787 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4788 "Unable to create print file: %s", strerror(errno
));
4792 if (!strcmp(scheme
, "file"))
4794 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4796 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4797 "Unable to access URI: %s", strerror(errno
));
4803 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4804 (errno
== EAGAIN
|| errno
== EINTR
))
4806 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4808 int error
= errno
; /* Write error */
4810 job
->state
= IPP_JSTATE_ABORTED
;
4818 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4819 "Unable to write print file: %s", strerror(error
));
4830 if (port
== 443 || !strcmp(scheme
, "https"))
4831 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4833 #endif /* HAVE_SSL */
4834 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4836 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4837 1, 30000, NULL
)) == NULL
)
4839 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4840 "Unable to connect to %s: %s", hostname
,
4841 cupsLastErrorString());
4842 job
->state
= IPP_JSTATE_ABORTED
;
4851 httpClearFields(http
);
4852 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4853 if (httpGet(http
, resource
))
4855 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4856 "Unable to GET URI: %s", strerror(errno
));
4858 job
->state
= IPP_JSTATE_ABORTED
;
4868 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4870 if (status
!= HTTP_STATUS_OK
)
4872 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4873 "Unable to GET URI: %s", httpStatus(status
));
4875 job
->state
= IPP_JSTATE_ABORTED
;
4885 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4887 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4889 int error
= errno
; /* Write error */
4891 job
->state
= IPP_JSTATE_ABORTED
;
4899 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4900 "Unable to write print file: %s", strerror(error
));
4910 int error
= errno
; /* Write error */
4912 job
->state
= IPP_JSTATE_ABORTED
;
4917 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4918 "Unable to write print file: %s", strerror(error
));
4922 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4925 job
->filename
= strdup(filename
);
4926 job
->state
= IPP_JSTATE_PENDING
;
4928 _cupsRWUnlock(&(client
->printer
->rwlock
));
4931 * Process the job, if possible...
4934 check_jobs(client
->printer
);
4937 * Return the job info...
4940 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4942 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4943 cupsArrayAdd(ra
, "job-id");
4944 cupsArrayAdd(ra
, "job-state");
4945 cupsArrayAdd(ra
, "job-state-reasons");
4946 cupsArrayAdd(ra
, "job-uri");
4948 copy_job_attributes(client
, job
, ra
);
4949 cupsArrayDelete(ra
);
4954 * 'ipp_update_active_jobs()' - Update the list of active jobs.
4958 ipp_update_active_jobs(
4959 _ipp_client_t
*client
) /* I - Client */
4961 _ipp_device_t
*device
; /* Output device */
4962 _ipp_job_t
*job
; /* Job */
4963 ipp_attribute_t
*job_ids
, /* job-ids */
4964 *job_states
; /* output-device-job-states */
4965 int i
, /* Looping var */
4966 count
, /* Number of values */
4968 /* Number of jobs with different states */
4969 different
[1000],/* Jobs with different states */
4970 num_unsupported
= 0,
4971 /* Number of unsupported job-ids */
4973 /* Unsupported job-ids */
4974 ipp_jstate_t states
[1000]; /* Different job state values */
4978 * Process the job-ids and output-device-job-states values...
4981 if ((device
= find_device(client
)) == NULL
)
4983 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
4987 if ((job_ids
= ippFindAttribute(client
->request
, "job-ids", IPP_TAG_ZERO
)) == NULL
|| ippGetGroupTag(job_ids
) != IPP_TAG_OPERATION
|| ippGetValueTag(job_ids
) != IPP_TAG_INTEGER
)
4989 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, job_ids
? "Bad job-ids attribute." : "Missing required job-ids attribute.");
4993 if ((job_states
= ippFindAttribute(client
->request
, "output-device-job-states", IPP_TAG_ZERO
)) == NULL
|| ippGetGroupTag(job_states
) != IPP_TAG_OPERATION
|| ippGetValueTag(job_states
) != IPP_TAG_ENUM
)
4995 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, job_ids
? "Bad output-device-job-states attribute." : "Missing required output-device-job-states attribute.");
4999 count
= ippGetCount(job_ids
);
5000 if (count
!= ippGetCount(job_states
))
5002 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The job-ids and output-device-job-states attributes do not have the same number of values.");
5006 for (i
= 0; i
< count
; i
++)
5008 if ((job
= find_job(client
, ippGetInteger(job_ids
, i
))) == NULL
|| !job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
5010 if (num_unsupported
< 1000)
5011 unsupported
[num_unsupported
++] = ippGetInteger(job_ids
, i
);
5015 ipp_jstate_t state
= (ipp_jstate_t
)ippGetInteger(job_states
, i
);
5017 if (job
->state
>= IPP_JSTATE_STOPPED
&& state
!= job
->state
)
5019 if (num_different
< 1000)
5021 different
[num_different
] = job
->id
;
5022 states
[num_different
++] = job
->state
;
5026 job
->dev_state
= state
;
5031 * Then look for jobs assigned to the device but not listed...
5034 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
5035 job
&& num_different
< 1000;
5036 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5038 if (job
->dev_uuid
&& !strcmp(job
->dev_uuid
, device
->uuid
) && !ippContainsInteger(job_ids
, job
->id
))
5040 different
[num_different
] = job
->id
;
5041 states
[num_different
++] = job
->state
;
5045 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5047 if (num_different
> 0)
5049 ippAddIntegers(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-ids", num_different
, different
);
5050 ippAddIntegers(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "output-device-job-states", num_different
, (int *)states
);
5053 if (num_unsupported
> 0)
5055 ippAddIntegers(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_INTEGER
, "job-ids", num_unsupported
, unsupported
);
5061 * 'ipp_update_document_status()' - Update the state of a document.
5065 ipp_update_document_status(
5066 _ipp_client_t
*client
) /* I - Client */
5068 _ipp_device_t
*device
; /* Device */
5069 _ipp_job_t
*job
; /* Job */
5070 ipp_attribute_t
*attr
; /* Attribute */
5073 if ((device
= find_device(client
)) == NULL
)
5075 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
5079 if ((job
= find_job(client
, 0)) == NULL
)
5081 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
5085 if (!job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
5087 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
5091 if ((attr
= ippFindAttribute(client
->request
, "document-number", IPP_TAG_ZERO
)) == NULL
|| ippGetGroupTag(attr
) != IPP_TAG_OPERATION
|| ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetCount(attr
) != 1 || ippGetInteger(attr
, 0) != 1)
5093 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, attr
? "Bad document-number attribute." : "Missing document-number attribute.");
5097 if ((attr
= ippFindAttribute(client
->request
, "impressions-completed", IPP_TAG_INTEGER
)) != NULL
)
5099 job
->impcompleted
= ippGetInteger(attr
, 0);
5100 add_event(client
->printer
, job
, _IPP_EVENT_JOB_PROGRESS
, NULL
);
5103 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5108 * 'ipp_update_job_status()' - Update the state of a job.
5112 ipp_update_job_status(
5113 _ipp_client_t
*client
) /* I - Client */
5115 _ipp_device_t
*device
; /* Device */
5116 _ipp_job_t
*job
; /* Job */
5117 ipp_attribute_t
*attr
; /* Attribute */
5118 _ipp_event_t events
= _IPP_EVENT_NONE
;
5122 if ((device
= find_device(client
)) == NULL
)
5124 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Device was not found.");
5128 if ((job
= find_job(client
, 0)) == NULL
)
5130 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job was not found.");
5134 if (!job
->dev_uuid
|| strcmp(job
->dev_uuid
, device
->uuid
))
5136 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job not assigned to device.");
5140 if ((attr
= ippFindAttribute(client
->request
, "job-impressions-completed", IPP_TAG_INTEGER
)) != NULL
)
5142 job
->impcompleted
= ippGetInteger(attr
, 0);
5143 events
|= _IPP_EVENT_JOB_PROGRESS
;
5146 if ((attr
= ippFindAttribute(client
->request
, "output-device-job-state", IPP_TAG_ENUM
)) != NULL
)
5148 job
->dev_state
= (ipp_jstate_t
)ippGetInteger(attr
, 0);
5149 events
|= _IPP_EVENT_JOB_STATE_CHANGED
;
5152 if ((attr
= ippFindAttribute(client
->request
, "output-device-job-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
5154 job
->dev_state_reasons
= get_job_state_reasons_bits(attr
);
5155 events
|= _IPP_EVENT_JOB_STATE_CHANGED
;
5159 add_event(client
->printer
, job
, events
, NULL
);
5161 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5166 * 'ipp_update_output_device_attributes()' - Update the values for an output device.
5170 ipp_update_output_device_attributes(
5171 _ipp_client_t
*client
) /* I - Client */
5173 _ipp_device_t
*device
; /* Device */
5174 ipp_attribute_t
*attr
, /* Current attribute */
5175 *dev_attr
; /* Device attribute */
5176 _ipp_event_t events
= _IPP_EVENT_NONE
;
5177 /* Config/state changed? */
5180 if ((device
= find_device(client
)) == NULL
)
5182 if ((device
= create_device(client
)) == NULL
)
5184 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Unable to add output device.");
5189 _cupsRWLockWrite(&device
->rwlock
);
5191 attr
= ippFirstAttribute(client
->request
);
5192 while (attr
&& ippGetGroupTag(attr
) != IPP_TAG_PRINTER
)
5193 attr
= ippNextAttribute(client
->request
);
5195 for (; attr
; attr
= ippNextAttribute(client
->request
))
5197 const char *attrname
= ippGetName(attr
),
5198 /* Attribute name */
5199 *dotptr
; /* Pointer to dot in name */
5202 * Skip attributes we don't care about...
5208 if (strncmp(attrname
, "copies", 6) && strncmp(attrname
, "document-format", 15) && strncmp(attrname
, "finishings", 10) && strncmp(attrname
, "media", 5) && strncmp(attrname
, "print-", 6) && strncmp(attrname
, "sides", 5) && strncmp(attrname
, "printer-alert", 13) && strncmp(attrname
, "printer-input", 13) && strncmp(attrname
, "printer-output", 14) && strncmp(attrname
, "printer-resolution", 18) && strncmp(attrname
, "pwg-raster", 10) && strncmp(attrname
, "urf-", 4))
5211 if (strncmp(attrname
, "printer-alert", 13) || strncmp(attrname
, "printer-state", 13))
5212 events
|= _IPP_EVENT_PRINTER_CONFIG_CHANGED
;
5214 events
|= _IPP_EVENT_PRINTER_STATE_CHANGED
;
5216 if (!strcmp(attrname
, "media-col-ready") || !strcmp(attrname
, "media-ready"))
5217 events
|= _IPP_EVENT_PRINTER_MEDIA_CHANGED
;
5219 if (!strcmp(attrname
, "finishings-col-ready") || !strcmp(attrname
, "finishings-ready"))
5220 events
|= _IPP_EVENT_PRINTER_FINISHINGS_CHANGED
;
5222 if ((dotptr
= strrchr(attrname
, '.')) != NULL
&& isdigit(dotptr
[1] & 255))
5226 * Sparse representation: name.NNN or name.NNN-NNN
5229 char temp
[256], /* Temporary name string */
5230 *tempptr
; /* Pointer into temporary string */
5231 int low
, high
; /* Low and high numbers in range */
5233 low
= (int)strtol(dotptr
+ 1, (char **)&dotptr
, 10);
5234 if (dotptr
&& *dotptr
== '-')
5235 high
= (int)strtol(dotptr
+ 1, NULL
, 10);
5239 strlcpy(temp
, attrname
, sizeof(temp
));
5240 if ((tempptr
= strrchr(temp
, '.')) != NULL
)
5243 if ((dev_attr
= ippFindAttribute(device
->attrs
, temp
, IPP_TAG_ZERO
)) != NULL
)
5248 respond_unsupported(client
, attr
);
5253 * Regular representation - replace or delete current attribute,
5257 if ((dev_attr
= ippFindAttribute(device
->attrs
, attrname
, IPP_TAG_ZERO
)) != NULL
)
5258 ippDeleteAttribute(device
->attrs
, dev_attr
);
5260 if (ippGetValueTag(attr
) != IPP_TAG_DELETEATTR
)
5261 ippCopyAttribute(device
->attrs
, attr
, 0);
5265 _cupsRWUnlock(&device
->rwlock
);
5269 _cupsRWLockWrite(&client
->printer
->rwlock
);
5270 if (events
& _IPP_EVENT_PRINTER_CONFIG_CHANGED
)
5271 update_device_attributes_no_lock(client
->printer
);
5272 if (events
& _IPP_EVENT_PRINTER_STATE_CHANGED
)
5273 update_device_state_no_lock(client
->printer
);
5274 _cupsRWUnlock(&client
->printer
->rwlock
);
5276 add_event(client
->printer
, NULL
, events
, NULL
);
5282 * 'ipp_validate_document()' - Validate document creation attributes.
5286 ipp_validate_document(
5287 _ipp_client_t
*client
) /* I - Client */
5289 if (valid_doc_attributes(client
))
5290 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5295 * 'ipp_validate_job()' - Validate job creation attributes.
5299 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
5301 if (valid_job_attributes(client
))
5302 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
5308 * 'parse_options()' - Parse URL options into CUPS options.
5310 * The client->options string is destroyed by this function.
5313 static int /* O - Number of options */
5314 parse_options(_ipp_client_t
*client
, /* I - Client */
5315 cups_option_t
**options
) /* O - Options */
5317 char *name
, /* Name */
5319 *next
; /* Next name=value pair */
5320 int num_options
= 0; /* Number of options */
5325 for (name
= client
->options
; name
&& *name
; name
= next
)
5327 if ((value
= strchr(name
, '=')) == NULL
)
5331 if ((next
= strchr(value
, '&')) != NULL
)
5334 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5337 return (num_options
);
5343 * 'process_client()' - Process client requests on a thread.
5346 static void * /* O - Exit status */
5347 process_client(_ipp_client_t
*client
) /* I - Client */
5350 * Loop until we are out of requests or timeout (30 seconds)...
5354 int first_time
= 1; /* First time request? */
5355 #endif /* HAVE_SSL */
5357 while (httpWait(client
->http
, 30000))
5363 * See if we need to negotiate a TLS connection...
5366 char buf
[1]; /* First byte from client */
5368 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5370 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5372 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5374 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5378 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5383 #endif /* HAVE_SSL */
5385 if (!process_http(client
))
5390 * Close the conection to the client and return...
5393 delete_client(client
);
5400 * 'process_http()' - Process a HTTP request.
5403 int /* O - 1 on success, 0 on failure */
5404 process_http(_ipp_client_t
*client
) /* I - Client connection */
5406 char uri
[1024]; /* URI */
5407 http_state_t http_state
; /* HTTP state */
5408 http_status_t http_status
; /* HTTP status */
5409 ipp_state_t ipp_state
; /* State of IPP transfer */
5410 char scheme
[32], /* Method/scheme */
5411 userpass
[128], /* Username:password */
5412 hostname
[HTTP_MAX_HOST
];
5414 int port
; /* Port number */
5415 const char *encoding
; /* Content-Encoding value */
5416 static const char * const http_states
[] =
5417 { /* Strings for logging HTTP method */
5438 * Clear state variables...
5441 ippDelete(client
->request
);
5442 ippDelete(client
->response
);
5444 client
->request
= NULL
;
5445 client
->response
= NULL
;
5446 client
->operation
= HTTP_STATE_WAITING
;
5449 * Read a request from the connection...
5452 while ((http_state
= httpReadRequest(client
->http
, uri
,
5453 sizeof(uri
))) == HTTP_STATE_WAITING
)
5457 * Parse the request line...
5460 if (http_state
== HTTP_STATE_ERROR
)
5462 if (httpError(client
->http
) == EPIPE
)
5463 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5465 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5466 strerror(httpError(client
->http
)));
5470 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5472 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5473 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5476 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5478 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5479 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5483 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5487 * Separate the URI into its components...
5490 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5491 userpass
, sizeof(userpass
),
5492 hostname
, sizeof(hostname
), &port
,
5493 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5494 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5496 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5497 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5501 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5502 *(client
->options
)++ = '\0';
5505 * Process the request...
5508 client
->start
= time(NULL
);
5509 client
->operation
= httpGetState(client
->http
);
5512 * Parse incoming parameters until the status changes...
5515 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5517 if (http_status
!= HTTP_STATUS_OK
)
5519 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5523 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5524 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5527 * HTTP/1.1 and higher require the "Host:" field...
5530 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5535 * Handle HTTP Upgrade...
5538 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5542 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5544 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5547 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5549 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5551 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5555 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5558 #endif /* HAVE_SSL */
5560 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5565 * Handle HTTP Expect...
5568 if (httpGetExpect(client
->http
) &&
5569 (client
->operation
== HTTP_STATE_POST
||
5570 client
->operation
== HTTP_STATE_PUT
))
5572 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5575 * Send 100-continue header...
5578 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5584 * Send 417-expectation-failed header...
5587 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5593 * Handle new transfers...
5596 encoding
= httpGetContentEncoding(client
->http
);
5598 switch (client
->operation
)
5600 case HTTP_STATE_OPTIONS
:
5602 * Do OPTIONS command...
5605 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5607 case HTTP_STATE_HEAD
:
5608 #if 0 /* TODO: Work out icon support */
5609 if (!strcmp(client
->uri
, "/icon.png"))
5610 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5613 if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5614 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5616 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5618 case HTTP_STATE_GET
:
5619 #if 0 /* TODO: Work out icon support */
5620 if (!strcmp(client
->uri
, "/icon.png"))
5623 * Send PNG icon file.
5626 int fd
; /* Icon file */
5627 struct stat fileinfo
; /* Icon file information */
5628 char buffer
[4096]; /* Copy buffer */
5629 ssize_t bytes
; /* Bytes */
5631 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5633 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5634 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5636 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5637 (size_t)fileinfo
.st_size
))
5643 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5644 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5646 httpFlushWrite(client
->http
);
5651 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5655 if (!strcmp(client
->uri
, "/"))
5658 * Show web status page...
5661 _ipp_job_t
*job
; /* Current job */
5662 int i
; /* Looping var */
5663 _ipp_preason_t reason
; /* Current reason */
5664 static const char * const reasons
[] =
5665 { /* Reason strings */
5668 "Input Tray Missing",
5669 "Marker Supply Empty",
5670 "Marker Supply Low",
5671 "Marker Waste Almost Full",
5672 "Marker Waste Full",
5684 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5687 html_header(client
, client
->printer
->name
);
5689 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5690 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5691 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5692 if (client
->printer
->state_reasons
& reason
)
5693 html_printf(client
, "\n<br> %s", reasons
[i
]);
5694 html_printf(client
, "</p>\n");
5696 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5698 _cupsRWLockRead(&(client
->printer
->rwlock
));
5700 html_printf(client
, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>When</th></tr></thead><tbody>\n");
5701 for (job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5703 char when
[256], /* When job queued/started/finished */
5704 hhmmss
[64]; /* Time HH:MM:SS */
5708 case IPP_JSTATE_PENDING
:
5709 case IPP_JSTATE_HELD
:
5710 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5712 case IPP_JSTATE_PROCESSING
:
5713 case IPP_JSTATE_STOPPED
:
5714 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5716 case IPP_JSTATE_ABORTED
:
5717 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5719 case IPP_JSTATE_CANCELED
:
5720 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5722 case IPP_JSTATE_COMPLETED
:
5723 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5727 html_printf(client
, "<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", job
->id
, job
->name
, job
->username
, when
);
5729 html_printf(client
, "</tbody></table>\n");
5731 _cupsRWUnlock(&(client
->printer
->rwlock
));
5733 html_footer(client
);
5737 #if 0 /* TODO: Pull media and supply info from device attrs */
5738 else if (!strcmp(client
->uri
, "/media"))
5741 * Show web media page...
5744 int i
, /* Looping var */
5745 num_options
; /* Number of form options */
5746 cups_option_t
*options
; /* Form options */
5747 static const char * const sizes
[] =
5748 { /* Size strings */
5761 static const char * const types
[] =
5778 static const int sheets
[] = /* Number of sheets */
5787 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5790 html_header(client
, client
->printer
->name
);
5792 if ((num_options
= parse_options(client
, &options
)) > 0)
5795 * WARNING: A real printer/server implementation MUST NOT implement
5796 * media updates via a GET request - GET requests are supposed to be
5797 * idempotent (without side-effects) and we obviously are not
5798 * authenticating access here. This form is provided solely to
5799 * enable testing and development!
5802 const char *val
; /* Form value */
5804 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5805 client
->printer
->main_size
= atoi(val
);
5806 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5807 client
->printer
->main_type
= atoi(val
);
5808 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5809 client
->printer
->main_level
= atoi(val
);
5811 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5812 client
->printer
->envelope_size
= atoi(val
);
5813 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5814 client
->printer
->envelope_level
= atoi(val
);
5816 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5817 client
->printer
->photo_size
= atoi(val
);
5818 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5819 client
->printer
->photo_type
= atoi(val
);
5820 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5821 client
->printer
->photo_level
= atoi(val
);
5823 if ((client
->printer
->main_level
< 100 && client
->printer
->main_level
> 0) || (client
->printer
->envelope_level
< 25 && client
->printer
->envelope_level
> 0) || (client
->printer
->photo_level
< 25 && client
->printer
->photo_level
> 0))
5824 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_LOW
;
5826 client
->printer
->state_reasons
&= (_ipp_preason_t
)~_IPP_PREASON_MEDIA_LOW
;
5828 if ((client
->printer
->main_level
== 0 && client
->printer
->main_size
> _IPP_MEDIA_SIZE_NONE
) || (client
->printer
->envelope_level
== 0 && client
->printer
->envelope_size
> _IPP_MEDIA_SIZE_NONE
) || (client
->printer
->photo_level
== 0 && client
->printer
->photo_size
> _IPP_MEDIA_SIZE_NONE
))
5830 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_EMPTY
;
5831 if (client
->printer
->active_job
)
5832 client
->printer
->state_reasons
|= _IPP_PREASON_MEDIA_NEEDED
;
5835 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MEDIA_EMPTY
| _IPP_PREASON_MEDIA_NEEDED
);
5837 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5840 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5842 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5843 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5844 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5845 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5846 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5847 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5848 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5849 if (!strstr(types
[i
], "Photo"))
5850 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5851 html_printf(client
, "</select> <select name=\"main_level\">");
5852 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5853 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5854 html_printf(client
, "</select></td></tr>\n");
5857 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5858 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5859 if (strstr(sizes
[i
], "Envelope"))
5860 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5861 html_printf(client
, "</select> <select name=\"envelope_level\">");
5862 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5863 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5864 html_printf(client
, "</select></td></tr>\n");
5867 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5868 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5869 if (strstr(sizes
[i
], "Photo"))
5870 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5871 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5872 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5873 if (strstr(types
[i
], "Photo"))
5874 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5875 html_printf(client
, "</select> <select name=\"photo_level\">");
5876 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5877 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5878 html_printf(client
, "</select></td></tr>\n");
5880 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5881 html_footer(client
);
5885 else if (!strcmp(client
->uri
, "/supplies"))
5888 * Show web supplies page...
5891 int i
, j
, /* Looping vars */
5892 num_options
; /* Number of form options */
5893 cups_option_t
*options
; /* Form options */
5894 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5896 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5899 html_header(client
, client
->printer
->name
);
5901 if ((num_options
= parse_options(client
, &options
)) > 0)
5904 * WARNING: A real printer/server implementation MUST NOT implement
5905 * supply updates via a GET request - GET requests are supposed to be
5906 * idempotent (without side-effects) and we obviously are not
5907 * authenticating access here. This form is provided solely to
5908 * enable testing and development!
5911 char name
[64]; /* Form field */
5912 const char *val
; /* Form value */
5914 client
->printer
->state_reasons
&= (_ipp_preason_t
)~(_IPP_PREASON_MARKER_SUPPLY_EMPTY
| _IPP_PREASON_MARKER_SUPPLY_LOW
| _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
| _IPP_PREASON_MARKER_WASTE_FULL
| _IPP_PREASON_TONER_EMPTY
| _IPP_PREASON_TONER_LOW
);
5916 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5918 snprintf(name
, sizeof(name
), "supply_%d", i
);
5919 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5921 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5927 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_EMPTY
;
5928 else if (level
< 10)
5929 client
->printer
->state_reasons
|= _IPP_PREASON_TONER_LOW
;
5934 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_FULL
;
5935 else if (level
> 90)
5936 client
->printer
->state_reasons
|= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL
;
5941 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5944 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5946 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5947 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5949 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5950 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5951 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5952 html_printf(client
, "</select></td></tr>\n");
5954 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5955 html_footer(client
);
5961 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5964 case HTTP_STATE_POST
:
5965 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5969 * Not an IPP request...
5972 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5976 * Read the IPP request...
5979 client
->request
= ippNew();
5981 while ((ipp_state
= ippRead(client
->http
,
5982 client
->request
)) != IPP_STATE_DATA
)
5984 if (ipp_state
== IPP_STATE_ERROR
)
5986 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5987 cupsLastErrorString());
5988 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5994 * Now that we have the IPP request, process the request...
5997 return (process_ipp(client
));
6000 break; /* Anti-compiler-warning-code */
6008 * 'process_ipp()' - Process an IPP request.
6011 static int /* O - 1 on success, 0 on error */
6012 process_ipp(_ipp_client_t
*client
) /* I - Client */
6014 ipp_tag_t group
; /* Current group tag */
6015 ipp_attribute_t
*attr
; /* Current attribute */
6016 ipp_attribute_t
*charset
; /* Character set attribute */
6017 ipp_attribute_t
*language
; /* Language attribute */
6018 ipp_attribute_t
*uri
; /* Printer URI attribute */
6019 int major
, minor
; /* Version number */
6020 const char *name
; /* Name of attribute */
6023 debug_attributes("Request", client
->request
, 1);
6026 * First build an empty response message for this request...
6029 client
->operation_id
= ippGetOperation(client
->request
);
6030 client
->response
= ippNewResponse(client
->request
);
6033 * Then validate the request header and required attributes...
6036 major
= ippGetVersion(client
->request
, &minor
);
6038 if (major
< 1 || major
> 2)
6041 * Return an error, since we only support IPP 1.x and 2.x.
6044 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
6045 "Bad request version number %d.%d.", major
, minor
);
6047 else if (ippGetRequestId(client
->request
) <= 0)
6048 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
6049 ippGetRequestId(client
->request
));
6050 else if (!ippFirstAttribute(client
->request
))
6051 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6052 "No attributes in request.");
6056 * Make sure that the attributes are provided in the correct order and
6057 * don't repeat groups...
6060 for (attr
= ippFirstAttribute(client
->request
),
6061 group
= ippGetGroupTag(attr
);
6063 attr
= ippNextAttribute(client
->request
))
6065 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
6068 * Out of order; return an error...
6071 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6072 "Attribute groups are out of order (%x < %x).",
6073 ippGetGroupTag(attr
), group
);
6077 group
= ippGetGroupTag(attr
);
6083 * Then make sure that the first three attributes are:
6085 * attributes-charset
6086 * attributes-natural-language
6087 * printer-uri/job-uri
6090 attr
= ippFirstAttribute(client
->request
);
6091 name
= ippGetName(attr
);
6092 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
6093 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
6098 attr
= ippNextAttribute(client
->request
);
6099 name
= ippGetName(attr
);
6101 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
6102 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
6107 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
6108 IPP_TAG_URI
)) != NULL
)
6110 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
6111 IPP_TAG_URI
)) != NULL
)
6117 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
6118 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
6121 * Bad character set...
6124 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6125 "Unsupported character set \"%s\".",
6126 ippGetString(charset
, 0, NULL
));
6128 else if (!charset
|| !language
|| !uri
)
6131 * Return an error, since attributes-charset,
6132 * attributes-natural-language, and printer-uri/job-uri are required
6133 * for all operations.
6136 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6137 "Missing required attributes.");
6141 char scheme
[32], /* URI scheme */
6142 userpass
[32], /* Username/password in URI */
6143 host
[256], /* Host name in URI */
6144 resource
[256]; /* Resource path in URI */
6145 int port
; /* Port number in URI */
6147 name
= ippGetName(uri
);
6149 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
6150 scheme
, sizeof(scheme
),
6151 userpass
, sizeof(userpass
),
6152 host
, sizeof(host
), &port
,
6153 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
6154 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6155 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
6156 else if ((!strcmp(name
, "job-uri") && strncmp(resource
, "/ipp/print/", 11)) ||
6157 (!strcmp(name
, "printer-uri") && strcmp(resource
, "/ipp/print")))
6158 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
6159 name
, ippGetString(uri
, 0, NULL
));
6163 * Try processing the operation...
6166 switch ((int)ippGetOperation(client
->request
))
6168 case IPP_OP_PRINT_JOB
:
6169 ipp_print_job(client
);
6172 case IPP_OP_PRINT_URI
:
6173 ipp_print_uri(client
);
6176 case IPP_OP_VALIDATE_JOB
:
6177 ipp_validate_job(client
);
6180 case IPP_OP_CREATE_JOB
:
6181 ipp_create_job(client
);
6184 case IPP_OP_SEND_DOCUMENT
:
6185 ipp_send_document(client
);
6188 case IPP_OP_SEND_URI
:
6189 ipp_send_uri(client
);
6192 case IPP_OP_CANCEL_JOB
:
6193 ipp_cancel_job(client
);
6196 case IPP_OP_CANCEL_MY_JOBS
:
6197 ipp_cancel_my_jobs(client
);
6200 case IPP_OP_GET_JOB_ATTRIBUTES
:
6201 ipp_get_job_attributes(client
);
6204 case IPP_OP_GET_JOBS
:
6205 ipp_get_jobs(client
);
6208 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
6209 ipp_get_printer_attributes(client
);
6212 case IPP_OP_GET_PRINTER_SUPPORTED_VALUES
:
6213 ipp_get_printer_supported_values(client
);
6216 case IPP_OP_CLOSE_JOB
:
6217 ipp_close_job(client
);
6220 case IPP_OP_IDENTIFY_PRINTER
:
6221 ipp_identify_printer(client
);
6224 case IPP_OP_CANCEL_SUBSCRIPTION
:
6225 ipp_cancel_subscription(client
);
6228 case IPP_OP_CREATE_JOB_SUBSCRIPTIONS
:
6229 case IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
:
6230 ipp_create_xxx_subscriptions(client
);
6233 case IPP_OP_GET_NOTIFICATIONS
:
6234 ipp_get_notifications(client
);
6237 case IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES
:
6238 ipp_get_subscription_attributes(client
);
6241 case IPP_OP_GET_SUBSCRIPTIONS
:
6242 ipp_get_subscriptions(client
);
6245 case IPP_OP_RENEW_SUBSCRIPTION
:
6246 ipp_renew_subscription(client
);
6249 case IPP_OP_GET_DOCUMENT_ATTRIBUTES
:
6250 ipp_get_document_attributes(client
);
6253 case IPP_OP_GET_DOCUMENTS
:
6254 ipp_get_documents(client
);
6257 case IPP_OP_VALIDATE_DOCUMENT
:
6258 ipp_validate_document(client
);
6261 case _IPP_OP_ACKNOWLEDGE_DOCUMENT
:
6262 ipp_acknowledge_document(client
);
6265 case _IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER
:
6266 ipp_acknowledge_identify_printer(client
);
6269 case _IPP_OP_ACKNOWLEDGE_JOB
:
6270 ipp_acknowledge_job(client
);
6273 case _IPP_OP_FETCH_DOCUMENT
:
6274 ipp_fetch_document(client
);
6277 case _IPP_OP_FETCH_JOB
:
6278 ipp_fetch_job(client
);
6281 case _IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES
:
6282 ipp_get_output_device_attributes(client
);
6285 case _IPP_OP_UPDATE_ACTIVE_JOBS
:
6286 ipp_update_active_jobs(client
);
6289 case _IPP_OP_UPDATE_DOCUMENT_STATUS
:
6290 ipp_update_document_status(client
);
6293 case _IPP_OP_UPDATE_JOB_STATUS
:
6294 ipp_update_job_status(client
);
6297 case _IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES
:
6298 ipp_update_output_device_attributes(client
);
6301 case _IPP_OP_DEREGISTER_OUTPUT_DEVICE
:
6302 ipp_deregister_output_device(client
);
6306 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
6307 "Operation not supported.");
6316 * Send the HTTP header and return...
6319 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6320 httpFlush(client
->http
); /* Flush trailing (junk) data */
6322 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6323 client
->fetch_file
>= 0 ? 0 : ippLength(client
->response
)));
6328 * 'process_job()' - Process a print job.
6331 static void * /* O - Thread exit status */
6332 process_job(_ipp_job_t
*job
) /* I - Job */
6334 job
->state
= IPP_JSTATE_PROCESSING
;
6335 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6336 job
->processing
= time(NULL
);
6337 job
->printer
->processing_job
= job
;
6339 add_event(job
->printer
, job
, _IPP_EVENT_JOB_STATE_CHANGED
, "Job processing.");
6342 * TODO: Perform any preprocessing needed...
6345 // job->state_reasons |= _IPP_JREASON_JOB_TRANSFORMING;
6346 // job->state_reasons &= ~_IPP_JREASON_JOB_TRANSFORMING;
6349 * Set the state to processing-stopped, fetchable, then send a
6353 job
->state
= IPP_JSTATE_STOPPED
;
6354 job
->state_reasons
|= _IPP_JREASON_JOB_FETCHABLE
;
6356 add_event(job
->printer
, job
, _IPP_EVENT_JOB_STATE_CHANGED
, "Job fetchable.");
6363 * 'respond_http()' - Send a HTTP response.
6366 int /* O - 1 on success, 0 on failure */
6368 _ipp_client_t
*client
, /* I - Client */
6369 http_status_t code
, /* I - HTTP status of response */
6370 const char *content_encoding
, /* I - Content-Encoding of response */
6371 const char *type
, /* I - MIME media type of response */
6372 size_t length
) /* I - Length of response */
6374 char message
[1024]; /* Text message */
6377 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6379 if (code
== HTTP_STATUS_CONTINUE
)
6382 * 100-continue doesn't send any headers...
6385 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6389 * Format an error message...
6392 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6394 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6396 type
= "text/plain";
6397 length
= strlen(message
);
6403 * Send the HTTP response header...
6406 httpClearFields(client
->http
);
6408 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6409 client
->operation
== HTTP_STATE_OPTIONS
)
6410 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6414 if (!strcmp(type
, "text/html"))
6415 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6416 "text/html; charset=utf-8");
6418 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6420 if (content_encoding
)
6421 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6424 httpSetLength(client
->http
, length
);
6426 if (httpWriteResponse(client
->http
, code
) < 0)
6430 * Send the response data...
6436 * Send a plain text message.
6439 if (httpPrintf(client
->http
, "%s", message
) < 0)
6442 if (httpWrite2(client
->http
, "", 0) < 0)
6445 else if (client
->response
)
6448 * Send an IPP response...
6451 debug_attributes("Response", client
->response
, 2);
6453 ippSetState(client
->response
, IPP_STATE_IDLE
);
6455 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6458 if (client
->fetch_file
>= 0)
6460 ssize_t bytes
; /* Bytes read */
6461 char buffer
[32768]; /* Buffer */
6463 if (client
->fetch_compression
)
6464 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, "gzip");
6466 while ((bytes
= read(client
->fetch_file
, buffer
, sizeof(buffer
))) > 0)
6467 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
6469 httpWrite2(client
->http
, "", 0);
6470 close(client
->fetch_file
);
6471 client
->fetch_file
= -1;
6480 * 'respond_ipp()' - Send an IPP response.
6484 respond_ipp(_ipp_client_t
*client
, /* I - Client */
6485 ipp_status_t status
, /* I - status-code */
6486 const char *message
, /* I - printf-style status-message */
6487 ...) /* I - Additional args as needed */
6489 const char *formatted
= NULL
; /* Formatted message */
6492 ippSetStatusCode(client
->response
, status
);
6496 va_list ap
; /* Pointer to additional args */
6497 ipp_attribute_t
*attr
; /* New status-message attribute */
6499 va_start(ap
, message
);
6500 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6501 IPP_TAG_TEXT
)) != NULL
)
6502 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6504 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6505 "status-message", NULL
, message
, ap
);
6508 formatted
= ippGetString(attr
, 0, NULL
);
6512 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6513 ippOpString(client
->operation_id
), ippErrorString(status
),
6516 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6517 ippOpString(client
->operation_id
), ippErrorString(status
));
6522 * 'respond_unsupported()' - Respond with an unsupported attribute.
6526 respond_unsupported(
6527 _ipp_client_t
*client
, /* I - Client */
6528 ipp_attribute_t
*attr
) /* I - Atribute */
6530 ipp_attribute_t
*temp
; /* Copy of attribute */
6533 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6534 "Unsupported %s %s%s value.", ippGetName(attr
),
6535 ippGetCount(attr
) > 1 ? "1setOf " : "",
6536 ippTagString(ippGetValueTag(attr
)));
6538 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6539 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6544 * 'run_printer()' - Run the printer service.
6548 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
6550 int num_fds
; /* Number of file descriptors */
6551 struct pollfd polldata
[3]; /* poll() data */
6552 int timeout
; /* Timeout for poll() */
6553 _ipp_client_t
*client
; /* New client */
6557 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6560 polldata
[0].fd
= printer
->ipv4
;
6561 polldata
[0].events
= POLLIN
;
6563 polldata
[1].fd
= printer
->ipv6
;
6564 polldata
[1].events
= POLLIN
;
6569 * Loop until we are killed or have a hard error...
6574 if (cupsArrayCount(printer
->jobs
))
6579 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6581 perror("poll() failed");
6585 if (polldata
[0].revents
& POLLIN
)
6587 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6589 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6591 perror("Unable to create client thread");
6592 delete_client(client
);
6597 if (polldata
[1].revents
& POLLIN
)
6599 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6601 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
6603 perror("Unable to create client thread");
6604 delete_client(client
);
6610 * Clean out old jobs...
6613 clean_jobs(printer
);
6619 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6623 time_string(time_t tv
, /* I - Time value */
6624 char *buffer
, /* I - Buffer */
6625 size_t bufsize
) /* I - Size of buffer */
6627 struct tm
*curtime
= localtime(&tv
);
6630 strftime(buffer
, bufsize
, "%X", curtime
);
6636 * 'update_device_attributes_no_lock()' - Update the composite device attributes.
6638 * Note: Caller MUST lock the printer object for writing before using.
6642 update_device_attributes_no_lock(
6643 _ipp_printer_t
*printer
) /* I - Printer */
6645 _ipp_device_t
*device
; /* Current device */
6646 ipp_t
*dev_attrs
; /* Device attributes */
6649 /* TODO: Support multiple output devices, icons, etc... */
6650 device
= (_ipp_device_t
*)cupsArrayFirst(printer
->devices
);
6651 dev_attrs
= ippNew();
6654 copy_attributes(dev_attrs
, device
->attrs
, NULL
, IPP_TAG_PRINTER
, 0);
6656 ippDelete(printer
->dev_attrs
);
6657 printer
->dev_attrs
= dev_attrs
;
6659 printer
->config_time
= time(NULL
);
6664 * 'update_device_status_no_lock()' - Update the composite device state.
6666 * Note: Caller MUST lock the printer object for writing before using.
6670 update_device_state_no_lock(
6671 _ipp_printer_t
*printer
) /* I - Printer */
6673 _ipp_device_t
*device
; /* Current device */
6674 ipp_attribute_t
*attr
; /* Current attribute */
6677 /* TODO: Support multiple output devices, icons, etc... */
6678 device
= (_ipp_device_t
*)cupsArrayFirst(printer
->devices
);
6680 if ((attr
= ippFindAttribute(device
->attrs
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
6681 printer
->dev_state
= (ipp_pstate_t
)ippGetInteger(attr
, 0);
6683 printer
->dev_state
= IPP_PSTATE_STOPPED
;
6685 if ((attr
= ippFindAttribute(device
->attrs
, "printer-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
6686 printer
->dev_reasons
= get_printer_state_reasons_bits(attr
);
6688 printer
->dev_reasons
= _IPP_PREASON_PAUSED
;
6690 printer
->state_time
= time(NULL
);
6695 * 'usage()' - Show program usage.
6699 usage(int status
) /* O - Exit status */
6703 puts(CUPS_SVERSION
" - Copyright 2010-2014 by Apple Inc. All rights reserved.");
6707 puts("Usage: ippinfra [options] \"name\"");
6710 printf("-d spool-directory Spool directory "
6711 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6712 puts("-h Show program help");
6713 puts("-k Keep job spool files");
6714 puts("-n hostname Hostname for printer");
6715 puts("-p port Port number (default=auto)");
6716 puts("-u user:pass Set proxy username and password");
6717 puts("-v[vvv] Be (very) verbose");
6724 * 'valid_doc_attributes()' - Determine whether the document attributes are
6727 * When one or more document attributes are invalid, this function adds a
6728 * suitable response and attributes to the unsupported group.
6731 static int /* O - 1 if valid, 0 if not */
6732 valid_doc_attributes(
6733 _ipp_client_t
*client
) /* I - Client */
6735 int valid
= 1; /* Valid attributes? */
6736 ipp_op_t op
= ippGetOperation(client
->request
);
6738 const char *op_name
= ippOpString(op
);
6739 /* IPP operation name */
6740 ipp_attribute_t
*attr
, /* Current attribute */
6741 *supported
; /* xxx-supported attribute */
6742 const char *compression
= NULL
,
6743 /* compression value */
6744 *format
= NULL
; /* document-format value */
6748 * Check operation attributes...
6751 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6754 * If compression is specified, only accept a supported value in a Print-Job
6755 * or Send-Document request...
6758 compression
= ippGetString(attr
, 0, NULL
);
6759 supported
= ippFindAttribute(client
->printer
->attrs
,
6760 "compression-supported", IPP_TAG_KEYWORD
);
6762 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6763 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6764 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6765 op
!= IPP_OP_VALIDATE_JOB
) ||
6766 !ippContainsString(supported
, compression
))
6768 respond_unsupported(client
, attr
);
6773 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6775 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6777 if (strcmp(compression
, "none"))
6780 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6781 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6787 * Is it a format we support?
6790 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6792 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6793 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6795 respond_unsupported(client
, attr
);
6800 format
= ippGetString(attr
, 0, NULL
);
6802 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6803 client
->hostname
, op_name
, format
);
6805 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6810 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6812 format
= "application/octet-stream"; /* Should never happen */
6814 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6817 if (!strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6820 * Auto-type the file using the first 8 bytes of the file...
6823 unsigned char header
[8]; /* First 8 bytes of file */
6825 memset(header
, 0, sizeof(header
));
6826 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6828 if (!memcmp(header
, "%PDF", 4))
6829 format
= "application/pdf";
6830 else if (!memcmp(header
, "%!", 2))
6831 format
= "application/postscript";
6832 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6833 format
= "image/jpeg";
6834 else if (!memcmp(header
, "\211PNG", 4))
6835 format
= "image/png";
6836 else if (!memcmp(header
, "RAS2", 4))
6837 format
= "image/pwg-raster";
6838 else if (!memcmp(header
, "UNIRAST", 8))
6839 format
= "image/urf";
6845 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6846 client
->hostname
, op_name
, format
);
6848 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6852 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6854 respond_unsupported(client
, attr
);
6862 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6863 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6870 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6872 * When one or more job attributes are invalid, this function adds a suitable
6873 * response and attributes to the unsupported group.
6876 static int /* O - 1 if valid, 0 if not */
6877 valid_job_attributes(
6878 _ipp_client_t
*client
) /* I - Client */
6880 int i
, /* Looping var */
6881 valid
= 1; /* Valid attributes? */
6882 ipp_attribute_t
*attr
, /* Current attribute */
6883 *supported
; /* xxx-supported attribute */
6887 * Check operation attributes...
6890 valid
= valid_doc_attributes(client
);
6893 * Check the various job template attributes...
6896 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6898 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6899 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6901 respond_unsupported(client
, attr
);
6906 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6908 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6910 respond_unsupported(client
, attr
);
6915 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6917 if (ippGetCount(attr
) != 1 ||
6918 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6919 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6920 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6921 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6923 respond_unsupported(client
, attr
);
6928 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6930 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6932 respond_unsupported(client
, attr
);
6937 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6939 if (ippGetCount(attr
) != 1 ||
6940 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6941 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6943 respond_unsupported(client
, attr
);
6947 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6950 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6952 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6954 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6955 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6957 respond_unsupported(client
, attr
);
6962 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6964 if (ippGetCount(attr
) != 1 ||
6965 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6966 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6967 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6968 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6970 respond_unsupported(client
, attr
);
6975 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6977 if (ippGetCount(attr
) != 1 ||
6978 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6979 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6980 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6982 respond_unsupported(client
, attr
);
6987 #if 0 /* TODO: Validate media */
6989 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
6991 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
6994 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
6996 respond_unsupported(client
, attr
);
7003 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7005 if (ippGetCount(attr
) != 1 ||
7006 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7008 respond_unsupported(client
, attr
);
7011 /* TODO: check for valid media-col */
7014 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7016 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7017 (strcmp(ippGetString(attr
, 0, NULL
),
7018 "separate-documents-uncollated-copies") &&
7019 strcmp(ippGetString(attr
, 0, NULL
),
7020 "separate-documents-collated-copies")))
7022 respond_unsupported(client
, attr
);
7027 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7029 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7030 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7031 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7033 respond_unsupported(client
, attr
);
7038 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7040 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7042 respond_unsupported(client
, attr
);
7047 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7049 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7050 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7051 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7053 respond_unsupported(client
, attr
);
7058 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7060 supported
= ippFindAttribute(client
->printer
->dev_attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7062 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7065 respond_unsupported(client
, attr
);
7070 int count
, /* Number of supported values */
7071 xdpi
, /* Horizontal resolution for job template attribute */
7072 ydpi
, /* Vertical resolution for job template attribute */
7073 sydpi
; /* Vertical resolution for supported value */
7074 ipp_res_t units
, /* Units for job template attribute */
7075 sunits
; /* Units for supported value */
7077 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7078 count
= ippGetCount(supported
);
7080 for (i
= 0; i
< count
; i
++)
7082 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7088 respond_unsupported(client
, attr
);
7094 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7096 const char *sides
= ippGetString(attr
, 0, NULL
);
7097 /* "sides" value... */
7099 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7101 respond_unsupported(client
, attr
);
7104 else if ((supported
= ippFindAttribute(client
->printer
->dev_attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7106 if (!ippContainsString(supported
, sides
))
7108 respond_unsupported(client
, attr
);
7112 else if (strcmp(sides
, "one-sided"))
7114 respond_unsupported(client
, attr
);