2 * IPP Everywhere printer application for CUPS.
4 * Copyright © 2010-2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * Note: This program began life as the "ippserver" sample code that first
10 * appeared in CUPS 1.4. The name has been changed in order to distinguish it
11 * from the PWG's much more ambitious "ippserver" program, which supports
12 * different kinds of IPP services and multiple services per instance - the
13 * "ippeveprinter" program exposes a single print service conforming to the
14 * current IPP Everywhere specification.
18 * Include necessary headers...
21 #include <cups/cups-private.h>
22 #include <cups/ppd-private.h> /* PPD API */
30 # define WEXITSTATUS(s) (s)
31 # include <winsock2.h>
35 extern char **environ
;
37 # include <sys/fcntl.h>
38 # include <sys/wait.h>
44 #elif defined(HAVE_AVAHI)
45 # include <avahi-client/client.h>
46 # include <avahi-client/publish.h>
47 # include <avahi-common/error.h>
48 # include <avahi-common/thread-watch.h>
49 #endif /* HAVE_DNSSD */
50 #ifdef HAVE_SYS_MOUNT_H
51 # include <sys/mount.h>
52 #endif /* HAVE_SYS_MOUNT_H */
53 #ifdef HAVE_SYS_STATFS_H
54 # include <sys/statfs.h>
55 #endif /* HAVE_SYS_STATFS_H */
56 #ifdef HAVE_SYS_STATVFS_H
57 # include <sys/statvfs.h>
58 #endif /* HAVE_SYS_STATVFS_H */
61 #endif /* HAVE_SYS_VFS_H */
68 enum ippeve_preason_e
/* printer-state-reasons bit values */
70 IPPEVE_PREASON_NONE
= 0x0000, /* none */
71 IPPEVE_PREASON_OTHER
= 0x0001, /* other */
72 IPPEVE_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
73 IPPEVE_PREASON_INPUT_TRAY_MISSING
= 0x0004,
74 /* input-tray-missing */
75 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
76 /* marker-supply-empty */
77 IPPEVE_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
78 /* marker-supply-low */
79 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
80 /* marker-waste-almost-full */
81 IPPEVE_PREASON_MARKER_WASTE_FULL
= 0x0040,
82 /* marker-waste-full */
83 IPPEVE_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
84 IPPEVE_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
85 IPPEVE_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
86 IPPEVE_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
87 IPPEVE_PREASON_MOVING_TO_PAUSED
= 0x0800,
88 /* moving-to-paused */
89 IPPEVE_PREASON_PAUSED
= 0x1000, /* paused */
90 IPPEVE_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
91 IPPEVE_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
92 IPPEVE_PREASON_TONER_LOW
= 0x8000 /* toner-low */
94 typedef unsigned int ippeve_preason_t
; /* Bitfield for printer-state-reasons */
95 static const char * const ippeve_preason_strings
[] =
96 { /* Strings for each bit */
97 /* "none" is implied for no bits set */
100 "input-tray-missing",
101 "marker-supply-empty",
103 "marker-waste-almost-full",
116 typedef enum ippeve_media_class_e
118 IPPEVE_GENERAL
, /* General-purpose size */
119 IPPEVE_PHOTO_ONLY
, /* Photo-only size */
120 IPPEVE_ENV_ONLY
/* Envelope-only size */
121 } ippeve_media_class_t
;
123 typedef enum ippeve_media_size_e
125 IPPEVE_MEDIA_SIZE_NONE
= -1,
126 IPPEVE_MEDIA_SIZE_A4
,
127 IPPEVE_MEDIA_SIZE_A5
,
128 IPPEVE_MEDIA_SIZE_A6
,
129 IPPEVE_MEDIA_SIZE_DL
,
130 IPPEVE_MEDIA_SIZE_LEGAL
,
131 IPPEVE_MEDIA_SIZE_LETTER
,
132 IPPEVE_MEDIA_SIZE_COM10
,
133 IPPEVE_MEDIA_SIZE_3x5
,
135 IPPEVE_MEDIA_SIZE_4x6
,
136 IPPEVE_MEDIA_SIZE_5x7
137 } ippeve_media_size_t
;
138 static const char * const media_supported
[] =
139 { /* media-supported values */
140 "iso_a4_210x297mm", /* A4 */
141 "iso_a5_148x210mm", /* A5 */
142 "iso_a6_105x148mm", /* A6 */
143 "iso_dl_110x220mm", /* DL */
144 "na_legal_8.5x14in", /* Legal */
145 "na_letter_8.5x11in", /* Letter */
146 "na_number-10_4.125x9.5in", /* #10 */
147 "na_index-3x5_3x5in", /* 3x5 */
148 "oe_photo-l_3.5x5in", /* L */
149 "na_index-4x6_4x6in", /* 4x6 */
150 "na_5x7_5x7in" /* 5x7 aka 2L */
152 static const int media_col_sizes
[][3] =
153 { /* media-col-database sizes */
154 { 21000, 29700, IPPEVE_GENERAL
}, /* A4 */
155 { 14800, 21000, IPPEVE_PHOTO_ONLY
}, /* A5 */
156 { 10500, 14800, IPPEVE_PHOTO_ONLY
}, /* A6 */
157 { 11000, 22000, IPPEVE_ENV_ONLY
}, /* DL */
158 { 21590, 35560, IPPEVE_GENERAL
}, /* Legal */
159 { 21590, 27940, IPPEVE_GENERAL
}, /* Letter */
160 { 10477, 24130, IPPEVE_ENV_ONLY
}, /* #10 */
161 { 7630, 12700, IPPEVE_PHOTO_ONLY
}, /* 3x5 */
162 { 8890, 12700, IPPEVE_PHOTO_ONLY
}, /* L */
163 { 10160, 15240, IPPEVE_PHOTO_ONLY
}, /* 4x6 */
164 { 12700, 17780, IPPEVE_PHOTO_ONLY
} /* 5x7 aka 2L */
167 typedef enum ippeve_media_source_e
169 IPPEVE_MEDIA_SOURCE_NONE
= -1,
170 IPPEVE_MEDIA_SOURCE_AUTO
,
171 IPPEVE_MEDIA_SOURCE_MAIN
,
172 IPPEVE_MEDIA_SOURCE_MANUAL
,
173 IPPEVE_MEDIA_SOURCE_ENVELOPE
,
174 IPPEVE_MEDIA_SOURCE_PHOTO
175 } ippeve_media_source_t
;
176 static const char * const media_source_supported
[] =
177 /* media-source-supported values */
186 typedef enum ippeve_media_type_e
188 IPPEVE_MEDIA_TYPE_NONE
= -1,
189 IPPEVE_MEDIA_TYPE_AUTO
,
190 IPPEVE_MEDIA_TYPE_CARDSTOCK
,
191 IPPEVE_MEDIA_TYPE_ENVELOPE
,
192 IPPEVE_MEDIA_TYPE_LABELS
,
193 IPPEVE_MEDIA_TYPE_OTHER
,
194 IPPEVE_MEDIA_TYPE_GLOSSY
,
195 IPPEVE_MEDIA_TYPE_HIGH_GLOSS
,
196 IPPEVE_MEDIA_TYPE_MATTE
,
197 IPPEVE_MEDIA_TYPE_SATIN
,
198 IPPEVE_MEDIA_TYPE_SEMI_GLOSS
,
199 IPPEVE_MEDIA_TYPE_STATIONERY
,
200 IPPEVE_MEDIA_TYPE_LETTERHEAD
,
201 IPPEVE_MEDIA_TYPE_TRANSPARENCY
202 } ippeve_media_type_t
;
203 static const char * const media_type_supported
[] =
204 /* media-type-supported values */
211 "photographic-glossy",
212 "photographic-high-gloss",
213 "photographic-matte",
214 "photographic-satin",
215 "photographic-semi-gloss",
217 "stationery-letterhead",
221 typedef enum ippeve_supply_e
223 IPPEVE_SUPPLY_CYAN
, /* Cyan Toner */
224 IPPEVE_SUPPLY_MAGENTA
, /* Magenta Toner */
225 IPPEVE_SUPPLY_YELLOW
, /* Yellow Toner */
226 IPPEVE_SUPPLY_BLACK
, /* Black Toner */
227 IPPEVE_SUPPLY_WASTE
/* Waste Toner */
229 static const char * const printer_supplies
[] =
230 { /* printer-supply-description values */
239 * URL scheme for web resources...
243 # define WEB_SCHEME "https"
245 # define WEB_SCHEME "http"
246 #endif /* HAVE_SSL */
254 typedef DNSServiceRef ippeve_srv_t
; /* Service reference */
255 typedef TXTRecordRef ippeve_txt_t
; /* TXT record */
257 #elif defined(HAVE_AVAHI)
258 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
259 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
262 typedef void *_ipp_srv_t
; /* Service reference */
263 typedef void *_ipp_txt_t
; /* TXT record */
264 #endif /* HAVE_DNSSD */
266 typedef struct ippeve_filter_s
/**** Attribute filter ****/
268 cups_array_t
*ra
; /* Requested attributes */
269 ipp_tag_t group_tag
; /* Group to copy */
272 typedef struct ippeve_job_s ippeve_job_t
;
274 typedef struct ippeve_printer_s
/**** Printer data ****/
276 int ipv4
, /* IPv4 listener */
277 ipv6
; /* IPv6 listener */
278 ippeve_srv_t ipp_ref
, /* Bonjour IPP service */
279 ipps_ref
, /* Bonjour IPPS service */
280 http_ref
, /* Bonjour HTTP service */
281 printer_ref
; /* Bonjour LPD service */
282 char *dnssd_name
, /* printer-dnssd-name */
283 *name
, /* printer-name */
284 *icon
, /* Icon filename */
285 *directory
, /* Spool directory */
286 *hostname
, /* Hostname */
287 *uri
, /* printer-uri-supported */
288 *command
; /* Command to run with job file */
290 size_t urilen
; /* Length of printer URI */
291 ipp_t
*attrs
; /* Static attributes */
292 time_t start_time
; /* Startup time */
293 time_t config_time
; /* printer-config-change-time */
294 ipp_pstate_t state
; /* printer-state value */
295 ippeve_preason_t state_reasons
; /* printer-state-reasons values */
296 time_t state_time
; /* printer-state-change-time */
297 cups_array_t
*jobs
; /* Jobs */
298 ippeve_job_t
*active_job
; /* Current active/pending job */
299 int next_job_id
; /* Next job-id value */
300 _cups_rwlock_t rwlock
; /* Printer lock */
301 ippeve_media_size_t main_size
; /* Ready media */
302 ippeve_media_type_t main_type
;
304 ippeve_media_size_t envelope_size
;
306 ippeve_media_size_t photo_size
;
307 ippeve_media_type_t photo_type
;
309 int supplies
[5]; /* Supply levels (0-100) */
312 struct ippeve_job_s
/**** Job data ****/
315 const char *name
, /* job-name */
316 *username
, /* job-originating-user-name */
317 *format
; /* document-format */
318 ipp_jstate_t state
; /* job-state value */
319 time_t created
, /* time-at-creation value */
320 processing
, /* time-at-processing value */
321 completed
; /* time-at-completed value */
322 int impressions
, /* job-impressions value */
323 impcompleted
; /* job-impressions-completed value */
324 ipp_t
*attrs
; /* Static attributes */
325 int cancel
; /* Non-zero when job canceled */
326 char *filename
; /* Print file name */
327 int fd
; /* Print file descriptor */
328 ippeve_printer_t
*printer
; /* Printer */
331 typedef struct ippeve_client_s
/**** Client data ****/
333 http_t
*http
; /* HTTP connection */
334 ipp_t
*request
, /* IPP request */
335 *response
; /* IPP response */
336 time_t start
; /* Request start time */
337 http_state_t operation
; /* Request operation */
338 ipp_op_t operation_id
; /* IPP operation-id */
339 char uri
[1024], /* Request URI */
340 *options
; /* URI options */
341 http_addr_t addr
; /* Client address */
342 char hostname
[256]; /* Client hostname */
343 ippeve_printer_t
*printer
; /* Printer */
344 ippeve_job_t
*job
; /* Current job, if any */
352 static void clean_jobs(ippeve_printer_t
*printer
);
353 static int compare_jobs(ippeve_job_t
*a
, ippeve_job_t
*b
);
354 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
, ipp_tag_t group_tag
, int quickcopy
);
355 static void copy_job_attributes(ippeve_client_t
*client
, ippeve_job_t
*job
, cups_array_t
*ra
);
356 static ippeve_client_t
*create_client(ippeve_printer_t
*printer
, int sock
);
357 static ippeve_job_t
*create_job(ippeve_client_t
*client
);
358 static int create_listener(const char *name
, int family
, int port
);
359 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
360 static ipp_t
*create_media_size(int width
, int length
);
361 static ippeve_printer_t
*create_printer(const char *servername
, int serverport
, const char *name
, const char *location
, const char *icon
, cups_array_t
*docformats
, const char *subtypes
, const char *directory
, const char *command
, const char *ppdfile
, const char *device_uri
, ipp_t
*attrs
);
362 static void debug_attributes(const char *title
, ipp_t
*ipp
, int response
);
363 static void delete_client(ippeve_client_t
*client
);
364 static void delete_job(ippeve_job_t
*job
);
365 static void delete_printer(ippeve_printer_t
*printer
);
367 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, DNSServiceErrorType errorCode
, const char *name
, const char *regtype
, const char *domain
, ippeve_printer_t
*printer
);
368 #elif defined(HAVE_AVAHI)
369 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
370 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
371 #endif /* HAVE_DNSSD */
372 static void dnssd_init(void);
373 static int filter_cb(ippeve_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
374 static ippeve_job_t
*find_job(ippeve_client_t
*client
);
375 static ipp_t
*get_collection(FILE *fp
, const char *filename
, int *linenum
);
376 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
377 static void html_escape(ippeve_client_t
*client
, const char *s
, size_t slen
);
378 static void html_footer(ippeve_client_t
*client
);
379 static void html_header(ippeve_client_t
*client
, const char *title
);
380 static void html_printf(ippeve_client_t
*client
, const char *format
, ...) _CUPS_FORMAT(2, 3);
381 static void ipp_cancel_job(ippeve_client_t
*client
);
382 static void ipp_close_job(ippeve_client_t
*client
);
383 static void ipp_create_job(ippeve_client_t
*client
);
384 static void ipp_get_job_attributes(ippeve_client_t
*client
);
385 static void ipp_get_jobs(ippeve_client_t
*client
);
386 static void ipp_get_printer_attributes(ippeve_client_t
*client
);
387 static void ipp_identify_printer(ippeve_client_t
*client
);
388 static void ipp_print_job(ippeve_client_t
*client
);
389 static void ipp_print_uri(ippeve_client_t
*client
);
390 static void ipp_send_document(ippeve_client_t
*client
);
391 static void ipp_send_uri(ippeve_client_t
*client
);
392 static void ipp_validate_job(ippeve_client_t
*client
);
393 static ipp_t
*load_ippserver_attributes(const char *servername
, int serverport
, const char *filename
, cups_array_t
*docformats
);
394 static ipp_t
*load_legacy_attributes(const char *make
, const char *model
, int ppm
, int ppm_color
, int duplex
, cups_array_t
*docformats
);
395 static ipp_t
*load_ppd_attributes(const char *ppdfile
, cups_array_t
*docformats
);
396 static int parse_options(ippeve_client_t
*client
, cups_option_t
**options
);
397 static void process_attr_message(ippeve_job_t
*job
, char *message
);
398 static void *process_client(ippeve_client_t
*client
);
399 static int process_http(ippeve_client_t
*client
);
400 static int process_ipp(ippeve_client_t
*client
);
401 static void *process_job(ippeve_job_t
*job
);
402 static void process_state_message(ippeve_job_t
*job
, char *message
);
403 static int register_printer(ippeve_printer_t
*printer
, const char *subtypes
);
404 static int respond_http(ippeve_client_t
*client
, http_status_t code
, const char *content_coding
, const char *type
, size_t length
);
405 static void respond_ipp(ippeve_client_t
*client
, ipp_status_t status
, const char *message
, ...) _CUPS_FORMAT(3, 4);
406 static void respond_unsupported(ippeve_client_t
*client
, ipp_attribute_t
*attr
);
407 static void run_printer(ippeve_printer_t
*printer
);
408 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
409 static void usage(int status
) _CUPS_NORETURN
;
410 static int valid_doc_attributes(ippeve_client_t
*client
);
411 static int valid_job_attributes(ippeve_client_t
*client
);
419 static DNSServiceRef DNSSDMaster
= NULL
;
420 #elif defined(HAVE_AVAHI)
421 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
422 static AvahiClient
*DNSSDClient
= NULL
;
423 #endif /* HAVE_DNSSD */
425 static int KeepFiles
= 0, /* Keep spooled job files? */
426 MaxVersion
= 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
427 Verbosity
= 0; /* Verbosity level */
431 * 'main()' - Main entry to the sample server.
434 int /* O - Exit status */
435 main(int argc
, /* I - Number of command-line args */
436 char *argv
[]) /* I - Command-line arguments */
438 int i
; /* Looping var */
439 const char *opt
, /* Current option character */
440 *attrfile
= NULL
, /* ippserver attributes file */
441 *command
= NULL
, /* Command to run with job files */
442 *icon
= "printer.png", /* Icon file */
444 *keypath
= NULL
, /* Keychain path */
445 #endif /* HAVE_SSL */
446 *location
= "", /* Location of printer */
447 *make
= "Test", /* Manufacturer */
448 *model
= "Printer", /* Model */
449 *name
= NULL
, /* Printer name */
450 *ppdfile
= NULL
, /* PPD file */
451 *subtypes
= "_print"; /* DNS-SD service subtype */
452 int legacy
= 0, /* Legacy mode? */
453 duplex
= 0, /* Duplex mode */
454 ppm
= 10, /* Pages per minute for mono */
455 ppm_color
= 0; /* Pages per minute for color */
456 ipp_t
*attrs
= NULL
; /* Printer attributes */
457 char directory
[1024] = ""; /* Spool directory */
458 cups_array_t
*docformats
= NULL
; /* Supported formats */
459 const char *servername
= NULL
; /* Server host name */
460 int serverport
= 0; /* Server port number (0 = auto) */
461 ippeve_printer_t
*printer
; /* Printer object */
465 * Parse command-line arguments...
468 for (i
= 1; i
< argc
; i
++)
470 if (!strcmp(argv
[i
], "--help"))
474 else if (!strcmp(argv
[i
], "--version"))
479 else if (!strncmp(argv
[i
], "--", 2))
481 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
484 else if (argv
[i
][0] == '-')
486 for (opt
= argv
[i
] + 1; *opt
; opt
++)
490 case '2' : /* -2 (enable 2-sided printing) */
496 case 'K' : /* -K keypath */
503 #endif /* HAVE_SSL */
505 case 'M' : /* -M manufacturer */
514 case 'P' : /* -P filename.ppd */
522 case 'V' : /* -V max-version */
527 if (!strcmp(argv
[i
], "2.0"))
529 else if (!strcmp(argv
[i
], "1.1"))
535 case 'a' : /* -a attributes-file */
543 case 'c' : /* -c command */
551 case 'd' : /* -d spool-directory */
556 strlcpy(directory
, argv
[i
], sizeof(directory
));
559 case 'f' : /* -f type/subtype[,...] */
564 docformats
= _cupsArrayNewStrings(argv
[i
], ',');
568 case 'h' : /* -h (show help) */
571 case 'i' : /* -i icon.png */
579 case 'k' : /* -k (keep files) */
583 case 'l' : /* -l location */
591 case 'm' : /* -m model */
600 case 'n' : /* -n hostname */
605 servername
= argv
[i
];
608 case 'p' : /* -p port */
610 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
613 serverport
= atoi(argv
[i
]);
616 case 'r' : /* -r subtype */
624 case 's' : /* -s speed[,color-speed] */
629 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
635 case 'v' : /* -v (be verbose) */
639 default : /* Unknown */
640 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), argv
[0], *opt
);
651 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
659 if (((ppdfile
!= NULL
) + (attrfile
!= NULL
) + legacy
) > 1)
663 * Apply defaults as needed...
667 docformats
= _cupsArrayNewStrings("application/pdf,image/jpeg,image/pwg-raster", ',');
673 * Windows is almost always used as a single user system, so use a default
674 * port number of 8631.
681 * Use 8000 + UID mod 1000 for the default port number...
684 serverport
= 8000 + ((int)getuid() % 1000);
687 cupsLangPrintf(stderr
, _("Listening on port %d."), serverport
);
692 const char *tmpdir
; /* Temporary directory */
695 if ((tmpdir
= getenv("TEMP")) == NULL
)
697 #elif defined(__APPLE__) && TARGET_OS_OSX
698 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
699 tmpdir
= "/private/tmp";
701 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
705 snprintf(directory
, sizeof(directory
), "%s/ippeveprinter.%d", tmpdir
, (int)getpid());
707 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
709 _cupsLangPrintf(stderr
, _("Unable to create spool directory \"%s\": %s", directory
, strerror(errno
));
714 _cupsLangPrintf(stderr
, _("Using spool directory \"%s\"."), directory
);
718 cupsSetServerCredentials(keypath
, servername
, 1);
719 #endif /* HAVE_SSL */
722 * Initialize DNS-SD...
728 * Create the printer...
732 docformats
= _cupsArrayNewStrings("application/octet-stream", ',');
735 attrs
= load_ippserver_attributes(attrfile
, servername
, serverport
, docformats
);
737 attrs
= load_ppd_attributes(ppdfile
, docformats
);
739 attrs
= load_legacy_attributes(make
, model
, ppm
, ppm_color
, duplex
, docformats
);
741 if ((printer
= create_printer(servername
, serverport
, name
, location
, icon
, docformats
, subtypes
, directory
, command
, attrs
)) == NULL
)
745 * Run the print service...
748 run_printer(printer
);
751 * Destroy the printer and exit...
754 delete_printer(printer
);
761 * 'clean_jobs()' - Clean out old (completed) jobs.
765 clean_jobs(ippeve_printer_t
*printer
) /* I - Printer */
767 ippeve_job_t
*job
; /* Current job */
768 time_t cleantime
; /* Clean time */
771 if (cupsArrayCount(printer
->jobs
) == 0)
774 cleantime
= time(NULL
) - 60;
776 _cupsRWLockWrite(&(printer
->rwlock
));
777 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
);
779 job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
780 if (job
->completed
&& job
->completed
< cleantime
)
782 cupsArrayRemove(printer
->jobs
, job
);
787 _cupsRWUnlock(&(printer
->rwlock
));
792 * 'compare_jobs()' - Compare two jobs.
795 static int /* O - Result of comparison */
796 compare_jobs(ippeve_job_t
*a
, /* I - First job */
797 ippeve_job_t
*b
) /* I - Second job */
799 return (b
->id
- a
->id
);
804 * 'copy_attributes()' - Copy attributes from one request to another.
808 copy_attributes(ipp_t
*to
, /* I - Destination request */
809 ipp_t
*from
, /* I - Source request */
810 cups_array_t
*ra
, /* I - Requested attributes */
811 ipp_tag_t group_tag
, /* I - Group to copy */
812 int quickcopy
) /* I - Do a quick copy? */
814 ippeve_filter_t filter
; /* Filter data */
818 filter
.group_tag
= group_tag
;
820 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
825 * 'copy_job_attrs()' - Copy job attributes to the response.
830 ippeve_client_t
*client
, /* I - Client */
831 ippeve_job_t
*job
, /* I - Job */
832 cups_array_t
*ra
) /* I - requested-attributes */
834 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
836 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
839 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
841 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
844 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
847 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
849 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
852 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
853 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
855 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
856 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
858 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
859 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
861 if (!ra
|| cupsArrayFind(ra
, "job-state"))
862 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
863 "job-state", job
->state
);
865 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
869 case IPP_JSTATE_PENDING
:
870 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
873 case IPP_JSTATE_HELD
:
875 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
876 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
877 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
879 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
882 case IPP_JSTATE_PROCESSING
:
884 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
886 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
889 case IPP_JSTATE_STOPPED
:
890 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
893 case IPP_JSTATE_CANCELED
:
894 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
897 case IPP_JSTATE_ABORTED
:
898 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
901 case IPP_JSTATE_COMPLETED
:
902 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
907 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
911 case IPP_JSTATE_PENDING
:
912 ippAddString(client
->response
, IPP_TAG_JOB
,
913 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
917 case IPP_JSTATE_HELD
:
919 ippAddString(client
->response
, IPP_TAG_JOB
,
920 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
921 "job-state-reasons", NULL
, "job-incoming");
922 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
923 ippAddString(client
->response
, IPP_TAG_JOB
,
924 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
925 "job-state-reasons", NULL
, "job-hold-until-specified");
927 ippAddString(client
->response
, IPP_TAG_JOB
,
928 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
929 "job-state-reasons", NULL
, "job-data-insufficient");
932 case IPP_JSTATE_PROCESSING
:
934 ippAddString(client
->response
, IPP_TAG_JOB
,
935 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
936 "job-state-reasons", NULL
, "processing-to-stop-point");
938 ippAddString(client
->response
, IPP_TAG_JOB
,
939 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
940 "job-state-reasons", NULL
, "job-printing");
943 case IPP_JSTATE_STOPPED
:
944 ippAddString(client
->response
, IPP_TAG_JOB
,
945 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
946 NULL
, "job-stopped");
949 case IPP_JSTATE_CANCELED
:
950 ippAddString(client
->response
, IPP_TAG_JOB
,
951 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
952 NULL
, "job-canceled-by-user");
955 case IPP_JSTATE_ABORTED
:
956 ippAddString(client
->response
, IPP_TAG_JOB
,
957 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
958 NULL
, "aborted-by-system");
961 case IPP_JSTATE_COMPLETED
:
962 ippAddString(client
->response
, IPP_TAG_JOB
,
963 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
964 NULL
, "job-completed-successfully");
969 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
970 ippAddInteger(client
->response
, IPP_TAG_JOB
,
971 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
972 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
974 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
975 ippAddInteger(client
->response
, IPP_TAG_JOB
,
976 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
977 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
982 * 'create_client()' - Accept a new network connection and create a client
986 static ippeve_client_t
* /* O - Client */
987 create_client(ippeve_printer_t
*printer
, /* I - Printer */
988 int sock
) /* I - Listen socket */
990 ippeve_client_t
*client
; /* Client */
993 if ((client
= calloc(1, sizeof(ippeve_client_t
))) == NULL
)
995 perror("Unable to allocate memory for client");
999 client
->printer
= printer
;
1002 * Accept the client and get the remote address...
1005 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
1007 perror("Unable to accept client connection");
1014 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
1017 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
1024 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1028 static ippeve_job_t
* /* O - Job */
1029 create_job(ippeve_client_t
*client
) /* I - Client */
1031 ippeve_job_t
*job
; /* Job */
1032 ipp_attribute_t
*attr
; /* Job attribute */
1033 char uri
[1024], /* job-uri value */
1034 uuid
[64]; /* job-uuid value */
1037 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1038 if (client
->printer
->active_job
&&
1039 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
1042 * Only accept a single job at a time...
1045 _cupsRWUnlock(&(client
->printer
->rwlock
));
1050 * Allocate and initialize the job object...
1053 if ((job
= calloc(1, sizeof(ippeve_job_t
))) == NULL
)
1055 perror("Unable to allocate memory for job");
1059 job
->printer
= client
->printer
;
1060 job
->attrs
= ippNew();
1061 job
->state
= IPP_JSTATE_HELD
;
1065 * Copy all of the job attributes...
1068 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1071 * Get the requesting-user-name, document format, and priority...
1074 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1075 job
->username
= ippGetString(attr
, 0, NULL
);
1077 job
->username
= "anonymous";
1079 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1081 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1083 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1084 job
->format
= ippGetString(attr
, 0, NULL
);
1085 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1086 job
->format
= ippGetString(attr
, 0, NULL
);
1088 job
->format
= "application/octet-stream";
1091 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1092 job
->impressions
= ippGetInteger(attr
, 0);
1094 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1095 job
->name
= ippGetString(attr
, 0, NULL
);
1098 * Add job description attributes and add to the jobs array...
1101 job
->id
= client
->printer
->next_job_id
++;
1103 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1104 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1106 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1107 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1108 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1109 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1110 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1111 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1113 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1114 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1116 cupsArrayAdd(client
->printer
->jobs
, job
);
1117 client
->printer
->active_job
= job
;
1119 _cupsRWUnlock(&(client
->printer
->rwlock
));
1126 * 'create_job_filename()' - Create the filename for a document in a job.
1129 static void create_job_filename(
1130 ippeve_printer_t
*printer
, /* I - Printer */
1131 ippeve_job_t
*job
, /* I - Job */
1132 char *fname
, /* I - Filename buffer */
1133 size_t fnamesize
) /* I - Size of filename buffer */
1135 char name
[256], /* "Safe" filename */
1136 *nameptr
; /* Pointer into filename */
1137 const char *ext
, /* Filename extension */
1138 *job_name
; /* job-name value */
1139 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1143 * Make a name from the job-name attribute...
1146 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1147 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1149 job_name
= "untitled";
1151 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1152 if (isalnum(*job_name
& 255) || *job_name
== '-')
1153 *nameptr
++ = (char)tolower(*job_name
& 255);
1160 * Figure out the extension...
1163 if (!strcasecmp(job
->format
, "image/jpeg"))
1165 else if (!strcasecmp(job
->format
, "image/png"))
1167 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1169 else if (!strcasecmp(job
->format
, "image/urf"))
1171 else if (!strcasecmp(job
->format
, "application/pdf"))
1173 else if (!strcasecmp(job
->format
, "application/postscript"))
1179 * Create a filename with the job-id, job-name, and document-format (extension)...
1182 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1187 * 'create_listener()' - Create a listener socket.
1190 static int /* O - Listener socket or -1 on error */
1191 create_listener(const char *name
, /* I - Host name (`NULL` for default) */
1192 int family
, /* I - Address family */
1193 int port
) /* I - Port number */
1195 int sock
; /* Listener socket */
1196 http_addrlist_t
*addrlist
; /* Listen address */
1197 char service
[255]; /* Service port */
1200 snprintf(service
, sizeof(service
), "%d", port
);
1201 if ((addrlist
= httpAddrGetList(name
, family
, service
)) == NULL
)
1204 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1206 httpAddrFreeList(addrlist
);
1213 * 'create_media_col()' - Create a media-col value.
1216 static ipp_t
* /* O - media-col collection */
1217 create_media_col(const char *media
, /* I - Media name */
1218 const char *source
, /* I - Media source */
1219 const char *type
, /* I - Media type */
1220 int width
, /* I - x-dimension in 2540ths */
1221 int length
, /* I - y-dimension in 2540ths */
1222 int margins
) /* I - Value for margins */
1224 ipp_t
*media_col
= ippNew(), /* media-col value */
1225 *media_size
= create_media_size(width
, length
);
1226 /* media-size value */
1227 char media_key
[256]; /* media-key value */
1231 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1233 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1235 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1237 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1239 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1241 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1242 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1243 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1244 "media-bottom-margin", margins
);
1245 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1246 "media-left-margin", margins
);
1247 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1248 "media-right-margin", margins
);
1249 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1250 "media-top-margin", margins
);
1252 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1254 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1256 ippDelete(media_size
);
1263 * 'create_media_size()' - Create a media-size value.
1266 static ipp_t
* /* O - media-col collection */
1267 create_media_size(int width
, /* I - x-dimension in 2540ths */
1268 int length
) /* I - y-dimension in 2540ths */
1270 ipp_t
*media_size
= ippNew(); /* media-size value */
1273 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1275 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1278 return (media_size
);
1283 * 'create_printer()' - Create, register, and listen for connections to a
1287 static ippeve_printer_t
* /* O - Printer */
1289 const char *servername
, /* I - Server hostname (NULL for default) */
1290 int serverport
, /* I - Server port */
1291 const char *name
, /* I - printer-name */
1292 const char *location
, /* I - printer-location */
1293 const char *icon
, /* I - printer-icons */
1294 cups_array_t
*docformats
, /* I - document-format-supported */
1295 const char *subtypes
, /* I - Bonjour service subtype(s) */
1296 const char *directory
, /* I - Spool directory */
1297 const char *command
, /* I - Command to run on job files, if any */
1298 const char *ppdfile
, /* I - PPD file, if any */
1299 const char *device_uri
, /* I - Output device, if any */
1300 ipp_t
*attrs
) /* I - Capability attributes */
1302 int i
, j
; /* Looping vars */
1303 ippeve_printer_t
*printer
; /* Printer */
1305 char path
[1024]; /* Full path to command */
1306 #endif /* !_WIN32 */
1307 char uri
[1024], /* Printer URI */
1309 securi
[1024], /* Secure printer URI */
1310 *uris
[2], /* All URIs */
1311 #endif /* HAVE_SSL */
1312 icons
[1024], /* printer-icons URI */
1313 adminurl
[1024], /* printer-more-info URI */
1314 supplyurl
[1024],/* printer-supply-info-uri URI */
1315 device_id
[1024],/* printer-device-id */
1316 make_model
[128],/* printer-make-and-model */
1317 uuid
[128]; /* printer-uuid */
1318 int num_formats
; /* Number of document-format-supported values */
1319 char *defformat
, /* document-format-default value */
1320 *formats
[100], /* document-format-supported values */
1321 *ptr
; /* Pointer into string */
1322 const char *prefix
; /* Prefix string */
1323 int num_database
; /* Number of database values */
1324 ipp_attribute_t
*media_col_database
,
1325 /* media-col-database value */
1326 *media_size_supported
;
1327 /* media-size-supported value */
1328 ipp_t
*media_col_default
;
1329 /* media-col-default value */
1330 int media_col_index
;/* Current media-col-database value */
1331 int k_supported
; /* Maximum file size supported */
1333 struct statvfs spoolinfo
; /* FS info for spool directory */
1334 double spoolsize
; /* FS size */
1335 #elif defined(HAVE_STATFS)
1336 struct statfs spoolinfo
; /* FS info for spool directory */
1337 double spoolsize
; /* FS size */
1338 #endif /* HAVE_STATVFS */
1339 static const char * const versions
[] =/* ipp-versions-supported values */
1344 static const char * const features
[] =/* ipp-features-supported values */
1348 static const int ops
[] = /* operations-supported values */
1352 IPP_OP_VALIDATE_JOB
,
1354 IPP_OP_SEND_DOCUMENT
,
1357 IPP_OP_GET_JOB_ATTRIBUTES
,
1359 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1360 IPP_OP_CANCEL_MY_JOBS
,
1362 IPP_OP_IDENTIFY_PRINTER
1364 static const char * const charsets
[] =/* charset-supported values */
1369 static const char * const compressions
[] =/* compression-supported values */
1374 #endif /* HAVE_LIBZ */
1377 static const char * const identify_actions
[] =
1382 static const char * const job_creation
[] =
1383 { /* job-creation-attributes-supported values */
1385 "ipp-attribute-fidelity",
1391 "multiple-document-handling",
1392 "orientation-requested",
1396 static const char * const media_col_supported
[] =
1397 { /* media-col-supported values */
1398 "media-bottom-margin",
1399 "media-left-margin",
1400 "media-right-margin",
1406 static const char * const multiple_document_handling
[] =
1407 { /* multiple-document-handling-supported values */
1408 "separate-documents-uncollated-copies",
1409 "separate-documents-collated-copies"
1411 static const char * const overrides
[] =
1412 { /* overrides-supported */
1416 static const char * const reference_uri_schemes_supported
[] =
1417 { /* reference-uri-schemes-supported */
1423 #endif /* HAVE_SSL */
1426 static const char * const uri_authentication_supported
[] =
1427 { /* uri-authentication-supported values */
1431 static const char * const uri_security_supported
[] =
1432 { /* uri-security-supported values */
1436 #endif /* HAVE_SSL */
1437 static const char * const which_jobs
[] =
1438 { /* which-jobs-supported values */
1447 "processing-stopped"
1453 * If a command was specified, make sure it exists and is executable...
1458 if (*command
== '/' || !strncmp(command
, "./", 2))
1460 if (access(command
, X_OK
))
1462 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1468 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1470 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1477 #endif /* !_WIN32 */
1480 * Allocate memory for the printer...
1483 if ((printer
= calloc(1, sizeof(ippeve_printer_t
))) == NULL
)
1485 perror("ippserver: Unable to allocate memory for printer");
1491 printer
->name
= strdup(name
);
1492 printer
->dnssd_name
= strdup(printer
->name
);
1493 printer
->command
= command
? strdup(command
) : NULL
;
1494 printer
->directory
= strdup(directory
);
1495 printer
->hostname
= strdup(servername
);
1496 printer
->port
= port
;
1497 printer
->start_time
= time(NULL
);
1498 printer
->config_time
= printer
->start_time
;
1499 printer
->state
= IPP_PSTATE_IDLE
;
1500 printer
->state_reasons
= IPPEVE_PREASON_NONE
;
1501 printer
->state_time
= printer
->start_time
;
1502 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1503 printer
->next_job_id
= 1;
1505 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1506 printer
->uri
= strdup(uri
);
1507 printer
->urilen
= strlen(uri
);
1510 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1511 #endif /* HAVE_SSL */
1514 printer
->icon
= strdup(icon
);
1516 printer
->main_size
= IPPEVE_MEDIA_SIZE_A4
;
1517 printer
->main_type
= IPPEVE_MEDIA_TYPE_STATIONERY
;
1518 printer
->main_level
= 500;
1520 printer
->envelope_size
= IPPEVE_MEDIA_SIZE_NONE
;
1521 printer
->envelope_level
= 0;
1523 printer
->photo_size
= IPPEVE_MEDIA_SIZE_NONE
;
1524 printer
->photo_type
= IPPEVE_MEDIA_TYPE_NONE
;
1525 printer
->photo_level
= 0;
1527 printer
->supplies
[IPPEVE_SUPPLY_CYAN
] = 100;
1528 printer
->supplies
[IPPEVE_SUPPLY_MAGENTA
] = 100;
1529 printer
->supplies
[IPPEVE_SUPPLY_YELLOW
] = 100;
1530 printer
->supplies
[IPPEVE_SUPPLY_BLACK
] = 100;
1531 printer
->supplies
[IPPEVE_SUPPLY_WASTE
] = 0;
1533 _cupsRWInit(&(printer
->rwlock
));
1536 * Create the listener sockets...
1539 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1541 perror("Unable to create IPv4 listener");
1545 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1547 perror("Unable to create IPv6 listener");
1552 * Prepare values for the printer attributes...
1555 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1556 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1557 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1561 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1562 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1563 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1566 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1569 formats
[0] = strdup(docformats
);
1570 defformat
= formats
[0];
1571 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1574 formats
[num_formats
++] = ptr
;
1576 if (!strcasecmp(ptr
, "application/octet-stream"))
1580 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1581 ptr
= device_id
+ strlen(device_id
);
1583 for (i
= 0; i
< num_formats
; i
++)
1585 if (!strcasecmp(formats
[i
], "application/pdf"))
1586 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1587 else if (!strcasecmp(formats
[i
], "application/postscript"))
1588 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1589 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1590 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1591 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1592 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1593 else if (!strcasecmp(formats
[i
], "image/png"))
1594 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1595 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1596 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1601 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1608 * Get the maximum spool size based on the size of the filesystem used for
1609 * the spool directory. If the host OS doesn't support the statfs call
1610 * or the filesystem is larger than 2TiB, always report INT_MAX.
1614 if (statvfs(printer
->directory
, &spoolinfo
))
1615 k_supported
= INT_MAX
;
1616 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1617 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1618 k_supported
= INT_MAX
;
1620 k_supported
= (int)spoolsize
;
1622 #elif defined(HAVE_STATFS)
1623 if (statfs(printer
->directory
, &spoolinfo
))
1624 k_supported
= INT_MAX
;
1625 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1626 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1627 k_supported
= INT_MAX
;
1629 k_supported
= (int)spoolsize
;
1632 k_supported
= INT_MAX
;
1633 #endif /* HAVE_STATVFS */
1636 * Create the printer attributes. This list of attributes is sorted to improve
1637 * performance when the client provides a requested-attributes attribute...
1640 printer
->attrs
= ippNew();
1643 load_attributes(attrfile
, printer
->attrs
);
1645 /* charset-configured */
1646 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1648 /* charset-supported */
1649 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1651 /* color-supported */
1652 if (!ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_ZERO
))
1653 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
1655 /* compression-supported */
1656 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1657 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1659 /* copies-default */
1660 if (!ippFindAttribute(printer
->attrs
, "copies-default", IPP_TAG_ZERO
))
1661 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
1663 /* copies-supported */
1664 if (!ippFindAttribute(printer
->attrs
, "copies-supported", IPP_TAG_ZERO
))
1665 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1667 /* document-format-default */
1668 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1669 "document-format-default", NULL
, defformat
);
1671 /* document-format-supported */
1672 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1673 "document-format-supported", num_formats
, NULL
,
1674 (const char * const *)formats
);
1676 /* document-password-supported */
1677 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
1678 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1680 /* finishings-default */
1681 if (!ippFindAttribute(printer
->attrs
, "finishings-default", IPP_TAG_ZERO
))
1682 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
1684 /* finishings-supported */
1685 if (!ippFindAttribute(printer
->attrs
, "finishings-supported", IPP_TAG_ZERO
))
1686 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
1688 /* generated-natural-language-supported */
1689 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1691 /* identify-actions-default */
1692 if (!ippFindAttribute(printer
->attrs
, "identify-actions-default", IPP_TAG_ZERO
))
1693 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1695 /* identify-actions-supported */
1696 if (!ippFindAttribute(printer
->attrs
, "identify-actions-supported", IPP_TAG_ZERO
))
1697 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-supported", sizeof(identify_actions
) / sizeof(identify_actions
[0]), NULL
, identify_actions
);
1699 /* ipp-features-supported */
1700 if (!ippFindAttribute(printer
->attrs
, "ipp-features-supported", IPP_TAG_ZERO
))
1701 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1703 /* ipp-versions-supported */
1704 if (!ippFindAttribute(printer
->attrs
, "ipp-versions-supported", IPP_TAG_ZERO
))
1706 int num_versions
= MaxVersion
== 11 ? 1 : MaxVersion
== 20 ? 2 : MaxVersion
== 21 ? 3 : 4;
1707 /* Number of supported versions */
1709 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", num_versions
, NULL
, versions
);
1712 /* job-account-id-default */
1713 if (!ippFindAttribute(printer
->attrs
, "job-account-id-default", IPP_TAG_ZERO
))
1714 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1716 /* job-account-id-supported */
1717 if (!ippFindAttribute(printer
->attrs
, "job-account-id-supported", IPP_TAG_ZERO
))
1718 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1720 /* job-accounting-user-id-default */
1721 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-default", IPP_TAG_ZERO
))
1722 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1724 /* job-accounting-user-id-supported */
1725 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-supported", IPP_TAG_ZERO
))
1726 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1728 /* job-creation-attributes-supported */
1729 if (!ippFindAttribute(printer
->attrs
, "job-creation-attributes-supported", IPP_TAG_ZERO
))
1730 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-creation-attributes-supported", sizeof(job_creation
) / sizeof(job_creation
[0]), NULL
, job_creation
);
1732 /* job-ids-supported */
1733 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1735 /* job-k-octets-supported */
1736 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1739 /* job-password-supported */
1740 if (!ippFindAttribute(printer
->attrs
, "job-password-supported", IPP_TAG_ZERO
))
1741 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-password-supported", 4);
1743 /* job-priority-default */
1744 if (!ippFindAttribute(printer
->attrs
, "job-priority-default", IPP_TAG_ZERO
))
1745 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1747 /* job-priority-supported */
1748 if (!ippFindAttribute(printer
->attrs
, "job-priority-supported", IPP_TAG_ZERO
))
1749 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 100);
1751 /* job-sheets-default */
1752 if (!ippFindAttribute(printer
->attrs
, "job-sheets-default", IPP_TAG_ZERO
))
1753 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1755 /* job-sheets-supported */
1756 if (!ippFindAttribute(printer
->attrs
, "job-sheets-supported", IPP_TAG_ZERO
))
1757 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1759 /* media-bottom-margin-supported */
1760 if (!ippFindAttribute(printer
->attrs
, "media-bottom-margin-supported", IPP_TAG_ZERO
))
1761 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin-supported", (int)(sizeof(media_xxx_margin_supported
) / sizeof(media_xxx_margin_supported
[0])), media_xxx_margin_supported
);
1763 /* media-col-database */
1764 if (!ippFindAttribute(printer
->attrs
, "media-col-database", IPP_TAG_ZERO
))
1766 for (num_database
= 0, i
= 0;
1767 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1770 if (media_col_sizes
[i
][2] == IPPEVE_ENV_ONLY
)
1771 num_database
+= 3; /* auto + manual + envelope */
1772 else if (media_col_sizes
[i
][2] == IPPEVE_PHOTO_ONLY
)
1773 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1775 num_database
+= 2; /* Regular + borderless */
1778 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
1779 for (media_col_index
= 0, i
= 0;
1780 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1783 switch (media_col_sizes
[i
][2])
1785 case IPPEVE_GENERAL
:
1787 * Regular + borderless for the general class; no source/type
1791 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], NULL
, NULL
, media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
1792 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], NULL
, NULL
, media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
1795 case IPPEVE_ENV_ONLY
:
1797 * Regular margins for "auto", "manual", and "envelope" sources.
1800 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "auto", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
1801 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "manual", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
1802 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "envelope", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
1804 case IPPEVE_PHOTO_ONLY
:
1806 * Photos have specific media types and can only be printed via
1807 * the auto, manual, and photo sources...
1811 j
< (int)(sizeof(media_type_supported
) /
1812 sizeof(media_type_supported
[0]));
1815 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1818 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "auto", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
1819 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "manual", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
1820 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "photo", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
1827 /* media-col-default */
1828 if (!ippFindAttribute(printer
->attrs
, "media-col-default", IPP_TAG_ZERO
))
1830 media_col_default
= create_media_col(media_supported
[0], media_source_supported
[0], media_type_supported
[0], media_col_sizes
[0][0], media_col_sizes
[0][1],media_xxx_margin_supported
[1]);
1832 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1834 ippDelete(media_col_default
);
1837 /* media-col-supported */
1838 if (!ippFindAttribute(printer
->attrs
, "media-col-supported", IPP_TAG_ZERO
))
1839 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-col-supported", (int)(sizeof(media_col_supported
) / sizeof(media_col_supported
[0])), NULL
, media_col_supported
);
1842 if (!ippFindAttribute(printer
->attrs
, "media-default", IPP_TAG_ZERO
))
1843 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
1845 /* media-left-margin-supported */
1846 if (!ippFindAttribute(printer
->attrs
, "media-left-margin-supported", IPP_TAG_ZERO
))
1847 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin-supported", (int)(sizeof(media_xxx_margin_supported
) / sizeof(media_xxx_margin_supported
[0])), media_xxx_margin_supported
);
1849 /* media-right-margin-supported */
1850 if (!ippFindAttribute(printer
->attrs
, "media-right-margin-supported", IPP_TAG_ZERO
))
1851 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin-supported", (int)(sizeof(media_xxx_margin_supported
) / sizeof(media_xxx_margin_supported
[0])), media_xxx_margin_supported
);
1853 /* media-supported */
1854 if (!ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
))
1855 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-supported", (int)(sizeof(media_supported
) / sizeof(media_supported
[0])), NULL
, media_supported
);
1857 /* media-size-supported */
1858 if (!ippFindAttribute(printer
->attrs
, "media-size-supported", IPP_TAG_ZERO
))
1860 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
1863 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1866 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1868 ippSetCollection(printer
->attrs
, &media_size_supported
, i
, size
);
1873 /* media-source-supported */
1874 if (!ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
))
1875 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-source-supported", (int)(sizeof(media_source_supported
) / sizeof(media_source_supported
[0])), NULL
, media_source_supported
);
1877 /* media-top-margin-supported */
1878 if (!ippFindAttribute(printer
->attrs
, "media-top-margin-supported", IPP_TAG_ZERO
))
1879 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin-supported", (int)(sizeof(media_xxx_margin_supported
) / sizeof(media_xxx_margin_supported
[0])), media_xxx_margin_supported
);
1881 /* media-type-supported */
1882 if (!ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
))
1883 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-type-supported", (int)(sizeof(media_type_supported
) / sizeof(media_type_supported
[0])), NULL
, media_type_supported
);
1885 /* multiple-document-handling-supported */
1886 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-document-handling-supported", sizeof(multiple_document_handling
) / sizeof(multiple_document_handling
[0]), NULL
, multiple_document_handling
);
1888 /* multiple-document-jobs-supported */
1889 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1891 /* multiple-operation-time-out */
1892 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1894 /* multiple-operation-time-out-action */
1895 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1897 /* natural-language-configured */
1898 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1899 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1900 "natural-language-configured", NULL
, "en");
1902 /* number-up-default */
1903 if (!ippFindAttribute(printer
->attrs
, "number-up-default", IPP_TAG_ZERO
))
1904 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
1906 /* number-up-supported */
1907 if (!ippFindAttribute(printer
->attrs
, "number-up-supported", IPP_TAG_ZERO
))
1908 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
1910 /* operations-supported */
1911 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1913 /* orientation-requested-default */
1914 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
1915 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
1917 /* orientation-requested-supported */
1918 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
1919 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
1921 /* output-bin-default */
1922 if (!ippFindAttribute(printer
->attrs
, "output-bin-default", IPP_TAG_ZERO
))
1923 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
1925 /* output-bin-supported */
1926 if (!ippFindAttribute(printer
->attrs
, "output-bin-supported", IPP_TAG_ZERO
))
1927 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
1929 /* overrides-supported */
1930 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
1931 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1933 /* page-ranges-supported */
1934 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
1935 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1937 /* pages-per-minute */
1938 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1939 "pages-per-minute", ppm
);
1941 /* pages-per-minute-color */
1943 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1944 "pages-per-minute-color", ppm_color
);
1946 /* pdl-override-supported */
1947 if (!ippFindAttribute(printer
->attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
1948 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1950 /* preferred-attributes-supported */
1951 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1953 /* print-color-mode-default */
1954 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
1955 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1957 /* print-color-mode-supported */
1958 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
1959 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported
) / sizeof(print_color_mode_supported
[0])), NULL
, print_color_mode_supported
);
1961 /* print-content-optimize-default */
1962 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
1963 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1965 /* print-content-optimize-supported */
1966 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
1967 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1969 /* print-rendering-intent-default */
1970 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
1971 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1973 /* print-rendering-intent-supported */
1974 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
1975 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1977 /* print-quality-default */
1978 if (!ippFindAttribute(printer
->attrs
, "print-quality-default", IPP_TAG_ZERO
))
1979 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1981 /* print-quality-supported */
1982 if (!ippFindAttribute(printer
->attrs
, "print-quality-supported", IPP_TAG_ZERO
))
1983 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
1985 /* printer-device-id */
1986 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1987 "printer-device-id", NULL
, device_id
);
1989 /* printer-get-attributes-supported */
1990 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1992 /* printer-geo-location */
1993 if (!ippFindAttribute(printer
->attrs
, "printer-geo-location", IPP_TAG_ZERO
))
1994 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
1997 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1998 "printer-icons", NULL
, icons
);
2000 /* printer-is-accepting-jobs */
2001 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
2004 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
2006 /* printer-location */
2007 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2008 "printer-location", NULL
, location
);
2010 /* printer-make-and-model */
2011 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2012 "printer-make-and-model", NULL
, make_model
);
2014 /* printer-mandatory-job-attributes */
2015 if (pin
&& !ippFindAttribute(printer
->attrs
, "", IPP_TAG_ZERO
))
2017 static const char * const names
[] =
2020 "job-accounting-user-id",
2024 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2025 "printer-mandatory-job-attributes",
2026 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2029 /* printer-more-info */
2030 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2033 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
2035 /* printer-organization */
2036 if (!ippFindAttribute(printer
->attrs
, "printer-organization", IPP_TAG_ZERO
))
2037 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2039 /* printer-organizational-unit */
2040 if (!ippFindAttribute(printer
->attrs
, "printer-organizational-unit", IPP_TAG_ZERO
))
2041 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2043 /* printer-resolution-default */
2044 if (!ippFindAttribute(printer
->attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
2045 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2047 /* printer-resolution-supported */
2048 if (!ippFindAttribute(printer
->attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
2049 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2051 /* printer-supply-description */
2052 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-supply-description", (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])), NULL
, printer_supplies
);
2054 /* printer-supply-info-uri */
2055 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2057 /* printer-uri-supported */
2062 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
2065 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2066 #endif /* HAVE_SSL */
2069 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2070 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2072 /* pwg-raster-document-xxx-supported */
2073 for (i
= 0; i
< num_formats
; i
++)
2074 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2077 if (i
< num_formats
)
2079 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
2080 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
, "pwg-raster-document-resolution-supported", (int)(sizeof(pwg_raster_document_resolution_supported
) / sizeof(pwg_raster_document_resolution_supported
[0])), IPP_RES_PER_INCH
, pwg_raster_document_resolution_supported
, pwg_raster_document_resolution_supported
);
2081 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
2082 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
2083 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
2084 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported
) / sizeof(pwg_raster_document_type_supported
[0])), NULL
, pwg_raster_document_type_supported
);
2087 /* reference-uri-scheme-supported */
2088 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_URISCHEME
), "reference-uri-schemes-supported", (int)(sizeof(reference_uri_schemes_supported
) / sizeof(reference_uri_schemes_supported
[0])), NULL
, reference_uri_schemes_supported
);
2091 if (!ippFindAttribute(printer
->attrs
, "sides-default", IPP_TAG_ZERO
))
2092 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
2094 /* sides-supported */
2095 if (!ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_ZERO
))
2096 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2099 for (i
= 0; i
< num_formats
; i
++)
2100 if (!strcasecmp(formats
[i
], "image/urf"))
2103 if (i
< num_formats
&& !ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_ZERO
))
2104 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2106 /* uri-authentication-supported */
2108 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
2110 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
2111 #endif /* HAVE_SSL */
2113 /* uri-security-supported */
2115 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
2117 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
2118 #endif /* HAVE_SSL */
2120 /* which-jobs-supported */
2121 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "which-jobs-supported", sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
2125 debug_attributes("Printer", printer
->attrs
, 0);
2128 * Register the printer with Bonjour...
2131 if (!register_printer(printer
, subtypes
))
2142 * If we get here we were unable to create the printer...
2147 delete_printer(printer
);
2153 * 'debug_attributes()' - Print attributes in a request or response.
2157 debug_attributes(const char *title
, /* I - Title */
2158 ipp_t
*ipp
, /* I - Request/response */
2159 int type
) /* I - 0 = object, 1 = request, 2 = response */
2161 ipp_tag_t group_tag
; /* Current group */
2162 ipp_attribute_t
*attr
; /* Current attribute */
2163 char buffer
[2048]; /* String buffer for value */
2164 int major
, minor
; /* Version */
2170 fprintf(stderr
, "%s:\n", title
);
2171 major
= ippGetVersion(ipp
, &minor
);
2172 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2174 fprintf(stderr
, " operation-id=%s(%04x)\n",
2175 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2177 fprintf(stderr
, " status-code=%s(%04x)\n",
2178 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2179 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2181 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2183 attr
= ippNextAttribute(ipp
))
2185 if (ippGetGroupTag(attr
) != group_tag
)
2187 group_tag
= ippGetGroupTag(attr
);
2188 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2191 if (ippGetName(attr
))
2193 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2194 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2195 ippGetCount(attr
) > 1 ? "1setOf " : "",
2196 ippTagString(ippGetValueTag(attr
)), buffer
);
2203 * 'delete_client()' - Close the socket and free all memory used by a client
2208 delete_client(ippeve_client_t
*client
) /* I - Client */
2211 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2214 * Flush pending writes before closing...
2217 httpFlushWrite(client
->http
);
2223 httpClose(client
->http
);
2225 ippDelete(client
->request
);
2226 ippDelete(client
->response
);
2233 * 'delete_job()' - Remove from the printer and free all memory used by a job
2238 delete_job(ippeve_job_t
*job
) /* I - Job */
2241 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2243 ippDelete(job
->attrs
);
2248 unlink(job
->filename
);
2250 free(job
->filename
);
2258 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2259 * used by a printer object.
2263 delete_printer(ippeve_printer_t
*printer
) /* I - Printer */
2265 if (printer
->ipv4
>= 0)
2266 close(printer
->ipv4
);
2268 if (printer
->ipv6
>= 0)
2269 close(printer
->ipv6
);
2272 if (printer
->printer_ref
)
2273 DNSServiceRefDeallocate(printer
->printer_ref
);
2274 if (printer
->ipp_ref
)
2275 DNSServiceRefDeallocate(printer
->ipp_ref
);
2276 if (printer
->ipps_ref
)
2277 DNSServiceRefDeallocate(printer
->ipps_ref
);
2278 if (printer
->http_ref
)
2279 DNSServiceRefDeallocate(printer
->http_ref
);
2280 #elif defined(HAVE_AVAHI)
2281 avahi_threaded_poll_lock(DNSSDMaster
);
2283 if (printer
->printer_ref
)
2284 avahi_entry_group_free(printer
->printer_ref
);
2285 if (printer
->ipp_ref
)
2286 avahi_entry_group_free(printer
->ipp_ref
);
2287 if (printer
->ipps_ref
)
2288 avahi_entry_group_free(printer
->ipps_ref
);
2289 if (printer
->http_ref
)
2290 avahi_entry_group_free(printer
->http_ref
);
2292 avahi_threaded_poll_unlock(DNSSDMaster
);
2293 #endif /* HAVE_DNSSD */
2295 if (printer
->dnssd_name
)
2296 free(printer
->dnssd_name
);
2298 free(printer
->name
);
2300 free(printer
->icon
);
2301 if (printer
->command
)
2302 free(printer
->command
);
2303 if (printer
->directory
)
2304 free(printer
->directory
);
2305 if (printer
->hostname
)
2306 free(printer
->hostname
);
2310 ippDelete(printer
->attrs
);
2311 cupsArrayDelete(printer
->jobs
);
2319 * 'dnssd_callback()' - Handle Bonjour registration events.
2322 static void DNSSD_API
2324 DNSServiceRef sdRef
, /* I - Service reference */
2325 DNSServiceFlags flags
, /* I - Status flags */
2326 DNSServiceErrorType errorCode
, /* I - Error, if any */
2327 const char *name
, /* I - Service name */
2328 const char *regtype
, /* I - Service type */
2329 const char *domain
, /* I - Domain for service */
2330 ippeve_printer_t
*printer
) /* I - Printer */
2338 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2339 regtype
, (int)errorCode
);
2342 else if (strcasecmp(name
, printer
->dnssd_name
))
2345 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2347 /* No lock needed since only the main thread accesses/changes this */
2348 free(printer
->dnssd_name
);
2349 printer
->dnssd_name
= strdup(name
);
2354 #elif defined(HAVE_AVAHI)
2356 * 'dnssd_callback()' - Handle Bonjour registration events.
2361 AvahiEntryGroup
*srv
, /* I - Service */
2362 AvahiEntryGroupState state
, /* I - Registration state */
2363 void *context
) /* I - Printer */
2372 * 'dnssd_client_cb()' - Client callback for Avahi.
2374 * Called whenever the client or server state changes...
2379 AvahiClient
*c
, /* I - Client */
2380 AvahiClientState state
, /* I - Current state */
2381 void *userdata
) /* I - User data (unused) */
2391 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2394 case AVAHI_CLIENT_FAILURE
:
2395 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2397 fputs("Avahi server crashed, exiting.\n", stderr
);
2403 #endif /* HAVE_DNSSD */
2407 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2414 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2416 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2420 #elif defined(HAVE_AVAHI)
2421 int error
; /* Error code, if any */
2423 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2425 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2429 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2431 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2435 avahi_threaded_poll_start(DNSSDMaster
);
2436 #endif /* HAVE_DNSSD */
2441 * 'filter_cb()' - Filter printer attributes based on the requested array.
2444 static int /* O - 1 to copy, 0 to ignore */
2445 filter_cb(ippeve_filter_t
*filter
, /* I - Filter parameters */
2446 ipp_t
*dst
, /* I - Destination (unused) */
2447 ipp_attribute_t
*attr
) /* I - Source attribute */
2450 * Filter attributes as needed...
2453 #ifndef _WIN32 /* Avoid MS compiler bug */
2455 #endif /* !_WIN32 */
2457 ipp_tag_t group
= ippGetGroupTag(attr
);
2458 const char *name
= ippGetName(attr
);
2460 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
)))
2463 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2468 * 'find_job()' - Find a job specified in a request.
2471 static ippeve_job_t
* /* O - Job or NULL */
2472 find_job(ippeve_client_t
*client
) /* I - Client */
2474 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2475 ippeve_job_t key
, /* Job search key */
2476 *job
; /* Matching job, if any */
2479 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2481 const char *uri
= ippGetString(attr
, 0, NULL
);
2483 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2484 uri
[client
->printer
->urilen
] == '/')
2485 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2489 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2490 key
.id
= ippGetInteger(attr
, 0);
2492 _cupsRWLockRead(&(client
->printer
->rwlock
));
2493 job
= (ippeve_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2494 _cupsRWUnlock(&(client
->printer
->rwlock
));
2501 * 'get_collection()' - Get a collection value from a file.
2504 static ipp_t
* /* O - Collection value */
2505 get_collection(FILE *fp
, /* I - File to read from */
2506 const char *filename
, /* I - Attributes filename */
2507 int *linenum
) /* IO - Line number */
2509 char token
[1024], /* Token from file */
2510 attr
[128]; /* Attribute name */
2511 ipp_tag_t value
; /* Current value type */
2512 ipp_t
*col
= ippNew(); /* Collection value */
2513 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2516 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2518 if (!strcmp(token
, "}"))
2520 else if (!strcmp(token
, "{") && lastcol
)
2523 * Another collection value
2526 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2527 /* Collection value */
2530 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
2534 else if (!_cups_strcasecmp(token
, "MEMBER"))
2542 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2544 fprintf(stderr
, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum
, filename
);
2548 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2550 fprintf(stderr
, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2554 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2556 fprintf(stderr
, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum
, filename
);
2560 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2562 fprintf(stderr
, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum
, filename
);
2568 case IPP_TAG_BOOLEAN
:
2569 if (!_cups_strcasecmp(token
, "true"))
2570 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2572 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
2575 case IPP_TAG_INTEGER
:
2577 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2580 case IPP_TAG_RESOLUTION
:
2582 int xres
, /* X resolution */
2583 yres
; /* Y resolution */
2584 char units
[6]; /* Units */
2586 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2587 (_cups_strcasecmp(units
, "dpi") &&
2588 _cups_strcasecmp(units
, "dpc") &&
2589 _cups_strcasecmp(units
, "dpcm") &&
2590 _cups_strcasecmp(units
, "other")))
2592 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2596 if (!_cups_strcasecmp(units
, "dpi"))
2597 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
2598 else if (!_cups_strcasecmp(units
, "dpc") ||
2599 !_cups_strcasecmp(units
, "dpcm"))
2600 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
2602 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
2606 case IPP_TAG_RANGE
:
2608 int lowers
[4], /* Lower value */
2609 uppers
[4], /* Upper values */
2610 num_vals
; /* Number of values */
2613 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2614 lowers
+ 0, uppers
+ 0,
2615 lowers
+ 1, uppers
+ 1,
2616 lowers
+ 2, uppers
+ 2,
2617 lowers
+ 3, uppers
+ 3);
2619 if ((num_vals
& 1) || num_vals
== 0)
2621 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2625 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2630 case IPP_TAG_BEGIN_COLLECTION
:
2631 if (!strcmp(token
, "{"))
2633 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2634 /* Collection value */
2638 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2646 fprintf(stderr
, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum
, filename
);
2650 case IPP_TAG_STRING
:
2651 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
2655 if (!strchr(token
, ','))
2656 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2660 * Multiple string values...
2663 int num_values
; /* Number of values */
2664 char *values
[100], /* Values */
2665 *ptr
; /* Pointer to next value */
2671 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2674 values
[num_values
] = ptr
;
2676 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
2680 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2681 NULL
, (const char **)values
);
2691 * If we get here there was a parse error; free memory and return.
2703 * 'get_token()' - Get a token from a file.
2706 static char * /* O - Token from file or NULL on EOF */
2707 get_token(FILE *fp
, /* I - File to read from */
2708 char *buf
, /* I - Buffer to read into */
2709 int buflen
, /* I - Length of buffer */
2710 int *linenum
) /* IO - Current line number */
2712 int ch
, /* Character from file */
2713 quote
; /* Quoting character */
2714 char *bufptr
, /* Pointer into buffer */
2715 *bufend
; /* End of buffer */
2721 * Skip whitespace...
2724 while (isspace(ch
= getc(fp
)))
2736 else if (ch
== '\'' || ch
== '\"')
2739 * Quoted text or regular expression...
2744 bufend
= buf
+ buflen
- 1;
2746 while ((ch
= getc(fp
)) != EOF
)
2751 * Escape next character...
2754 if (bufptr
< bufend
)
2755 *bufptr
++ = (char)ch
;
2757 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2758 *bufptr
++ = (char)ch
;
2760 else if (ch
== quote
)
2762 else if (bufptr
< bufend
)
2763 *bufptr
++ = (char)ch
;
2776 while ((ch
= getc(fp
)) != EOF
)
2782 else if (ch
== '{' || ch
== '}' || ch
== ',')
2792 * Whitespace delimited text...
2798 bufend
= buf
+ buflen
- 1;
2800 while ((ch
= getc(fp
)) != EOF
)
2801 if (isspace(ch
) || ch
== '#')
2803 else if (bufptr
< bufend
)
2804 *bufptr
++ = (char)ch
;
2808 else if (ch
== '\n')
2820 * 'html_escape()' - Write a HTML-safe string.
2824 html_escape(ippeve_client_t
*client
, /* I - Client */
2825 const char *s
, /* I - String to write */
2826 size_t slen
) /* I - Number of characters to write */
2828 const char *start
, /* Start of segment */
2829 *end
; /* End of string */
2833 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2835 while (*s
&& s
< end
)
2837 if (*s
== '&' || *s
== '<')
2840 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2843 httpWrite2(client
->http
, "&", 5);
2845 httpWrite2(client
->http
, "<", 4);
2854 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2859 * 'html_footer()' - Show the web interface footer.
2861 * This function also writes the trailing 0-length chunk.
2865 html_footer(ippeve_client_t
*client
) /* I - Client */
2871 httpWrite2(client
->http
, "", 0);
2876 * 'html_header()' - Show the web interface header and title.
2880 html_header(ippeve_client_t
*client
, /* I - Client */
2881 const char *title
) /* I - Title */
2887 "<title>%s</title>\n"
2888 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2889 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2890 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2891 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2893 "body { font-family: sans-serif; margin: 0; }\n"
2894 "div.body { padding: 0px 10px 10px; }\n"
2895 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2896 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2897 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2898 "table.form th { text-align: right; }\n"
2899 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2900 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2901 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2902 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2903 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2904 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2905 "table.nav td { margin: 0; text-align: center; }\n"
2906 "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"
2907 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2908 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2909 "td.nav:hover { background: #666; color: #fff; }\n"
2910 "td.nav:active { background: #000; color: #ff0; }\n"
2914 "<table class=\"nav\"><tr>"
2915 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2916 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2917 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2919 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2924 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2928 html_printf(ippeve_client_t
*client
, /* I - Client */
2929 const char *format
, /* I - Printf-style format string */
2930 ...) /* I - Additional arguments as needed */
2932 va_list ap
; /* Pointer to arguments */
2933 const char *start
; /* Start of string */
2934 char size
, /* Size character (h, l, L) */
2935 type
; /* Format type character */
2936 int width
, /* Width of field */
2937 prec
; /* Number of characters of precision */
2938 char tformat
[100], /* Temporary format string for sprintf() */
2939 *tptr
, /* Pointer into temporary format */
2940 temp
[1024]; /* Buffer for formatted numbers */
2941 char *s
; /* Pointer to string */
2945 * Loop through the format string, formatting as needed...
2948 va_start(ap
, format
);
2956 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2959 *tptr
++ = *format
++;
2963 httpWrite2(client
->http
, "%", 1);
2968 else if (strchr(" -+#\'", *format
))
2969 *tptr
++ = *format
++;
2974 * Get width from argument...
2978 width
= va_arg(ap
, int);
2980 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2981 tptr
+= strlen(tptr
);
2987 while (isdigit(*format
& 255))
2989 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2992 width
= width
* 10 + *format
++ - '0';
2998 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3006 * Get precision from argument...
3010 prec
= va_arg(ap
, int);
3012 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
3013 tptr
+= strlen(tptr
);
3019 while (isdigit(*format
& 255))
3021 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3024 prec
= prec
* 10 + *format
++ - '0';
3029 if (*format
== 'l' && format
[1] == 'l')
3033 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
3041 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
3043 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3058 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3067 case 'E' : /* Floating point formats */
3072 if ((size_t)(width
+ 2) > sizeof(temp
))
3075 sprintf(temp
, tformat
, va_arg(ap
, double));
3077 httpWrite2(client
->http
, temp
, strlen(temp
));
3080 case 'B' : /* Integer formats */
3088 if ((size_t)(width
+ 2) > sizeof(temp
))
3091 # ifdef HAVE_LONG_LONG
3093 sprintf(temp
, tformat
, va_arg(ap
, long long));
3095 # endif /* HAVE_LONG_LONG */
3097 sprintf(temp
, tformat
, va_arg(ap
, long));
3099 sprintf(temp
, tformat
, va_arg(ap
, int));
3101 httpWrite2(client
->http
, temp
, strlen(temp
));
3104 case 'p' : /* Pointer value */
3105 if ((size_t)(width
+ 2) > sizeof(temp
))
3108 sprintf(temp
, tformat
, va_arg(ap
, void *));
3110 httpWrite2(client
->http
, temp
, strlen(temp
));
3113 case 'c' : /* Character or character array */
3116 temp
[0] = (char)va_arg(ap
, int);
3118 html_escape(client
, temp
, 1);
3121 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3124 case 's' : /* String */
3125 if ((s
= va_arg(ap
, char *)) == NULL
)
3128 html_escape(client
, s
, strlen(s
));
3137 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3144 * 'ipp_cancel_job()' - Cancel a job.
3148 ipp_cancel_job(ippeve_client_t
*client
) /* I - Client */
3150 ippeve_job_t
*job
; /* Job information */
3157 if ((job
= find_job(client
)) == NULL
)
3159 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3164 * See if the job is already completed, canceled, or aborted; if so,
3165 * we can't cancel...
3170 case IPP_JSTATE_CANCELED
:
3171 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3172 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3175 case IPP_JSTATE_ABORTED
:
3176 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3177 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3180 case IPP_JSTATE_COMPLETED
:
3181 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3182 "Job #%d is already completed - can\'t cancel.", job
->id
);
3190 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3192 if (job
->state
== IPP_JSTATE_PROCESSING
||
3193 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3197 job
->state
= IPP_JSTATE_CANCELED
;
3198 job
->completed
= time(NULL
);
3201 _cupsRWUnlock(&(client
->printer
->rwlock
));
3203 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3210 * 'ipp_close_job()' - Close an open job.
3214 ipp_close_job(ippeve_client_t
*client
) /* I - Client */
3216 ippeve_job_t
*job
; /* Job information */
3223 if ((job
= find_job(client
)) == NULL
)
3225 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3230 * See if the job is already completed, canceled, or aborted; if so,
3231 * we can't cancel...
3236 case IPP_JSTATE_CANCELED
:
3237 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3238 "Job #%d is canceled - can\'t close.", job
->id
);
3241 case IPP_JSTATE_ABORTED
:
3242 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3243 "Job #%d is aborted - can\'t close.", job
->id
);
3246 case IPP_JSTATE_COMPLETED
:
3247 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3248 "Job #%d is completed - can\'t close.", job
->id
);
3251 case IPP_JSTATE_PROCESSING
:
3252 case IPP_JSTATE_STOPPED
:
3253 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3254 "Job #%d is already closed.", job
->id
);
3258 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3265 * 'ipp_create_job()' - Create a job object.
3269 ipp_create_job(ippeve_client_t
*client
) /* I - Client */
3271 ippeve_job_t
*job
; /* New job */
3272 cups_array_t
*ra
; /* Attributes to send in response */
3276 * Validate print job attributes...
3279 if (!valid_job_attributes(client
))
3281 httpFlush(client
->http
);
3286 * Do we have a file to print?
3289 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3291 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3292 "Unexpected document data following request.");
3300 if ((job
= create_job(client
)) == NULL
)
3302 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3303 "Currently printing another job.");
3308 * Return the job info...
3311 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3313 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3314 cupsArrayAdd(ra
, "job-id");
3315 cupsArrayAdd(ra
, "job-state");
3316 cupsArrayAdd(ra
, "job-state-message");
3317 cupsArrayAdd(ra
, "job-state-reasons");
3318 cupsArrayAdd(ra
, "job-uri");
3320 copy_job_attributes(client
, job
, ra
);
3321 cupsArrayDelete(ra
);
3326 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3330 ipp_get_job_attributes(
3331 ippeve_client_t
*client
) /* I - Client */
3333 ippeve_job_t
*job
; /* Job */
3334 cups_array_t
*ra
; /* requested-attributes */
3337 if ((job
= find_job(client
)) == NULL
)
3339 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3343 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3345 ra
= ippCreateRequestedArray(client
->request
);
3346 copy_job_attributes(client
, job
, ra
);
3347 cupsArrayDelete(ra
);
3352 * 'ipp_get_jobs()' - Get a list of job objects.
3356 ipp_get_jobs(ippeve_client_t
*client
) /* I - Client */
3358 ipp_attribute_t
*attr
; /* Current attribute */
3359 const char *which_jobs
= NULL
;
3360 /* which-jobs values */
3361 int job_comparison
; /* Job comparison */
3362 ipp_jstate_t job_state
; /* job-state value */
3363 int first_job_id
, /* First job ID */
3364 limit
, /* Maximum number of jobs to return */
3365 count
; /* Number of jobs that match */
3366 const char *username
; /* Username */
3367 ippeve_job_t
*job
; /* Current job pointer */
3368 cups_array_t
*ra
; /* Requested attributes array */
3372 * See if the "which-jobs" attribute have been specified...
3375 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3376 IPP_TAG_KEYWORD
)) != NULL
)
3378 which_jobs
= ippGetString(attr
, 0, NULL
);
3379 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3382 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3384 job_comparison
= -1;
3385 job_state
= IPP_JSTATE_STOPPED
;
3387 else if (!strcmp(which_jobs
, "completed"))
3390 job_state
= IPP_JSTATE_CANCELED
;
3392 else if (!strcmp(which_jobs
, "aborted"))
3395 job_state
= IPP_JSTATE_ABORTED
;
3397 else if (!strcmp(which_jobs
, "all"))
3400 job_state
= IPP_JSTATE_PENDING
;
3402 else if (!strcmp(which_jobs
, "canceled"))
3405 job_state
= IPP_JSTATE_CANCELED
;
3407 else if (!strcmp(which_jobs
, "pending"))
3410 job_state
= IPP_JSTATE_PENDING
;
3412 else if (!strcmp(which_jobs
, "pending-held"))
3415 job_state
= IPP_JSTATE_HELD
;
3417 else if (!strcmp(which_jobs
, "processing"))
3420 job_state
= IPP_JSTATE_PROCESSING
;
3422 else if (!strcmp(which_jobs
, "processing-stopped"))
3425 job_state
= IPP_JSTATE_STOPPED
;
3429 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3430 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3431 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3432 "which-jobs", NULL
, which_jobs
);
3437 * See if they want to limit the number of jobs reported...
3440 if ((attr
= ippFindAttribute(client
->request
, "limit",
3441 IPP_TAG_INTEGER
)) != NULL
)
3443 limit
= ippGetInteger(attr
, 0);
3445 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3450 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3451 IPP_TAG_INTEGER
)) != NULL
)
3453 first_job_id
= ippGetInteger(attr
, 0);
3455 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3462 * See if we only want to see jobs for a specific user...
3467 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3468 IPP_TAG_BOOLEAN
)) != NULL
)
3470 int my_jobs
= ippGetBoolean(attr
, 0);
3472 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3473 my_jobs
? "true" : "false");
3477 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3478 IPP_TAG_NAME
)) == NULL
)
3480 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3481 "Need requesting-user-name with my-jobs.");
3485 username
= ippGetString(attr
, 0, NULL
);
3487 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3488 client
->hostname
, username
);
3493 * OK, build a list of jobs for this printer...
3496 ra
= ippCreateRequestedArray(client
->request
);
3498 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3500 _cupsRWLockRead(&(client
->printer
->rwlock
));
3502 for (count
= 0, job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3503 (limit
<= 0 || count
< limit
) && job
;
3504 job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3507 * Filter out jobs that don't match...
3510 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3511 (job_comparison
== 0 && job
->state
!= job_state
) ||
3512 (job_comparison
> 0 && job
->state
< job_state
) ||
3513 job
->id
< first_job_id
||
3514 (username
&& job
->username
&&
3515 strcasecmp(username
, job
->username
)))
3519 ippAddSeparator(client
->response
);
3522 copy_job_attributes(client
, job
, ra
);
3525 cupsArrayDelete(ra
);
3527 _cupsRWUnlock(&(client
->printer
->rwlock
));
3532 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3536 ipp_get_printer_attributes(
3537 ippeve_client_t
*client
) /* I - Client */
3539 cups_array_t
*ra
; /* Requested attributes array */
3540 ippeve_printer_t
*printer
; /* Printer */
3544 * Send the attributes...
3547 ra
= ippCreateRequestedArray(client
->request
);
3548 printer
= client
->printer
;
3550 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3552 _cupsRWLockRead(&(printer
->rwlock
));
3554 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3555 IPP_TAG_CUPS_CONST
);
3557 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3559 int i
, /* Looping var */
3560 num_ready
= 0; /* Number of ready media */
3561 ipp_t
*ready
[3]; /* Ready media */
3563 if (printer
->main_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3565 if (printer
->main_type
!= IPPEVE_MEDIA_TYPE_NONE
)
3566 ready
[num_ready
++] = create_media_col(media_supported
[printer
->main_size
], "main", media_type_supported
[printer
->main_type
], media_col_sizes
[printer
->main_size
][0], media_col_sizes
[printer
->main_size
][1], 635);
3568 ready
[num_ready
++] = create_media_col(media_supported
[printer
->main_size
], "main", NULL
, media_col_sizes
[printer
->main_size
][0], media_col_sizes
[printer
->main_size
][1], 635);
3570 if (printer
->envelope_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3571 ready
[num_ready
++] = create_media_col(media_supported
[printer
->envelope_size
], "envelope", NULL
, media_col_sizes
[printer
->envelope_size
][0], media_col_sizes
[printer
->envelope_size
][1], 635);
3572 if (printer
->photo_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3574 if (printer
->photo_type
!= IPPEVE_MEDIA_TYPE_NONE
)
3575 ready
[num_ready
++] = create_media_col(media_supported
[printer
->photo_size
], "photo", media_type_supported
[printer
->photo_type
], media_col_sizes
[printer
->photo_size
][0], media_col_sizes
[printer
->photo_size
][1], 0);
3577 ready
[num_ready
++] = create_media_col(media_supported
[printer
->photo_size
], "photo", NULL
, media_col_sizes
[printer
->photo_size
][0], media_col_sizes
[printer
->photo_size
][1], 0);
3582 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3583 for (i
= 0; i
< num_ready
; i
++)
3584 ippDelete(ready
[i
]);
3587 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3590 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3592 int num_ready
= 0; /* Number of ready media */
3593 const char *ready
[3]; /* Ready media */
3595 if (printer
->main_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3596 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3598 if (printer
->envelope_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3599 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3601 if (printer
->photo_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3602 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3605 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3607 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3610 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3611 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3613 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3614 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3616 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3617 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3620 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3621 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3622 "printer-state", printer
->state
);
3624 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3625 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3627 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3628 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3630 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3632 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3634 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3637 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3639 if (printer
->state_reasons
== IPPEVE_PREASON_NONE
)
3640 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3641 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3642 "printer-state-reasons", NULL
, "none");
3645 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3646 ippeve_preason_t bit
; /* Reason bit */
3647 int i
; /* Looping var */
3648 char reason
[32]; /* Reason string */
3650 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
3652 if (printer
->state_reasons
& bit
)
3654 snprintf(reason
, sizeof(reason
), "%s-%s", ippeve_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3656 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3658 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3664 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3666 int i
; /* Looping var */
3667 char buffer
[256]; /* Supply value buffer */
3668 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3669 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3671 for (i
= 0; i
< 5; i
++)
3673 snprintf(buffer
, sizeof(buffer
), "index=%d;class=%s;type=%s;unit=percent;maxcapacity=100;level=%d;colorantname=%s;", i
+ 1, i
< 4 ? "supplyThatIsConsumed" : "receptacleThatIsFilled", i
< 4 ? "toner" : "wasteToner", printer
->supplies
[i
], colorants
[i
]);
3676 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3678 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3682 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3683 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3685 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3686 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3687 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3689 _cupsRWUnlock(&(printer
->rwlock
));
3691 cupsArrayDelete(ra
);
3696 * 'ipp_identify_printer()' - Beep or display a message.
3700 ipp_identify_printer(
3701 ippeve_client_t
*client
) /* I - Client */
3703 ipp_attribute_t
*actions
, /* identify-actions */
3704 *message
; /* message */
3707 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3708 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3710 if (!actions
|| ippContainsString(actions
, "sound"))
3716 if (ippContainsString(actions
, "display"))
3717 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3719 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3724 * 'ipp_print_job()' - Create a job object with an attached document.
3728 ipp_print_job(ippeve_client_t
*client
) /* I - Client */
3730 ippeve_job_t
*job
; /* New job */
3731 char filename
[1024], /* Filename buffer */
3732 buffer
[4096]; /* Copy buffer */
3733 ssize_t bytes
; /* Bytes read */
3734 cups_array_t
*ra
; /* Attributes to send in response */
3735 _cups_thread_t t
; /* Thread */
3739 * Validate print job attributes...
3742 if (!valid_job_attributes(client
))
3744 httpFlush(client
->http
);
3749 * Do we have a file to print?
3752 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3754 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3762 if ((job
= create_job(client
)) == NULL
)
3764 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3765 "Currently printing another job.");
3770 * Create a file for the request data...
3773 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3776 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3778 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3780 job
->state
= IPP_JSTATE_ABORTED
;
3782 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3783 "Unable to create print file: %s", strerror(errno
));
3787 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3789 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3791 int error
= errno
; /* Write error */
3793 job
->state
= IPP_JSTATE_ABORTED
;
3800 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3801 "Unable to write print file: %s", strerror(error
));
3809 * Got an error while reading the print data, so abort this job.
3812 job
->state
= IPP_JSTATE_ABORTED
;
3819 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3820 "Unable to read print file.");
3826 int error
= errno
; /* Write error */
3828 job
->state
= IPP_JSTATE_ABORTED
;
3833 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3834 "Unable to write print file: %s", strerror(error
));
3839 job
->filename
= strdup(filename
);
3840 job
->state
= IPP_JSTATE_PENDING
;
3843 * Process the job...
3846 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
3850 _cupsThreadDetach(t
);
3854 job
->state
= IPP_JSTATE_ABORTED
;
3855 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3860 * Return the job info...
3863 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3865 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3866 cupsArrayAdd(ra
, "job-id");
3867 cupsArrayAdd(ra
, "job-state");
3868 cupsArrayAdd(ra
, "job-state-message");
3869 cupsArrayAdd(ra
, "job-state-reasons");
3870 cupsArrayAdd(ra
, "job-uri");
3872 copy_job_attributes(client
, job
, ra
);
3873 cupsArrayDelete(ra
);
3878 * 'ipp_print_uri()' - Create a job object with a referenced document.
3882 ipp_print_uri(ippeve_client_t
*client
) /* I - Client */
3884 ippeve_job_t
*job
; /* New job */
3885 ipp_attribute_t
*uri
; /* document-uri */
3886 char scheme
[256], /* URI scheme */
3887 userpass
[256], /* Username and password info */
3888 hostname
[256], /* Hostname */
3889 resource
[1024]; /* Resource path */
3890 int port
; /* Port number */
3891 http_uri_status_t uri_status
; /* URI decode status */
3892 http_encryption_t encryption
; /* Encryption to use, if any */
3893 http_t
*http
; /* Connection for http/https URIs */
3894 http_status_t status
; /* Access status for http/https URIs */
3895 int infile
; /* Input file for local file URIs */
3896 char filename
[1024], /* Filename buffer */
3897 buffer
[4096]; /* Copy buffer */
3898 ssize_t bytes
; /* Bytes read */
3899 cups_array_t
*ra
; /* Attributes to send in response */
3900 static const char * const uri_status_strings
[] =
3901 { /* URI decode errors */
3903 "Bad arguments to function.",
3904 "Bad resource in URI.",
3905 "Bad port number in URI.",
3906 "Bad hostname in URI.",
3907 "Bad username in URI.",
3908 "Bad scheme in URI.",
3914 * Validate print job attributes...
3917 if (!valid_job_attributes(client
))
3919 httpFlush(client
->http
);
3924 * Do we have a file to print?
3927 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3929 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3930 "Unexpected document data following request.");
3935 * Do we have a document URI?
3938 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3939 IPP_TAG_URI
)) == NULL
)
3941 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3945 if (ippGetCount(uri
) != 1)
3947 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3948 "Too many document-uri values.");
3952 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3953 scheme
, sizeof(scheme
), userpass
,
3954 sizeof(userpass
), hostname
, sizeof(hostname
),
3955 &port
, resource
, sizeof(resource
));
3956 if (uri_status
< HTTP_URI_STATUS_OK
)
3958 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3959 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3963 if (strcmp(scheme
, "file") &&
3965 strcmp(scheme
, "https") &&
3966 #endif /* HAVE_SSL */
3967 strcmp(scheme
, "http"))
3969 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3970 "URI scheme \"%s\" not supported.", scheme
);
3974 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3976 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3977 "Unable to access URI: %s", strerror(errno
));
3985 if ((job
= create_job(client
)) == NULL
)
3987 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3988 "Currently printing another job.");
3993 * Create a file for the request data...
3996 if (!strcasecmp(job
->format
, "image/jpeg"))
3997 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3998 client
->printer
->directory
, job
->id
);
3999 else if (!strcasecmp(job
->format
, "image/png"))
4000 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4001 client
->printer
->directory
, job
->id
);
4002 else if (!strcasecmp(job
->format
, "application/pdf"))
4003 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4004 client
->printer
->directory
, job
->id
);
4005 else if (!strcasecmp(job
->format
, "application/postscript"))
4006 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4007 client
->printer
->directory
, job
->id
);
4009 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4010 client
->printer
->directory
, job
->id
);
4012 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4014 job
->state
= IPP_JSTATE_ABORTED
;
4016 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4017 "Unable to create print file: %s", strerror(errno
));
4021 if (!strcmp(scheme
, "file"))
4023 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4025 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4026 "Unable to access URI: %s", strerror(errno
));
4032 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4033 (errno
== EAGAIN
|| errno
== EINTR
))
4035 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4037 int error
= errno
; /* Write error */
4039 job
->state
= IPP_JSTATE_ABORTED
;
4047 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4048 "Unable to write print file: %s", strerror(error
));
4059 if (port
== 443 || !strcmp(scheme
, "https"))
4060 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4062 #endif /* HAVE_SSL */
4063 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4065 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4066 1, 30000, NULL
)) == NULL
)
4068 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4069 "Unable to connect to %s: %s", hostname
,
4070 cupsLastErrorString());
4071 job
->state
= IPP_JSTATE_ABORTED
;
4080 httpClearFields(http
);
4081 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4082 if (httpGet(http
, resource
))
4084 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4085 "Unable to GET URI: %s", strerror(errno
));
4087 job
->state
= IPP_JSTATE_ABORTED
;
4097 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4099 if (status
!= HTTP_STATUS_OK
)
4101 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4102 "Unable to GET URI: %s", httpStatus(status
));
4104 job
->state
= IPP_JSTATE_ABORTED
;
4114 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4116 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4118 int error
= errno
; /* Write error */
4120 job
->state
= IPP_JSTATE_ABORTED
;
4128 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4129 "Unable to write print file: %s", strerror(error
));
4139 int error
= errno
; /* Write error */
4141 job
->state
= IPP_JSTATE_ABORTED
;
4146 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4147 "Unable to write print file: %s", strerror(error
));
4152 job
->filename
= strdup(filename
);
4153 job
->state
= IPP_JSTATE_PENDING
;
4156 * Process the job...
4162 * Return the job info...
4165 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4167 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4168 cupsArrayAdd(ra
, "job-id");
4169 cupsArrayAdd(ra
, "job-state");
4170 cupsArrayAdd(ra
, "job-state-reasons");
4171 cupsArrayAdd(ra
, "job-uri");
4173 copy_job_attributes(client
, job
, ra
);
4174 cupsArrayDelete(ra
);
4179 * 'ipp_send_document()' - Add an attached document to a job object created with
4184 ipp_send_document(ippeve_client_t
*client
)/* I - Client */
4186 ippeve_job_t
*job
; /* Job information */
4187 char filename
[1024], /* Filename buffer */
4188 buffer
[4096]; /* Copy buffer */
4189 ssize_t bytes
; /* Bytes read */
4190 ipp_attribute_t
*attr
; /* Current attribute */
4191 cups_array_t
*ra
; /* Attributes to send in response */
4198 if ((job
= find_job(client
)) == NULL
)
4200 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4201 httpFlush(client
->http
);
4206 * See if we already have a document for this job or the job has already
4207 * in a non-pending state...
4210 if (job
->state
> IPP_JSTATE_HELD
)
4212 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4213 "Job is not in a pending state.");
4214 httpFlush(client
->http
);
4217 else if (job
->filename
|| job
->fd
>= 0)
4219 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4220 "Multiple document jobs are not supported.");
4221 httpFlush(client
->http
);
4225 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4226 IPP_TAG_ZERO
)) == NULL
)
4228 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4229 "Missing required last-document attribute.");
4230 httpFlush(client
->http
);
4233 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4234 !ippGetBoolean(attr
, 0))
4236 respond_unsupported(client
, attr
);
4237 httpFlush(client
->http
);
4242 * Validate document attributes...
4245 if (!valid_doc_attributes(client
))
4247 httpFlush(client
->http
);
4251 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4254 * Get the document format for the job...
4257 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4259 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4260 job
->format
= ippGetString(attr
, 0, NULL
);
4261 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4262 job
->format
= ippGetString(attr
, 0, NULL
);
4264 job
->format
= "application/octet-stream";
4267 * Create a file for the request data...
4270 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
4273 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4275 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4277 _cupsRWUnlock(&(client
->printer
->rwlock
));
4281 job
->state
= IPP_JSTATE_ABORTED
;
4283 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4284 "Unable to create print file: %s", strerror(errno
));
4288 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4290 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4292 int error
= errno
; /* Write error */
4294 job
->state
= IPP_JSTATE_ABORTED
;
4301 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4302 "Unable to write print file: %s", strerror(error
));
4310 * Got an error while reading the print data, so abort this job.
4313 job
->state
= IPP_JSTATE_ABORTED
;
4320 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4321 "Unable to read print file.");
4327 int error
= errno
; /* Write error */
4329 job
->state
= IPP_JSTATE_ABORTED
;
4334 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4335 "Unable to write print file: %s", strerror(error
));
4339 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4342 job
->filename
= strdup(filename
);
4343 job
->state
= IPP_JSTATE_PENDING
;
4345 _cupsRWUnlock(&(client
->printer
->rwlock
));
4348 * Process the job...
4354 * Return the job info...
4357 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4359 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4360 cupsArrayAdd(ra
, "job-id");
4361 cupsArrayAdd(ra
, "job-state");
4362 cupsArrayAdd(ra
, "job-state-reasons");
4363 cupsArrayAdd(ra
, "job-uri");
4365 copy_job_attributes(client
, job
, ra
);
4366 cupsArrayDelete(ra
);
4371 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4376 ipp_send_uri(ippeve_client_t
*client
) /* I - Client */
4378 ippeve_job_t
*job
; /* Job information */
4379 ipp_attribute_t
*uri
; /* document-uri */
4380 char scheme
[256], /* URI scheme */
4381 userpass
[256], /* Username and password info */
4382 hostname
[256], /* Hostname */
4383 resource
[1024]; /* Resource path */
4384 int port
; /* Port number */
4385 http_uri_status_t uri_status
; /* URI decode status */
4386 http_encryption_t encryption
; /* Encryption to use, if any */
4387 http_t
*http
; /* Connection for http/https URIs */
4388 http_status_t status
; /* Access status for http/https URIs */
4389 int infile
; /* Input file for local file URIs */
4390 char filename
[1024], /* Filename buffer */
4391 buffer
[4096]; /* Copy buffer */
4392 ssize_t bytes
; /* Bytes read */
4393 ipp_attribute_t
*attr
; /* Current attribute */
4394 cups_array_t
*ra
; /* Attributes to send in response */
4395 static const char * const uri_status_strings
[] =
4396 { /* URI decode errors */
4398 "Bad arguments to function.",
4399 "Bad resource in URI.",
4400 "Bad port number in URI.",
4401 "Bad hostname in URI.",
4402 "Bad username in URI.",
4403 "Bad scheme in URI.",
4412 if ((job
= find_job(client
)) == NULL
)
4414 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4415 httpFlush(client
->http
);
4420 * See if we already have a document for this job or the job has already
4421 * in a non-pending state...
4424 if (job
->state
> IPP_JSTATE_HELD
)
4426 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4427 "Job is not in a pending state.");
4428 httpFlush(client
->http
);
4431 else if (job
->filename
|| job
->fd
>= 0)
4433 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4434 "Multiple document jobs are not supported.");
4435 httpFlush(client
->http
);
4439 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4440 IPP_TAG_ZERO
)) == NULL
)
4442 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4443 "Missing required last-document attribute.");
4444 httpFlush(client
->http
);
4447 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4448 !ippGetBoolean(attr
, 0))
4450 respond_unsupported(client
, attr
);
4451 httpFlush(client
->http
);
4456 * Validate document attributes...
4459 if (!valid_doc_attributes(client
))
4461 httpFlush(client
->http
);
4466 * Do we have a file to print?
4469 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4471 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4472 "Unexpected document data following request.");
4477 * Do we have a document URI?
4480 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4481 IPP_TAG_URI
)) == NULL
)
4483 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4487 if (ippGetCount(uri
) != 1)
4489 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4490 "Too many document-uri values.");
4494 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4495 scheme
, sizeof(scheme
), userpass
,
4496 sizeof(userpass
), hostname
, sizeof(hostname
),
4497 &port
, resource
, sizeof(resource
));
4498 if (uri_status
< HTTP_URI_STATUS_OK
)
4500 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4501 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4505 if (strcmp(scheme
, "file") &&
4507 strcmp(scheme
, "https") &&
4508 #endif /* HAVE_SSL */
4509 strcmp(scheme
, "http"))
4511 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4512 "URI scheme \"%s\" not supported.", scheme
);
4516 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4518 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4519 "Unable to access URI: %s", strerror(errno
));
4524 * Get the document format for the job...
4527 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4529 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4530 IPP_TAG_MIMETYPE
)) != NULL
)
4531 job
->format
= ippGetString(attr
, 0, NULL
);
4533 job
->format
= "application/octet-stream";
4536 * Create a file for the request data...
4539 if (!strcasecmp(job
->format
, "image/jpeg"))
4540 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4541 client
->printer
->directory
, job
->id
);
4542 else if (!strcasecmp(job
->format
, "image/png"))
4543 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4544 client
->printer
->directory
, job
->id
);
4545 else if (!strcasecmp(job
->format
, "application/pdf"))
4546 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4547 client
->printer
->directory
, job
->id
);
4548 else if (!strcasecmp(job
->format
, "application/postscript"))
4549 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4550 client
->printer
->directory
, job
->id
);
4552 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4553 client
->printer
->directory
, job
->id
);
4555 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4557 _cupsRWUnlock(&(client
->printer
->rwlock
));
4561 job
->state
= IPP_JSTATE_ABORTED
;
4563 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4564 "Unable to create print file: %s", strerror(errno
));
4568 if (!strcmp(scheme
, "file"))
4570 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4572 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4573 "Unable to access URI: %s", strerror(errno
));
4579 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4580 (errno
== EAGAIN
|| errno
== EINTR
))
4582 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4584 int error
= errno
; /* Write error */
4586 job
->state
= IPP_JSTATE_ABORTED
;
4594 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4595 "Unable to write print file: %s", strerror(error
));
4606 if (port
== 443 || !strcmp(scheme
, "https"))
4607 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4609 #endif /* HAVE_SSL */
4610 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4612 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4613 1, 30000, NULL
)) == NULL
)
4615 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4616 "Unable to connect to %s: %s", hostname
,
4617 cupsLastErrorString());
4618 job
->state
= IPP_JSTATE_ABORTED
;
4627 httpClearFields(http
);
4628 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4629 if (httpGet(http
, resource
))
4631 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4632 "Unable to GET URI: %s", strerror(errno
));
4634 job
->state
= IPP_JSTATE_ABORTED
;
4644 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4646 if (status
!= HTTP_STATUS_OK
)
4648 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4649 "Unable to GET URI: %s", httpStatus(status
));
4651 job
->state
= IPP_JSTATE_ABORTED
;
4661 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4663 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4665 int error
= errno
; /* Write error */
4667 job
->state
= IPP_JSTATE_ABORTED
;
4675 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4676 "Unable to write print file: %s", strerror(error
));
4686 int error
= errno
; /* Write error */
4688 job
->state
= IPP_JSTATE_ABORTED
;
4693 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4694 "Unable to write print file: %s", strerror(error
));
4698 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4701 job
->filename
= strdup(filename
);
4702 job
->state
= IPP_JSTATE_PENDING
;
4704 _cupsRWUnlock(&(client
->printer
->rwlock
));
4707 * Process the job...
4713 * Return the job info...
4716 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4718 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4719 cupsArrayAdd(ra
, "job-id");
4720 cupsArrayAdd(ra
, "job-state");
4721 cupsArrayAdd(ra
, "job-state-reasons");
4722 cupsArrayAdd(ra
, "job-uri");
4724 copy_job_attributes(client
, job
, ra
);
4725 cupsArrayDelete(ra
);
4730 * 'ipp_validate_job()' - Validate job creation attributes.
4734 ipp_validate_job(ippeve_client_t
*client
) /* I - Client */
4736 if (valid_job_attributes(client
))
4737 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4742 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
4745 static int /* O - 1 to use, 0 to ignore */
4747 _ipp_file_t
*f
, /* I - IPP file */
4748 void *user_data
, /* I - User data pointer (unused) */
4749 const char *attr
) /* I - Attribute name */
4751 int i
, /* Current element */
4752 result
; /* Result of comparison */
4753 static const char * const ignored
[] =
4754 { /* Ignored attributes */
4755 "attributes-charset",
4756 "attributes-natural-language",
4757 "charset-configured",
4758 "charset-supported",
4759 "device-service-count",
4761 "document-format-varying-attributes",
4762 "generated-natural-language-supported",
4763 "ippget-event-life",
4764 "job-hold-until-supported",
4765 "job-hold-until-time-supported",
4766 "job-ids-supported",
4767 "job-k-octets-supported",
4768 "job-settable-attributes-supported",
4769 "multiple-document-jobs-supported",
4770 "multiple-operation-time-out",
4771 "multiple-operation-time-out-action",
4772 "natural-language-configured",
4773 "notify-attributes-supported",
4774 "notify-events-default",
4775 "notify-events-supported",
4776 "notify-lease-duration-default",
4777 "notify-lease-duration-supported",
4778 "notify-max-events-supported",
4779 "notify-pull-method-supported",
4780 "operations-supported",
4782 "printer-alert-description",
4783 "printer-camera-image-uri",
4784 "printer-charge-info",
4785 "printer-charge-info-uri",
4786 "printer-config-change-date-time",
4787 "printer-config-change-time",
4788 "printer-current-time",
4789 "printer-detailed-status-messages",
4790 "printer-dns-sd-name",
4791 "printer-fax-log-uri",
4792 "printer-get-attributes-supported",
4796 "printer-is-accepting-jobs",
4797 "printer-message-date-time",
4798 "printer-message-from-operator",
4799 "printer-message-time",
4800 "printer-more-info",
4801 "printer-service-type",
4802 "printer-settable-attributes-supported",
4804 "printer-state-message",
4805 "printer-state-reasons",
4806 "printer-static-resource-directory-uri",
4807 "printer-static-resource-k-octets-free",
4808 "printer-static-resource-k-octets-supported",
4809 "printer-strings-languages-supported",
4810 "printer-strings-uri",
4811 "printer-supply-info-uri",
4813 "printer-uri-supported",
4814 "printer-xri-supported",
4816 "reference-uri-scheme-supported",
4817 "uri-authentication-supported",
4818 "uri-security-supported",
4819 "which-jobs-supported",
4820 "xri-authentication-supported",
4821 "xri-security-supported",
4822 "xri-uri-scheme-supported"
4829 for (i
= 0, result
= 1; i
< (int)(sizeof(ignored
) / sizeof(ignored
[0])); i
++)
4831 if ((result
= strcmp(attr
, ignored
[i
])) <= 0)
4835 return (result
!= 0);
4840 * 'ippserver_error_cb()' - Log an error message.
4843 static int /* O - 1 to continue, 0 to stop */
4845 _ipp_file_t
*f
, /* I - IPP file data */
4846 void *user_data
, /* I - User data pointer (unused) */
4847 const char *error
) /* I - Error message */
4852 _cupsLangPrintf(stderr
, "%s\n", error
);
4859 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
4862 static int /* O - 1 to continue, 0 to stop */
4864 _ipp_file_t
*f
, /* I - IPP file data */
4865 _ipp_vars_t
*vars
, /* I - IPP variables */
4866 void *user_data
, /* I - User data pointer (unused) */
4867 const char *token
) /* I - Current token */
4869 char temp
[1024], /* Temporary string */
4870 value
[1024]; /* Value string */
4878 * NULL token means do the initial setup - create an empty IPP message and
4882 f
->attrs
= ippNew();
4883 f
->group_tag
= IPP_TAG_PRINTER
;
4887 _cupsLangPrintf(stderr
, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token
, f
->linenum
, f
->filename
);
4895 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
4898 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4899 load_ippserver_attributes(
4900 const char *servername
, /* I - Server name or `NULL` for default */
4901 int serverport
, /* I - Server port number */
4902 const char *filename
, /* I - ippserver attribute filename */
4903 cups_array_t
*docformats
) /* I - document-format-supported values */
4905 ipp_t
*attrs
; /* IPP attributes */
4906 _ipp_vars_t vars
; /* IPP variables */
4907 char temp
[256]; /* Temporary string */
4911 * Setup callbacks and variables for the printer configuration file...
4913 * The following additional variables are supported:
4915 * - SERVERNAME: The host name of the server.
4916 * - SERVERPORT: The default port of the server.
4919 _ippVarsInit(&vars
, (_ipp_fattr_cb_t
)ippserver_attr_cb
, (_ipp_ferror_cb_t
)ippserver_error_cb
, (_ipp_ftoken_cb_t
)ippserver_token_cb
);
4923 _ippVarsSet(&vars
, "SERVERNAME", servername
);
4927 httpGetHostname(NULL
, temp
, sizeof(temp
));
4928 _ippVarsSet(&vars
, "SERVERNAME", temp
);
4931 snprintf(temp
, sizeof(temp
), "%d", serverport
);
4932 _ippVarsSet(&vars
, "SERVERPORT", temp
);
4935 * Load attributes and values for the printer...
4938 attrs
= _ippFileParse(&vars
, filename
, NULL
);
4941 * Free memory and return...
4944 _ippVarsDeinit(&vars
);
4951 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
4955 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4956 load_legacy_attributes(
4957 const char *make
, /* I - Manufacturer name */
4958 const char *model
, /* I - Model name */
4959 int ppm
, /* I - pages-per-minute */
4960 int ppm_color
, /* I - pages-per-minute-color */
4961 int duplex
, /* I - Duplex support? */
4962 cups_array_t
*docformats
) /* I - document-format-supported values */
4964 ipp_t
*attrs
; /* IPP attributes */
4965 char device_id
[1024],/* printer-device-id */
4966 make_model
[128];/* printer-make-and-model */
4967 static const int orientation_requested_supported
[4] =
4968 { /* orientation-requested-supported values */
4969 IPP_ORIENT_PORTRAIT
,
4970 IPP_ORIENT_LANDSCAPE
,
4971 IPP_ORIENT_REVERSE_LANDSCAPE
,
4972 IPP_ORIENT_REVERSE_PORTRAIT
4974 static const char * const print_color_mode_supported
[] =
4975 { /* print-color-mode-supported values */
4980 static const int print_quality_supported
[] =
4981 { /* print-quality-supported values */
4986 static const int pwg_raster_document_resolution_supported
[] =
4991 static const char * const pwg_raster_document_type_supported
[] =
4999 static const char * const sides_supported
[] =
5000 { /* sides-supported values */
5002 "two-sided-long-edge",
5003 "two-sided-short-edge"
5005 static const char * const urf_supported
[] =
5006 { /* urf-supported values */
5009 "MT1-2-3-4-5-6-8-9-10-11-12-13",
5020 /* color-supported */
5021 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
5023 /* copies-default */
5024 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
5026 /* copies-supported */
5027 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, strstr(formats
, "application/pdf") != NULL
? 999 : 1);
5029 /* finishings-default */
5030 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
5032 /* finishings-supported */
5033 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
5035 /* media-bottom-margin-supported */
5036 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin-supported", 635);
5038 /* media-col-database */
5039 if (!ippFindAttribute(attrs
, "media-col-database", IPP_TAG_ZERO
))
5041 for (num_database
= 0, i
= 0;
5042 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
5045 if (media_col_sizes
[i
][2] == IPPEVE_ENV_ONLY
)
5046 num_database
+= 3; /* auto + manual + envelope */
5047 else if (media_col_sizes
[i
][2] == IPPEVE_PHOTO_ONLY
)
5048 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
5050 num_database
+= 2; /* Regular + borderless */
5053 media_col_database
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
5054 for (media_col_index
= 0, i
= 0;
5055 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
5058 switch (media_col_sizes
[i
][2])
5060 case IPPEVE_GENERAL
:
5062 * Regular + borderless for the general class; no source/type
5066 ippSetCollection(attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], NULL
, NULL
, media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
5067 ippSetCollection(attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], NULL
, NULL
, media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
5070 case IPPEVE_ENV_ONLY
:
5072 * Regular margins for "auto", "manual", and "envelope" sources.
5075 ippSetCollection(attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "auto", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
5076 ippSetCollection(attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "manual", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
5077 ippSetCollection(attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "envelope", "envelope", media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[1]));
5079 case IPPEVE_PHOTO_ONLY
:
5081 * Photos have specific media types and can only be printed via
5082 * the auto, manual, and photo sources...
5086 j
< (int)(sizeof(media_type_supported
) /
5087 sizeof(media_type_supported
[0]));
5090 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
5093 ippSetCollection(attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "auto", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
5094 ippSetCollection(attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "manual", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
5095 ippSetCollection(attrs
, &media_col_database
, media_col_index
++, create_media_col(media_supported
[i
], "photo", media_type_supported
[j
], media_col_sizes
[i
][0], media_col_sizes
[i
][1], media_xxx_margin_supported
[0]));
5102 /* media-col-default */
5103 if (!ippFindAttribute(attrs
, "media-col-default", IPP_TAG_ZERO
))
5105 media_col_default
= create_media_col(media_supported
[0], media_source_supported
[0], media_type_supported
[0], media_col_sizes
[0][0], media_col_sizes
[0][1],media_xxx_margin_supported
[1]);
5107 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default",
5109 ippDelete(media_col_default
);
5112 /* media-col-supported */
5113 if (!ippFindAttribute(attrs
, "media-col-supported", IPP_TAG_ZERO
))
5114 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-col-supported", (int)(sizeof(media_col_supported
) / sizeof(media_col_supported
[0])), NULL
, media_col_supported
);
5117 if (!ippFindAttribute(attrs
, "media-default", IPP_TAG_ZERO
))
5118 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
5120 /* media-left-margin-supported */
5121 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin-supported", 635);
5123 /* media-right-margin-supported */
5124 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin-supported", 635);
5126 /* media-supported */
5127 if (!ippFindAttribute(attrs
, "media-supported", IPP_TAG_ZERO
))
5128 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-supported", (int)(sizeof(media_supported
) / sizeof(media_supported
[0])), NULL
, media_supported
);
5130 /* media-size-supported */
5131 if (!ippFindAttribute(attrs
, "media-size-supported", IPP_TAG_ZERO
))
5133 media_size_supported
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
5136 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
5139 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
5141 ippSetCollection(attrs
, &media_size_supported
, i
, size
);
5146 /* media-source-supported */
5147 if (!ippFindAttribute(attrs
, "media-source-supported", IPP_TAG_ZERO
))
5148 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-source-supported", (int)(sizeof(media_source_supported
) / sizeof(media_source_supported
[0])), NULL
, media_source_supported
);
5150 /* media-top-margin-supported */
5151 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin-supported", 635);
5153 /* media-type-supported */
5154 if (!ippFindAttribute(attrs
, "media-type-supported", IPP_TAG_ZERO
))
5155 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-type-supported", (int)(sizeof(media_type_supported
) / sizeof(media_type_supported
[0])), NULL
, media_type_supported
);
5157 /* multiple-document-handling-supported */
5158 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-document-handling-supported", sizeof(multiple_document_handling
) / sizeof(multiple_document_handling
[0]), NULL
, multiple_document_handling
);
5160 /* multiple-document-jobs-supported */
5161 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
5163 /* multiple-operation-time-out */
5164 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
5166 /* multiple-operation-time-out-action */
5167 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
5169 /* natural-language-configured */
5170 ippAddString(attrs
, IPP_TAG_PRINTER
,
5171 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
5172 "natural-language-configured", NULL
, "en");
5174 /* number-up-default */
5175 if (!ippFindAttribute(attrs
, "number-up-default", IPP_TAG_ZERO
))
5176 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
5178 /* number-up-supported */
5179 if (!ippFindAttribute(attrs
, "number-up-supported", IPP_TAG_ZERO
))
5180 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
5182 /* operations-supported */
5183 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
5185 /* orientation-requested-default */
5186 if (!ippFindAttribute(attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
5187 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
5189 /* orientation-requested-supported */
5190 if (!ippFindAttribute(attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
5191 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
5193 /* output-bin-default */
5194 if (!ippFindAttribute(attrs
, "output-bin-default", IPP_TAG_ZERO
))
5195 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
5197 /* output-bin-supported */
5198 if (!ippFindAttribute(attrs
, "output-bin-supported", IPP_TAG_ZERO
))
5199 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
5201 /* overrides-supported */
5202 if (!ippFindAttribute(attrs
, "overrides-supported", IPP_TAG_ZERO
))
5203 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
5205 /* page-ranges-supported */
5206 if (!ippFindAttribute(attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
5207 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
5209 /* pages-per-minute */
5210 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
5211 "pages-per-minute", ppm
);
5213 /* pages-per-minute-color */
5215 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
5216 "pages-per-minute-color", ppm_color
);
5218 /* pdl-override-supported */
5219 if (!ippFindAttribute(attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
5220 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
5222 /* preferred-attributes-supported */
5223 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
5225 /* print-color-mode-default */
5226 if (!ippFindAttribute(attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
5227 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
5229 /* print-color-mode-supported */
5230 if (!ippFindAttribute(attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
5231 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported
) / sizeof(print_color_mode_supported
[0])), NULL
, print_color_mode_supported
);
5233 /* print-content-optimize-default */
5234 if (!ippFindAttribute(attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
5235 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
5237 /* print-content-optimize-supported */
5238 if (!ippFindAttribute(attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
5239 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
5241 /* print-rendering-intent-default */
5242 if (!ippFindAttribute(attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
5243 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
5245 /* print-rendering-intent-supported */
5246 if (!ippFindAttribute(attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
5247 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
5249 /* print-quality-default */
5250 if (!ippFindAttribute(attrs
, "print-quality-default", IPP_TAG_ZERO
))
5251 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
5253 /* print-quality-supported */
5254 if (!ippFindAttribute(attrs
, "print-quality-supported", IPP_TAG_ZERO
))
5255 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
5257 /* printer-device-id */
5258 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
5260 /* printer-make-and-model */
5261 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
5262 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, make_model
);
5264 /* printer-resolution-default */
5265 if (!ippFindAttribute(attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
5266 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
5268 /* printer-resolution-supported */
5269 if (!ippFindAttribute(attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
5270 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
5272 /* printer-supply-description */
5273 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-supply-description", (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])), NULL
, printer_supplies
);
5275 /* pwg-raster-document-xxx-supported */
5276 for (i
= 0; i
< num_formats
; i
++)
5277 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
5280 if (i
< num_formats
)
5282 if (!ippFindAttribute(attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
5283 ippAddResolutions(attrs
, IPP_TAG_PRINTER
, "pwg-raster-document-resolution-supported", (int)(sizeof(pwg_raster_document_resolution_supported
) / sizeof(pwg_raster_document_resolution_supported
[0])), IPP_RES_PER_INCH
, pwg_raster_document_resolution_supported
, pwg_raster_document_resolution_supported
);
5284 if (!ippFindAttribute(attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
5285 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
5286 if (!ippFindAttribute(attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
5287 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported
) / sizeof(pwg_raster_document_type_supported
[0])), NULL
, pwg_raster_document_type_supported
);
5291 if (!ippFindAttribute(attrs
, "sides-default", IPP_TAG_ZERO
))
5292 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
5294 /* sides-supported */
5295 if (!ippFindAttribute(attrs
, "sides-supported", IPP_TAG_ZERO
))
5296 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
5299 for (i
= 0; i
< num_formats
; i
++)
5300 if (!strcasecmp(formats
[i
], "image/urf"))
5303 if (i
< num_formats
&& !ippFindAttribute(attrs
, "urf-supported", IPP_TAG_ZERO
))
5304 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
5311 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
5314 static ipp_t
* /* O - IPP attributes or `NULL` on error */
5315 load_ppd_attributes(
5316 const char *ppdfile
, /* I - PPD filename */
5317 cups_array_t
*docformats
) /* I - document-format-supported values */
5323 * 'parse_options()' - Parse URL options into CUPS options.
5325 * The client->options string is destroyed by this function.
5328 static int /* O - Number of options */
5329 parse_options(ippeve_client_t
*client
, /* I - Client */
5330 cups_option_t
**options
)/* O - Options */
5332 char *name
, /* Name */
5334 *next
; /* Next name=value pair */
5335 int num_options
= 0; /* Number of options */
5340 for (name
= client
->options
; name
&& *name
; name
= next
)
5342 if ((value
= strchr(name
, '=')) == NULL
)
5346 if ((next
= strchr(value
, '&')) != NULL
)
5349 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5352 return (num_options
);
5357 * 'process_attr_message()' - Process an ATTR: message from a command.
5361 process_attr_message(
5362 ippeve_job_t
*job
, /* I - Job */
5363 char *message
) /* I - Message */
5371 * 'process_client()' - Process client requests on a thread.
5374 static void * /* O - Exit status */
5375 process_client(ippeve_client_t
*client
) /* I - Client */
5378 * Loop until we are out of requests or timeout (30 seconds)...
5382 int first_time
= 1; /* First time request? */
5383 #endif /* HAVE_SSL */
5385 while (httpWait(client
->http
, 30000))
5391 * See if we need to negotiate a TLS connection...
5394 char buf
[1]; /* First byte from client */
5396 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5398 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5400 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5402 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5406 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5411 #endif /* HAVE_SSL */
5413 if (!process_http(client
))
5418 * Close the conection to the client and return...
5421 delete_client(client
);
5428 * 'process_http()' - Process a HTTP request.
5431 int /* O - 1 on success, 0 on failure */
5432 process_http(ippeve_client_t
*client
) /* I - Client connection */
5434 char uri
[1024]; /* URI */
5435 http_state_t http_state
; /* HTTP state */
5436 http_status_t http_status
; /* HTTP status */
5437 ipp_state_t ipp_state
; /* State of IPP transfer */
5438 char scheme
[32], /* Method/scheme */
5439 userpass
[128], /* Username:password */
5440 hostname
[HTTP_MAX_HOST
];
5442 int port
; /* Port number */
5443 const char *encoding
; /* Content-Encoding value */
5444 static const char * const http_states
[] =
5445 { /* Strings for logging HTTP method */
5466 * Clear state variables...
5469 ippDelete(client
->request
);
5470 ippDelete(client
->response
);
5472 client
->request
= NULL
;
5473 client
->response
= NULL
;
5474 client
->operation
= HTTP_STATE_WAITING
;
5477 * Read a request from the connection...
5480 while ((http_state
= httpReadRequest(client
->http
, uri
,
5481 sizeof(uri
))) == HTTP_STATE_WAITING
)
5485 * Parse the request line...
5488 if (http_state
== HTTP_STATE_ERROR
)
5490 if (httpError(client
->http
) == EPIPE
)
5491 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5493 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5494 strerror(httpError(client
->http
)));
5498 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5500 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5501 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5504 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5506 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5507 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5511 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5515 * Separate the URI into its components...
5518 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5519 userpass
, sizeof(userpass
),
5520 hostname
, sizeof(hostname
), &port
,
5521 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5522 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5524 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5525 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5529 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5530 *(client
->options
)++ = '\0';
5533 * Process the request...
5536 client
->start
= time(NULL
);
5537 client
->operation
= httpGetState(client
->http
);
5540 * Parse incoming parameters until the status changes...
5543 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5545 if (http_status
!= HTTP_STATUS_OK
)
5547 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5551 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5552 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5555 * HTTP/1.1 and higher require the "Host:" field...
5558 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5563 * Handle HTTP Upgrade...
5566 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5570 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5572 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5575 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5577 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5579 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5583 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5586 #endif /* HAVE_SSL */
5588 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5593 * Handle HTTP Expect...
5596 if (httpGetExpect(client
->http
) &&
5597 (client
->operation
== HTTP_STATE_POST
||
5598 client
->operation
== HTTP_STATE_PUT
))
5600 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5603 * Send 100-continue header...
5606 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5612 * Send 417-expectation-failed header...
5615 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5621 * Handle new transfers...
5624 encoding
= httpGetContentEncoding(client
->http
);
5626 switch (client
->operation
)
5628 case HTTP_STATE_OPTIONS
:
5630 * Do OPTIONS command...
5633 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5635 case HTTP_STATE_HEAD
:
5636 if (!strcmp(client
->uri
, "/icon.png"))
5637 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5638 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5639 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5641 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5643 case HTTP_STATE_GET
:
5644 if (!strcmp(client
->uri
, "/icon.png"))
5647 * Send PNG icon file.
5650 int fd
; /* Icon file */
5651 struct stat fileinfo
; /* Icon file information */
5652 char buffer
[4096]; /* Copy buffer */
5653 ssize_t bytes
; /* Bytes */
5655 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5657 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5658 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5660 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5661 (size_t)fileinfo
.st_size
))
5667 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5668 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5670 httpFlushWrite(client
->http
);
5675 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5677 else if (!strcmp(client
->uri
, "/"))
5680 * Show web status page...
5683 ippeve_job_t
*job
; /* Current job */
5684 int i
; /* Looping var */
5685 ippeve_preason_t reason
; /* Current reason */
5686 static const char * const reasons
[] =
5687 { /* Reason strings */
5690 "Input Tray Missing",
5691 "Marker Supply Empty",
5692 "Marker Supply Low",
5693 "Marker Waste Almost Full",
5694 "Marker Waste Full",
5706 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5709 html_header(client
, client
->printer
->name
);
5711 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5712 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5713 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5714 if (client
->printer
->state_reasons
& reason
)
5715 html_printf(client
, "\n<br> %s", reasons
[i
]);
5716 html_printf(client
, "</p>\n");
5718 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5720 _cupsRWLockRead(&(client
->printer
->rwlock
));
5722 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");
5723 for (job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5725 char when
[256], /* When job queued/started/finished */
5726 hhmmss
[64]; /* Time HH:MM:SS */
5730 case IPP_JSTATE_PENDING
:
5731 case IPP_JSTATE_HELD
:
5732 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5734 case IPP_JSTATE_PROCESSING
:
5735 case IPP_JSTATE_STOPPED
:
5736 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5738 case IPP_JSTATE_ABORTED
:
5739 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5741 case IPP_JSTATE_CANCELED
:
5742 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5744 case IPP_JSTATE_COMPLETED
:
5745 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5749 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
);
5751 html_printf(client
, "</tbody></table>\n");
5753 _cupsRWUnlock(&(client
->printer
->rwlock
));
5755 html_footer(client
);
5759 else if (!strcmp(client
->uri
, "/media"))
5762 * Show web media page...
5765 int i
, /* Looping var */
5766 num_options
; /* Number of form options */
5767 cups_option_t
*options
; /* Form options */
5768 static const char * const sizes
[] =
5769 { /* Size strings */
5782 static const char * const types
[] =
5799 static const int sheets
[] = /* Number of sheets */
5808 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5811 html_header(client
, client
->printer
->name
);
5813 if ((num_options
= parse_options(client
, &options
)) > 0)
5816 * WARNING: A real printer/server implementation MUST NOT implement
5817 * media updates via a GET request - GET requests are supposed to be
5818 * idempotent (without side-effects) and we obviously are not
5819 * authenticating access here. This form is provided solely to
5820 * enable testing and development!
5823 const char *val
; /* Form value */
5825 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5826 client
->printer
->main_size
= atoi(val
);
5827 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5828 client
->printer
->main_type
= atoi(val
);
5829 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5830 client
->printer
->main_level
= atoi(val
);
5832 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5833 client
->printer
->envelope_size
= atoi(val
);
5834 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5835 client
->printer
->envelope_level
= atoi(val
);
5837 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5838 client
->printer
->photo_size
= atoi(val
);
5839 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5840 client
->printer
->photo_type
= atoi(val
);
5841 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5842 client
->printer
->photo_level
= atoi(val
);
5844 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))
5845 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_LOW
;
5847 client
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_LOW
;
5849 if ((client
->printer
->main_level
== 0 && client
->printer
->main_size
> IPPEVE_MEDIA_SIZE_NONE
) || (client
->printer
->envelope_level
== 0 && client
->printer
->envelope_size
> IPPEVE_MEDIA_SIZE_NONE
) || (client
->printer
->photo_level
== 0 && client
->printer
->photo_size
> IPPEVE_MEDIA_SIZE_NONE
))
5851 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_EMPTY
;
5852 if (client
->printer
->active_job
)
5853 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
5856 client
->printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MEDIA_EMPTY
| IPPEVE_PREASON_MEDIA_NEEDED
);
5858 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5861 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5863 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5864 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5865 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5866 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5867 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5868 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5869 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5870 if (!strstr(types
[i
], "Photo"))
5871 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5872 html_printf(client
, "</select> <select name=\"main_level\">");
5873 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5874 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5875 html_printf(client
, "</select></td></tr>\n");
5878 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5879 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5880 if (strstr(sizes
[i
], "Envelope"))
5881 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5882 html_printf(client
, "</select> <select name=\"envelope_level\">");
5883 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5884 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5885 html_printf(client
, "</select></td></tr>\n");
5888 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5889 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5890 if (strstr(sizes
[i
], "Photo"))
5891 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5892 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5893 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5894 if (strstr(types
[i
], "Photo"))
5895 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5896 html_printf(client
, "</select> <select name=\"photo_level\">");
5897 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5898 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5899 html_printf(client
, "</select></td></tr>\n");
5901 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5902 html_footer(client
);
5906 else if (!strcmp(client
->uri
, "/supplies"))
5909 * Show web supplies page...
5912 int i
, j
, /* Looping vars */
5913 num_options
; /* Number of form options */
5914 cups_option_t
*options
; /* Form options */
5915 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5917 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5920 html_header(client
, client
->printer
->name
);
5922 if ((num_options
= parse_options(client
, &options
)) > 0)
5925 * WARNING: A real printer/server implementation MUST NOT implement
5926 * supply updates via a GET request - GET requests are supposed to be
5927 * idempotent (without side-effects) and we obviously are not
5928 * authenticating access here. This form is provided solely to
5929 * enable testing and development!
5932 char name
[64]; /* Form field */
5933 const char *val
; /* Form value */
5935 client
->printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
| IPPEVE_PREASON_MARKER_SUPPLY_LOW
| IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
| IPPEVE_PREASON_MARKER_WASTE_FULL
| IPPEVE_PREASON_TONER_EMPTY
| IPPEVE_PREASON_TONER_LOW
);
5937 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5939 snprintf(name
, sizeof(name
), "supply_%d", i
);
5940 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5942 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5948 client
->printer
->state_reasons
|= IPPEVE_PREASON_TONER_EMPTY
;
5949 else if (level
< 10)
5950 client
->printer
->state_reasons
|= IPPEVE_PREASON_TONER_LOW
;
5955 client
->printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_FULL
;
5956 else if (level
> 90)
5957 client
->printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
;
5962 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5965 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5967 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5968 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5970 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5971 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5972 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5973 html_printf(client
, "</select></td></tr>\n");
5975 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5976 html_footer(client
);
5981 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5984 case HTTP_STATE_POST
:
5985 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5989 * Not an IPP request...
5992 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5996 * Read the IPP request...
5999 client
->request
= ippNew();
6001 while ((ipp_state
= ippRead(client
->http
,
6002 client
->request
)) != IPP_STATE_DATA
)
6004 if (ipp_state
== IPP_STATE_ERROR
)
6006 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
6007 cupsLastErrorString());
6008 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
6014 * Now that we have the IPP request, process the request...
6017 return (process_ipp(client
));
6020 break; /* Anti-compiler-warning-code */
6028 * 'process_ipp()' - Process an IPP request.
6031 static int /* O - 1 on success, 0 on error */
6032 process_ipp(ippeve_client_t
*client
) /* I - Client */
6034 ipp_tag_t group
; /* Current group tag */
6035 ipp_attribute_t
*attr
; /* Current attribute */
6036 ipp_attribute_t
*charset
; /* Character set attribute */
6037 ipp_attribute_t
*language
; /* Language attribute */
6038 ipp_attribute_t
*uri
; /* Printer URI attribute */
6039 int major
, minor
; /* Version number */
6040 const char *name
; /* Name of attribute */
6043 debug_attributes("Request", client
->request
, 1);
6046 * First build an empty response message for this request...
6049 client
->operation_id
= ippGetOperation(client
->request
);
6050 client
->response
= ippNewResponse(client
->request
);
6053 * Then validate the request header and required attributes...
6056 major
= ippGetVersion(client
->request
, &minor
);
6058 if (major
< 1 || major
> 2)
6061 * Return an error, since we only support IPP 1.x and 2.x.
6064 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
6066 else if ((major
* 10 + minor
) > MaxVersion
)
6068 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6069 httpFlush(client
->http
); /* Flush trailing (junk) data */
6071 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
6074 else if (ippGetRequestId(client
->request
) <= 0)
6076 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
6078 else if (!ippFirstAttribute(client
->request
))
6080 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
6085 * Make sure that the attributes are provided in the correct order and
6086 * don't repeat groups...
6089 for (attr
= ippFirstAttribute(client
->request
),
6090 group
= ippGetGroupTag(attr
);
6092 attr
= ippNextAttribute(client
->request
))
6094 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
6097 * Out of order; return an error...
6100 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6101 "Attribute groups are out of order (%x < %x).",
6102 ippGetGroupTag(attr
), group
);
6106 group
= ippGetGroupTag(attr
);
6112 * Then make sure that the first three attributes are:
6114 * attributes-charset
6115 * attributes-natural-language
6116 * printer-uri/job-uri
6119 attr
= ippFirstAttribute(client
->request
);
6120 name
= ippGetName(attr
);
6121 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
6122 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
6127 attr
= ippNextAttribute(client
->request
);
6128 name
= ippGetName(attr
);
6130 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
6131 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
6136 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
6137 IPP_TAG_URI
)) != NULL
)
6139 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
6140 IPP_TAG_URI
)) != NULL
)
6146 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
6147 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
6150 * Bad character set...
6153 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6154 "Unsupported character set \"%s\".",
6155 ippGetString(charset
, 0, NULL
));
6157 else if (!charset
|| !language
|| !uri
)
6160 * Return an error, since attributes-charset,
6161 * attributes-natural-language, and printer-uri/job-uri are required
6162 * for all operations.
6165 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6166 "Missing required attributes.");
6170 char scheme
[32], /* URI scheme */
6171 userpass
[32], /* Username/password in URI */
6172 host
[256], /* Host name in URI */
6173 resource
[256]; /* Resource path in URI */
6174 int port
; /* Port number in URI */
6176 name
= ippGetName(uri
);
6178 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
6179 scheme
, sizeof(scheme
),
6180 userpass
, sizeof(userpass
),
6181 host
, sizeof(host
), &port
,
6182 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
6183 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6184 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
6185 else if ((!strcmp(name
, "job-uri") &&
6186 strncmp(resource
, "/ipp/print/", 11)) ||
6187 (!strcmp(name
, "printer-uri") &&
6188 strcmp(resource
, "/ipp/print")))
6189 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
6190 name
, ippGetString(uri
, 0, NULL
));
6194 * Try processing the operation...
6197 switch (ippGetOperation(client
->request
))
6199 case IPP_OP_PRINT_JOB
:
6200 ipp_print_job(client
);
6203 case IPP_OP_PRINT_URI
:
6204 ipp_print_uri(client
);
6207 case IPP_OP_VALIDATE_JOB
:
6208 ipp_validate_job(client
);
6211 case IPP_OP_CREATE_JOB
:
6212 ipp_create_job(client
);
6215 case IPP_OP_SEND_DOCUMENT
:
6216 ipp_send_document(client
);
6219 case IPP_OP_SEND_URI
:
6220 ipp_send_uri(client
);
6223 case IPP_OP_CANCEL_JOB
:
6224 ipp_cancel_job(client
);
6227 case IPP_OP_GET_JOB_ATTRIBUTES
:
6228 ipp_get_job_attributes(client
);
6231 case IPP_OP_GET_JOBS
:
6232 ipp_get_jobs(client
);
6235 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
6236 ipp_get_printer_attributes(client
);
6239 case IPP_OP_CLOSE_JOB
:
6240 ipp_close_job(client
);
6243 case IPP_OP_IDENTIFY_PRINTER
:
6244 ipp_identify_printer(client
);
6248 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
6249 "Operation not supported.");
6258 * Send the HTTP header and return...
6261 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6262 httpFlush(client
->http
); /* Flush trailing (junk) data */
6264 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6265 ippLength(client
->response
)));
6270 * 'process_job()' - Process a print job.
6273 static void * /* O - Thread exit status */
6274 process_job(ippeve_job_t
*job
) /* I - Job */
6276 job
->state
= IPP_JSTATE_PROCESSING
;
6277 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6278 job
->processing
= time(NULL
);
6280 while (job
->printer
->state_reasons
& IPPEVE_PREASON_MEDIA_EMPTY
)
6282 job
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
6287 job
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_NEEDED
;
6289 if (job
->printer
->command
)
6292 * Execute a command with the job spool file and wait for it to complete...
6295 int pid
, /* Process ID */
6296 status
; /* Exit status */
6297 time_t start
, /* Start time */
6299 char *myargv
[3], /* Command-line arguments */
6300 *myenvp
[200]; /* Environment variables */
6301 int myenvc
; /* Number of environment variables */
6302 ipp_attribute_t
*attr
; /* Job attribute */
6303 char val
[1280], /* IPP_NAME=value */
6304 *valptr
; /* Pointer into string */
6306 int mypipe
[2]; /* Pipe for stderr */
6307 char line
[2048], /* Line from stderr */
6308 *ptr
, /* Pointer into line */
6309 *endptr
; /* End of line */
6310 ssize_t bytes
; /* Bytes read */
6311 #endif /* !_WIN32 */
6313 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
6318 * Setup the command-line arguments...
6321 myargv
[0] = job
->printer
->command
;
6322 myargv
[1] = job
->filename
;
6326 * Copy the current environment, then add ENV variables for every Job
6330 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6331 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6333 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6336 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6337 * value(s) from the attribute.
6340 const char *name
= ippGetName(attr
);
6349 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6354 *valptr
++ = (char)toupper(*name
& 255);
6359 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6361 myenvp
[myenvc
++] = strdup(val
);
6363 myenvp
[myenvc
] = NULL
;
6366 * Now run the program...
6370 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6375 perror("Unable to create pipe for stderr");
6376 mypipe
[0] = mypipe
[1] = -1;
6379 if ((pid
= fork()) == 0)
6382 * Child comes here...
6390 execve(job
->printer
->command
, myargv
, myenvp
);
6396 * Unable to fork process...
6399 perror("Unable to start job processing command");
6406 * Free memory used for environment...
6410 free(myenvp
[-- myenvc
]);
6415 * Free memory used for environment...
6419 free(myenvp
[-- myenvc
]);
6422 * If the pipe exists, read from it until EOF...
6430 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6435 while ((ptr
= strchr(line
, '\n')) != NULL
)
6439 if (!strncmp(line
, "STATE:", 6))
6442 * Process printer-state-reasons keywords.
6445 process_state_message(job
, line
);
6447 else if (!strncmp(line
, "ATTR:", 5))
6450 * Process printer attribute update.
6453 process_attr_message(job
, line
);
6455 else if (Verbosity
> 1)
6456 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
6460 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6470 * Wait for child to complete...
6473 # ifdef HAVE_WAITPID
6474 while (waitpid(pid
, &status
, 0) < 0);
6476 while (wait(&status
) < 0);
6477 # endif /* HAVE_WAITPID */
6484 if (WIFEXITED(status
))
6485 #endif /* !_WIN32 */
6486 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
6487 job
->printer
->command
, WEXITSTATUS(status
));
6490 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
6491 job
->printer
->command
, WTERMSIG(status
));
6492 #endif /* !_WIN32 */
6493 job
->state
= IPP_JSTATE_ABORTED
;
6495 else if (status
< 0)
6496 job
->state
= IPP_JSTATE_ABORTED
;
6498 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
6499 job
->printer
->command
);
6502 * Make sure processing takes at least 5 seconds...
6506 if ((end
- start
) < 5)
6512 * Sleep for a random amount of time to simulate job processing.
6515 sleep((unsigned)(5 + (rand() % 11)));
6519 job
->state
= IPP_JSTATE_CANCELED
;
6520 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6521 job
->state
= IPP_JSTATE_COMPLETED
;
6523 job
->completed
= time(NULL
);
6524 job
->printer
->state
= IPP_PSTATE_IDLE
;
6525 job
->printer
->active_job
= NULL
;
6532 * 'process_state_message()' - Process a STATE: message from a command.
6536 process_state_message(
6537 ippeve_job_t
*job
, /* I - Job */
6538 char *message
) /* I - Message */
6540 int i
; /* Looping var */
6541 ippeve_preason_t state_reasons
, /* printer-state-reasons values */
6542 bit
; /* Current reason bit */
6543 char *ptr
, /* Pointer into message */
6544 *next
; /* Next keyword in message */
6545 int remove
; /* Non-zero if we are removing keywords */
6549 * Skip leading "STATE:" and any whitespace...
6552 for (message
+= 6; *message
; message
++)
6553 if (*message
!= ' ' && *message
!= '\t')
6557 * Support the following forms of message:
6559 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6561 * "-keyword[,keyword,...]" to remove keywords.
6563 * "+keyword[,keyword,...]" to add keywords.
6565 * Keywords may or may not have a suffix (-report, -warning, -error) per
6569 if (*message
== '-')
6572 state_reasons
= job
->printer
->state_reasons
;
6575 else if (*message
== '+')
6578 state_reasons
= job
->printer
->state_reasons
;
6584 state_reasons
= IPPEVE_PREASON_NONE
;
6589 if ((next
= strchr(message
, ',')) != NULL
)
6592 if ((ptr
= strstr(message
, "-error")) != NULL
)
6594 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6596 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6599 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
6601 if (!strcmp(message
, ippeve_preason_strings
[i
]))
6604 state_reasons
&= ~bit
;
6606 state_reasons
|= bit
;
6616 job
->printer
->state_reasons
= state_reasons
;
6621 * 'register_printer()' - Register a printer object via Bonjour.
6624 static int /* O - 1 on success, 0 on error */
6626 ippeve_printer_t
*printer
, /* I - Printer */
6627 const char *location
, /* I - Location */
6628 const char *make
, /* I - Manufacturer */
6629 const char *model
, /* I - Model name */
6630 const char *formats
, /* I - Supported formats */
6631 const char *adminurl
, /* I - Web interface URL */
6632 const char *uuid
, /* I - Printer UUID */
6633 int color
, /* I - 1 = color, 0 = monochrome */
6634 int duplex
, /* I - 1 = duplex, 0 = simplex */
6635 const char *subtype
) /* I - Service subtype */
6637 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6638 ippeve_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6639 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6641 DNSServiceErrorType error
; /* Error from Bonjour */
6642 char make_model
[256],/* Make and model together */
6643 product
[256], /* Product string */
6644 regtype
[256]; /* Bonjour service type */
6648 * Build the TXT record for IPP...
6651 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
6652 snprintf(product
, sizeof(product
), "(%s)", model
);
6654 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6655 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6656 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
6658 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
6661 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
6663 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
6665 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
6667 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
6668 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
6669 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
6671 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
6673 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
6675 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6676 # endif /* HAVE_SSL */
6677 if (strstr(formats
, "image/urf"))
6678 TXTRecordSetValue(&ipp_txt
, "URF", 66, "CP1,IS1-5-7,MT1-2-3-4-5-6-8-9-10-11-12-13,RS300,SRGB24,V1.4,W8,DM1");
6680 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6681 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6684 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6685 * defend our service name but not actually support LPD...
6688 printer
->printer_ref
= DNSSDMaster
;
6690 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
6691 kDNSServiceFlagsShareConnection
,
6692 0 /* interfaceIndex */, printer
->dnssd_name
,
6693 "_printer._tcp", NULL
/* domain */,
6694 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
6695 NULL
/* txtRecord */,
6696 (DNSServiceRegisterReply
)dnssd_callback
,
6697 printer
)) != kDNSServiceErr_NoError
)
6699 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
6700 printer
->dnssd_name
, error
);
6705 * Then register the ippeve._tcp (IPP) service type with the real port number to
6706 * advertise our IPP printer...
6709 printer
->ipp_ref
= DNSSDMaster
;
6711 if (subtype
&& *subtype
)
6712 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
6714 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6716 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
6717 kDNSServiceFlagsShareConnection
,
6718 0 /* interfaceIndex */, printer
->dnssd_name
,
6719 regtype
, NULL
/* domain */,
6720 NULL
/* host */, htons(printer
->port
),
6721 TXTRecordGetLength(&ipp_txt
),
6722 TXTRecordGetBytesPtr(&ipp_txt
),
6723 (DNSServiceRegisterReply
)dnssd_callback
,
6724 printer
)) != kDNSServiceErr_NoError
)
6726 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6727 printer
->dnssd_name
, regtype
, error
);
6733 * Then register the ippeves._tcp (IPP) service type with the real port number to
6734 * advertise our IPPS printer...
6737 printer
->ipps_ref
= DNSSDMaster
;
6739 if (subtype
&& *subtype
)
6740 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
6742 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6744 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
6745 kDNSServiceFlagsShareConnection
,
6746 0 /* interfaceIndex */, printer
->dnssd_name
,
6747 regtype
, NULL
/* domain */,
6748 NULL
/* host */, htons(printer
->port
),
6749 TXTRecordGetLength(&ipp_txt
),
6750 TXTRecordGetBytesPtr(&ipp_txt
),
6751 (DNSServiceRegisterReply
)dnssd_callback
,
6752 printer
)) != kDNSServiceErr_NoError
)
6754 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6755 printer
->dnssd_name
, regtype
, error
);
6758 # endif /* HAVE_SSL */
6761 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6762 * real port number to advertise our IPP printer...
6765 printer
->http_ref
= DNSSDMaster
;
6767 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
6768 kDNSServiceFlagsShareConnection
,
6769 0 /* interfaceIndex */, printer
->dnssd_name
,
6770 "_http._tcp,_printer", NULL
/* domain */,
6771 NULL
/* host */, htons(printer
->port
),
6772 0 /* txtLen */, NULL
, /* txtRecord */
6773 (DNSServiceRegisterReply
)dnssd_callback
,
6774 printer
)) != kDNSServiceErr_NoError
)
6776 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6777 printer
->dnssd_name
, regtype
, error
);
6781 TXTRecordDeallocate(&ipp_txt
);
6783 #elif defined(HAVE_AVAHI)
6784 char temp
[256]; /* Subtype service string */
6787 * Create the TXT record...
6791 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6792 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
6793 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
6795 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
6796 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
6797 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6798 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
6799 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
6800 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
6801 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
6802 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
6804 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6805 # endif /* HAVE_SSL */
6808 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6811 avahi_threaded_poll_lock(DNSSDMaster
);
6813 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6815 avahi_entry_group_add_service_strlst(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_printer._tcp", NULL
, NULL
, 0, NULL
);
6818 * Then register the ippeve._tcp (IPP)...
6821 avahi_entry_group_add_service_strlst(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, NULL
, printer
->port
, ipp_txt
);
6822 if (subtype
&& *subtype
)
6824 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6825 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6830 * ippeves._tcp (IPPS) for secure printing...
6833 avahi_entry_group_add_service_strlst(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, NULL
, printer
->port
, ipp_txt
);
6834 if (subtype
&& *subtype
)
6836 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6837 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6839 #endif /* HAVE_SSL */
6842 * Finally _http.tcp (HTTP) for the web interface...
6845 avahi_entry_group_add_service_strlst(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_http._tcp", NULL
, NULL
, printer
->port
, NULL
);
6846 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_http._tcp", NULL
, "_printer._sub._http._tcp");
6852 avahi_entry_group_commit(printer
->ipp_ref
);
6853 avahi_threaded_poll_unlock(DNSSDMaster
);
6855 avahi_string_list_free(ipp_txt
);
6856 #endif /* HAVE_DNSSD */
6863 * 'respond_http()' - Send a HTTP response.
6866 int /* O - 1 on success, 0 on failure */
6868 ippeve_client_t
*client
, /* I - Client */
6869 http_status_t code
, /* I - HTTP status of response */
6870 const char *content_encoding
, /* I - Content-Encoding of response */
6871 const char *type
, /* I - MIME media type of response */
6872 size_t length
) /* I - Length of response */
6874 char message
[1024]; /* Text message */
6877 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6879 if (code
== HTTP_STATUS_CONTINUE
)
6882 * 100-continue doesn't send any headers...
6885 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6889 * Format an error message...
6892 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6894 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6896 type
= "text/plain";
6897 length
= strlen(message
);
6903 * Send the HTTP response header...
6906 httpClearFields(client
->http
);
6908 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6909 client
->operation
== HTTP_STATE_OPTIONS
)
6910 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6914 if (!strcmp(type
, "text/html"))
6915 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6916 "text/html; charset=utf-8");
6918 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6920 if (content_encoding
)
6921 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6924 httpSetLength(client
->http
, length
);
6926 if (httpWriteResponse(client
->http
, code
) < 0)
6930 * Send the response data...
6936 * Send a plain text message.
6939 if (httpPrintf(client
->http
, "%s", message
) < 0)
6942 if (httpWrite2(client
->http
, "", 0) < 0)
6945 else if (client
->response
)
6948 * Send an IPP response...
6951 debug_attributes("Response", client
->response
, 2);
6953 ippSetState(client
->response
, IPP_STATE_IDLE
);
6955 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6964 * 'respond_ipp()' - Send an IPP response.
6968 respond_ipp(ippeve_client_t
*client
, /* I - Client */
6969 ipp_status_t status
, /* I - status-code */
6970 const char *message
, /* I - printf-style status-message */
6971 ...) /* I - Additional args as needed */
6973 const char *formatted
= NULL
; /* Formatted message */
6976 ippSetStatusCode(client
->response
, status
);
6980 va_list ap
; /* Pointer to additional args */
6981 ipp_attribute_t
*attr
; /* New status-message attribute */
6983 va_start(ap
, message
);
6984 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6985 IPP_TAG_TEXT
)) != NULL
)
6986 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6988 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6989 "status-message", NULL
, message
, ap
);
6992 formatted
= ippGetString(attr
, 0, NULL
);
6996 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6997 ippOpString(client
->operation_id
), ippErrorString(status
),
7000 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
7001 ippOpString(client
->operation_id
), ippErrorString(status
));
7006 * 'respond_unsupported()' - Respond with an unsupported attribute.
7010 respond_unsupported(
7011 ippeve_client_t
*client
, /* I - Client */
7012 ipp_attribute_t
*attr
) /* I - Atribute */
7014 ipp_attribute_t
*temp
; /* Copy of attribute */
7017 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
7018 "Unsupported %s %s%s value.", ippGetName(attr
),
7019 ippGetCount(attr
) > 1 ? "1setOf " : "",
7020 ippTagString(ippGetValueTag(attr
)));
7022 temp
= ippCopyAttribute(client
->response
, attr
, 0);
7023 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
7028 * 'run_printer()' - Run the printer service.
7032 run_printer(ippeve_printer_t
*printer
) /* I - Printer */
7034 int num_fds
; /* Number of file descriptors */
7035 struct pollfd polldata
[3]; /* poll() data */
7036 int timeout
; /* Timeout for poll() */
7037 ippeve_client_t
*client
; /* New client */
7041 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
7044 polldata
[0].fd
= printer
->ipv4
;
7045 polldata
[0].events
= POLLIN
;
7047 polldata
[1].fd
= printer
->ipv6
;
7048 polldata
[1].events
= POLLIN
;
7053 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
7054 polldata
[num_fds
++].events
= POLLIN
;
7055 #endif /* HAVE_DNSSD */
7058 * Loop until we are killed or have a hard error...
7063 if (cupsArrayCount(printer
->jobs
))
7068 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
7070 perror("poll() failed");
7074 if (polldata
[0].revents
& POLLIN
)
7076 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
7078 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
7082 _cupsThreadDetach(t
);
7086 perror("Unable to create client thread");
7087 delete_client(client
);
7092 if (polldata
[1].revents
& POLLIN
)
7094 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
7096 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
7100 _cupsThreadDetach(t
);
7104 perror("Unable to create client thread");
7105 delete_client(client
);
7111 if (polldata
[2].revents
& POLLIN
)
7112 DNSServiceProcessResult(DNSSDMaster
);
7113 #endif /* HAVE_DNSSD */
7116 * Clean out old jobs...
7119 clean_jobs(printer
);
7125 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7129 time_string(time_t tv
, /* I - Time value */
7130 char *buffer
, /* I - Buffer */
7131 size_t bufsize
) /* I - Size of buffer */
7133 struct tm
*curtime
= localtime(&tv
);
7136 strftime(buffer
, bufsize
, "%X", curtime
);
7142 * 'usage()' - Show program usage.
7146 usage(int status
) /* O - Exit status */
7150 puts(CUPS_SVERSION
" - Copyright (c) 2010-2018 by Apple Inc. All rights reserved.");
7154 puts("Usage: ippserver [options] \"name\"");
7157 puts("-2 Supports 2-sided printing (default=1-sided)");
7158 puts("-M manufacturer Manufacturer name (default=Test)");
7159 puts("-P PIN printing mode");
7160 puts("-V max-version Set maximum supported IPP version");
7161 puts("-a attributes-file Load printer attributes from file");
7162 puts("-c command Run command for every print job");
7163 printf("-d spool-directory Spool directory "
7164 "(default=/tmp/ippserver.%d)\n", (int)getpid());
7165 puts("-f type/subtype[,...] List of supported types "
7166 "(default=application/pdf,image/jpeg)");
7167 puts("-h Show program help");
7168 puts("-i iconfile.png PNG icon file (default=printer.png)");
7169 puts("-k Keep job spool files");
7170 puts("-l location Location of printer (default=empty string)");
7171 puts("-m model Model name (default=Printer)");
7172 puts("-n hostname Hostname for printer");
7173 puts("-p port Port number (default=auto)");
7174 puts("-r subtype Bonjour service subtype (default=_print)");
7175 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
7176 puts("-v[vvv] Be (very) verbose");
7183 * 'valid_doc_attributes()' - Determine whether the document attributes are
7186 * When one or more document attributes are invalid, this function adds a
7187 * suitable response and attributes to the unsupported group.
7190 static int /* O - 1 if valid, 0 if not */
7191 valid_doc_attributes(
7192 ippeve_client_t
*client
) /* I - Client */
7194 int valid
= 1; /* Valid attributes? */
7195 ipp_op_t op
= ippGetOperation(client
->request
);
7197 const char *op_name
= ippOpString(op
);
7198 /* IPP operation name */
7199 ipp_attribute_t
*attr
, /* Current attribute */
7200 *supported
; /* xxx-supported attribute */
7201 const char *compression
= NULL
,
7202 /* compression value */
7203 *format
= NULL
; /* document-format value */
7207 * Check operation attributes...
7210 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
7213 * If compression is specified, only accept a supported value in a Print-Job
7214 * or Send-Document request...
7217 compression
= ippGetString(attr
, 0, NULL
);
7218 supported
= ippFindAttribute(client
->printer
->attrs
,
7219 "compression-supported", IPP_TAG_KEYWORD
);
7221 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7222 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
7223 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
7224 op
!= IPP_OP_VALIDATE_JOB
) ||
7225 !ippContainsString(supported
, compression
))
7227 respond_unsupported(client
, attr
);
7232 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
7234 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
7236 if (strcmp(compression
, "none"))
7239 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
7240 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
7246 * Is it a format we support?
7249 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
7251 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
7252 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
7254 respond_unsupported(client
, attr
);
7259 format
= ippGetString(attr
, 0, NULL
);
7261 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
7262 client
->hostname
, op_name
, format
);
7264 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
7269 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
7271 format
= "application/octet-stream"; /* Should never happen */
7273 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
7276 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7279 * Auto-type the file using the first 8 bytes of the file...
7282 unsigned char header
[8]; /* First 8 bytes of file */
7284 memset(header
, 0, sizeof(header
));
7285 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7287 if (!memcmp(header
, "%PDF", 4))
7288 format
= "application/pdf";
7289 else if (!memcmp(header
, "%!", 2))
7290 format
= "application/postscript";
7291 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7292 format
= "image/jpeg";
7293 else if (!memcmp(header
, "\211PNG", 4))
7294 format
= "image/png";
7295 else if (!memcmp(header
, "RAS2", 4))
7296 format
= "image/pwg-raster";
7297 else if (!memcmp(header
, "UNIRAST", 8))
7298 format
= "image/urf";
7304 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
7305 client
->hostname
, op_name
, format
);
7307 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7311 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7313 respond_unsupported(client
, attr
);
7321 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7322 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7329 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7331 * When one or more job attributes are invalid, this function adds a suitable
7332 * response and attributes to the unsupported group.
7335 static int /* O - 1 if valid, 0 if not */
7336 valid_job_attributes(
7337 ippeve_client_t
*client
) /* I - Client */
7339 int i
, /* Looping var */
7340 count
, /* Number of values */
7341 valid
= 1; /* Valid attributes? */
7342 ipp_attribute_t
*attr
, /* Current attribute */
7343 *supported
; /* xxx-supported attribute */
7347 * Check operation attributes...
7350 valid
= valid_doc_attributes(client
);
7353 * Check the various job template attributes...
7356 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7358 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7359 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7361 respond_unsupported(client
, attr
);
7366 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7368 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7370 respond_unsupported(client
, attr
);
7375 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7377 if (ippGetCount(attr
) != 1 ||
7378 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7379 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7380 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7381 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7383 respond_unsupported(client
, attr
);
7388 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7390 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7392 respond_unsupported(client
, attr
);
7397 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7399 if (ippGetCount(attr
) != 1 ||
7400 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7401 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7403 respond_unsupported(client
, attr
);
7407 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7410 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7412 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7414 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7415 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7417 respond_unsupported(client
, attr
);
7422 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7424 if (ippGetCount(attr
) != 1 ||
7425 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7426 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7427 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7428 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7430 respond_unsupported(client
, attr
);
7435 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7437 if (ippGetCount(attr
) != 1 ||
7438 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7439 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7440 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7442 respond_unsupported(client
, attr
);
7447 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7449 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7451 respond_unsupported(client
, attr
);
7457 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7459 ipp_t
*col
, /* media-col collection */
7460 *size
; /* media-size collection */
7461 ipp_attribute_t
*member
, /* Member attribute */
7462 *x_dim
, /* x-dimension */
7463 *y_dim
; /* y-dimension */
7464 int x_value
, /* y-dimension value */
7465 y_value
; /* x-dimension value */
7467 if (ippGetCount(attr
) != 1 ||
7468 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7470 respond_unsupported(client
, attr
);
7474 col
= ippGetCollection(attr
, 0);
7476 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7478 if (ippGetCount(member
) != 1 ||
7479 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7480 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7481 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7483 respond_unsupported(client
, attr
);
7488 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7490 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7492 respond_unsupported(client
, attr
);
7497 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7499 if (ippGetCount(member
) != 1)
7501 respond_unsupported(client
, attr
);
7506 size
= ippGetCollection(member
, 0);
7508 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7509 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
7511 respond_unsupported(client
, attr
);
7516 x_value
= ippGetInteger(x_dim
, 0);
7517 y_value
= ippGetInteger(y_dim
, 0);
7518 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
7519 count
= ippGetCount(supported
);
7521 for (i
= 0; i
< count
; i
++)
7523 size
= ippGetCollection(supported
, i
);
7524 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
7525 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
7527 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
7533 respond_unsupported(client
, attr
);
7541 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7543 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7544 (strcmp(ippGetString(attr
, 0, NULL
),
7545 "separate-documents-uncollated-copies") &&
7546 strcmp(ippGetString(attr
, 0, NULL
),
7547 "separate-documents-collated-copies")))
7549 respond_unsupported(client
, attr
);
7554 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7556 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7557 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7558 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7560 respond_unsupported(client
, attr
);
7565 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7567 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7569 respond_unsupported(client
, attr
);
7574 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7576 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7577 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7578 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7580 respond_unsupported(client
, attr
);
7585 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7587 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7589 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7592 respond_unsupported(client
, attr
);
7597 int xdpi
, /* Horizontal resolution for job template attribute */
7598 ydpi
, /* Vertical resolution for job template attribute */
7599 sydpi
; /* Vertical resolution for supported value */
7600 ipp_res_t units
, /* Units for job template attribute */
7601 sunits
; /* Units for supported value */
7603 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7604 count
= ippGetCount(supported
);
7606 for (i
= 0; i
< count
; i
++)
7608 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7614 respond_unsupported(client
, attr
);
7620 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7622 const char *sides
= ippGetString(attr
, 0, NULL
);
7623 /* "sides" value... */
7625 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7627 respond_unsupported(client
, attr
);
7630 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7632 if (!ippContainsString(supported
, sides
))
7634 respond_unsupported(client
, attr
);
7638 else if (strcmp(sides
, "one-sided"))
7640 respond_unsupported(client
, attr
);