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