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
11 * Include necessary headers...
14 #include <config.h> /* CUPS configuration header */
15 #include <cups/cups.h> /* Public API */
16 #include <cups/ppd.h> /* PPD API */
17 #include <cups/string-private.h> /* CUPS string functions */
18 #include <cups/thread-private.h> /* For multithreading functions */
31 # define WEXITSTATUS(s) (s)
32 # include <winsock2.h>
36 extern char **environ
;
38 # include <sys/fcntl.h>
39 # include <sys/wait.h>
45 #elif defined(HAVE_AVAHI)
46 # include <avahi-client/client.h>
47 # include <avahi-client/publish.h>
48 # include <avahi-common/error.h>
49 # include <avahi-common/thread-watch.h>
50 #endif /* HAVE_DNSSD */
51 #ifdef HAVE_SYS_MOUNT_H
52 # include <sys/mount.h>
53 #endif /* HAVE_SYS_MOUNT_H */
54 #ifdef HAVE_SYS_STATFS_H
55 # include <sys/statfs.h>
56 #endif /* HAVE_SYS_STATFS_H */
57 #ifdef HAVE_SYS_STATVFS_H
58 # include <sys/statvfs.h>
59 #endif /* HAVE_SYS_STATVFS_H */
62 #endif /* HAVE_SYS_VFS_H */
69 enum ippeve_preason_e
/* printer-state-reasons bit values */
71 IPPEVE_PREASON_NONE
= 0x0000, /* none */
72 IPPEVE_PREASON_OTHER
= 0x0001, /* other */
73 IPPEVE_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
74 IPPEVE_PREASON_INPUT_TRAY_MISSING
= 0x0004,
75 /* input-tray-missing */
76 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
77 /* marker-supply-empty */
78 IPPEVE_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
79 /* marker-supply-low */
80 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
81 /* marker-waste-almost-full */
82 IPPEVE_PREASON_MARKER_WASTE_FULL
= 0x0040,
83 /* marker-waste-full */
84 IPPEVE_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
85 IPPEVE_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
86 IPPEVE_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
87 IPPEVE_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
88 IPPEVE_PREASON_MOVING_TO_PAUSED
= 0x0800,
89 /* moving-to-paused */
90 IPPEVE_PREASON_PAUSED
= 0x1000, /* paused */
91 IPPEVE_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
92 IPPEVE_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
93 IPPEVE_PREASON_TONER_LOW
= 0x8000 /* toner-low */
95 typedef unsigned int ippeve_preason_t
; /* Bitfield for printer-state-reasons */
96 static const char * const ippeve_preason_strings
[] =
97 { /* Strings for each bit */
98 /* "none" is implied for no bits set */
101 "input-tray-missing",
102 "marker-supply-empty",
104 "marker-waste-almost-full",
117 typedef enum ippeve_media_class_e
119 IPPEVE_GENERAL
, /* General-purpose size */
120 IPPEVE_PHOTO_ONLY
, /* Photo-only size */
121 IPPEVE_ENV_ONLY
/* Envelope-only size */
122 } ippeve_media_class_t
;
124 typedef enum ippeve_media_size_e
126 IPPEVE_MEDIA_SIZE_NONE
= -1,
127 IPPEVE_MEDIA_SIZE_A4
,
128 IPPEVE_MEDIA_SIZE_A5
,
129 IPPEVE_MEDIA_SIZE_A6
,
130 IPPEVE_MEDIA_SIZE_DL
,
131 IPPEVE_MEDIA_SIZE_LEGAL
,
132 IPPEVE_MEDIA_SIZE_LETTER
,
133 IPPEVE_MEDIA_SIZE_COM10
,
134 IPPEVE_MEDIA_SIZE_3x5
,
136 IPPEVE_MEDIA_SIZE_4x6
,
137 IPPEVE_MEDIA_SIZE_5x7
138 } ippeve_media_size_t
;
139 static const char * const media_supported
[] =
140 { /* media-supported values */
141 "iso_a4_210x297mm", /* A4 */
142 "iso_a5_148x210mm", /* A5 */
143 "iso_a6_105x148mm", /* A6 */
144 "iso_dl_110x220mm", /* DL */
145 "na_legal_8.5x14in", /* Legal */
146 "na_letter_8.5x11in", /* Letter */
147 "na_number-10_4.125x9.5in", /* #10 */
148 "na_index-3x5_3x5in", /* 3x5 */
149 "oe_photo-l_3.5x5in", /* L */
150 "na_index-4x6_4x6in", /* 4x6 */
151 "na_5x7_5x7in" /* 5x7 aka 2L */
153 static const int media_col_sizes
[][3] =
154 { /* media-col-database sizes */
155 { 21000, 29700, IPPEVE_GENERAL
}, /* A4 */
156 { 14800, 21000, IPPEVE_PHOTO_ONLY
}, /* A5 */
157 { 10500, 14800, IPPEVE_PHOTO_ONLY
}, /* A6 */
158 { 11000, 22000, IPPEVE_ENV_ONLY
}, /* DL */
159 { 21590, 35560, IPPEVE_GENERAL
}, /* Legal */
160 { 21590, 27940, IPPEVE_GENERAL
}, /* Letter */
161 { 10477, 24130, IPPEVE_ENV_ONLY
}, /* #10 */
162 { 7630, 12700, IPPEVE_PHOTO_ONLY
}, /* 3x5 */
163 { 8890, 12700, IPPEVE_PHOTO_ONLY
}, /* L */
164 { 10160, 15240, IPPEVE_PHOTO_ONLY
}, /* 4x6 */
165 { 12700, 17780, IPPEVE_PHOTO_ONLY
} /* 5x7 aka 2L */
168 typedef enum ippeve_media_source_e
170 IPPEVE_MEDIA_SOURCE_NONE
= -1,
171 IPPEVE_MEDIA_SOURCE_AUTO
,
172 IPPEVE_MEDIA_SOURCE_MAIN
,
173 IPPEVE_MEDIA_SOURCE_MANUAL
,
174 IPPEVE_MEDIA_SOURCE_ENVELOPE
,
175 IPPEVE_MEDIA_SOURCE_PHOTO
176 } ippeve_media_source_t
;
177 static const char * const media_source_supported
[] =
178 /* media-source-supported values */
187 typedef enum ippeve_media_type_e
189 IPPEVE_MEDIA_TYPE_NONE
= -1,
190 IPPEVE_MEDIA_TYPE_AUTO
,
191 IPPEVE_MEDIA_TYPE_CARDSTOCK
,
192 IPPEVE_MEDIA_TYPE_ENVELOPE
,
193 IPPEVE_MEDIA_TYPE_LABELS
,
194 IPPEVE_MEDIA_TYPE_OTHER
,
195 IPPEVE_MEDIA_TYPE_GLOSSY
,
196 IPPEVE_MEDIA_TYPE_HIGH_GLOSS
,
197 IPPEVE_MEDIA_TYPE_MATTE
,
198 IPPEVE_MEDIA_TYPE_SATIN
,
199 IPPEVE_MEDIA_TYPE_SEMI_GLOSS
,
200 IPPEVE_MEDIA_TYPE_STATIONERY
,
201 IPPEVE_MEDIA_TYPE_LETTERHEAD
,
202 IPPEVE_MEDIA_TYPE_TRANSPARENCY
203 } ippeve_media_type_t
;
204 static const char * const media_type_supported
[] =
205 /* media-type-supported values */
212 "photographic-glossy",
213 "photographic-high-gloss",
214 "photographic-matte",
215 "photographic-satin",
216 "photographic-semi-gloss",
218 "stationery-letterhead",
222 typedef enum ippeve_supply_e
224 IPPEVE_SUPPLY_CYAN
, /* Cyan Toner */
225 IPPEVE_SUPPLY_MAGENTA
, /* Magenta Toner */
226 IPPEVE_SUPPLY_YELLOW
, /* Yellow Toner */
227 IPPEVE_SUPPLY_BLACK
, /* Black Toner */
228 IPPEVE_SUPPLY_WASTE
/* Waste Toner */
230 static const char * const printer_supplies
[] =
231 { /* printer-supply-description values */
240 * URL scheme for web resources...
244 # define WEB_SCHEME "https"
246 # define WEB_SCHEME "http"
247 #endif /* HAVE_SSL */
255 typedef DNSServiceRef ippeve_srv_t
; /* Service reference */
256 typedef TXTRecordRef ippeve_txt_t
; /* TXT record */
258 #elif defined(HAVE_AVAHI)
259 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
260 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
263 typedef void *_ipp_srv_t
; /* Service reference */
264 typedef void *_ipp_txt_t
; /* TXT record */
265 #endif /* HAVE_DNSSD */
267 typedef struct ippeve_filter_s
/**** Attribute filter ****/
269 cups_array_t
*ra
; /* Requested attributes */
270 ipp_tag_t group_tag
; /* Group to copy */
273 typedef struct ippeve_job_s ippeve_job_t
;
275 typedef struct ippeve_printer_s
/**** Printer data ****/
277 int ipv4
, /* IPv4 listener */
278 ipv6
; /* IPv6 listener */
279 ippeve_srv_t ipp_ref
, /* Bonjour IPP service */
280 ipps_ref
, /* Bonjour IPPS service */
281 http_ref
, /* Bonjour HTTP service */
282 printer_ref
; /* Bonjour LPD service */
283 char *dnssd_name
, /* printer-dnssd-name */
284 *name
, /* printer-name */
285 *icon
, /* Icon filename */
286 *directory
, /* Spool directory */
287 *hostname
, /* Hostname */
288 *uri
, /* printer-uri-supported */
289 *command
; /* Command to run with job file */
291 size_t urilen
; /* Length of printer URI */
292 ipp_t
*attrs
; /* Static attributes */
293 time_t start_time
; /* Startup time */
294 time_t config_time
; /* printer-config-change-time */
295 ipp_pstate_t state
; /* printer-state value */
296 ippeve_preason_t state_reasons
; /* printer-state-reasons values */
297 time_t state_time
; /* printer-state-change-time */
298 cups_array_t
*jobs
; /* Jobs */
299 ippeve_job_t
*active_job
; /* Current active/pending job */
300 int next_job_id
; /* Next job-id value */
301 _cups_rwlock_t rwlock
; /* Printer lock */
302 ippeve_media_size_t main_size
; /* Ready media */
303 ippeve_media_type_t main_type
;
305 ippeve_media_size_t envelope_size
;
307 ippeve_media_size_t photo_size
;
308 ippeve_media_type_t photo_type
;
310 int supplies
[5]; /* Supply levels (0-100) */
313 struct ippeve_job_s
/**** Job data ****/
316 const char *name
, /* job-name */
317 *username
, /* job-originating-user-name */
318 *format
; /* document-format */
319 ipp_jstate_t state
; /* job-state value */
320 time_t created
, /* time-at-creation value */
321 processing
, /* time-at-processing value */
322 completed
; /* time-at-completed value */
323 int impressions
, /* job-impressions value */
324 impcompleted
; /* job-impressions-completed value */
325 ipp_t
*attrs
; /* Static attributes */
326 int cancel
; /* Non-zero when job canceled */
327 char *filename
; /* Print file name */
328 int fd
; /* Print file descriptor */
329 ippeve_printer_t
*printer
; /* Printer */
332 typedef struct ippeve_client_s
/**** Client data ****/
334 http_t
*http
; /* HTTP connection */
335 ipp_t
*request
, /* IPP request */
336 *response
; /* IPP response */
337 time_t start
; /* Request start time */
338 http_state_t operation
; /* Request operation */
339 ipp_op_t operation_id
; /* IPP operation-id */
340 char uri
[1024], /* Request URI */
341 *options
; /* URI options */
342 http_addr_t addr
; /* Client address */
343 char hostname
[256]; /* Client hostname */
344 ippeve_printer_t
*printer
; /* Printer */
345 ippeve_job_t
*job
; /* Current job, if any */
353 static void clean_jobs(ippeve_printer_t
*printer
);
354 static int compare_jobs(ippeve_job_t
*a
, ippeve_job_t
*b
);
355 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
356 ipp_tag_t group_tag
, int quickcopy
);
357 static void copy_job_attributes(ippeve_client_t
*client
,
358 ippeve_job_t
*job
, cups_array_t
*ra
);
359 static ippeve_client_t
*create_client(ippeve_printer_t
*printer
, int sock
);
360 static ippeve_job_t
*create_job(ippeve_client_t
*client
);
361 static int create_listener(int family
, int port
);
362 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int margins
);
363 static ipp_t
*create_media_size(int width
, int length
);
364 static ippeve_printer_t
*create_printer(const char *servername
,
365 const char *name
, const char *location
,
367 const char *docformats
, int port
,
369 const char *directory
,
371 const char *attrfile
);
372 static void debug_attributes(const char *title
, ipp_t
*ipp
,
374 static void delete_client(ippeve_client_t
*client
);
375 static void delete_job(ippeve_job_t
*job
);
376 static void delete_printer(ippeve_printer_t
*printer
);
378 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
,
379 DNSServiceFlags flags
,
380 DNSServiceErrorType errorCode
,
384 ippeve_printer_t
*printer
);
385 #elif defined(HAVE_AVAHI)
386 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
387 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
388 #endif /* HAVE_DNSSD */
389 static void dnssd_init(void);
390 static int filter_cb(ippeve_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
391 static ippeve_job_t
*find_job(ippeve_client_t
*client
);
392 static ipp_t
*get_collection(FILE *fp
, const char *filename
, int *linenum
);
393 static char *get_token(FILE *fp
, char *buf
, int buflen
, int *linenum
);
394 static void html_escape(ippeve_client_t
*client
, const char *s
,
396 static void html_footer(ippeve_client_t
*client
);
397 static void html_header(ippeve_client_t
*client
, const char *title
);
398 static void html_printf(ippeve_client_t
*client
, const char *format
,
399 ...) _CUPS_FORMAT(2, 3);
400 static void ipp_cancel_job(ippeve_client_t
*client
);
401 static void ipp_close_job(ippeve_client_t
*client
);
402 static void ipp_create_job(ippeve_client_t
*client
);
403 static void ipp_get_job_attributes(ippeve_client_t
*client
);
404 static void ipp_get_jobs(ippeve_client_t
*client
);
405 static void ipp_get_printer_attributes(ippeve_client_t
*client
);
406 static void ipp_identify_printer(ippeve_client_t
*client
);
407 static void ipp_print_job(ippeve_client_t
*client
);
408 static void ipp_print_uri(ippeve_client_t
*client
);
409 static void ipp_send_document(ippeve_client_t
*client
);
410 static void ipp_send_uri(ippeve_client_t
*client
);
411 static void ipp_validate_job(ippeve_client_t
*client
);
412 static void load_attributes(const char *filename
, ipp_t
*attrs
);
413 static char *make_ppd_attributes(const char *ppdfile
, char *buffer
, size_t bufsize
);
414 static char *make_std_attributes(const char *make
, const char *model
, int ppm
, int ppm_color
, int duplex
, char *buffer
, size_t bufsize
);
415 static int parse_options(ippeve_client_t
*client
, cups_option_t
**options
);
416 static void process_attr_message(ippeve_job_t
*job
, char *message
);
417 static void *process_client(ippeve_client_t
*client
);
418 static int process_http(ippeve_client_t
*client
);
419 static int process_ipp(ippeve_client_t
*client
);
420 static void *process_job(ippeve_job_t
*job
);
421 static void process_state_message(ippeve_job_t
*job
, char *message
);
422 static int register_printer(ippeve_printer_t
*printer
, const char *location
, const char *make
, const char *model
, const char *formats
, const char *adminurl
, const char *uuid
, int color
, int duplex
, const char *regtype
);
423 static int respond_http(ippeve_client_t
*client
, http_status_t code
,
424 const char *content_coding
,
425 const char *type
, size_t length
);
426 static void respond_ipp(ippeve_client_t
*client
, ipp_status_t status
,
427 const char *message
, ...) _CUPS_FORMAT(3, 4);
428 static void respond_unsupported(ippeve_client_t
*client
,
429 ipp_attribute_t
*attr
);
430 static void run_printer(ippeve_printer_t
*printer
);
431 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
432 static void usage(int status
) _CUPS_NORETURN
;
433 static int valid_doc_attributes(ippeve_client_t
*client
);
434 static int valid_job_attributes(ippeve_client_t
*client
);
442 static DNSServiceRef DNSSDMaster
= NULL
;
443 #elif defined(HAVE_AVAHI)
444 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
445 static AvahiClient
*DNSSDClient
= NULL
;
446 #endif /* HAVE_DNSSD */
448 static int KeepFiles
= 0,
454 * 'main()' - Main entry to the sample server.
457 int /* O - Exit status */
458 main(int argc
, /* I - Number of command-line args */
459 char *argv
[]) /* I - Command-line arguments */
461 int i
; /* Looping var */
462 const char *opt
, /* Current option character */
463 *attrfile
= NULL
, /* Attributes file */
464 *command
= NULL
, /* Command to run with job files */
465 *servername
= NULL
, /* Server host name */
466 *name
= NULL
, /* Printer name */
467 *location
= "", /* Location of printer */
468 *make
= "Test", /* Manufacturer */
469 *model
= "Printer", /* Model */
470 *icon
= "printer.png", /* Icon file */
471 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
472 /* Supported formats */
474 const char *keypath
= NULL
; /* Keychain path */
475 #endif /* HAVE_SSL */
476 const char *subtype
= "_print"; /* Bonjour service subtype */
477 int port
= 0, /* Port number (0 = auto) */
478 duplex
= 0, /* Duplex mode */
479 ppm
= 10, /* Pages per minute for mono */
480 ppm_color
= 0, /* Pages per minute for color */
481 pin
= 0; /* PIN printing mode? */
482 char directory
[1024] = "", /* Spool directory */
483 hostname
[1024]; /* Auto-detected hostname */
484 ippeve_printer_t
*printer
; /* Printer object */
488 * Parse command-line arguments...
491 for (i
= 1; i
< argc
; i
++)
492 if (argv
[i
][0] == '-')
494 for (opt
= argv
[i
] + 1; *opt
; opt
++)
498 case '2' : /* -2 (enable 2-sided printing) */
503 case 'K' : /* -K keypath */
509 #endif /* HAVE_SSL */
511 case 'M' : /* -M manufacturer */
518 case 'P' : /* -P (PIN printing mode) */
522 case 'V' : /* -V max-version */
527 if (!strcmp(argv
[i
], "2.2"))
529 else if (!strcmp(argv
[i
], "2.1"))
531 else if (!strcmp(argv
[i
], "2.0"))
533 else if (!strcmp(argv
[i
], "1.1"))
539 case 'a' : /* -a attributes-file */
547 case 'c' : /* -c command */
555 case 'd' : /* -d spool-directory */
559 strlcpy(directory
, argv
[i
], sizeof(directory
));
562 case 'f' : /* -f type/subtype[,...] */
569 case 'h' : /* -h (show help) */
572 case 'i' : /* -i icon.png */
579 case 'k' : /* -k (keep files) */
583 case 'l' : /* -l location */
590 case 'm' : /* -m model */
597 case 'n' : /* -n hostname */
601 servername
= argv
[i
];
604 case 'p' : /* -p port */
606 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
608 port
= atoi(argv
[i
]);
611 case 'r' : /* -r subtype */
618 case 's' : /* -s speed[,color-speed] */
622 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
626 case 'v' : /* -v (be verbose) */
630 default : /* Unknown */
631 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
642 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
650 * Apply defaults as needed...
654 servername
= httpGetHostname(NULL
, hostname
, sizeof(hostname
));
660 * Windows is almost always used as a single user system, so use a default
661 * port number of 8631.
668 * Use 8000 + UID mod 1000 for the default port number...
671 port
= 8000 + ((int)getuid() % 1000);
674 fprintf(stderr
, "Listening on port %d.\n", port
);
679 const char *tmpdir
; /* Temporary directory */
682 if ((tmpdir
= getenv("TEMP")) == NULL
)
684 #elif defined(__APPLE__) && !TARGET_OS_IOS
685 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
686 tmpdir
= "/private/tmp";
688 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
692 snprintf(directory
, sizeof(directory
), "%s/ippserver.%d", tmpdir
, (int)getpid());
694 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
696 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
697 directory
, strerror(errno
));
702 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
706 cupsSetServerCredentials(keypath
, servername
, 1);
707 #endif /* HAVE_SSL */
710 * Initialize Bonjour...
716 * Create the printer...
719 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
720 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
721 subtype
, directory
, command
, attrfile
)) == NULL
)
725 * Run the print service...
728 run_printer(printer
);
731 * Destroy the printer and exit...
734 delete_printer(printer
);
741 * 'clean_jobs()' - Clean out old (completed) jobs.
745 clean_jobs(ippeve_printer_t
*printer
) /* I - Printer */
747 ippeve_job_t
*job
; /* Current job */
748 time_t cleantime
; /* Clean time */
751 if (cupsArrayCount(printer
->jobs
) == 0)
754 cleantime
= time(NULL
) - 60;
756 _cupsRWLockWrite(&(printer
->rwlock
));
757 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
);
759 job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
760 if (job
->completed
&& job
->completed
< cleantime
)
762 cupsArrayRemove(printer
->jobs
, job
);
767 _cupsRWUnlock(&(printer
->rwlock
));
772 * 'compare_jobs()' - Compare two jobs.
775 static int /* O - Result of comparison */
776 compare_jobs(ippeve_job_t
*a
, /* I - First job */
777 ippeve_job_t
*b
) /* I - Second job */
779 return (b
->id
- a
->id
);
784 * 'copy_attributes()' - Copy attributes from one request to another.
788 copy_attributes(ipp_t
*to
, /* I - Destination request */
789 ipp_t
*from
, /* I - Source request */
790 cups_array_t
*ra
, /* I - Requested attributes */
791 ipp_tag_t group_tag
, /* I - Group to copy */
792 int quickcopy
) /* I - Do a quick copy? */
794 ippeve_filter_t filter
; /* Filter data */
798 filter
.group_tag
= group_tag
;
800 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
805 * 'copy_job_attrs()' - Copy job attributes to the response.
810 ippeve_client_t
*client
, /* I - Client */
811 ippeve_job_t
*job
, /* I - Job */
812 cups_array_t
*ra
) /* I - requested-attributes */
814 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
816 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
819 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
821 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
824 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
827 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
829 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
832 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
833 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
835 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
836 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
838 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
839 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
841 if (!ra
|| cupsArrayFind(ra
, "job-state"))
842 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
843 "job-state", job
->state
);
845 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
849 case IPP_JSTATE_PENDING
:
850 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
853 case IPP_JSTATE_HELD
:
855 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
856 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
857 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
859 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
862 case IPP_JSTATE_PROCESSING
:
864 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
866 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
869 case IPP_JSTATE_STOPPED
:
870 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
873 case IPP_JSTATE_CANCELED
:
874 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
877 case IPP_JSTATE_ABORTED
:
878 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
881 case IPP_JSTATE_COMPLETED
:
882 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
887 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
891 case IPP_JSTATE_PENDING
:
892 ippAddString(client
->response
, IPP_TAG_JOB
,
893 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
897 case IPP_JSTATE_HELD
:
899 ippAddString(client
->response
, IPP_TAG_JOB
,
900 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
901 "job-state-reasons", NULL
, "job-incoming");
902 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
903 ippAddString(client
->response
, IPP_TAG_JOB
,
904 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
905 "job-state-reasons", NULL
, "job-hold-until-specified");
907 ippAddString(client
->response
, IPP_TAG_JOB
,
908 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
909 "job-state-reasons", NULL
, "job-data-insufficient");
912 case IPP_JSTATE_PROCESSING
:
914 ippAddString(client
->response
, IPP_TAG_JOB
,
915 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
916 "job-state-reasons", NULL
, "processing-to-stop-point");
918 ippAddString(client
->response
, IPP_TAG_JOB
,
919 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
920 "job-state-reasons", NULL
, "job-printing");
923 case IPP_JSTATE_STOPPED
:
924 ippAddString(client
->response
, IPP_TAG_JOB
,
925 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
926 NULL
, "job-stopped");
929 case IPP_JSTATE_CANCELED
:
930 ippAddString(client
->response
, IPP_TAG_JOB
,
931 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
932 NULL
, "job-canceled-by-user");
935 case IPP_JSTATE_ABORTED
:
936 ippAddString(client
->response
, IPP_TAG_JOB
,
937 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
938 NULL
, "aborted-by-system");
941 case IPP_JSTATE_COMPLETED
:
942 ippAddString(client
->response
, IPP_TAG_JOB
,
943 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
944 NULL
, "job-completed-successfully");
949 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
950 ippAddInteger(client
->response
, IPP_TAG_JOB
,
951 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
952 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
954 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
955 ippAddInteger(client
->response
, IPP_TAG_JOB
,
956 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
957 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
962 * 'create_client()' - Accept a new network connection and create a client
966 static ippeve_client_t
* /* O - Client */
967 create_client(ippeve_printer_t
*printer
, /* I - Printer */
968 int sock
) /* I - Listen socket */
970 ippeve_client_t
*client
; /* Client */
973 if ((client
= calloc(1, sizeof(ippeve_client_t
))) == NULL
)
975 perror("Unable to allocate memory for client");
979 client
->printer
= printer
;
982 * Accept the client and get the remote address...
985 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
987 perror("Unable to accept client connection");
994 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
997 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
1004 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1008 static ippeve_job_t
* /* O - Job */
1009 create_job(ippeve_client_t
*client
) /* I - Client */
1011 ippeve_job_t
*job
; /* Job */
1012 ipp_attribute_t
*attr
; /* Job attribute */
1013 char uri
[1024], /* job-uri value */
1014 uuid
[64]; /* job-uuid value */
1017 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1018 if (client
->printer
->active_job
&&
1019 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
1022 * Only accept a single job at a time...
1025 _cupsRWUnlock(&(client
->printer
->rwlock
));
1030 * Allocate and initialize the job object...
1033 if ((job
= calloc(1, sizeof(ippeve_job_t
))) == NULL
)
1035 perror("Unable to allocate memory for job");
1039 job
->printer
= client
->printer
;
1040 job
->attrs
= ippNew();
1041 job
->state
= IPP_JSTATE_HELD
;
1045 * Copy all of the job attributes...
1048 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1051 * Get the requesting-user-name, document format, and priority...
1054 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1055 job
->username
= ippGetString(attr
, 0, NULL
);
1057 job
->username
= "anonymous";
1059 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1061 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1063 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1064 job
->format
= ippGetString(attr
, 0, NULL
);
1065 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1066 job
->format
= ippGetString(attr
, 0, NULL
);
1068 job
->format
= "application/octet-stream";
1071 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1072 job
->impressions
= ippGetInteger(attr
, 0);
1074 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1075 job
->name
= ippGetString(attr
, 0, NULL
);
1078 * Add job description attributes and add to the jobs array...
1081 job
->id
= client
->printer
->next_job_id
++;
1083 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1084 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1086 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1087 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1088 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1089 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1090 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1091 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1093 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1094 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1096 cupsArrayAdd(client
->printer
->jobs
, job
);
1097 client
->printer
->active_job
= job
;
1099 _cupsRWUnlock(&(client
->printer
->rwlock
));
1106 * 'create_job_filename()' - Create the filename for a document in a job.
1109 static void create_job_filename(
1110 ippeve_printer_t
*printer
, /* I - Printer */
1111 ippeve_job_t
*job
, /* I - Job */
1112 char *fname
, /* I - Filename buffer */
1113 size_t fnamesize
) /* I - Size of filename buffer */
1115 char name
[256], /* "Safe" filename */
1116 *nameptr
; /* Pointer into filename */
1117 const char *ext
, /* Filename extension */
1118 *job_name
; /* job-name value */
1119 ipp_attribute_t
*job_name_attr
; /* job-name attribute */
1123 * Make a name from the job-name attribute...
1126 if ((job_name_attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
1127 job_name
= ippGetString(job_name_attr
, 0, NULL
);
1129 job_name
= "untitled";
1131 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1132 if (isalnum(*job_name
& 255) || *job_name
== '-')
1133 *nameptr
++ = (char)tolower(*job_name
& 255);
1140 * Figure out the extension...
1143 if (!strcasecmp(job
->format
, "image/jpeg"))
1145 else if (!strcasecmp(job
->format
, "image/png"))
1147 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1149 else if (!strcasecmp(job
->format
, "image/urf"))
1151 else if (!strcasecmp(job
->format
, "application/pdf"))
1153 else if (!strcasecmp(job
->format
, "application/postscript"))
1159 * Create a filename with the job-id, job-name, and document-format (extension)...
1162 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1167 * 'create_listener()' - Create a listener socket.
1170 static int /* O - Listener socket or -1 on error */
1171 create_listener(int family
, /* I - Address family */
1172 int port
) /* I - Port number */
1174 int sock
; /* Listener socket */
1175 http_addrlist_t
*addrlist
; /* Listen address */
1176 char service
[255]; /* Service port */
1179 snprintf(service
, sizeof(service
), "%d", port
);
1180 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
1183 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1185 httpAddrFreeList(addrlist
);
1192 * 'create_media_col()' - Create a media-col value.
1195 static ipp_t
* /* O - media-col collection */
1196 create_media_col(const char *media
, /* I - Media name */
1197 const char *source
, /* I - Media source */
1198 const char *type
, /* I - Media type */
1199 int width
, /* I - x-dimension in 2540ths */
1200 int length
, /* I - y-dimension in 2540ths */
1201 int margins
) /* I - Value for margins */
1203 ipp_t
*media_col
= ippNew(), /* media-col value */
1204 *media_size
= create_media_size(width
, length
);
1205 /* media-size value */
1206 char media_key
[256]; /* media-key value */
1210 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, margins
== 0 ? "_borderless" : "");
1212 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, margins
== 0 ? "_borderless" : "");
1214 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, margins
== 0 ? "_borderless" : "");
1216 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, margins
== 0 ? "_borderless" : "");
1218 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1220 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1221 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1222 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1223 "media-bottom-margin", margins
);
1224 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1225 "media-left-margin", margins
);
1226 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1227 "media-right-margin", margins
);
1228 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1229 "media-top-margin", margins
);
1231 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1233 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1235 ippDelete(media_size
);
1242 * 'create_media_size()' - Create a media-size value.
1245 static ipp_t
* /* O - media-col collection */
1246 create_media_size(int width
, /* I - x-dimension in 2540ths */
1247 int length
) /* I - y-dimension in 2540ths */
1249 ipp_t
*media_size
= ippNew(); /* media-size value */
1252 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1254 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1257 return (media_size
);
1262 * 'create_printer()' - Create, register, and listen for connections to a
1266 static ippeve_printer_t
* /* O - Printer */
1267 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1268 const char *name
, /* I - printer-name */
1269 const char *location
, /* I - printer-location */
1270 const char *icon
, /* I - printer-icons */
1271 const char *docformats
, /* I - document-format-supported */
1272 int port
, /* I - Port for listeners or 0 for auto */
1273 const char *subtype
, /* I - Bonjour service subtype(s) */
1274 const char *directory
, /* I - Spool directory */
1275 const char *command
, /* I - Command to run on job files */
1276 const char *attrfile
) /* I - Attributes file */
1278 int i
, j
; /* Looping vars */
1279 ippeve_printer_t
*printer
; /* Printer */
1281 char path
[1024]; /* Full path to command */
1282 #endif /* !_WIN32 */
1283 char uri
[1024], /* Printer URI */
1285 securi
[1024], /* Secure printer URI */
1286 *uris
[2], /* All URIs */
1287 #endif /* HAVE_SSL */
1288 icons
[1024], /* printer-icons URI */
1289 adminurl
[1024], /* printer-more-info URI */
1290 supplyurl
[1024],/* printer-supply-info-uri URI */
1291 device_id
[1024],/* printer-device-id */
1292 make_model
[128],/* printer-make-and-model */
1293 uuid
[128]; /* printer-uuid */
1294 int num_formats
; /* Number of document-format-supported values */
1295 char *defformat
, /* document-format-default value */
1296 *formats
[100], /* document-format-supported values */
1297 *ptr
; /* Pointer into string */
1298 const char *prefix
; /* Prefix string */
1299 int num_database
; /* Number of database values */
1300 ipp_attribute_t
*media_col_database
,
1301 /* media-col-database value */
1302 *media_size_supported
;
1303 /* media-size-supported value */
1304 ipp_t
*media_col_default
;
1305 /* media-col-default value */
1306 int media_col_index
;/* Current media-col-database value */
1307 int k_supported
; /* Maximum file size supported */
1309 struct statvfs spoolinfo
; /* FS info for spool directory */
1310 double spoolsize
; /* FS size */
1311 #elif defined(HAVE_STATFS)
1312 struct statfs spoolinfo
; /* FS info for spool directory */
1313 double spoolsize
; /* FS size */
1314 #endif /* HAVE_STATVFS */
1315 static const int orients
[4] = /* orientation-requested-supported values */
1317 IPP_ORIENT_PORTRAIT
,
1318 IPP_ORIENT_LANDSCAPE
,
1319 IPP_ORIENT_REVERSE_LANDSCAPE
,
1320 IPP_ORIENT_REVERSE_PORTRAIT
1322 static const char * const versions
[] =/* ipp-versions-supported values */
1329 static const char * const features
[] =/* ipp-features-supported values */
1333 static const int ops
[] = /* operations-supported values */
1337 IPP_OP_VALIDATE_JOB
,
1339 IPP_OP_SEND_DOCUMENT
,
1342 IPP_OP_GET_JOB_ATTRIBUTES
,
1344 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1345 IPP_OP_CANCEL_MY_JOBS
,
1347 IPP_OP_IDENTIFY_PRINTER
1349 static const char * const charsets
[] =/* charset-supported values */
1354 static const char * const compressions
[] =/* compression-supported values */
1359 #endif /* HAVE_LIBZ */
1362 static const char * const identify_actions
[] =
1367 static const char * const job_creation
[] =
1368 { /* job-creation-attributes-supported values */
1370 "ipp-attribute-fidelity",
1372 "job-accounting-user-id",
1378 "multiple-document-handling",
1379 "orientation-requested",
1383 static const char * const media_col_supported
[] =
1384 { /* media-col-supported values */
1385 "media-bottom-margin",
1386 "media-left-margin",
1387 "media-right-margin",
1393 static const int media_xxx_margin_supported
[] =
1394 { /* media-xxx-margin-supported values */
1398 static const char * const multiple_document_handling
[] =
1399 { /* multiple-document-handling-supported values */
1400 "separate-documents-uncollated-copies",
1401 "separate-documents-collated-copies"
1403 static const char * const overrides
[] =
1404 { /* overrides-supported */
1408 static const char * const print_color_mode_supported
[] =
1409 { /* print-color-mode-supported values */
1414 static const int print_quality_supported
[] =
1415 { /* print-quality-supported values */
1420 static const int pwg_raster_document_resolution_supported
[] =
1425 static const char * const pwg_raster_document_type_supported
[] =
1433 static const char * const reference_uri_schemes_supported
[] =
1434 { /* reference-uri-schemes-supported */
1440 #endif /* HAVE_SSL */
1442 static const char * const sides_supported
[] =
1443 { /* sides-supported values */
1445 "two-sided-long-edge",
1446 "two-sided-short-edge"
1448 static const char * const urf_supported
[] =
1449 { /* urf-supported values */
1452 "MT1-2-3-4-5-6-8-9-10-11-12-13",
1460 static const char * const uri_authentication_supported
[] =
1461 { /* uri-authentication-supported values */
1465 static const char * const uri_security_supported
[] =
1466 { /* uri-security-supported values */
1470 #endif /* HAVE_SSL */
1471 static const char * const which_jobs
[] =
1472 { /* which-jobs-supported values */
1481 "processing-stopped"
1487 * If a command was specified, make sure it exists and is executable...
1492 if (*command
== '/' || !strncmp(command
, "./", 2))
1494 if (access(command
, X_OK
))
1496 fprintf(stderr
, "ippserver: Unable to execute command \"%s\": %s\n", command
, strerror(errno
));
1502 if (!cupsFileFind(command
, getenv("PATH"), 1, path
, sizeof(path
)))
1504 fprintf(stderr
, "ippserver: Unable to find command \"%s\".\n", command
);
1511 #endif /* !_WIN32 */
1514 * Allocate memory for the printer...
1517 if ((printer
= calloc(1, sizeof(ippeve_printer_t
))) == NULL
)
1519 perror("ippserver: Unable to allocate memory for printer");
1525 printer
->name
= strdup(name
);
1526 printer
->dnssd_name
= strdup(printer
->name
);
1527 printer
->command
= command
? strdup(command
) : NULL
;
1528 printer
->directory
= strdup(directory
);
1529 printer
->hostname
= strdup(servername
);
1530 printer
->port
= port
;
1531 printer
->start_time
= time(NULL
);
1532 printer
->config_time
= printer
->start_time
;
1533 printer
->state
= IPP_PSTATE_IDLE
;
1534 printer
->state_reasons
= IPPEVE_PREASON_NONE
;
1535 printer
->state_time
= printer
->start_time
;
1536 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1537 printer
->next_job_id
= 1;
1539 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1540 printer
->uri
= strdup(uri
);
1541 printer
->urilen
= strlen(uri
);
1544 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1545 #endif /* HAVE_SSL */
1548 printer
->icon
= strdup(icon
);
1550 printer
->main_size
= IPPEVE_MEDIA_SIZE_A4
;
1551 printer
->main_type
= IPPEVE_MEDIA_TYPE_STATIONERY
;
1552 printer
->main_level
= 500;
1554 printer
->envelope_size
= IPPEVE_MEDIA_SIZE_NONE
;
1555 printer
->envelope_level
= 0;
1557 printer
->photo_size
= IPPEVE_MEDIA_SIZE_NONE
;
1558 printer
->photo_type
= IPPEVE_MEDIA_TYPE_NONE
;
1559 printer
->photo_level
= 0;
1561 printer
->supplies
[IPPEVE_SUPPLY_CYAN
] = 100;
1562 printer
->supplies
[IPPEVE_SUPPLY_MAGENTA
] = 100;
1563 printer
->supplies
[IPPEVE_SUPPLY_YELLOW
] = 100;
1564 printer
->supplies
[IPPEVE_SUPPLY_BLACK
] = 100;
1565 printer
->supplies
[IPPEVE_SUPPLY_WASTE
] = 0;
1567 _cupsRWInit(&(printer
->rwlock
));
1570 * Create the listener sockets...
1573 if ((printer
->ipv4
= create_listener(AF_INET
, printer
->port
)) < 0)
1575 perror("Unable to create IPv4 listener");
1579 if ((printer
->ipv6
= create_listener(AF_INET6
, printer
->port
)) < 0)
1581 perror("Unable to create IPv6 listener");
1586 * Prepare values for the printer attributes...
1589 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1590 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1591 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1595 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1596 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1597 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1600 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1603 formats
[0] = strdup(docformats
);
1604 defformat
= formats
[0];
1605 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1608 formats
[num_formats
++] = ptr
;
1610 if (!strcasecmp(ptr
, "application/octet-stream"))
1614 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1615 ptr
= device_id
+ strlen(device_id
);
1617 for (i
= 0; i
< num_formats
; i
++)
1619 if (!strcasecmp(formats
[i
], "application/pdf"))
1620 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1621 else if (!strcasecmp(formats
[i
], "application/postscript"))
1622 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1623 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1624 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1625 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1626 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1627 else if (!strcasecmp(formats
[i
], "image/png"))
1628 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1629 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1630 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1635 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
1642 * Get the maximum spool size based on the size of the filesystem used for
1643 * the spool directory. If the host OS doesn't support the statfs call
1644 * or the filesystem is larger than 2TiB, always report INT_MAX.
1648 if (statvfs(printer
->directory
, &spoolinfo
))
1649 k_supported
= INT_MAX
;
1650 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1651 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1652 k_supported
= INT_MAX
;
1654 k_supported
= (int)spoolsize
;
1656 #elif defined(HAVE_STATFS)
1657 if (statfs(printer
->directory
, &spoolinfo
))
1658 k_supported
= INT_MAX
;
1659 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1660 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1661 k_supported
= INT_MAX
;
1663 k_supported
= (int)spoolsize
;
1666 k_supported
= INT_MAX
;
1667 #endif /* HAVE_STATVFS */
1670 * Create the printer attributes. This list of attributes is sorted to improve
1671 * performance when the client provides a requested-attributes attribute...
1674 printer
->attrs
= ippNew();
1677 load_attributes(attrfile
, printer
->attrs
);
1679 /* charset-configured */
1680 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1682 /* charset-supported */
1683 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1685 /* color-supported */
1686 if (!ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_ZERO
))
1687 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
1689 /* compression-supported */
1690 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1691 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1693 /* copies-default */
1694 if (!ippFindAttribute(printer
->attrs
, "copies-default", IPP_TAG_ZERO
))
1695 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
1697 /* copies-supported */
1698 if (!ippFindAttribute(printer
->attrs
, "copies-supported", IPP_TAG_ZERO
))
1699 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1701 /* document-format-default */
1702 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1703 "document-format-default", NULL
, defformat
);
1705 /* document-format-supported */
1706 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1707 "document-format-supported", num_formats
, NULL
,
1708 (const char * const *)formats
);
1710 /* document-password-supported */
1711 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
1712 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1714 /* finishings-default */
1715 if (!ippFindAttribute(printer
->attrs
, "finishings-default", IPP_TAG_ZERO
))
1716 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
1718 /* finishings-supported */
1719 if (!ippFindAttribute(printer
->attrs
, "finishings-supported", IPP_TAG_ZERO
))
1720 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
1722 /* generated-natural-language-supported */
1723 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1725 /* identify-actions-default */
1726 if (!ippFindAttribute(printer
->attrs
, "identify-actions-default", IPP_TAG_ZERO
))
1727 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1729 /* identify-actions-supported */
1730 if (!ippFindAttribute(printer
->attrs
, "identify-actions-supported", IPP_TAG_ZERO
))
1731 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
);
1733 /* ipp-features-supported */
1734 if (!ippFindAttribute(printer
->attrs
, "ipp-features-supported", IPP_TAG_ZERO
))
1735 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1737 /* ipp-versions-supported */
1738 if (!ippFindAttribute(printer
->attrs
, "ipp-versions-supported", IPP_TAG_ZERO
))
1740 int num_versions
= MaxVersion
== 11 ? 1 : MaxVersion
== 20 ? 2 : MaxVersion
== 21 ? 3 : 4;
1741 /* Number of supported versions */
1743 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", num_versions
, NULL
, versions
);
1746 /* job-account-id-default */
1747 if (!ippFindAttribute(printer
->attrs
, "job-account-id-default", IPP_TAG_ZERO
))
1748 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1750 /* job-account-id-supported */
1751 if (!ippFindAttribute(printer
->attrs
, "job-account-id-supported", IPP_TAG_ZERO
))
1752 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1754 /* job-accounting-user-id-default */
1755 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-default", IPP_TAG_ZERO
))
1756 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1758 /* job-accounting-user-id-supported */
1759 if (!ippFindAttribute(printer
->attrs
, "job-accounting-user-id-supported", IPP_TAG_ZERO
))
1760 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1762 /* job-creation-attributes-supported */
1763 if (!ippFindAttribute(printer
->attrs
, "job-creation-attributes-supported", IPP_TAG_ZERO
))
1764 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
);
1766 /* job-ids-supported */
1767 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1769 /* job-k-octets-supported */
1770 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1773 /* job-password-supported */
1774 if (!ippFindAttribute(printer
->attrs
, "job-password-supported", IPP_TAG_ZERO
))
1775 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-password-supported", 4);
1777 /* job-priority-default */
1778 if (!ippFindAttribute(printer
->attrs
, "job-priority-default", IPP_TAG_ZERO
))
1779 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1781 /* job-priority-supported */
1782 if (!ippFindAttribute(printer
->attrs
, "job-priority-supported", IPP_TAG_ZERO
))
1783 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 100);
1785 /* job-sheets-default */
1786 if (!ippFindAttribute(printer
->attrs
, "job-sheets-default", IPP_TAG_ZERO
))
1787 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1789 /* job-sheets-supported */
1790 if (!ippFindAttribute(printer
->attrs
, "job-sheets-supported", IPP_TAG_ZERO
))
1791 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1793 /* media-bottom-margin-supported */
1794 if (!ippFindAttribute(printer
->attrs
, "media-bottom-margin-supported", IPP_TAG_ZERO
))
1795 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
);
1797 /* media-col-database */
1798 if (!ippFindAttribute(printer
->attrs
, "media-col-database", IPP_TAG_ZERO
))
1800 for (num_database
= 0, i
= 0;
1801 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1804 if (media_col_sizes
[i
][2] == IPPEVE_ENV_ONLY
)
1805 num_database
+= 3; /* auto + manual + envelope */
1806 else if (media_col_sizes
[i
][2] == IPPEVE_PHOTO_ONLY
)
1807 num_database
+= 6 * 3; /* auto + photographic-* from auto, manual, and photo */
1809 num_database
+= 2; /* Regular + borderless */
1812 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-database", num_database
, NULL
);
1813 for (media_col_index
= 0, i
= 0;
1814 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1817 switch (media_col_sizes
[i
][2])
1819 case IPPEVE_GENERAL
:
1821 * Regular + borderless for the general class; no source/type
1825 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]));
1826 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]));
1829 case IPPEVE_ENV_ONLY
:
1831 * Regular margins for "auto", "manual", and "envelope" sources.
1834 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]));
1835 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]));
1836 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]));
1838 case IPPEVE_PHOTO_ONLY
:
1840 * Photos have specific media types and can only be printed via
1841 * the auto, manual, and photo sources...
1845 j
< (int)(sizeof(media_type_supported
) /
1846 sizeof(media_type_supported
[0]));
1849 if (strcmp(media_type_supported
[j
], "auto") && strncmp(media_type_supported
[j
], "photographic-", 13))
1852 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]));
1853 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]));
1854 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]));
1861 /* media-col-default */
1862 if (!ippFindAttribute(printer
->attrs
, "media-col-default", IPP_TAG_ZERO
))
1864 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]);
1866 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1868 ippDelete(media_col_default
);
1871 /* media-col-supported */
1872 if (!ippFindAttribute(printer
->attrs
, "media-col-supported", IPP_TAG_ZERO
))
1873 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
);
1876 if (!ippFindAttribute(printer
->attrs
, "media-default", IPP_TAG_ZERO
))
1877 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media_supported
[0]);
1879 /* media-left-margin-supported */
1880 if (!ippFindAttribute(printer
->attrs
, "media-left-margin-supported", IPP_TAG_ZERO
))
1881 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
);
1883 /* media-right-margin-supported */
1884 if (!ippFindAttribute(printer
->attrs
, "media-right-margin-supported", IPP_TAG_ZERO
))
1885 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
);
1887 /* media-supported */
1888 if (!ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
))
1889 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
);
1891 /* media-size-supported */
1892 if (!ippFindAttribute(printer
->attrs
, "media-size-supported", IPP_TAG_ZERO
))
1894 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
, "media-size-supported", (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0])), NULL
);
1897 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1900 ipp_t
*size
= create_media_size(media_col_sizes
[i
][0], media_col_sizes
[i
][1]);
1902 ippSetCollection(printer
->attrs
, &media_size_supported
, i
, size
);
1907 /* media-source-supported */
1908 if (!ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
))
1909 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
);
1911 /* media-top-margin-supported */
1912 if (!ippFindAttribute(printer
->attrs
, "media-top-margin-supported", IPP_TAG_ZERO
))
1913 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
);
1915 /* media-type-supported */
1916 if (!ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
))
1917 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
);
1919 /* multiple-document-handling-supported */
1920 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
);
1922 /* multiple-document-jobs-supported */
1923 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1925 /* multiple-operation-time-out */
1926 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1928 /* multiple-operation-time-out-action */
1929 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1931 /* natural-language-configured */
1932 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1933 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1934 "natural-language-configured", NULL
, "en");
1936 /* number-up-default */
1937 if (!ippFindAttribute(printer
->attrs
, "number-up-default", IPP_TAG_ZERO
))
1938 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-default", 1);
1940 /* number-up-supported */
1941 if (!ippFindAttribute(printer
->attrs
, "number-up-supported", IPP_TAG_ZERO
))
1942 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "number-up-supported", 1);
1944 /* operations-supported */
1945 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1947 /* orientation-requested-default */
1948 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-default", IPP_TAG_ZERO
))
1949 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "orientation-requested-default", 0);
1951 /* orientation-requested-supported */
1952 if (!ippFindAttribute(printer
->attrs
, "orientation-requested-supported", IPP_TAG_ZERO
))
1953 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", 4, orients
);
1955 /* output-bin-default */
1956 if (!ippFindAttribute(printer
->attrs
, "output-bin-default", IPP_TAG_ZERO
))
1957 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
1959 /* output-bin-supported */
1960 if (!ippFindAttribute(printer
->attrs
, "output-bin-supported", IPP_TAG_ZERO
))
1961 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
1963 /* overrides-supported */
1964 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
1965 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1967 /* page-ranges-supported */
1968 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
1969 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1971 /* pages-per-minute */
1972 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1973 "pages-per-minute", ppm
);
1975 /* pages-per-minute-color */
1977 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1978 "pages-per-minute-color", ppm_color
);
1980 /* pdl-override-supported */
1981 if (!ippFindAttribute(printer
->attrs
, "pdl-override-supported", IPP_TAG_ZERO
))
1982 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1984 /* preferred-attributes-supported */
1985 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1987 /* print-color-mode-default */
1988 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-default", IPP_TAG_ZERO
))
1989 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1991 /* print-color-mode-supported */
1992 if (!ippFindAttribute(printer
->attrs
, "print-color-mode-supported", IPP_TAG_ZERO
))
1993 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
);
1995 /* print-content-optimize-default */
1996 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-default", IPP_TAG_ZERO
))
1997 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1999 /* print-content-optimize-supported */
2000 if (!ippFindAttribute(printer
->attrs
, "print-content-optimize-supported", IPP_TAG_ZERO
))
2001 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
2003 /* print-rendering-intent-default */
2004 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-default", IPP_TAG_ZERO
))
2005 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
2007 /* print-rendering-intent-supported */
2008 if (!ippFindAttribute(printer
->attrs
, "print-rendering-intent-supported", IPP_TAG_ZERO
))
2009 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
2011 /* print-quality-default */
2012 if (!ippFindAttribute(printer
->attrs
, "print-quality-default", IPP_TAG_ZERO
))
2013 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
2015 /* print-quality-supported */
2016 if (!ippFindAttribute(printer
->attrs
, "print-quality-supported", IPP_TAG_ZERO
))
2017 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
);
2019 /* printer-device-id */
2020 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2021 "printer-device-id", NULL
, device_id
);
2023 /* printer-get-attributes-supported */
2024 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
2026 /* printer-geo-location */
2027 if (!ippFindAttribute(printer
->attrs
, "printer-geo-location", IPP_TAG_ZERO
))
2028 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location", 0);
2031 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
2032 "printer-icons", NULL
, icons
);
2034 /* printer-is-accepting-jobs */
2035 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
2038 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
2040 /* printer-location */
2041 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2042 "printer-location", NULL
, location
);
2044 /* printer-make-and-model */
2045 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2046 "printer-make-and-model", NULL
, make_model
);
2048 /* printer-mandatory-job-attributes */
2049 if (pin
&& !ippFindAttribute(printer
->attrs
, "", IPP_TAG_ZERO
))
2051 static const char * const names
[] =
2054 "job-accounting-user-id",
2058 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
2059 "printer-mandatory-job-attributes",
2060 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
2063 /* printer-more-info */
2064 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
2067 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
2069 /* printer-organization */
2070 if (!ippFindAttribute(printer
->attrs
, "printer-organization", IPP_TAG_ZERO
))
2071 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
2073 /* printer-organizational-unit */
2074 if (!ippFindAttribute(printer
->attrs
, "printer-organizational-unit", IPP_TAG_ZERO
))
2075 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
2077 /* printer-resolution-default */
2078 if (!ippFindAttribute(printer
->attrs
, "printer-resolution-default", IPP_TAG_ZERO
))
2079 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
2081 /* printer-resolution-supported */
2082 if (!ippFindAttribute(printer
->attrs
, "printer-resolutions-supported", IPP_TAG_ZERO
))
2083 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
2085 /* printer-supply-description */
2086 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
);
2088 /* printer-supply-info-uri */
2089 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
2091 /* printer-uri-supported */
2096 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
2099 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
2100 #endif /* HAVE_SSL */
2103 httpAssembleUUID(printer
->hostname
, port
, name
, 0, uuid
, sizeof(uuid
));
2104 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
2106 /* pwg-raster-document-xxx-supported */
2107 for (i
= 0; i
< num_formats
; i
++)
2108 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
2111 if (i
< num_formats
)
2113 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO
))
2114 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
);
2115 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-sheet-back", IPP_TAG_ZERO
))
2116 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "pwg-raster-document-sheet-back", NULL
, "normal");
2117 if (!ippFindAttribute(printer
->attrs
, "pwg-raster-document-type-supported", IPP_TAG_ZERO
))
2118 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
);
2121 /* reference-uri-scheme-supported */
2122 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
);
2125 if (!ippFindAttribute(printer
->attrs
, "sides-default", IPP_TAG_ZERO
))
2126 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
2128 /* sides-supported */
2129 if (!ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_ZERO
))
2130 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
2133 for (i
= 0; i
< num_formats
; i
++)
2134 if (!strcasecmp(formats
[i
], "image/urf"))
2137 if (i
< num_formats
&& !ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_ZERO
))
2138 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])) - !duplex
, NULL
, urf_supported
);
2140 /* uri-authentication-supported */
2142 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
2144 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
2145 #endif /* HAVE_SSL */
2147 /* uri-security-supported */
2149 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
2151 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
2152 #endif /* HAVE_SSL */
2154 /* which-jobs-supported */
2155 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
);
2159 debug_attributes("Printer", printer
->attrs
, 0);
2162 * Register the printer with Bonjour...
2165 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
, uuid
+ 9, ppm_color
> 0, duplex
, subtype
))
2176 * If we get here we were unable to create the printer...
2181 delete_printer(printer
);
2187 * 'debug_attributes()' - Print attributes in a request or response.
2191 debug_attributes(const char *title
, /* I - Title */
2192 ipp_t
*ipp
, /* I - Request/response */
2193 int type
) /* I - 0 = object, 1 = request, 2 = response */
2195 ipp_tag_t group_tag
; /* Current group */
2196 ipp_attribute_t
*attr
; /* Current attribute */
2197 char buffer
[2048]; /* String buffer for value */
2198 int major
, minor
; /* Version */
2204 fprintf(stderr
, "%s:\n", title
);
2205 major
= ippGetVersion(ipp
, &minor
);
2206 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
2208 fprintf(stderr
, " operation-id=%s(%04x)\n",
2209 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
2211 fprintf(stderr
, " status-code=%s(%04x)\n",
2212 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
2213 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
2215 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
2217 attr
= ippNextAttribute(ipp
))
2219 if (ippGetGroupTag(attr
) != group_tag
)
2221 group_tag
= ippGetGroupTag(attr
);
2222 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2225 if (ippGetName(attr
))
2227 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2228 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2229 ippGetCount(attr
) > 1 ? "1setOf " : "",
2230 ippTagString(ippGetValueTag(attr
)), buffer
);
2237 * 'delete_client()' - Close the socket and free all memory used by a client
2242 delete_client(ippeve_client_t
*client
) /* I - Client */
2245 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2248 * Flush pending writes before closing...
2251 httpFlushWrite(client
->http
);
2257 httpClose(client
->http
);
2259 ippDelete(client
->request
);
2260 ippDelete(client
->response
);
2267 * 'delete_job()' - Remove from the printer and free all memory used by a job
2272 delete_job(ippeve_job_t
*job
) /* I - Job */
2275 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2277 ippDelete(job
->attrs
);
2282 unlink(job
->filename
);
2284 free(job
->filename
);
2292 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2293 * used by a printer object.
2297 delete_printer(ippeve_printer_t
*printer
) /* I - Printer */
2299 if (printer
->ipv4
>= 0)
2300 close(printer
->ipv4
);
2302 if (printer
->ipv6
>= 0)
2303 close(printer
->ipv6
);
2306 if (printer
->printer_ref
)
2307 DNSServiceRefDeallocate(printer
->printer_ref
);
2308 if (printer
->ipp_ref
)
2309 DNSServiceRefDeallocate(printer
->ipp_ref
);
2310 if (printer
->ipps_ref
)
2311 DNSServiceRefDeallocate(printer
->ipps_ref
);
2312 if (printer
->http_ref
)
2313 DNSServiceRefDeallocate(printer
->http_ref
);
2314 #elif defined(HAVE_AVAHI)
2315 avahi_threaded_poll_lock(DNSSDMaster
);
2317 if (printer
->printer_ref
)
2318 avahi_entry_group_free(printer
->printer_ref
);
2319 if (printer
->ipp_ref
)
2320 avahi_entry_group_free(printer
->ipp_ref
);
2321 if (printer
->ipps_ref
)
2322 avahi_entry_group_free(printer
->ipps_ref
);
2323 if (printer
->http_ref
)
2324 avahi_entry_group_free(printer
->http_ref
);
2326 avahi_threaded_poll_unlock(DNSSDMaster
);
2327 #endif /* HAVE_DNSSD */
2329 if (printer
->dnssd_name
)
2330 free(printer
->dnssd_name
);
2332 free(printer
->name
);
2334 free(printer
->icon
);
2335 if (printer
->command
)
2336 free(printer
->command
);
2337 if (printer
->directory
)
2338 free(printer
->directory
);
2339 if (printer
->hostname
)
2340 free(printer
->hostname
);
2344 ippDelete(printer
->attrs
);
2345 cupsArrayDelete(printer
->jobs
);
2353 * 'dnssd_callback()' - Handle Bonjour registration events.
2356 static void DNSSD_API
2358 DNSServiceRef sdRef
, /* I - Service reference */
2359 DNSServiceFlags flags
, /* I - Status flags */
2360 DNSServiceErrorType errorCode
, /* I - Error, if any */
2361 const char *name
, /* I - Service name */
2362 const char *regtype
, /* I - Service type */
2363 const char *domain
, /* I - Domain for service */
2364 ippeve_printer_t
*printer
) /* I - Printer */
2372 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2373 regtype
, (int)errorCode
);
2376 else if (strcasecmp(name
, printer
->dnssd_name
))
2379 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2381 /* No lock needed since only the main thread accesses/changes this */
2382 free(printer
->dnssd_name
);
2383 printer
->dnssd_name
= strdup(name
);
2388 #elif defined(HAVE_AVAHI)
2390 * 'dnssd_callback()' - Handle Bonjour registration events.
2395 AvahiEntryGroup
*srv
, /* I - Service */
2396 AvahiEntryGroupState state
, /* I - Registration state */
2397 void *context
) /* I - Printer */
2406 * 'dnssd_client_cb()' - Client callback for Avahi.
2408 * Called whenever the client or server state changes...
2413 AvahiClient
*c
, /* I - Client */
2414 AvahiClientState state
, /* I - Current state */
2415 void *userdata
) /* I - User data (unused) */
2425 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
2428 case AVAHI_CLIENT_FAILURE
:
2429 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2431 fputs("Avahi server crashed, exiting.\n", stderr
);
2437 #endif /* HAVE_DNSSD */
2441 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2448 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2450 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2454 #elif defined(HAVE_AVAHI)
2455 int error
; /* Error code, if any */
2457 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2459 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2463 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2465 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2469 avahi_threaded_poll_start(DNSSDMaster
);
2470 #endif /* HAVE_DNSSD */
2475 * 'filter_cb()' - Filter printer attributes based on the requested array.
2478 static int /* O - 1 to copy, 0 to ignore */
2479 filter_cb(ippeve_filter_t
*filter
, /* I - Filter parameters */
2480 ipp_t
*dst
, /* I - Destination (unused) */
2481 ipp_attribute_t
*attr
) /* I - Source attribute */
2484 * Filter attributes as needed...
2487 #ifndef _WIN32 /* Avoid MS compiler bug */
2489 #endif /* !_WIN32 */
2491 ipp_tag_t group
= ippGetGroupTag(attr
);
2492 const char *name
= ippGetName(attr
);
2494 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
)))
2497 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2502 * 'find_job()' - Find a job specified in a request.
2505 static ippeve_job_t
* /* O - Job or NULL */
2506 find_job(ippeve_client_t
*client
) /* I - Client */
2508 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2509 ippeve_job_t key
, /* Job search key */
2510 *job
; /* Matching job, if any */
2513 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2515 const char *uri
= ippGetString(attr
, 0, NULL
);
2517 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2518 uri
[client
->printer
->urilen
] == '/')
2519 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2523 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2524 key
.id
= ippGetInteger(attr
, 0);
2526 _cupsRWLockRead(&(client
->printer
->rwlock
));
2527 job
= (ippeve_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2528 _cupsRWUnlock(&(client
->printer
->rwlock
));
2535 * 'get_collection()' - Get a collection value from a file.
2538 static ipp_t
* /* O - Collection value */
2539 get_collection(FILE *fp
, /* I - File to read from */
2540 const char *filename
, /* I - Attributes filename */
2541 int *linenum
) /* IO - Line number */
2543 char token
[1024], /* Token from file */
2544 attr
[128]; /* Attribute name */
2545 ipp_tag_t value
; /* Current value type */
2546 ipp_t
*col
= ippNew(); /* Collection value */
2547 ipp_attribute_t
*lastcol
= NULL
; /* Last collection attribute */
2550 while (get_token(fp
, token
, sizeof(token
), linenum
) != NULL
)
2552 if (!strcmp(token
, "}"))
2554 else if (!strcmp(token
, "{") && lastcol
)
2557 * Another collection value
2560 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2561 /* Collection value */
2564 ippSetCollection(col
, &lastcol
, ippGetCount(lastcol
), subcol
);
2568 else if (!_cups_strcasecmp(token
, "MEMBER"))
2576 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2578 fprintf(stderr
, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum
, filename
);
2582 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
2584 fprintf(stderr
, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2588 if (!get_token(fp
, attr
, sizeof(attr
), linenum
))
2590 fprintf(stderr
, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum
, filename
);
2594 if (!get_token(fp
, token
, sizeof(token
), linenum
))
2596 fprintf(stderr
, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum
, filename
);
2602 case IPP_TAG_BOOLEAN
:
2603 if (!_cups_strcasecmp(token
, "true"))
2604 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, 1);
2606 ippAddBoolean(col
, IPP_TAG_ZERO
, attr
, (char)atoi(token
));
2609 case IPP_TAG_INTEGER
:
2611 ippAddInteger(col
, IPP_TAG_ZERO
, value
, attr
, atoi(token
));
2614 case IPP_TAG_RESOLUTION
:
2616 int xres
, /* X resolution */
2617 yres
; /* Y resolution */
2618 char units
[6]; /* Units */
2620 if (sscanf(token
, "%dx%d%5s", &xres
, &yres
, units
) != 3 ||
2621 (_cups_strcasecmp(units
, "dpi") &&
2622 _cups_strcasecmp(units
, "dpc") &&
2623 _cups_strcasecmp(units
, "dpcm") &&
2624 _cups_strcasecmp(units
, "other")))
2626 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2630 if (!_cups_strcasecmp(units
, "dpi"))
2631 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_INCH
, xres
, yres
);
2632 else if (!_cups_strcasecmp(units
, "dpc") ||
2633 !_cups_strcasecmp(units
, "dpcm"))
2634 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, IPP_RES_PER_CM
, xres
, yres
);
2636 ippAddResolution(col
, IPP_TAG_ZERO
, attr
, (ipp_res_t
)0, xres
, yres
);
2640 case IPP_TAG_RANGE
:
2642 int lowers
[4], /* Lower value */
2643 uppers
[4], /* Upper values */
2644 num_vals
; /* Number of values */
2647 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
2648 lowers
+ 0, uppers
+ 0,
2649 lowers
+ 1, uppers
+ 1,
2650 lowers
+ 2, uppers
+ 2,
2651 lowers
+ 3, uppers
+ 3);
2653 if ((num_vals
& 1) || num_vals
== 0)
2655 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token
, *linenum
, filename
);
2659 ippAddRanges(col
, IPP_TAG_ZERO
, attr
, num_vals
/ 2, lowers
,
2664 case IPP_TAG_BEGIN_COLLECTION
:
2665 if (!strcmp(token
, "{"))
2667 ipp_t
*subcol
= get_collection(fp
, filename
, linenum
);
2668 /* Collection value */
2672 lastcol
= ippAddCollection(col
, IPP_TAG_ZERO
, attr
, subcol
);
2680 fprintf(stderr
, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum
, filename
);
2684 case IPP_TAG_STRING
:
2685 ippAddOctetString(col
, IPP_TAG_ZERO
, attr
, token
, (int)strlen(token
));
2689 if (!strchr(token
, ','))
2690 ippAddString(col
, IPP_TAG_ZERO
, value
, attr
, NULL
, token
);
2694 * Multiple string values...
2697 int num_values
; /* Number of values */
2698 char *values
[100], /* Values */
2699 *ptr
; /* Pointer to next value */
2705 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
2708 values
[num_values
] = ptr
;
2710 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
2714 ippAddStrings(col
, IPP_TAG_ZERO
, value
, attr
, num_values
,
2715 NULL
, (const char **)values
);
2725 * If we get here there was a parse error; free memory and return.
2737 * 'get_token()' - Get a token from a file.
2740 static char * /* O - Token from file or NULL on EOF */
2741 get_token(FILE *fp
, /* I - File to read from */
2742 char *buf
, /* I - Buffer to read into */
2743 int buflen
, /* I - Length of buffer */
2744 int *linenum
) /* IO - Current line number */
2746 int ch
, /* Character from file */
2747 quote
; /* Quoting character */
2748 char *bufptr
, /* Pointer into buffer */
2749 *bufend
; /* End of buffer */
2755 * Skip whitespace...
2758 while (isspace(ch
= getc(fp
)))
2770 else if (ch
== '\'' || ch
== '\"')
2773 * Quoted text or regular expression...
2778 bufend
= buf
+ buflen
- 1;
2780 while ((ch
= getc(fp
)) != EOF
)
2785 * Escape next character...
2788 if (bufptr
< bufend
)
2789 *bufptr
++ = (char)ch
;
2791 if ((ch
= getc(fp
)) != EOF
&& bufptr
< bufend
)
2792 *bufptr
++ = (char)ch
;
2794 else if (ch
== quote
)
2796 else if (bufptr
< bufend
)
2797 *bufptr
++ = (char)ch
;
2810 while ((ch
= getc(fp
)) != EOF
)
2816 else if (ch
== '{' || ch
== '}' || ch
== ',')
2826 * Whitespace delimited text...
2832 bufend
= buf
+ buflen
- 1;
2834 while ((ch
= getc(fp
)) != EOF
)
2835 if (isspace(ch
) || ch
== '#')
2837 else if (bufptr
< bufend
)
2838 *bufptr
++ = (char)ch
;
2842 else if (ch
== '\n')
2854 * 'html_escape()' - Write a HTML-safe string.
2858 html_escape(ippeve_client_t
*client
, /* I - Client */
2859 const char *s
, /* I - String to write */
2860 size_t slen
) /* I - Number of characters to write */
2862 const char *start
, /* Start of segment */
2863 *end
; /* End of string */
2867 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2869 while (*s
&& s
< end
)
2871 if (*s
== '&' || *s
== '<')
2874 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2877 httpWrite2(client
->http
, "&", 5);
2879 httpWrite2(client
->http
, "<", 4);
2888 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2893 * 'html_footer()' - Show the web interface footer.
2895 * This function also writes the trailing 0-length chunk.
2899 html_footer(ippeve_client_t
*client
) /* I - Client */
2905 httpWrite2(client
->http
, "", 0);
2910 * 'html_header()' - Show the web interface header and title.
2914 html_header(ippeve_client_t
*client
, /* I - Client */
2915 const char *title
) /* I - Title */
2921 "<title>%s</title>\n"
2922 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2923 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2924 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2925 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2927 "body { font-family: sans-serif; margin: 0; }\n"
2928 "div.body { padding: 0px 10px 10px; }\n"
2929 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2930 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2931 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2932 "table.form th { text-align: right; }\n"
2933 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2934 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2935 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2936 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2937 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2938 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2939 "table.nav td { margin: 0; text-align: center; }\n"
2940 "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"
2941 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2942 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2943 "td.nav:hover { background: #666; color: #fff; }\n"
2944 "td.nav:active { background: #000; color: #ff0; }\n"
2948 "<table class=\"nav\"><tr>"
2949 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2950 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2951 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2953 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2958 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2962 html_printf(ippeve_client_t
*client
, /* I - Client */
2963 const char *format
, /* I - Printf-style format string */
2964 ...) /* I - Additional arguments as needed */
2966 va_list ap
; /* Pointer to arguments */
2967 const char *start
; /* Start of string */
2968 char size
, /* Size character (h, l, L) */
2969 type
; /* Format type character */
2970 int width
, /* Width of field */
2971 prec
; /* Number of characters of precision */
2972 char tformat
[100], /* Temporary format string for sprintf() */
2973 *tptr
, /* Pointer into temporary format */
2974 temp
[1024]; /* Buffer for formatted numbers */
2975 char *s
; /* Pointer to string */
2979 * Loop through the format string, formatting as needed...
2982 va_start(ap
, format
);
2990 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2993 *tptr
++ = *format
++;
2997 httpWrite2(client
->http
, "%", 1);
3002 else if (strchr(" -+#\'", *format
))
3003 *tptr
++ = *format
++;
3008 * Get width from argument...
3012 width
= va_arg(ap
, int);
3014 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
3015 tptr
+= strlen(tptr
);
3021 while (isdigit(*format
& 255))
3023 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3026 width
= width
* 10 + *format
++ - '0';
3032 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3040 * Get precision from argument...
3044 prec
= va_arg(ap
, int);
3046 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
3047 tptr
+= strlen(tptr
);
3053 while (isdigit(*format
& 255))
3055 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3058 prec
= prec
* 10 + *format
++ - '0';
3063 if (*format
== 'l' && format
[1] == 'l')
3067 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
3075 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
3077 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3092 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
3101 case 'E' : /* Floating point formats */
3106 if ((size_t)(width
+ 2) > sizeof(temp
))
3109 sprintf(temp
, tformat
, va_arg(ap
, double));
3111 httpWrite2(client
->http
, temp
, strlen(temp
));
3114 case 'B' : /* Integer formats */
3122 if ((size_t)(width
+ 2) > sizeof(temp
))
3125 # ifdef HAVE_LONG_LONG
3127 sprintf(temp
, tformat
, va_arg(ap
, long long));
3129 # endif /* HAVE_LONG_LONG */
3131 sprintf(temp
, tformat
, va_arg(ap
, long));
3133 sprintf(temp
, tformat
, va_arg(ap
, int));
3135 httpWrite2(client
->http
, temp
, strlen(temp
));
3138 case 'p' : /* Pointer value */
3139 if ((size_t)(width
+ 2) > sizeof(temp
))
3142 sprintf(temp
, tformat
, va_arg(ap
, void *));
3144 httpWrite2(client
->http
, temp
, strlen(temp
));
3147 case 'c' : /* Character or character array */
3150 temp
[0] = (char)va_arg(ap
, int);
3152 html_escape(client
, temp
, 1);
3155 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3158 case 's' : /* String */
3159 if ((s
= va_arg(ap
, char *)) == NULL
)
3162 html_escape(client
, s
, strlen(s
));
3171 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3178 * 'ipp_cancel_job()' - Cancel a job.
3182 ipp_cancel_job(ippeve_client_t
*client
) /* I - Client */
3184 ippeve_job_t
*job
; /* Job information */
3191 if ((job
= find_job(client
)) == NULL
)
3193 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3198 * See if the job is already completed, canceled, or aborted; if so,
3199 * we can't cancel...
3204 case IPP_JSTATE_CANCELED
:
3205 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3206 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3209 case IPP_JSTATE_ABORTED
:
3210 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3211 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3214 case IPP_JSTATE_COMPLETED
:
3215 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3216 "Job #%d is already completed - can\'t cancel.", job
->id
);
3224 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3226 if (job
->state
== IPP_JSTATE_PROCESSING
||
3227 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3231 job
->state
= IPP_JSTATE_CANCELED
;
3232 job
->completed
= time(NULL
);
3235 _cupsRWUnlock(&(client
->printer
->rwlock
));
3237 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3244 * 'ipp_close_job()' - Close an open job.
3248 ipp_close_job(ippeve_client_t
*client
) /* I - Client */
3250 ippeve_job_t
*job
; /* Job information */
3257 if ((job
= find_job(client
)) == NULL
)
3259 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3264 * See if the job is already completed, canceled, or aborted; if so,
3265 * we can't cancel...
3270 case IPP_JSTATE_CANCELED
:
3271 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3272 "Job #%d is canceled - can\'t close.", job
->id
);
3275 case IPP_JSTATE_ABORTED
:
3276 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3277 "Job #%d is aborted - can\'t close.", job
->id
);
3280 case IPP_JSTATE_COMPLETED
:
3281 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3282 "Job #%d is completed - can\'t close.", job
->id
);
3285 case IPP_JSTATE_PROCESSING
:
3286 case IPP_JSTATE_STOPPED
:
3287 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3288 "Job #%d is already closed.", job
->id
);
3292 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3299 * 'ipp_create_job()' - Create a job object.
3303 ipp_create_job(ippeve_client_t
*client
) /* I - Client */
3305 ippeve_job_t
*job
; /* New job */
3306 cups_array_t
*ra
; /* Attributes to send in response */
3310 * Validate print job attributes...
3313 if (!valid_job_attributes(client
))
3315 httpFlush(client
->http
);
3320 * Do we have a file to print?
3323 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3325 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3326 "Unexpected document data following request.");
3334 if ((job
= create_job(client
)) == NULL
)
3336 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3337 "Currently printing another job.");
3342 * Return the job info...
3345 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3347 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3348 cupsArrayAdd(ra
, "job-id");
3349 cupsArrayAdd(ra
, "job-state");
3350 cupsArrayAdd(ra
, "job-state-message");
3351 cupsArrayAdd(ra
, "job-state-reasons");
3352 cupsArrayAdd(ra
, "job-uri");
3354 copy_job_attributes(client
, job
, ra
);
3355 cupsArrayDelete(ra
);
3360 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3364 ipp_get_job_attributes(
3365 ippeve_client_t
*client
) /* I - Client */
3367 ippeve_job_t
*job
; /* Job */
3368 cups_array_t
*ra
; /* requested-attributes */
3371 if ((job
= find_job(client
)) == NULL
)
3373 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3377 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3379 ra
= ippCreateRequestedArray(client
->request
);
3380 copy_job_attributes(client
, job
, ra
);
3381 cupsArrayDelete(ra
);
3386 * 'ipp_get_jobs()' - Get a list of job objects.
3390 ipp_get_jobs(ippeve_client_t
*client
) /* I - Client */
3392 ipp_attribute_t
*attr
; /* Current attribute */
3393 const char *which_jobs
= NULL
;
3394 /* which-jobs values */
3395 int job_comparison
; /* Job comparison */
3396 ipp_jstate_t job_state
; /* job-state value */
3397 int first_job_id
, /* First job ID */
3398 limit
, /* Maximum number of jobs to return */
3399 count
; /* Number of jobs that match */
3400 const char *username
; /* Username */
3401 ippeve_job_t
*job
; /* Current job pointer */
3402 cups_array_t
*ra
; /* Requested attributes array */
3406 * See if the "which-jobs" attribute have been specified...
3409 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3410 IPP_TAG_KEYWORD
)) != NULL
)
3412 which_jobs
= ippGetString(attr
, 0, NULL
);
3413 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3416 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3418 job_comparison
= -1;
3419 job_state
= IPP_JSTATE_STOPPED
;
3421 else if (!strcmp(which_jobs
, "completed"))
3424 job_state
= IPP_JSTATE_CANCELED
;
3426 else if (!strcmp(which_jobs
, "aborted"))
3429 job_state
= IPP_JSTATE_ABORTED
;
3431 else if (!strcmp(which_jobs
, "all"))
3434 job_state
= IPP_JSTATE_PENDING
;
3436 else if (!strcmp(which_jobs
, "canceled"))
3439 job_state
= IPP_JSTATE_CANCELED
;
3441 else if (!strcmp(which_jobs
, "pending"))
3444 job_state
= IPP_JSTATE_PENDING
;
3446 else if (!strcmp(which_jobs
, "pending-held"))
3449 job_state
= IPP_JSTATE_HELD
;
3451 else if (!strcmp(which_jobs
, "processing"))
3454 job_state
= IPP_JSTATE_PROCESSING
;
3456 else if (!strcmp(which_jobs
, "processing-stopped"))
3459 job_state
= IPP_JSTATE_STOPPED
;
3463 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3464 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3465 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3466 "which-jobs", NULL
, which_jobs
);
3471 * See if they want to limit the number of jobs reported...
3474 if ((attr
= ippFindAttribute(client
->request
, "limit",
3475 IPP_TAG_INTEGER
)) != NULL
)
3477 limit
= ippGetInteger(attr
, 0);
3479 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3484 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3485 IPP_TAG_INTEGER
)) != NULL
)
3487 first_job_id
= ippGetInteger(attr
, 0);
3489 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
3496 * See if we only want to see jobs for a specific user...
3501 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3502 IPP_TAG_BOOLEAN
)) != NULL
)
3504 int my_jobs
= ippGetBoolean(attr
, 0);
3506 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
3507 my_jobs
? "true" : "false");
3511 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3512 IPP_TAG_NAME
)) == NULL
)
3514 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3515 "Need requesting-user-name with my-jobs.");
3519 username
= ippGetString(attr
, 0, NULL
);
3521 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
3522 client
->hostname
, username
);
3527 * OK, build a list of jobs for this printer...
3530 ra
= ippCreateRequestedArray(client
->request
);
3532 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3534 _cupsRWLockRead(&(client
->printer
->rwlock
));
3536 for (count
= 0, job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3537 (limit
<= 0 || count
< limit
) && job
;
3538 job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3541 * Filter out jobs that don't match...
3544 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3545 (job_comparison
== 0 && job
->state
!= job_state
) ||
3546 (job_comparison
> 0 && job
->state
< job_state
) ||
3547 job
->id
< first_job_id
||
3548 (username
&& job
->username
&&
3549 strcasecmp(username
, job
->username
)))
3553 ippAddSeparator(client
->response
);
3556 copy_job_attributes(client
, job
, ra
);
3559 cupsArrayDelete(ra
);
3561 _cupsRWUnlock(&(client
->printer
->rwlock
));
3566 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3570 ipp_get_printer_attributes(
3571 ippeve_client_t
*client
) /* I - Client */
3573 cups_array_t
*ra
; /* Requested attributes array */
3574 ippeve_printer_t
*printer
; /* Printer */
3578 * Send the attributes...
3581 ra
= ippCreateRequestedArray(client
->request
);
3582 printer
= client
->printer
;
3584 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3586 _cupsRWLockRead(&(printer
->rwlock
));
3588 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3589 IPP_TAG_CUPS_CONST
);
3591 if (!ra
|| cupsArrayFind(ra
, "media-col-ready"))
3593 int i
, /* Looping var */
3594 num_ready
= 0; /* Number of ready media */
3595 ipp_t
*ready
[3]; /* Ready media */
3597 if (printer
->main_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3599 if (printer
->main_type
!= IPPEVE_MEDIA_TYPE_NONE
)
3600 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);
3602 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);
3604 if (printer
->envelope_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3605 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);
3606 if (printer
->photo_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3608 if (printer
->photo_type
!= IPPEVE_MEDIA_TYPE_NONE
)
3609 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);
3611 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);
3616 ippAddCollections(client
->response
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, (const ipp_t
**)ready
);
3617 for (i
= 0; i
< num_ready
; i
++)
3618 ippDelete(ready
[i
]);
3621 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
3624 if (!ra
|| cupsArrayFind(ra
, "media-ready"))
3626 int num_ready
= 0; /* Number of ready media */
3627 const char *ready
[3]; /* Ready media */
3629 if (printer
->main_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3630 ready
[num_ready
++] = media_supported
[printer
->main_size
];
3632 if (printer
->envelope_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3633 ready
[num_ready
++] = media_supported
[printer
->envelope_size
];
3635 if (printer
->photo_size
!= IPPEVE_MEDIA_SIZE_NONE
)
3636 ready
[num_ready
++] = media_supported
[printer
->photo_size
];
3639 ippAddStrings(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-ready", num_ready
, NULL
, ready
);
3641 ippAddOutOfBand(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
3644 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3645 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3647 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3648 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3650 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3651 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3654 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3655 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3656 "printer-state", printer
->state
);
3658 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3659 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3661 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3662 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3664 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3666 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3668 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3671 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3673 if (printer
->state_reasons
== IPPEVE_PREASON_NONE
)
3674 ippAddString(client
->response
, IPP_TAG_PRINTER
,
3675 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
3676 "printer-state-reasons", NULL
, "none");
3679 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3680 ippeve_preason_t bit
; /* Reason bit */
3681 int i
; /* Looping var */
3682 char reason
[32]; /* Reason string */
3684 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
3686 if (printer
->state_reasons
& bit
)
3688 snprintf(reason
, sizeof(reason
), "%s-%s", ippeve_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3690 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3692 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3698 if (!ra
|| cupsArrayFind(ra
, "printer-supply"))
3700 int i
; /* Looping var */
3701 char buffer
[256]; /* Supply value buffer */
3702 ipp_attribute_t
*attr
= NULL
; /* Attribute */
3703 static const char * const colorants
[] = { "cyan", "magenta", "yellow", "black", "unknown" };
3705 for (i
= 0; i
< 5; i
++)
3707 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
]);
3710 attr
= ippAddOctetString(client
->response
, IPP_TAG_PRINTER
, "printer-supply", buffer
, (int)strlen(buffer
));
3712 ippSetOctetString(client
->response
, &attr
, i
, buffer
, (int)strlen(buffer
));
3716 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3717 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3719 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3720 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3721 "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3723 _cupsRWUnlock(&(printer
->rwlock
));
3725 cupsArrayDelete(ra
);
3730 * 'ipp_identify_printer()' - Beep or display a message.
3734 ipp_identify_printer(
3735 ippeve_client_t
*client
) /* I - Client */
3737 ipp_attribute_t
*actions
, /* identify-actions */
3738 *message
; /* message */
3741 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3742 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3744 if (!actions
|| ippContainsString(actions
, "sound"))
3750 if (ippContainsString(actions
, "display"))
3751 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3753 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3758 * 'ipp_print_job()' - Create a job object with an attached document.
3762 ipp_print_job(ippeve_client_t
*client
) /* I - Client */
3764 ippeve_job_t
*job
; /* New job */
3765 char filename
[1024], /* Filename buffer */
3766 buffer
[4096]; /* Copy buffer */
3767 ssize_t bytes
; /* Bytes read */
3768 cups_array_t
*ra
; /* Attributes to send in response */
3769 _cups_thread_t t
; /* Thread */
3773 * Validate print job attributes...
3776 if (!valid_job_attributes(client
))
3778 httpFlush(client
->http
);
3783 * Do we have a file to print?
3786 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3788 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3796 if ((job
= create_job(client
)) == NULL
)
3798 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3799 "Currently printing another job.");
3804 * Create a file for the request data...
3807 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
3810 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3812 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3814 job
->state
= IPP_JSTATE_ABORTED
;
3816 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3817 "Unable to create print file: %s", strerror(errno
));
3821 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3823 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3825 int error
= errno
; /* Write error */
3827 job
->state
= IPP_JSTATE_ABORTED
;
3834 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3835 "Unable to write print file: %s", strerror(error
));
3843 * Got an error while reading the print data, so abort this job.
3846 job
->state
= IPP_JSTATE_ABORTED
;
3853 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3854 "Unable to read print file.");
3860 int error
= errno
; /* Write error */
3862 job
->state
= IPP_JSTATE_ABORTED
;
3867 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3868 "Unable to write print file: %s", strerror(error
));
3873 job
->filename
= strdup(filename
);
3874 job
->state
= IPP_JSTATE_PENDING
;
3877 * Process the job...
3880 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
3884 _cupsThreadDetach(t
);
3888 job
->state
= IPP_JSTATE_ABORTED
;
3889 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3894 * Return the job info...
3897 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3899 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3900 cupsArrayAdd(ra
, "job-id");
3901 cupsArrayAdd(ra
, "job-state");
3902 cupsArrayAdd(ra
, "job-state-message");
3903 cupsArrayAdd(ra
, "job-state-reasons");
3904 cupsArrayAdd(ra
, "job-uri");
3906 copy_job_attributes(client
, job
, ra
);
3907 cupsArrayDelete(ra
);
3912 * 'ipp_print_uri()' - Create a job object with a referenced document.
3916 ipp_print_uri(ippeve_client_t
*client
) /* I - Client */
3918 ippeve_job_t
*job
; /* New job */
3919 ipp_attribute_t
*uri
; /* document-uri */
3920 char scheme
[256], /* URI scheme */
3921 userpass
[256], /* Username and password info */
3922 hostname
[256], /* Hostname */
3923 resource
[1024]; /* Resource path */
3924 int port
; /* Port number */
3925 http_uri_status_t uri_status
; /* URI decode status */
3926 http_encryption_t encryption
; /* Encryption to use, if any */
3927 http_t
*http
; /* Connection for http/https URIs */
3928 http_status_t status
; /* Access status for http/https URIs */
3929 int infile
; /* Input file for local file URIs */
3930 char filename
[1024], /* Filename buffer */
3931 buffer
[4096]; /* Copy buffer */
3932 ssize_t bytes
; /* Bytes read */
3933 cups_array_t
*ra
; /* Attributes to send in response */
3934 static const char * const uri_status_strings
[] =
3935 { /* URI decode errors */
3937 "Bad arguments to function.",
3938 "Bad resource in URI.",
3939 "Bad port number in URI.",
3940 "Bad hostname in URI.",
3941 "Bad username in URI.",
3942 "Bad scheme in URI.",
3948 * Validate print job attributes...
3951 if (!valid_job_attributes(client
))
3953 httpFlush(client
->http
);
3958 * Do we have a file to print?
3961 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3963 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3964 "Unexpected document data following request.");
3969 * Do we have a document URI?
3972 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3973 IPP_TAG_URI
)) == NULL
)
3975 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3979 if (ippGetCount(uri
) != 1)
3981 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3982 "Too many document-uri values.");
3986 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3987 scheme
, sizeof(scheme
), userpass
,
3988 sizeof(userpass
), hostname
, sizeof(hostname
),
3989 &port
, resource
, sizeof(resource
));
3990 if (uri_status
< HTTP_URI_STATUS_OK
)
3992 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3993 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3997 if (strcmp(scheme
, "file") &&
3999 strcmp(scheme
, "https") &&
4000 #endif /* HAVE_SSL */
4001 strcmp(scheme
, "http"))
4003 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4004 "URI scheme \"%s\" not supported.", scheme
);
4008 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4010 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4011 "Unable to access URI: %s", strerror(errno
));
4019 if ((job
= create_job(client
)) == NULL
)
4021 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
4022 "Currently printing another job.");
4027 * Create a file for the request data...
4030 if (!strcasecmp(job
->format
, "image/jpeg"))
4031 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4032 client
->printer
->directory
, job
->id
);
4033 else if (!strcasecmp(job
->format
, "image/png"))
4034 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4035 client
->printer
->directory
, job
->id
);
4036 else if (!strcasecmp(job
->format
, "application/pdf"))
4037 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4038 client
->printer
->directory
, job
->id
);
4039 else if (!strcasecmp(job
->format
, "application/postscript"))
4040 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4041 client
->printer
->directory
, job
->id
);
4043 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4044 client
->printer
->directory
, job
->id
);
4046 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
4048 job
->state
= IPP_JSTATE_ABORTED
;
4050 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4051 "Unable to create print file: %s", strerror(errno
));
4055 if (!strcmp(scheme
, "file"))
4057 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4059 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4060 "Unable to access URI: %s", strerror(errno
));
4066 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4067 (errno
== EAGAIN
|| errno
== EINTR
))
4069 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4071 int error
= errno
; /* Write error */
4073 job
->state
= IPP_JSTATE_ABORTED
;
4081 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4082 "Unable to write print file: %s", strerror(error
));
4093 if (port
== 443 || !strcmp(scheme
, "https"))
4094 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4096 #endif /* HAVE_SSL */
4097 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4099 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4100 1, 30000, NULL
)) == NULL
)
4102 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4103 "Unable to connect to %s: %s", hostname
,
4104 cupsLastErrorString());
4105 job
->state
= IPP_JSTATE_ABORTED
;
4114 httpClearFields(http
);
4115 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4116 if (httpGet(http
, resource
))
4118 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4119 "Unable to GET URI: %s", strerror(errno
));
4121 job
->state
= IPP_JSTATE_ABORTED
;
4131 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4133 if (status
!= HTTP_STATUS_OK
)
4135 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4136 "Unable to GET URI: %s", httpStatus(status
));
4138 job
->state
= IPP_JSTATE_ABORTED
;
4148 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4150 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4152 int error
= errno
; /* Write error */
4154 job
->state
= IPP_JSTATE_ABORTED
;
4162 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4163 "Unable to write print file: %s", strerror(error
));
4173 int error
= errno
; /* Write error */
4175 job
->state
= IPP_JSTATE_ABORTED
;
4180 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4181 "Unable to write print file: %s", strerror(error
));
4186 job
->filename
= strdup(filename
);
4187 job
->state
= IPP_JSTATE_PENDING
;
4190 * Process the job...
4196 * Return the job info...
4199 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4201 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4202 cupsArrayAdd(ra
, "job-id");
4203 cupsArrayAdd(ra
, "job-state");
4204 cupsArrayAdd(ra
, "job-state-reasons");
4205 cupsArrayAdd(ra
, "job-uri");
4207 copy_job_attributes(client
, job
, ra
);
4208 cupsArrayDelete(ra
);
4213 * 'ipp_send_document()' - Add an attached document to a job object created with
4218 ipp_send_document(ippeve_client_t
*client
)/* I - Client */
4220 ippeve_job_t
*job
; /* Job information */
4221 char filename
[1024], /* Filename buffer */
4222 buffer
[4096]; /* Copy buffer */
4223 ssize_t bytes
; /* Bytes read */
4224 ipp_attribute_t
*attr
; /* Current attribute */
4225 cups_array_t
*ra
; /* Attributes to send in response */
4232 if ((job
= find_job(client
)) == NULL
)
4234 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4235 httpFlush(client
->http
);
4240 * See if we already have a document for this job or the job has already
4241 * in a non-pending state...
4244 if (job
->state
> IPP_JSTATE_HELD
)
4246 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4247 "Job is not in a pending state.");
4248 httpFlush(client
->http
);
4251 else if (job
->filename
|| job
->fd
>= 0)
4253 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4254 "Multiple document jobs are not supported.");
4255 httpFlush(client
->http
);
4259 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4260 IPP_TAG_ZERO
)) == NULL
)
4262 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4263 "Missing required last-document attribute.");
4264 httpFlush(client
->http
);
4267 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4268 !ippGetBoolean(attr
, 0))
4270 respond_unsupported(client
, attr
);
4271 httpFlush(client
->http
);
4276 * Validate document attributes...
4279 if (!valid_doc_attributes(client
))
4281 httpFlush(client
->http
);
4285 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
4288 * Get the document format for the job...
4291 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4293 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
4294 job
->format
= ippGetString(attr
, 0, NULL
);
4295 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
4296 job
->format
= ippGetString(attr
, 0, NULL
);
4298 job
->format
= "application/octet-stream";
4301 * Create a file for the request data...
4304 create_job_filename(client
->printer
, job
, filename
, sizeof(filename
));
4307 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
4309 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4311 _cupsRWUnlock(&(client
->printer
->rwlock
));
4315 job
->state
= IPP_JSTATE_ABORTED
;
4317 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4318 "Unable to create print file: %s", strerror(errno
));
4322 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
4324 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4326 int error
= errno
; /* Write error */
4328 job
->state
= IPP_JSTATE_ABORTED
;
4335 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4336 "Unable to write print file: %s", strerror(error
));
4344 * Got an error while reading the print data, so abort this job.
4347 job
->state
= IPP_JSTATE_ABORTED
;
4354 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4355 "Unable to read print file.");
4361 int error
= errno
; /* Write error */
4363 job
->state
= IPP_JSTATE_ABORTED
;
4368 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4369 "Unable to write print file: %s", strerror(error
));
4373 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4376 job
->filename
= strdup(filename
);
4377 job
->state
= IPP_JSTATE_PENDING
;
4379 _cupsRWUnlock(&(client
->printer
->rwlock
));
4382 * Process the job...
4388 * Return the job info...
4391 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4393 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4394 cupsArrayAdd(ra
, "job-id");
4395 cupsArrayAdd(ra
, "job-state");
4396 cupsArrayAdd(ra
, "job-state-reasons");
4397 cupsArrayAdd(ra
, "job-uri");
4399 copy_job_attributes(client
, job
, ra
);
4400 cupsArrayDelete(ra
);
4405 * 'ipp_send_uri()' - Add a referenced document to a job object created with
4410 ipp_send_uri(ippeve_client_t
*client
) /* I - Client */
4412 ippeve_job_t
*job
; /* Job information */
4413 ipp_attribute_t
*uri
; /* document-uri */
4414 char scheme
[256], /* URI scheme */
4415 userpass
[256], /* Username and password info */
4416 hostname
[256], /* Hostname */
4417 resource
[1024]; /* Resource path */
4418 int port
; /* Port number */
4419 http_uri_status_t uri_status
; /* URI decode status */
4420 http_encryption_t encryption
; /* Encryption to use, if any */
4421 http_t
*http
; /* Connection for http/https URIs */
4422 http_status_t status
; /* Access status for http/https URIs */
4423 int infile
; /* Input file for local file URIs */
4424 char filename
[1024], /* Filename buffer */
4425 buffer
[4096]; /* Copy buffer */
4426 ssize_t bytes
; /* Bytes read */
4427 ipp_attribute_t
*attr
; /* Current attribute */
4428 cups_array_t
*ra
; /* Attributes to send in response */
4429 static const char * const uri_status_strings
[] =
4430 { /* URI decode errors */
4432 "Bad arguments to function.",
4433 "Bad resource in URI.",
4434 "Bad port number in URI.",
4435 "Bad hostname in URI.",
4436 "Bad username in URI.",
4437 "Bad scheme in URI.",
4446 if ((job
= find_job(client
)) == NULL
)
4448 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
4449 httpFlush(client
->http
);
4454 * See if we already have a document for this job or the job has already
4455 * in a non-pending state...
4458 if (job
->state
> IPP_JSTATE_HELD
)
4460 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
4461 "Job is not in a pending state.");
4462 httpFlush(client
->http
);
4465 else if (job
->filename
|| job
->fd
>= 0)
4467 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
4468 "Multiple document jobs are not supported.");
4469 httpFlush(client
->http
);
4473 if ((attr
= ippFindAttribute(client
->request
, "last-document",
4474 IPP_TAG_ZERO
)) == NULL
)
4476 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4477 "Missing required last-document attribute.");
4478 httpFlush(client
->http
);
4481 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
4482 !ippGetBoolean(attr
, 0))
4484 respond_unsupported(client
, attr
);
4485 httpFlush(client
->http
);
4490 * Validate document attributes...
4493 if (!valid_doc_attributes(client
))
4495 httpFlush(client
->http
);
4500 * Do we have a file to print?
4503 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
4505 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4506 "Unexpected document data following request.");
4511 * Do we have a document URI?
4514 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
4515 IPP_TAG_URI
)) == NULL
)
4517 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
4521 if (ippGetCount(uri
) != 1)
4523 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4524 "Too many document-uri values.");
4528 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4529 scheme
, sizeof(scheme
), userpass
,
4530 sizeof(userpass
), hostname
, sizeof(hostname
),
4531 &port
, resource
, sizeof(resource
));
4532 if (uri_status
< HTTP_URI_STATUS_OK
)
4534 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
4535 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
4539 if (strcmp(scheme
, "file") &&
4541 strcmp(scheme
, "https") &&
4542 #endif /* HAVE_SSL */
4543 strcmp(scheme
, "http"))
4545 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
4546 "URI scheme \"%s\" not supported.", scheme
);
4550 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
4552 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4553 "Unable to access URI: %s", strerror(errno
));
4558 * Get the document format for the job...
4561 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4563 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
4564 IPP_TAG_MIMETYPE
)) != NULL
)
4565 job
->format
= ippGetString(attr
, 0, NULL
);
4567 job
->format
= "application/octet-stream";
4570 * Create a file for the request data...
4573 if (!strcasecmp(job
->format
, "image/jpeg"))
4574 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
4575 client
->printer
->directory
, job
->id
);
4576 else if (!strcasecmp(job
->format
, "image/png"))
4577 snprintf(filename
, sizeof(filename
), "%s/%d.png",
4578 client
->printer
->directory
, job
->id
);
4579 else if (!strcasecmp(job
->format
, "application/pdf"))
4580 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
4581 client
->printer
->directory
, job
->id
);
4582 else if (!strcasecmp(job
->format
, "application/postscript"))
4583 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
4584 client
->printer
->directory
, job
->id
);
4586 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
4587 client
->printer
->directory
, job
->id
);
4589 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
4591 _cupsRWUnlock(&(client
->printer
->rwlock
));
4595 job
->state
= IPP_JSTATE_ABORTED
;
4597 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4598 "Unable to create print file: %s", strerror(errno
));
4602 if (!strcmp(scheme
, "file"))
4604 if ((infile
= open(resource
, O_RDONLY
)) < 0)
4606 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4607 "Unable to access URI: %s", strerror(errno
));
4613 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
4614 (errno
== EAGAIN
|| errno
== EINTR
))
4616 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4618 int error
= errno
; /* Write error */
4620 job
->state
= IPP_JSTATE_ABORTED
;
4628 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4629 "Unable to write print file: %s", strerror(error
));
4640 if (port
== 443 || !strcmp(scheme
, "https"))
4641 encryption
= HTTP_ENCRYPTION_ALWAYS
;
4643 #endif /* HAVE_SSL */
4644 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
4646 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
4647 1, 30000, NULL
)) == NULL
)
4649 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4650 "Unable to connect to %s: %s", hostname
,
4651 cupsLastErrorString());
4652 job
->state
= IPP_JSTATE_ABORTED
;
4661 httpClearFields(http
);
4662 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
4663 if (httpGet(http
, resource
))
4665 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4666 "Unable to GET URI: %s", strerror(errno
));
4668 job
->state
= IPP_JSTATE_ABORTED
;
4678 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
4680 if (status
!= HTTP_STATUS_OK
)
4682 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
4683 "Unable to GET URI: %s", httpStatus(status
));
4685 job
->state
= IPP_JSTATE_ABORTED
;
4695 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
4697 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
4699 int error
= errno
; /* Write error */
4701 job
->state
= IPP_JSTATE_ABORTED
;
4709 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4710 "Unable to write print file: %s", strerror(error
));
4720 int error
= errno
; /* Write error */
4722 job
->state
= IPP_JSTATE_ABORTED
;
4727 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
4728 "Unable to write print file: %s", strerror(error
));
4732 _cupsRWLockWrite(&(client
->printer
->rwlock
));
4735 job
->filename
= strdup(filename
);
4736 job
->state
= IPP_JSTATE_PENDING
;
4738 _cupsRWUnlock(&(client
->printer
->rwlock
));
4741 * Process the job...
4747 * Return the job info...
4750 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4752 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
4753 cupsArrayAdd(ra
, "job-id");
4754 cupsArrayAdd(ra
, "job-state");
4755 cupsArrayAdd(ra
, "job-state-reasons");
4756 cupsArrayAdd(ra
, "job-uri");
4758 copy_job_attributes(client
, job
, ra
);
4759 cupsArrayDelete(ra
);
4764 * 'ipp_validate_job()' - Validate job creation attributes.
4768 ipp_validate_job(ippeve_client_t
*client
) /* I - Client */
4770 if (valid_job_attributes(client
))
4771 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
4776 * 'load_attributes()' - Load printer attributes from a file.
4778 * Syntax is based on ipptool format:
4780 * ATTR value-tag name value
4784 load_attributes(const char *filename
, /* I - File to load */
4785 ipp_t
*attrs
) /* I - Printer attributes */
4787 int linenum
= 0; /* Current line number */
4788 FILE *fp
= NULL
; /* Test file */
4789 char attr
[128], /* Attribute name */
4790 token
[1024], /* Token from file */
4791 *tokenptr
; /* Pointer into token */
4792 ipp_tag_t value
; /* Current value type */
4793 ipp_attribute_t
*attrptr
, /* Attribute pointer */
4794 *lastcol
= NULL
; /* Last collection attribute */
4797 if ((fp
= fopen(filename
, "r")) == NULL
)
4799 fprintf(stderr
, "ippserver: Unable to open \"%s\": %s\n", filename
, strerror(errno
));
4803 while (get_token(fp
, token
, sizeof(token
), &linenum
) != NULL
)
4805 if (!_cups_strcasecmp(token
, "ATTR"))
4811 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4813 fprintf(stderr
, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum
, filename
);
4817 if ((value
= ippTagValue(token
)) == IPP_TAG_ZERO
)
4819 fprintf(stderr
, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4823 if (!get_token(fp
, attr
, sizeof(attr
), &linenum
))
4825 fprintf(stderr
, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum
, filename
);
4829 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4831 fprintf(stderr
, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum
, filename
);
4839 case IPP_TAG_BOOLEAN
:
4840 if (!_cups_strcasecmp(token
, "true"))
4841 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, 1);
4843 attrptr
= ippAddBoolean(attrs
, IPP_TAG_PRINTER
, attr
, (char)atoi(token
));
4846 case IPP_TAG_INTEGER
:
4848 if (!strchr(token
, ','))
4849 attrptr
= ippAddInteger(attrs
, IPP_TAG_PRINTER
, value
, attr
, (int)strtol(token
, &tokenptr
, 0));
4852 int values
[100], /* Values */
4853 num_values
= 1; /* Number of values */
4855 values
[0] = (int)strtol(token
, &tokenptr
, 10);
4856 while (tokenptr
&& *tokenptr
&&
4857 num_values
< (int)(sizeof(values
) / sizeof(values
[0])))
4859 if (*tokenptr
== ',')
4861 else if (!isdigit(*tokenptr
& 255) && *tokenptr
!= '-')
4864 values
[num_values
] = (int)strtol(tokenptr
, &tokenptr
, 0);
4868 attrptr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, values
);
4871 if (!tokenptr
|| *tokenptr
)
4873 fprintf(stderr
, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value
), token
, linenum
, filename
);
4878 case IPP_TAG_RESOLUTION
:
4880 int xres
, /* X resolution */
4881 yres
; /* Y resolution */
4882 ipp_res_t units
; /* Units */
4883 char *start
, /* Start of value */
4884 *ptr
, /* Pointer into value */
4885 *next
= NULL
; /* Next value */
4887 for (start
= token
; start
; start
= next
)
4889 xres
= yres
= (int)strtol(start
, (char **)&ptr
, 10);
4890 if (ptr
> start
&& xres
> 0)
4893 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
4896 if (ptr
&& (next
= strchr(ptr
, ',')) != NULL
)
4899 if (ptr
<= start
|| xres
<= 0 || yres
<= 0 || !ptr
||
4900 (_cups_strcasecmp(ptr
, "dpi") &&
4901 _cups_strcasecmp(ptr
, "dpc") &&
4902 _cups_strcasecmp(ptr
, "dpcm") &&
4903 _cups_strcasecmp(ptr
, "other")))
4905 fprintf(stderr
, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4909 if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
4910 units
= IPP_RES_PER_CM
;
4912 units
= IPP_RES_PER_INCH
;
4915 ippSetResolution(attrs
, &attrptr
, ippGetCount(attrptr
), units
, xres
, yres
);
4917 attrptr
= ippAddResolution(attrs
, IPP_TAG_PRINTER
, attr
, units
, xres
, yres
);
4922 case IPP_TAG_RANGE
:
4924 int lowers
[4], /* Lower value */
4925 uppers
[4], /* Upper values */
4926 num_vals
; /* Number of values */
4929 num_vals
= sscanf(token
, "%d-%d,%d-%d,%d-%d,%d-%d",
4930 lowers
+ 0, uppers
+ 0,
4931 lowers
+ 1, uppers
+ 1,
4932 lowers
+ 2, uppers
+ 2,
4933 lowers
+ 3, uppers
+ 3);
4935 if ((num_vals
& 1) || num_vals
== 0)
4937 fprintf(stderr
, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token
, linenum
, filename
);
4941 attrptr
= ippAddRanges(attrs
, IPP_TAG_PRINTER
, attr
, num_vals
/ 2, lowers
,
4946 case IPP_TAG_BEGIN_COLLECTION
:
4947 if (!strcmp(token
, "{"))
4949 ipp_t
*col
= get_collection(fp
, filename
, &linenum
);
4950 /* Collection value */
4954 attrptr
= lastcol
= ippAddCollection(attrs
, IPP_TAG_PRINTER
, attr
, col
);
4962 fprintf(stderr
, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum
, filename
);
4968 ipp_t
*col
; /* Collection value */
4969 long pos
= ftell(fp
); /* Save position of file */
4971 if (!get_token(fp
, token
, sizeof(token
), &linenum
))
4974 if (strcmp(token
, ","))
4976 fseek(fp
, pos
, SEEK_SET
);
4980 if (!get_token(fp
, token
, sizeof(token
), &linenum
) || strcmp(token
, "{"))
4982 fprintf(stderr
, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
4986 if ((col
= get_collection(fp
, filename
, &linenum
)) == NULL
)
4989 ippSetCollection(attrs
, &attrptr
, ippGetCount(attrptr
), col
);
4991 while (!strcmp(token
, "{"));
4994 case IPP_TAG_STRING
:
4995 attrptr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, attr
, token
, (int)strlen(token
));
4999 fprintf(stderr
, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value
), linenum
, filename
);
5002 case IPP_TAG_TEXTLANG
:
5003 case IPP_TAG_NAMELANG
:
5006 case IPP_TAG_KEYWORD
:
5008 case IPP_TAG_URISCHEME
:
5009 case IPP_TAG_CHARSET
:
5010 case IPP_TAG_LANGUAGE
:
5011 case IPP_TAG_MIMETYPE
:
5012 if (!strchr(token
, ','))
5013 attrptr
= ippAddString(attrs
, IPP_TAG_PRINTER
, value
, attr
, NULL
, token
);
5017 * Multiple string values...
5020 int num_values
; /* Number of values */
5021 char *values
[100], /* Values */
5022 *ptr
; /* Pointer to next value */
5028 for (ptr
= strchr(token
, ','); ptr
; ptr
= strchr(ptr
, ','))
5030 if (ptr
> token
&& ptr
[-1] == '\\')
5031 _cups_strcpy(ptr
- 1, ptr
);
5035 values
[num_values
] = ptr
;
5037 if (num_values
>= (int)(sizeof(values
) / sizeof(values
[0])))
5042 attrptr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, value
, attr
, num_values
, NULL
, (const char **)values
);
5049 fprintf(stderr
, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum
, filename
, cupsLastErrorString());
5055 fprintf(stderr
, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token
, linenum
, filename
);
5065 * 'make_ppd_attributes()' - Make a temporary IPP attributes file from a PPD.
5068 static char * /* O - Temporary filename or `NULL` */
5069 make_ppd_attributes(const char *ppdfile
,/* I - PPD filename */
5070 char *buffer
, /* I - Temporary filename buffer */
5071 size_t bufsize
) /* I - Size of buffer */
5077 * 'make_std_attributes()' - Make a temporary IPP attributes file from the old
5078 * ippserver options.
5081 static char * /* O - Temporary filename or `NULL` */
5082 make_std_attributes(
5083 const char *make
, /* I - Manufacturer name */
5084 const char *model
, /* I - Model name */
5085 int ppm
, /* I - pages-per-minute */
5086 int ppm_color
, /* I - pages-per-minute-color */
5087 int duplex
, /* I - Duplex support? */
5088 char *buffer
, /* I - Temporary filename buffer */
5089 size_t bufsize
) /* I - Size of buffer */
5095 * 'parse_options()' - Parse URL options into CUPS options.
5097 * The client->options string is destroyed by this function.
5100 static int /* O - Number of options */
5101 parse_options(ippeve_client_t
*client
, /* I - Client */
5102 cups_option_t
**options
) /* O - Options */
5104 char *name
, /* Name */
5106 *next
; /* Next name=value pair */
5107 int num_options
= 0; /* Number of options */
5112 for (name
= client
->options
; name
&& *name
; name
= next
)
5114 if ((value
= strchr(name
, '=')) == NULL
)
5118 if ((next
= strchr(value
, '&')) != NULL
)
5121 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5124 return (num_options
);
5129 * 'process_attr_message()' - Process an ATTR: message from a command.
5133 process_attr_message(
5134 ippeve_job_t
*job
, /* I - Job */
5135 char *message
) /* I - Message */
5143 * 'process_client()' - Process client requests on a thread.
5146 static void * /* O - Exit status */
5147 process_client(ippeve_client_t
*client
) /* I - Client */
5150 * Loop until we are out of requests or timeout (30 seconds)...
5154 int first_time
= 1; /* First time request? */
5155 #endif /* HAVE_SSL */
5157 while (httpWait(client
->http
, 30000))
5163 * See if we need to negotiate a TLS connection...
5166 char buf
[1]; /* First byte from client */
5168 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5170 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5172 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5174 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5178 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5183 #endif /* HAVE_SSL */
5185 if (!process_http(client
))
5190 * Close the conection to the client and return...
5193 delete_client(client
);
5200 * 'process_http()' - Process a HTTP request.
5203 int /* O - 1 on success, 0 on failure */
5204 process_http(ippeve_client_t
*client
) /* I - Client connection */
5206 char uri
[1024]; /* URI */
5207 http_state_t http_state
; /* HTTP state */
5208 http_status_t http_status
; /* HTTP status */
5209 ipp_state_t ipp_state
; /* State of IPP transfer */
5210 char scheme
[32], /* Method/scheme */
5211 userpass
[128], /* Username:password */
5212 hostname
[HTTP_MAX_HOST
];
5214 int port
; /* Port number */
5215 const char *encoding
; /* Content-Encoding value */
5216 static const char * const http_states
[] =
5217 { /* Strings for logging HTTP method */
5238 * Clear state variables...
5241 ippDelete(client
->request
);
5242 ippDelete(client
->response
);
5244 client
->request
= NULL
;
5245 client
->response
= NULL
;
5246 client
->operation
= HTTP_STATE_WAITING
;
5249 * Read a request from the connection...
5252 while ((http_state
= httpReadRequest(client
->http
, uri
,
5253 sizeof(uri
))) == HTTP_STATE_WAITING
)
5257 * Parse the request line...
5260 if (http_state
== HTTP_STATE_ERROR
)
5262 if (httpError(client
->http
) == EPIPE
)
5263 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5265 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
5266 strerror(httpError(client
->http
)));
5270 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5272 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5273 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5276 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5278 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5279 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5283 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
5287 * Separate the URI into its components...
5290 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5291 userpass
, sizeof(userpass
),
5292 hostname
, sizeof(hostname
), &port
,
5293 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5294 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5296 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5297 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5301 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5302 *(client
->options
)++ = '\0';
5305 * Process the request...
5308 client
->start
= time(NULL
);
5309 client
->operation
= httpGetState(client
->http
);
5312 * Parse incoming parameters until the status changes...
5315 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5317 if (http_status
!= HTTP_STATUS_OK
)
5319 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5323 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5324 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5327 * HTTP/1.1 and higher require the "Host:" field...
5330 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5335 * Handle HTTP Upgrade...
5338 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5342 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5344 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5347 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5349 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5351 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5355 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5358 #endif /* HAVE_SSL */
5360 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5365 * Handle HTTP Expect...
5368 if (httpGetExpect(client
->http
) &&
5369 (client
->operation
== HTTP_STATE_POST
||
5370 client
->operation
== HTTP_STATE_PUT
))
5372 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5375 * Send 100-continue header...
5378 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5384 * Send 417-expectation-failed header...
5387 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5393 * Handle new transfers...
5396 encoding
= httpGetContentEncoding(client
->http
);
5398 switch (client
->operation
)
5400 case HTTP_STATE_OPTIONS
:
5402 * Do OPTIONS command...
5405 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5407 case HTTP_STATE_HEAD
:
5408 if (!strcmp(client
->uri
, "/icon.png"))
5409 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5410 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5411 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5413 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5415 case HTTP_STATE_GET
:
5416 if (!strcmp(client
->uri
, "/icon.png"))
5419 * Send PNG icon file.
5422 int fd
; /* Icon file */
5423 struct stat fileinfo
; /* Icon file information */
5424 char buffer
[4096]; /* Copy buffer */
5425 ssize_t bytes
; /* Bytes */
5427 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5429 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5430 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5432 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5433 (size_t)fileinfo
.st_size
))
5439 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5440 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5442 httpFlushWrite(client
->http
);
5447 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5449 else if (!strcmp(client
->uri
, "/"))
5452 * Show web status page...
5455 ippeve_job_t
*job
; /* Current job */
5456 int i
; /* Looping var */
5457 ippeve_preason_t reason
; /* Current reason */
5458 static const char * const reasons
[] =
5459 { /* Reason strings */
5462 "Input Tray Missing",
5463 "Marker Supply Empty",
5464 "Marker Supply Low",
5465 "Marker Waste Almost Full",
5466 "Marker Waste Full",
5478 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5481 html_header(client
, client
->printer
->name
);
5483 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5484 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5485 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5486 if (client
->printer
->state_reasons
& reason
)
5487 html_printf(client
, "\n<br> %s", reasons
[i
]);
5488 html_printf(client
, "</p>\n");
5490 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5492 _cupsRWLockRead(&(client
->printer
->rwlock
));
5494 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");
5495 for (job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5497 char when
[256], /* When job queued/started/finished */
5498 hhmmss
[64]; /* Time HH:MM:SS */
5502 case IPP_JSTATE_PENDING
:
5503 case IPP_JSTATE_HELD
:
5504 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5506 case IPP_JSTATE_PROCESSING
:
5507 case IPP_JSTATE_STOPPED
:
5508 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5510 case IPP_JSTATE_ABORTED
:
5511 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5513 case IPP_JSTATE_CANCELED
:
5514 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5516 case IPP_JSTATE_COMPLETED
:
5517 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5521 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
);
5523 html_printf(client
, "</tbody></table>\n");
5525 _cupsRWUnlock(&(client
->printer
->rwlock
));
5527 html_footer(client
);
5531 else if (!strcmp(client
->uri
, "/media"))
5534 * Show web media page...
5537 int i
, /* Looping var */
5538 num_options
; /* Number of form options */
5539 cups_option_t
*options
; /* Form options */
5540 static const char * const sizes
[] =
5541 { /* Size strings */
5554 static const char * const types
[] =
5571 static const int sheets
[] = /* Number of sheets */
5580 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5583 html_header(client
, client
->printer
->name
);
5585 if ((num_options
= parse_options(client
, &options
)) > 0)
5588 * WARNING: A real printer/server implementation MUST NOT implement
5589 * media updates via a GET request - GET requests are supposed to be
5590 * idempotent (without side-effects) and we obviously are not
5591 * authenticating access here. This form is provided solely to
5592 * enable testing and development!
5595 const char *val
; /* Form value */
5597 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5598 client
->printer
->main_size
= atoi(val
);
5599 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5600 client
->printer
->main_type
= atoi(val
);
5601 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5602 client
->printer
->main_level
= atoi(val
);
5604 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5605 client
->printer
->envelope_size
= atoi(val
);
5606 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5607 client
->printer
->envelope_level
= atoi(val
);
5609 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5610 client
->printer
->photo_size
= atoi(val
);
5611 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5612 client
->printer
->photo_type
= atoi(val
);
5613 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5614 client
->printer
->photo_level
= atoi(val
);
5616 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))
5617 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_LOW
;
5619 client
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_LOW
;
5621 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
))
5623 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_EMPTY
;
5624 if (client
->printer
->active_job
)
5625 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
5628 client
->printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MEDIA_EMPTY
| IPPEVE_PREASON_MEDIA_NEEDED
);
5630 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5633 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5635 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5636 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5637 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5638 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5639 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5640 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5641 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5642 if (!strstr(types
[i
], "Photo"))
5643 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5644 html_printf(client
, "</select> <select name=\"main_level\">");
5645 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5646 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5647 html_printf(client
, "</select></td></tr>\n");
5650 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5651 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5652 if (strstr(sizes
[i
], "Envelope"))
5653 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5654 html_printf(client
, "</select> <select name=\"envelope_level\">");
5655 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5656 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5657 html_printf(client
, "</select></td></tr>\n");
5660 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5661 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5662 if (strstr(sizes
[i
], "Photo"))
5663 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5664 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5665 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5666 if (strstr(types
[i
], "Photo"))
5667 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5668 html_printf(client
, "</select> <select name=\"photo_level\">");
5669 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5670 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5671 html_printf(client
, "</select></td></tr>\n");
5673 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5674 html_footer(client
);
5678 else if (!strcmp(client
->uri
, "/supplies"))
5681 * Show web supplies page...
5684 int i
, j
, /* Looping vars */
5685 num_options
; /* Number of form options */
5686 cups_option_t
*options
; /* Form options */
5687 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5689 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5692 html_header(client
, client
->printer
->name
);
5694 if ((num_options
= parse_options(client
, &options
)) > 0)
5697 * WARNING: A real printer/server implementation MUST NOT implement
5698 * supply updates via a GET request - GET requests are supposed to be
5699 * idempotent (without side-effects) and we obviously are not
5700 * authenticating access here. This form is provided solely to
5701 * enable testing and development!
5704 char name
[64]; /* Form field */
5705 const char *val
; /* Form value */
5707 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
);
5709 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5711 snprintf(name
, sizeof(name
), "supply_%d", i
);
5712 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5714 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5720 client
->printer
->state_reasons
|= IPPEVE_PREASON_TONER_EMPTY
;
5721 else if (level
< 10)
5722 client
->printer
->state_reasons
|= IPPEVE_PREASON_TONER_LOW
;
5727 client
->printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_FULL
;
5728 else if (level
> 90)
5729 client
->printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
;
5734 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5737 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5739 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5740 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5742 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5743 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5744 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5745 html_printf(client
, "</select></td></tr>\n");
5747 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5748 html_footer(client
);
5753 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5756 case HTTP_STATE_POST
:
5757 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5761 * Not an IPP request...
5764 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5768 * Read the IPP request...
5771 client
->request
= ippNew();
5773 while ((ipp_state
= ippRead(client
->http
,
5774 client
->request
)) != IPP_STATE_DATA
)
5776 if (ipp_state
== IPP_STATE_ERROR
)
5778 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5779 cupsLastErrorString());
5780 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5786 * Now that we have the IPP request, process the request...
5789 return (process_ipp(client
));
5792 break; /* Anti-compiler-warning-code */
5800 * 'process_ipp()' - Process an IPP request.
5803 static int /* O - 1 on success, 0 on error */
5804 process_ipp(ippeve_client_t
*client
) /* I - Client */
5806 ipp_tag_t group
; /* Current group tag */
5807 ipp_attribute_t
*attr
; /* Current attribute */
5808 ipp_attribute_t
*charset
; /* Character set attribute */
5809 ipp_attribute_t
*language
; /* Language attribute */
5810 ipp_attribute_t
*uri
; /* Printer URI attribute */
5811 int major
, minor
; /* Version number */
5812 const char *name
; /* Name of attribute */
5815 debug_attributes("Request", client
->request
, 1);
5818 * First build an empty response message for this request...
5821 client
->operation_id
= ippGetOperation(client
->request
);
5822 client
->response
= ippNewResponse(client
->request
);
5825 * Then validate the request header and required attributes...
5828 major
= ippGetVersion(client
->request
, &minor
);
5830 if (major
< 1 || major
> 2)
5833 * Return an error, since we only support IPP 1.x and 2.x.
5836 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
5838 else if ((major
* 10 + minor
) > MaxVersion
)
5840 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5841 httpFlush(client
->http
); /* Flush trailing (junk) data */
5843 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5846 else if (ippGetRequestId(client
->request
) <= 0)
5848 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
5850 else if (!ippFirstAttribute(client
->request
))
5852 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
5857 * Make sure that the attributes are provided in the correct order and
5858 * don't repeat groups...
5861 for (attr
= ippFirstAttribute(client
->request
),
5862 group
= ippGetGroupTag(attr
);
5864 attr
= ippNextAttribute(client
->request
))
5866 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5869 * Out of order; return an error...
5872 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5873 "Attribute groups are out of order (%x < %x).",
5874 ippGetGroupTag(attr
), group
);
5878 group
= ippGetGroupTag(attr
);
5884 * Then make sure that the first three attributes are:
5886 * attributes-charset
5887 * attributes-natural-language
5888 * printer-uri/job-uri
5891 attr
= ippFirstAttribute(client
->request
);
5892 name
= ippGetName(attr
);
5893 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5894 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5899 attr
= ippNextAttribute(client
->request
);
5900 name
= ippGetName(attr
);
5902 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5903 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5908 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5909 IPP_TAG_URI
)) != NULL
)
5911 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5912 IPP_TAG_URI
)) != NULL
)
5918 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5919 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5922 * Bad character set...
5925 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5926 "Unsupported character set \"%s\".",
5927 ippGetString(charset
, 0, NULL
));
5929 else if (!charset
|| !language
|| !uri
)
5932 * Return an error, since attributes-charset,
5933 * attributes-natural-language, and printer-uri/job-uri are required
5934 * for all operations.
5937 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5938 "Missing required attributes.");
5942 char scheme
[32], /* URI scheme */
5943 userpass
[32], /* Username/password in URI */
5944 host
[256], /* Host name in URI */
5945 resource
[256]; /* Resource path in URI */
5946 int port
; /* Port number in URI */
5948 name
= ippGetName(uri
);
5950 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5951 scheme
, sizeof(scheme
),
5952 userpass
, sizeof(userpass
),
5953 host
, sizeof(host
), &port
,
5954 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5955 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5956 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5957 else if ((!strcmp(name
, "job-uri") &&
5958 strncmp(resource
, "/ipp/print/", 11)) ||
5959 (!strcmp(name
, "printer-uri") &&
5960 strcmp(resource
, "/ipp/print")))
5961 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5962 name
, ippGetString(uri
, 0, NULL
));
5966 * Try processing the operation...
5969 switch (ippGetOperation(client
->request
))
5971 case IPP_OP_PRINT_JOB
:
5972 ipp_print_job(client
);
5975 case IPP_OP_PRINT_URI
:
5976 ipp_print_uri(client
);
5979 case IPP_OP_VALIDATE_JOB
:
5980 ipp_validate_job(client
);
5983 case IPP_OP_CREATE_JOB
:
5984 ipp_create_job(client
);
5987 case IPP_OP_SEND_DOCUMENT
:
5988 ipp_send_document(client
);
5991 case IPP_OP_SEND_URI
:
5992 ipp_send_uri(client
);
5995 case IPP_OP_CANCEL_JOB
:
5996 ipp_cancel_job(client
);
5999 case IPP_OP_GET_JOB_ATTRIBUTES
:
6000 ipp_get_job_attributes(client
);
6003 case IPP_OP_GET_JOBS
:
6004 ipp_get_jobs(client
);
6007 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
6008 ipp_get_printer_attributes(client
);
6011 case IPP_OP_CLOSE_JOB
:
6012 ipp_close_job(client
);
6015 case IPP_OP_IDENTIFY_PRINTER
:
6016 ipp_identify_printer(client
);
6020 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
6021 "Operation not supported.");
6030 * Send the HTTP header and return...
6033 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6034 httpFlush(client
->http
); /* Flush trailing (junk) data */
6036 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6037 ippLength(client
->response
)));
6042 * 'process_job()' - Process a print job.
6045 static void * /* O - Thread exit status */
6046 process_job(ippeve_job_t
*job
) /* I - Job */
6048 job
->state
= IPP_JSTATE_PROCESSING
;
6049 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6050 job
->processing
= time(NULL
);
6052 while (job
->printer
->state_reasons
& IPPEVE_PREASON_MEDIA_EMPTY
)
6054 job
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
6059 job
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_NEEDED
;
6061 if (job
->printer
->command
)
6064 * Execute a command with the job spool file and wait for it to complete...
6067 int pid
, /* Process ID */
6068 status
; /* Exit status */
6069 time_t start
, /* Start time */
6071 char *myargv
[3], /* Command-line arguments */
6072 *myenvp
[200]; /* Environment variables */
6073 int myenvc
; /* Number of environment variables */
6074 ipp_attribute_t
*attr
; /* Job attribute */
6075 char val
[1280], /* IPP_NAME=value */
6076 *valptr
; /* Pointer into string */
6078 int mypipe
[2]; /* Pipe for stderr */
6079 char line
[2048], /* Line from stderr */
6080 *ptr
, /* Pointer into line */
6081 *endptr
; /* End of line */
6082 ssize_t bytes
; /* Bytes read */
6083 #endif /* !_WIN32 */
6085 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
6090 * Setup the command-line arguments...
6093 myargv
[0] = job
->printer
->command
;
6094 myargv
[1] = job
->filename
;
6098 * Copy the current environment, then add ENV variables for every Job
6102 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6103 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6105 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6108 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6109 * value(s) from the attribute.
6112 const char *name
= ippGetName(attr
);
6121 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6126 *valptr
++ = (char)toupper(*name
& 255);
6131 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6133 myenvp
[myenvc
++] = strdup(val
);
6135 myenvp
[myenvc
] = NULL
;
6138 * Now run the program...
6142 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6147 perror("Unable to create pipe for stderr");
6148 mypipe
[0] = mypipe
[1] = -1;
6151 if ((pid
= fork()) == 0)
6154 * Child comes here...
6162 execve(job
->printer
->command
, myargv
, myenvp
);
6168 * Unable to fork process...
6171 perror("Unable to start job processing command");
6178 * Free memory used for environment...
6182 free(myenvp
[-- myenvc
]);
6187 * Free memory used for environment...
6191 free(myenvp
[-- myenvc
]);
6194 * If the pipe exists, read from it until EOF...
6202 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6207 while ((ptr
= strchr(line
, '\n')) != NULL
)
6211 if (!strncmp(line
, "STATE:", 6))
6214 * Process printer-state-reasons keywords.
6217 process_state_message(job
, line
);
6219 else if (!strncmp(line
, "ATTR:", 5))
6222 * Process printer attribute update.
6225 process_attr_message(job
, line
);
6227 else if (Verbosity
> 1)
6228 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
6232 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6242 * Wait for child to complete...
6245 # ifdef HAVE_WAITPID
6246 while (waitpid(pid
, &status
, 0) < 0);
6248 while (wait(&status
) < 0);
6249 # endif /* HAVE_WAITPID */
6256 if (WIFEXITED(status
))
6257 #endif /* !_WIN32 */
6258 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
6259 job
->printer
->command
, WEXITSTATUS(status
));
6262 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
6263 job
->printer
->command
, WTERMSIG(status
));
6264 #endif /* !_WIN32 */
6265 job
->state
= IPP_JSTATE_ABORTED
;
6267 else if (status
< 0)
6268 job
->state
= IPP_JSTATE_ABORTED
;
6270 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
6271 job
->printer
->command
);
6274 * Make sure processing takes at least 5 seconds...
6278 if ((end
- start
) < 5)
6284 * Sleep for a random amount of time to simulate job processing.
6287 sleep((unsigned)(5 + (rand() % 11)));
6291 job
->state
= IPP_JSTATE_CANCELED
;
6292 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6293 job
->state
= IPP_JSTATE_COMPLETED
;
6295 job
->completed
= time(NULL
);
6296 job
->printer
->state
= IPP_PSTATE_IDLE
;
6297 job
->printer
->active_job
= NULL
;
6304 * 'process_state_message()' - Process a STATE: message from a command.
6308 process_state_message(
6309 ippeve_job_t
*job
, /* I - Job */
6310 char *message
) /* I - Message */
6312 int i
; /* Looping var */
6313 ippeve_preason_t state_reasons
, /* printer-state-reasons values */
6314 bit
; /* Current reason bit */
6315 char *ptr
, /* Pointer into message */
6316 *next
; /* Next keyword in message */
6317 int remove
; /* Non-zero if we are removing keywords */
6321 * Skip leading "STATE:" and any whitespace...
6324 for (message
+= 6; *message
; message
++)
6325 if (*message
!= ' ' && *message
!= '\t')
6329 * Support the following forms of message:
6331 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6333 * "-keyword[,keyword,...]" to remove keywords.
6335 * "+keyword[,keyword,...]" to add keywords.
6337 * Keywords may or may not have a suffix (-report, -warning, -error) per
6341 if (*message
== '-')
6344 state_reasons
= job
->printer
->state_reasons
;
6347 else if (*message
== '+')
6350 state_reasons
= job
->printer
->state_reasons
;
6356 state_reasons
= IPPEVE_PREASON_NONE
;
6361 if ((next
= strchr(message
, ',')) != NULL
)
6364 if ((ptr
= strstr(message
, "-error")) != NULL
)
6366 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6368 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6371 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
6373 if (!strcmp(message
, ippeve_preason_strings
[i
]))
6376 state_reasons
&= ~bit
;
6378 state_reasons
|= bit
;
6388 job
->printer
->state_reasons
= state_reasons
;
6393 * 'register_printer()' - Register a printer object via Bonjour.
6396 static int /* O - 1 on success, 0 on error */
6398 ippeve_printer_t
*printer
, /* I - Printer */
6399 const char *location
, /* I - Location */
6400 const char *make
, /* I - Manufacturer */
6401 const char *model
, /* I - Model name */
6402 const char *formats
, /* I - Supported formats */
6403 const char *adminurl
, /* I - Web interface URL */
6404 const char *uuid
, /* I - Printer UUID */
6405 int color
, /* I - 1 = color, 0 = monochrome */
6406 int duplex
, /* I - 1 = duplex, 0 = simplex */
6407 const char *subtype
) /* I - Service subtype */
6409 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6410 ippeve_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6411 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6413 DNSServiceErrorType error
; /* Error from Bonjour */
6414 char make_model
[256],/* Make and model together */
6415 product
[256], /* Product string */
6416 regtype
[256]; /* Bonjour service type */
6420 * Build the TXT record for IPP...
6423 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
6424 snprintf(product
, sizeof(product
), "(%s)", model
);
6426 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6427 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6428 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(make_model
),
6430 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(adminurl
),
6433 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(location
),
6435 TXTRecordSetValue(&ipp_txt
, "product", (uint8_t)strlen(product
),
6437 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
),
6439 TXTRecordSetValue(&ipp_txt
, "Color", 1, color
? "T" : "F");
6440 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, duplex
? "T" : "F");
6441 TXTRecordSetValue(&ipp_txt
, "usb_MFG", (uint8_t)strlen(make
),
6443 TXTRecordSetValue(&ipp_txt
, "usb_MDL", (uint8_t)strlen(model
),
6445 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(uuid
), uuid
);
6447 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6448 # endif /* HAVE_SSL */
6449 if (strstr(formats
, "image/urf"))
6450 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");
6452 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6453 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6456 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6457 * defend our service name but not actually support LPD...
6460 printer
->printer_ref
= DNSSDMaster
;
6462 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
6463 kDNSServiceFlagsShareConnection
,
6464 0 /* interfaceIndex */, printer
->dnssd_name
,
6465 "_printer._tcp", NULL
/* domain */,
6466 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
6467 NULL
/* txtRecord */,
6468 (DNSServiceRegisterReply
)dnssd_callback
,
6469 printer
)) != kDNSServiceErr_NoError
)
6471 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
6472 printer
->dnssd_name
, error
);
6477 * Then register the ippeve._tcp (IPP) service type with the real port number to
6478 * advertise our IPP printer...
6481 printer
->ipp_ref
= DNSSDMaster
;
6483 if (subtype
&& *subtype
)
6484 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
6486 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6488 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
6489 kDNSServiceFlagsShareConnection
,
6490 0 /* interfaceIndex */, printer
->dnssd_name
,
6491 regtype
, NULL
/* domain */,
6492 NULL
/* host */, htons(printer
->port
),
6493 TXTRecordGetLength(&ipp_txt
),
6494 TXTRecordGetBytesPtr(&ipp_txt
),
6495 (DNSServiceRegisterReply
)dnssd_callback
,
6496 printer
)) != kDNSServiceErr_NoError
)
6498 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6499 printer
->dnssd_name
, regtype
, error
);
6505 * Then register the ippeves._tcp (IPP) service type with the real port number to
6506 * advertise our IPPS printer...
6509 printer
->ipps_ref
= DNSSDMaster
;
6511 if (subtype
&& *subtype
)
6512 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
6514 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6516 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
6517 kDNSServiceFlagsShareConnection
,
6518 0 /* interfaceIndex */, printer
->dnssd_name
,
6519 regtype
, NULL
/* domain */,
6520 NULL
/* host */, htons(printer
->port
),
6521 TXTRecordGetLength(&ipp_txt
),
6522 TXTRecordGetBytesPtr(&ipp_txt
),
6523 (DNSServiceRegisterReply
)dnssd_callback
,
6524 printer
)) != kDNSServiceErr_NoError
)
6526 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6527 printer
->dnssd_name
, regtype
, error
);
6530 # endif /* HAVE_SSL */
6533 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6534 * real port number to advertise our IPP printer...
6537 printer
->http_ref
= DNSSDMaster
;
6539 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
6540 kDNSServiceFlagsShareConnection
,
6541 0 /* interfaceIndex */, printer
->dnssd_name
,
6542 "_http._tcp,_printer", NULL
/* domain */,
6543 NULL
/* host */, htons(printer
->port
),
6544 0 /* txtLen */, NULL
, /* txtRecord */
6545 (DNSServiceRegisterReply
)dnssd_callback
,
6546 printer
)) != kDNSServiceErr_NoError
)
6548 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
6549 printer
->dnssd_name
, regtype
, error
);
6553 TXTRecordDeallocate(&ipp_txt
);
6555 #elif defined(HAVE_AVAHI)
6556 char temp
[256]; /* Subtype service string */
6559 * Create the TXT record...
6563 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6564 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s %s", make
, model
);
6565 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", adminurl
);
6567 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", location
);
6568 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "product=(%s)", model
);
6569 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6570 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", color
? "T" : "F");
6571 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", duplex
? "T" : "F");
6572 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MFG=%s", make
);
6573 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "usb_MDL=%s", model
);
6574 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", uuid
);
6576 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6577 # endif /* HAVE_SSL */
6580 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6583 avahi_threaded_poll_lock(DNSSDMaster
);
6585 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6587 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
);
6590 * Then register the ippeve._tcp (IPP)...
6593 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
);
6594 if (subtype
&& *subtype
)
6596 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6597 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6602 * ippeves._tcp (IPPS) for secure printing...
6605 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
);
6606 if (subtype
&& *subtype
)
6608 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6609 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6611 #endif /* HAVE_SSL */
6614 * Finally _http.tcp (HTTP) for the web interface...
6617 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
);
6618 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");
6624 avahi_entry_group_commit(printer
->ipp_ref
);
6625 avahi_threaded_poll_unlock(DNSSDMaster
);
6627 avahi_string_list_free(ipp_txt
);
6628 #endif /* HAVE_DNSSD */
6635 * 'respond_http()' - Send a HTTP response.
6638 int /* O - 1 on success, 0 on failure */
6640 ippeve_client_t
*client
, /* I - Client */
6641 http_status_t code
, /* I - HTTP status of response */
6642 const char *content_encoding
, /* I - Content-Encoding of response */
6643 const char *type
, /* I - MIME media type of response */
6644 size_t length
) /* I - Length of response */
6646 char message
[1024]; /* Text message */
6649 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6651 if (code
== HTTP_STATUS_CONTINUE
)
6654 * 100-continue doesn't send any headers...
6657 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6661 * Format an error message...
6664 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6666 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6668 type
= "text/plain";
6669 length
= strlen(message
);
6675 * Send the HTTP response header...
6678 httpClearFields(client
->http
);
6680 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6681 client
->operation
== HTTP_STATE_OPTIONS
)
6682 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6686 if (!strcmp(type
, "text/html"))
6687 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6688 "text/html; charset=utf-8");
6690 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6692 if (content_encoding
)
6693 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6696 httpSetLength(client
->http
, length
);
6698 if (httpWriteResponse(client
->http
, code
) < 0)
6702 * Send the response data...
6708 * Send a plain text message.
6711 if (httpPrintf(client
->http
, "%s", message
) < 0)
6714 if (httpWrite2(client
->http
, "", 0) < 0)
6717 else if (client
->response
)
6720 * Send an IPP response...
6723 debug_attributes("Response", client
->response
, 2);
6725 ippSetState(client
->response
, IPP_STATE_IDLE
);
6727 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6736 * 'respond_ipp()' - Send an IPP response.
6740 respond_ipp(ippeve_client_t
*client
, /* I - Client */
6741 ipp_status_t status
, /* I - status-code */
6742 const char *message
, /* I - printf-style status-message */
6743 ...) /* I - Additional args as needed */
6745 const char *formatted
= NULL
; /* Formatted message */
6748 ippSetStatusCode(client
->response
, status
);
6752 va_list ap
; /* Pointer to additional args */
6753 ipp_attribute_t
*attr
; /* New status-message attribute */
6755 va_start(ap
, message
);
6756 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6757 IPP_TAG_TEXT
)) != NULL
)
6758 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6760 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6761 "status-message", NULL
, message
, ap
);
6764 formatted
= ippGetString(attr
, 0, NULL
);
6768 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6769 ippOpString(client
->operation_id
), ippErrorString(status
),
6772 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6773 ippOpString(client
->operation_id
), ippErrorString(status
));
6778 * 'respond_unsupported()' - Respond with an unsupported attribute.
6782 respond_unsupported(
6783 ippeve_client_t
*client
, /* I - Client */
6784 ipp_attribute_t
*attr
) /* I - Atribute */
6786 ipp_attribute_t
*temp
; /* Copy of attribute */
6789 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6790 "Unsupported %s %s%s value.", ippGetName(attr
),
6791 ippGetCount(attr
) > 1 ? "1setOf " : "",
6792 ippTagString(ippGetValueTag(attr
)));
6794 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6795 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6800 * 'run_printer()' - Run the printer service.
6804 run_printer(ippeve_printer_t
*printer
) /* I - Printer */
6806 int num_fds
; /* Number of file descriptors */
6807 struct pollfd polldata
[3]; /* poll() data */
6808 int timeout
; /* Timeout for poll() */
6809 ippeve_client_t
*client
; /* New client */
6813 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6816 polldata
[0].fd
= printer
->ipv4
;
6817 polldata
[0].events
= POLLIN
;
6819 polldata
[1].fd
= printer
->ipv6
;
6820 polldata
[1].events
= POLLIN
;
6825 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6826 polldata
[num_fds
++].events
= POLLIN
;
6827 #endif /* HAVE_DNSSD */
6830 * Loop until we are killed or have a hard error...
6835 if (cupsArrayCount(printer
->jobs
))
6840 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6842 perror("poll() failed");
6846 if (polldata
[0].revents
& POLLIN
)
6848 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6850 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6854 _cupsThreadDetach(t
);
6858 perror("Unable to create client thread");
6859 delete_client(client
);
6864 if (polldata
[1].revents
& POLLIN
)
6866 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6868 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6872 _cupsThreadDetach(t
);
6876 perror("Unable to create client thread");
6877 delete_client(client
);
6883 if (polldata
[2].revents
& POLLIN
)
6884 DNSServiceProcessResult(DNSSDMaster
);
6885 #endif /* HAVE_DNSSD */
6888 * Clean out old jobs...
6891 clean_jobs(printer
);
6897 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6901 time_string(time_t tv
, /* I - Time value */
6902 char *buffer
, /* I - Buffer */
6903 size_t bufsize
) /* I - Size of buffer */
6905 struct tm
*curtime
= localtime(&tv
);
6908 strftime(buffer
, bufsize
, "%X", curtime
);
6914 * 'usage()' - Show program usage.
6918 usage(int status
) /* O - Exit status */
6922 puts(CUPS_SVERSION
" - Copyright (c) 2010-2018 by Apple Inc. All rights reserved.");
6926 puts("Usage: ippserver [options] \"name\"");
6929 puts("-2 Supports 2-sided printing (default=1-sided)");
6930 puts("-M manufacturer Manufacturer name (default=Test)");
6931 puts("-P PIN printing mode");
6932 puts("-V max-version Set maximum supported IPP version");
6933 puts("-a attributes-file Load printer attributes from file");
6934 puts("-c command Run command for every print job");
6935 printf("-d spool-directory Spool directory "
6936 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6937 puts("-f type/subtype[,...] List of supported types "
6938 "(default=application/pdf,image/jpeg)");
6939 puts("-h Show program help");
6940 puts("-i iconfile.png PNG icon file (default=printer.png)");
6941 puts("-k Keep job spool files");
6942 puts("-l location Location of printer (default=empty string)");
6943 puts("-m model Model name (default=Printer)");
6944 puts("-n hostname Hostname for printer");
6945 puts("-p port Port number (default=auto)");
6946 puts("-r subtype Bonjour service subtype (default=_print)");
6947 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6948 puts("-v[vvv] Be (very) verbose");
6955 * 'valid_doc_attributes()' - Determine whether the document attributes are
6958 * When one or more document attributes are invalid, this function adds a
6959 * suitable response and attributes to the unsupported group.
6962 static int /* O - 1 if valid, 0 if not */
6963 valid_doc_attributes(
6964 ippeve_client_t
*client
) /* I - Client */
6966 int valid
= 1; /* Valid attributes? */
6967 ipp_op_t op
= ippGetOperation(client
->request
);
6969 const char *op_name
= ippOpString(op
);
6970 /* IPP operation name */
6971 ipp_attribute_t
*attr
, /* Current attribute */
6972 *supported
; /* xxx-supported attribute */
6973 const char *compression
= NULL
,
6974 /* compression value */
6975 *format
= NULL
; /* document-format value */
6979 * Check operation attributes...
6982 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6985 * If compression is specified, only accept a supported value in a Print-Job
6986 * or Send-Document request...
6989 compression
= ippGetString(attr
, 0, NULL
);
6990 supported
= ippFindAttribute(client
->printer
->attrs
,
6991 "compression-supported", IPP_TAG_KEYWORD
);
6993 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6994 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6995 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6996 op
!= IPP_OP_VALIDATE_JOB
) ||
6997 !ippContainsString(supported
, compression
))
6999 respond_unsupported(client
, attr
);
7004 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
7006 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
7008 if (strcmp(compression
, "none"))
7011 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
7012 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
7018 * Is it a format we support?
7021 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
7023 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
7024 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
7026 respond_unsupported(client
, attr
);
7031 format
= ippGetString(attr
, 0, NULL
);
7033 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
7034 client
->hostname
, op_name
, format
);
7036 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
7041 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
7043 format
= "application/octet-stream"; /* Should never happen */
7045 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
7048 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7051 * Auto-type the file using the first 8 bytes of the file...
7054 unsigned char header
[8]; /* First 8 bytes of file */
7056 memset(header
, 0, sizeof(header
));
7057 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7059 if (!memcmp(header
, "%PDF", 4))
7060 format
= "application/pdf";
7061 else if (!memcmp(header
, "%!", 2))
7062 format
= "application/postscript";
7063 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7064 format
= "image/jpeg";
7065 else if (!memcmp(header
, "\211PNG", 4))
7066 format
= "image/png";
7067 else if (!memcmp(header
, "RAS2", 4))
7068 format
= "image/pwg-raster";
7069 else if (!memcmp(header
, "UNIRAST", 8))
7070 format
= "image/urf";
7076 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
7077 client
->hostname
, op_name
, format
);
7079 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7083 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7085 respond_unsupported(client
, attr
);
7093 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7094 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7101 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7103 * When one or more job attributes are invalid, this function adds a suitable
7104 * response and attributes to the unsupported group.
7107 static int /* O - 1 if valid, 0 if not */
7108 valid_job_attributes(
7109 ippeve_client_t
*client
) /* I - Client */
7111 int i
, /* Looping var */
7112 count
, /* Number of values */
7113 valid
= 1; /* Valid attributes? */
7114 ipp_attribute_t
*attr
, /* Current attribute */
7115 *supported
; /* xxx-supported attribute */
7119 * Check operation attributes...
7122 valid
= valid_doc_attributes(client
);
7125 * Check the various job template attributes...
7128 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7130 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7131 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7133 respond_unsupported(client
, attr
);
7138 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7140 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7142 respond_unsupported(client
, attr
);
7147 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7149 if (ippGetCount(attr
) != 1 ||
7150 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7151 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7152 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7153 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7155 respond_unsupported(client
, attr
);
7160 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7162 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7164 respond_unsupported(client
, attr
);
7169 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7171 if (ippGetCount(attr
) != 1 ||
7172 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7173 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7175 respond_unsupported(client
, attr
);
7179 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7182 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7184 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7186 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7187 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7189 respond_unsupported(client
, attr
);
7194 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7196 if (ippGetCount(attr
) != 1 ||
7197 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7198 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7199 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7200 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7202 respond_unsupported(client
, attr
);
7207 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7209 if (ippGetCount(attr
) != 1 ||
7210 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7211 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7212 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7214 respond_unsupported(client
, attr
);
7219 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7221 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7223 respond_unsupported(client
, attr
);
7229 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7231 ipp_t
*col
, /* media-col collection */
7232 *size
; /* media-size collection */
7233 ipp_attribute_t
*member
, /* Member attribute */
7234 *x_dim
, /* x-dimension */
7235 *y_dim
; /* y-dimension */
7236 int x_value
, /* y-dimension value */
7237 y_value
; /* x-dimension value */
7239 if (ippGetCount(attr
) != 1 ||
7240 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7242 respond_unsupported(client
, attr
);
7246 col
= ippGetCollection(attr
, 0);
7248 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7250 if (ippGetCount(member
) != 1 ||
7251 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7252 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7253 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7255 respond_unsupported(client
, attr
);
7260 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7262 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7264 respond_unsupported(client
, attr
);
7269 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7271 if (ippGetCount(member
) != 1)
7273 respond_unsupported(client
, attr
);
7278 size
= ippGetCollection(member
, 0);
7280 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7281 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
7283 respond_unsupported(client
, attr
);
7288 x_value
= ippGetInteger(x_dim
, 0);
7289 y_value
= ippGetInteger(y_dim
, 0);
7290 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
7291 count
= ippGetCount(supported
);
7293 for (i
= 0; i
< count
; i
++)
7295 size
= ippGetCollection(supported
, i
);
7296 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
7297 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
7299 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
7305 respond_unsupported(client
, attr
);
7313 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7315 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7316 (strcmp(ippGetString(attr
, 0, NULL
),
7317 "separate-documents-uncollated-copies") &&
7318 strcmp(ippGetString(attr
, 0, NULL
),
7319 "separate-documents-collated-copies")))
7321 respond_unsupported(client
, attr
);
7326 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7328 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7329 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7330 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7332 respond_unsupported(client
, attr
);
7337 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7339 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7341 respond_unsupported(client
, attr
);
7346 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7348 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7349 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7350 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7352 respond_unsupported(client
, attr
);
7357 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7359 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7361 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7364 respond_unsupported(client
, attr
);
7369 int xdpi
, /* Horizontal resolution for job template attribute */
7370 ydpi
, /* Vertical resolution for job template attribute */
7371 sydpi
; /* Vertical resolution for supported value */
7372 ipp_res_t units
, /* Units for job template attribute */
7373 sunits
; /* Units for supported value */
7375 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7376 count
= ippGetCount(supported
);
7378 for (i
= 0; i
< count
; i
++)
7380 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7386 respond_unsupported(client
, attr
);
7392 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7394 const char *sides
= ippGetString(attr
, 0, NULL
);
7395 /* "sides" value... */
7397 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7399 respond_unsupported(client
, attr
);
7402 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7404 if (!ippContainsString(supported
, sides
))
7406 respond_unsupported(client
, attr
);
7410 else if (strcmp(sides
, "one-sided"))
7412 respond_unsupported(client
, attr
);