2 * IPP Everywhere printer application for CUPS.
4 * Copyright © 2010-2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * Note: This program began life as the "ippserver" sample code that first
10 * appeared in CUPS 1.4. The name has been changed in order to distinguish it
11 * from the PWG's much more ambitious "ippserver" program, which supports
12 * different kinds of IPP services and multiple services per instance - the
13 * "ippeveprinter" program exposes a single print service conforming to the
14 * current IPP Everywhere specification, thus the new name.
18 * Include necessary headers...
21 #include <cups/cups-private.h>
23 # include <cups/ppd-private.h>
24 #endif /* !CUPS_LITE */
33 # define WEXITSTATUS(s) (s)
34 # include <winsock2.h>
38 extern char **environ
;
40 # include <sys/fcntl.h>
41 # include <sys/wait.h>
47 #elif defined(HAVE_AVAHI)
48 # include <avahi-client/client.h>
49 # include <avahi-client/publish.h>
50 # include <avahi-common/error.h>
51 # include <avahi-common/thread-watch.h>
52 #endif /* HAVE_DNSSD */
53 #ifdef HAVE_SYS_MOUNT_H
54 # include <sys/mount.h>
55 #endif /* HAVE_SYS_MOUNT_H */
56 #ifdef HAVE_SYS_STATFS_H
57 # include <sys/statfs.h>
58 #endif /* HAVE_SYS_STATFS_H */
59 #ifdef HAVE_SYS_STATVFS_H
60 # include <sys/statvfs.h>
61 #endif /* HAVE_SYS_STATVFS_H */
64 #endif /* HAVE_SYS_VFS_H */
66 #include "printer-png.h"
73 enum ippeve_preason_e
/* printer-state-reasons bit values */
75 IPPEVE_PREASON_NONE
= 0x0000, /* none */
76 IPPEVE_PREASON_OTHER
= 0x0001, /* other */
77 IPPEVE_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
78 IPPEVE_PREASON_INPUT_TRAY_MISSING
= 0x0004,
79 /* input-tray-missing */
80 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
81 /* marker-supply-empty */
82 IPPEVE_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
83 /* marker-supply-low */
84 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
85 /* marker-waste-almost-full */
86 IPPEVE_PREASON_MARKER_WASTE_FULL
= 0x0040,
87 /* marker-waste-full */
88 IPPEVE_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
89 IPPEVE_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
90 IPPEVE_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
91 IPPEVE_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
92 IPPEVE_PREASON_MOVING_TO_PAUSED
= 0x0800,
93 /* moving-to-paused */
94 IPPEVE_PREASON_PAUSED
= 0x1000, /* paused */
95 IPPEVE_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
96 IPPEVE_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
97 IPPEVE_PREASON_TONER_LOW
= 0x8000 /* toner-low */
99 typedef unsigned int ippeve_preason_t
; /* Bitfield for printer-state-reasons */
100 static const char * const ippeve_preason_strings
[] =
101 { /* Strings for each bit */
102 /* "none" is implied for no bits set */
105 "input-tray-missing",
106 "marker-supply-empty",
108 "marker-waste-almost-full",
123 * URL scheme for web resources...
127 # define WEB_SCHEME "https"
129 # define WEB_SCHEME "http"
130 #endif /* HAVE_SSL */
138 typedef DNSServiceRef ippeve_srv_t
; /* Service reference */
139 typedef TXTRecordRef ippeve_txt_t
; /* TXT record */
141 #elif defined(HAVE_AVAHI)
142 typedef AvahiEntryGroup
*ippeve_srv_t
; /* Service reference */
143 typedef AvahiStringList
*ippeve_txt_t
; /* TXT record */
146 typedef void *ippeve_srv_t
; /* Service reference */
147 typedef void *ippeve_txt_t
; /* TXT record */
148 #endif /* HAVE_DNSSD */
150 typedef struct ippeve_filter_s
/**** Attribute filter ****/
152 cups_array_t
*ra
; /* Requested attributes */
153 ipp_tag_t group_tag
; /* Group to copy */
156 typedef struct ippeve_job_s ippeve_job_t
;
158 typedef struct ippeve_printer_s
/**** Printer data ****/
160 /* TODO: One IPv4 and one IPv6 listener are really not sufficient */
161 int ipv4
, /* IPv4 listener */
162 ipv6
; /* IPv6 listener */
163 ippeve_srv_t ipp_ref
, /* Bonjour IPP service */
164 ipps_ref
, /* Bonjour IPPS service */
165 http_ref
, /* Bonjour HTTP service */
166 printer_ref
; /* Bonjour LPD service */
167 char *dnssd_name
, /* printer-dnssd-name */
168 *name
, /* printer-name */
169 *icon
, /* Icon filename */
170 *directory
, /* Spool directory */
171 *hostname
, /* Hostname */
172 *uri
, /* printer-uri-supported */
173 *device_uri
, /* Device URI (if any) */
175 *ppdfile
, /* PPD file (if any) */
176 #endif /* !CUPS_LITE */
177 *command
; /* Command to run with job file */
179 int web_forms
; /* Enable web interface forms? */
180 size_t urilen
; /* Length of printer URI */
181 ipp_t
*attrs
; /* Static attributes */
182 time_t start_time
; /* Startup time */
183 time_t config_time
; /* printer-config-change-time */
184 ipp_pstate_t state
; /* printer-state value */
185 ippeve_preason_t state_reasons
; /* printer-state-reasons values */
186 time_t state_time
; /* printer-state-change-time */
187 cups_array_t
*jobs
; /* Jobs */
188 ippeve_job_t
*active_job
; /* Current active/pending job */
189 int next_job_id
; /* Next job-id value */
190 _cups_rwlock_t rwlock
; /* Printer lock */
193 struct ippeve_job_s
/**** Job data ****/
196 const char *name
, /* job-name */
197 *username
, /* job-originating-user-name */
198 *format
; /* document-format */
199 ipp_jstate_t state
; /* job-state value */
200 char *message
; /* job-state-message value */
201 int msglevel
; /* job-state-message log level (0=error, 1=info) */
202 time_t created
, /* time-at-creation value */
203 processing
, /* time-at-processing value */
204 completed
; /* time-at-completed value */
205 int impressions
, /* job-impressions value */
206 impcompleted
; /* job-impressions-completed value */
207 ipp_t
*attrs
; /* Static attributes */
208 int cancel
; /* Non-zero when job canceled */
209 char *filename
; /* Print file name */
210 int fd
; /* Print file descriptor */
211 ippeve_printer_t
*printer
; /* Printer */
214 typedef struct ippeve_client_s
/**** Client data ****/
216 http_t
*http
; /* HTTP connection */
217 ipp_t
*request
, /* IPP request */
218 *response
; /* IPP response */
219 time_t start
; /* Request start time */
220 http_state_t operation
; /* Request operation */
221 ipp_op_t operation_id
; /* IPP operation-id */
222 char uri
[1024], /* Request URI */
223 *options
; /* URI options */
224 http_addr_t addr
; /* Client address */
225 char hostname
[256]; /* Client hostname */
226 ippeve_printer_t
*printer
; /* Printer */
227 ippeve_job_t
*job
; /* Current job, if any */
235 static void clean_jobs(ippeve_printer_t
*printer
);
236 static int compare_jobs(ippeve_job_t
*a
, ippeve_job_t
*b
);
237 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
, ipp_tag_t group_tag
, int quickcopy
);
238 static void copy_job_attributes(ippeve_client_t
*client
, ippeve_job_t
*job
, cups_array_t
*ra
);
239 static ippeve_client_t
*create_client(ippeve_printer_t
*printer
, int sock
);
240 static ippeve_job_t
*create_job(ippeve_client_t
*client
);
241 static int create_job_file(ippeve_job_t
*job
, char *fname
, size_t fnamesize
, const char *dir
, const char *ext
);
242 static int create_listener(const char *name
, int port
, int family
);
243 static ipp_t
*create_media_col(const char *media
, const char *source
, const char *type
, int width
, int length
, int bottom
, int left
, int right
, int top
);
244 static ipp_t
*create_media_size(int width
, int length
);
245 static ippeve_printer_t
*create_printer(const char *servername
, int serverport
, const char *name
, const char *location
, const char *icon
, cups_array_t
*docformats
, const char *subtypes
, const char *directory
, const char *command
, const char *device_uri
, ipp_t
*attrs
);
246 static void debug_attributes(const char *title
, ipp_t
*ipp
, int response
);
247 static void delete_client(ippeve_client_t
*client
);
248 static void delete_job(ippeve_job_t
*job
);
249 static void delete_printer(ippeve_printer_t
*printer
);
251 static void DNSSD_API
dnssd_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, DNSServiceErrorType errorCode
, const char *name
, const char *regtype
, const char *domain
, ippeve_printer_t
*printer
);
252 #elif defined(HAVE_AVAHI)
253 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
254 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
255 #endif /* HAVE_DNSSD */
256 static void dnssd_init(void);
257 static int filter_cb(ippeve_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
258 static ippeve_job_t
*find_job(ippeve_client_t
*client
);
259 static void finish_document_data(ippeve_client_t
*client
, ippeve_job_t
*job
);
260 static void finish_document_uri(ippeve_client_t
*client
, ippeve_job_t
*job
);
261 static void html_escape(ippeve_client_t
*client
, const char *s
, size_t slen
);
262 static void html_footer(ippeve_client_t
*client
);
263 static void html_header(ippeve_client_t
*client
, const char *title
, int refresh
);
264 static void html_printf(ippeve_client_t
*client
, const char *format
, ...) _CUPS_FORMAT(2, 3);
265 static void ipp_cancel_job(ippeve_client_t
*client
);
266 static void ipp_close_job(ippeve_client_t
*client
);
267 static void ipp_create_job(ippeve_client_t
*client
);
268 static void ipp_get_job_attributes(ippeve_client_t
*client
);
269 static void ipp_get_jobs(ippeve_client_t
*client
);
270 static void ipp_get_printer_attributes(ippeve_client_t
*client
);
271 static void ipp_identify_printer(ippeve_client_t
*client
);
272 static void ipp_print_job(ippeve_client_t
*client
);
273 static void ipp_print_uri(ippeve_client_t
*client
);
274 static void ipp_send_document(ippeve_client_t
*client
);
275 static void ipp_send_uri(ippeve_client_t
*client
);
276 static void ipp_validate_job(ippeve_client_t
*client
);
277 static ipp_t
*load_ippserver_attributes(const char *servername
, int serverport
, const char *filename
, cups_array_t
*docformats
);
278 static ipp_t
*load_legacy_attributes(const char *make
, const char *model
, int ppm
, int ppm_color
, int duplex
, cups_array_t
*docformats
);
280 static ipp_t
*load_ppd_attributes(const char *ppdfile
, cups_array_t
*docformats
);
281 #endif /* !CUPS_LITE */
282 static int parse_options(ippeve_client_t
*client
, cups_option_t
**options
);
283 static void process_attr_message(ippeve_job_t
*job
, char *message
);
284 static void *process_client(ippeve_client_t
*client
);
285 static int process_http(ippeve_client_t
*client
);
286 static int process_ipp(ippeve_client_t
*client
);
287 static void *process_job(ippeve_job_t
*job
);
288 static void process_state_message(ippeve_job_t
*job
, char *message
);
289 static int register_printer(ippeve_printer_t
*printer
, const char *subtypes
);
290 static int respond_http(ippeve_client_t
*client
, http_status_t code
, const char *content_coding
, const char *type
, size_t length
);
291 static void respond_ipp(ippeve_client_t
*client
, ipp_status_t status
, const char *message
, ...) _CUPS_FORMAT(3, 4);
292 static void respond_unsupported(ippeve_client_t
*client
, ipp_attribute_t
*attr
);
293 static void run_printer(ippeve_printer_t
*printer
);
294 static int show_media(ippeve_client_t
*client
);
295 static int show_status(ippeve_client_t
*client
);
296 static int show_supplies(ippeve_client_t
*client
);
297 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
298 static void usage(int status
) _CUPS_NORETURN
;
299 static int valid_doc_attributes(ippeve_client_t
*client
);
300 static int valid_job_attributes(ippeve_client_t
*client
);
308 static DNSServiceRef DNSSDMaster
= NULL
;
309 #elif defined(HAVE_AVAHI)
310 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
311 static AvahiClient
*DNSSDClient
= NULL
;
312 #endif /* HAVE_DNSSD */
314 static int KeepFiles
= 0, /* Keep spooled job files? */
315 MaxVersion
= 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
316 Verbosity
= 0; /* Verbosity level */
320 * 'main()' - Main entry to the sample server.
323 int /* O - Exit status */
324 main(int argc
, /* I - Number of command-line args */
325 char *argv
[]) /* I - Command-line arguments */
327 int i
; /* Looping var */
328 const char *opt
, /* Current option character */
329 *attrfile
= NULL
, /* ippserver attributes file */
330 *command
= NULL
, /* Command to run with job files */
331 *device_uri
= NULL
, /* Device URI */
332 *icon
= NULL
, /* Icon file */
334 *keypath
= NULL
, /* Keychain path */
335 #endif /* HAVE_SSL */
336 *location
= "", /* Location of printer */
337 *make
= "Example", /* Manufacturer */
338 *model
= "Printer", /* Model */
339 *name
= NULL
, /* Printer name */
341 *ppdfile
= NULL
, /* PPD file */
342 #endif /* !CUPS_LITE */
343 *subtypes
= "_print"; /* DNS-SD service subtype */
344 int legacy
= 0, /* Legacy mode? */
345 duplex
= 0, /* Duplex mode */
346 ppm
= 10, /* Pages per minute for mono */
347 ppm_color
= 0, /* Pages per minute for color */
348 web_forms
= 1; /* Enable web site forms? */
349 ipp_t
*attrs
= NULL
; /* Printer attributes */
350 char directory
[1024] = ""; /* Spool directory */
351 cups_array_t
*docformats
= NULL
; /* Supported formats */
352 const char *servername
= NULL
; /* Server host name */
353 int serverport
= 0; /* Server port number (0 = auto) */
354 ippeve_printer_t
*printer
; /* Printer object */
358 * Parse command-line arguments...
361 for (i
= 1; i
< argc
; i
++)
363 if (!strcmp(argv
[i
], "--help"))
367 else if (!strcmp(argv
[i
], "--no-web-forms"))
371 else if (!strcmp(argv
[i
], "--version"))
376 else if (!strncmp(argv
[i
], "--", 2))
378 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
381 else if (argv
[i
][0] == '-')
383 for (opt
= argv
[i
] + 1; *opt
; opt
++)
387 case '2' : /* -2 (enable 2-sided printing) */
392 case 'D' : /* -D device-uri */
397 device_uri
= argv
[i
];
401 case 'K' : /* -K keypath */
408 #endif /* HAVE_SSL */
410 case 'M' : /* -M manufacturer */
420 case 'P' : /* -P filename.ppd */
427 #endif /* !CUPS_LITE */
429 case 'V' : /* -V max-version */
434 if (!strcmp(argv
[i
], "2.0"))
436 else if (!strcmp(argv
[i
], "1.1"))
442 case 'a' : /* -a attributes-file */
450 case 'c' : /* -c command */
458 case 'd' : /* -d spool-directory */
463 strlcpy(directory
, argv
[i
], sizeof(directory
));
466 case 'f' : /* -f type/subtype[,...] */
471 docformats
= _cupsArrayNewStrings(argv
[i
], ',');
475 case 'i' : /* -i icon.png */
483 case 'k' : /* -k (keep files) */
487 case 'l' : /* -l location */
495 case 'm' : /* -m model */
504 case 'n' : /* -n hostname */
509 servername
= argv
[i
];
512 case 'p' : /* -p port */
514 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
517 serverport
= atoi(argv
[i
]);
520 case 'r' : /* -r subtype */
528 case 's' : /* -s speed[,color-speed] */
533 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
539 case 'v' : /* -v (be verbose) */
543 default : /* Unknown */
544 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), argv
[0], *opt
);
555 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
564 if (attrfile
!= NULL
&& legacy
)
567 if (((ppdfile
!= NULL
) + (attrfile
!= NULL
) + legacy
) > 1)
569 #endif /* CUPS_LITE */
572 * Apply defaults as needed...
579 * Windows is almost always used as a single user system, so use a default
580 * port number of 8631.
587 * Use 8000 + UID mod 1000 for the default port number...
590 serverport
= 8000 + ((int)getuid() % 1000);
593 _cupsLangPrintf(stderr
, _("Listening on port %d."), serverport
);
598 const char *tmpdir
; /* Temporary directory */
601 if ((tmpdir
= getenv("TEMP")) == NULL
)
603 #elif defined(__APPLE__) && TARGET_OS_OSX
604 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
605 tmpdir
= "/private/tmp";
607 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
611 snprintf(directory
, sizeof(directory
), "%s/ippeveprinter.%d", tmpdir
, (int)getpid());
613 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
615 _cupsLangPrintf(stderr
, _("Unable to create spool directory \"%s\": %s"), directory
, strerror(errno
));
620 _cupsLangPrintf(stderr
, _("Using spool directory \"%s\"."), directory
);
624 cupsSetServerCredentials(keypath
, servername
, 1);
625 #endif /* HAVE_SSL */
628 * Initialize DNS-SD...
634 * Create the printer...
638 docformats
= _cupsArrayNewStrings(ppm_color
> 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ',');
641 attrs
= load_ippserver_attributes(servername
, serverport
, attrfile
, docformats
);
645 attrs
= load_ppd_attributes(ppdfile
, docformats
);
648 command
= "ippeveps";
650 #endif /* !CUPS_LITE */
652 attrs
= load_legacy_attributes(make
, model
, ppm
, ppm_color
, duplex
, docformats
);
654 if ((printer
= create_printer(servername
, serverport
, name
, location
, icon
, docformats
, subtypes
, directory
, command
, device_uri
, attrs
)) == NULL
)
657 printer
->web_forms
= web_forms
;
661 printer
->ppdfile
= strdup(ppdfile
);
662 #endif /* !CUPS_LITE */
665 * Run the print service...
668 run_printer(printer
);
671 * Destroy the printer and exit...
674 delete_printer(printer
);
681 * 'clean_jobs()' - Clean out old (completed) jobs.
685 clean_jobs(ippeve_printer_t
*printer
) /* I - Printer */
687 ippeve_job_t
*job
; /* Current job */
688 time_t cleantime
; /* Clean time */
691 if (cupsArrayCount(printer
->jobs
) == 0)
694 cleantime
= time(NULL
) - 60;
696 _cupsRWLockWrite(&(printer
->rwlock
));
697 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
);
699 job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
700 if (job
->completed
&& job
->completed
< cleantime
)
702 cupsArrayRemove(printer
->jobs
, job
);
707 _cupsRWUnlock(&(printer
->rwlock
));
712 * 'compare_jobs()' - Compare two jobs.
715 static int /* O - Result of comparison */
716 compare_jobs(ippeve_job_t
*a
, /* I - First job */
717 ippeve_job_t
*b
) /* I - Second job */
719 return (b
->id
- a
->id
);
724 * 'copy_attributes()' - Copy attributes from one request to another.
728 copy_attributes(ipp_t
*to
, /* I - Destination request */
729 ipp_t
*from
, /* I - Source request */
730 cups_array_t
*ra
, /* I - Requested attributes */
731 ipp_tag_t group_tag
, /* I - Group to copy */
732 int quickcopy
) /* I - Do a quick copy? */
734 ippeve_filter_t filter
; /* Filter data */
738 filter
.group_tag
= group_tag
;
740 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
745 * 'copy_job_attrs()' - Copy job attributes to the response.
750 ippeve_client_t
*client
, /* I - Client */
751 ippeve_job_t
*job
, /* I - Job */
752 cups_array_t
*ra
) /* I - requested-attributes */
754 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
756 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
759 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
761 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
764 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
767 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
769 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
772 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
773 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
775 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
776 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
778 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
779 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
781 if (!ra
|| cupsArrayFind(ra
, "job-state"))
782 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state", (int)job
->state
);
784 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
788 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_TAG_TEXT
, "job-state-message", NULL
, job
->message
);
794 case IPP_JSTATE_PENDING
:
795 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
798 case IPP_JSTATE_HELD
:
800 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
801 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
802 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
804 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
807 case IPP_JSTATE_PROCESSING
:
809 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
811 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
814 case IPP_JSTATE_STOPPED
:
815 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
818 case IPP_JSTATE_CANCELED
:
819 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
822 case IPP_JSTATE_ABORTED
:
823 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
826 case IPP_JSTATE_COMPLETED
:
827 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
833 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
837 case IPP_JSTATE_PENDING
:
838 ippAddString(client
->response
, IPP_TAG_JOB
,
839 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
843 case IPP_JSTATE_HELD
:
845 ippAddString(client
->response
, IPP_TAG_JOB
,
846 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
847 "job-state-reasons", NULL
, "job-incoming");
848 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
849 ippAddString(client
->response
, IPP_TAG_JOB
,
850 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
851 "job-state-reasons", NULL
, "job-hold-until-specified");
853 ippAddString(client
->response
, IPP_TAG_JOB
,
854 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
855 "job-state-reasons", NULL
, "job-data-insufficient");
858 case IPP_JSTATE_PROCESSING
:
860 ippAddString(client
->response
, IPP_TAG_JOB
,
861 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
862 "job-state-reasons", NULL
, "processing-to-stop-point");
864 ippAddString(client
->response
, IPP_TAG_JOB
,
865 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
866 "job-state-reasons", NULL
, "job-printing");
869 case IPP_JSTATE_STOPPED
:
870 ippAddString(client
->response
, IPP_TAG_JOB
,
871 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
872 NULL
, "job-stopped");
875 case IPP_JSTATE_CANCELED
:
876 ippAddString(client
->response
, IPP_TAG_JOB
,
877 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
878 NULL
, "job-canceled-by-user");
881 case IPP_JSTATE_ABORTED
:
882 ippAddString(client
->response
, IPP_TAG_JOB
,
883 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
884 NULL
, "aborted-by-system");
887 case IPP_JSTATE_COMPLETED
:
888 ippAddString(client
->response
, IPP_TAG_JOB
,
889 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
890 NULL
, "job-completed-successfully");
895 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
896 ippAddInteger(client
->response
, IPP_TAG_JOB
,
897 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
898 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
900 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
901 ippAddInteger(client
->response
, IPP_TAG_JOB
,
902 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
903 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
908 * 'create_client()' - Accept a new network connection and create a client
912 static ippeve_client_t
* /* O - Client */
913 create_client(ippeve_printer_t
*printer
, /* I - Printer */
914 int sock
) /* I - Listen socket */
916 ippeve_client_t
*client
; /* Client */
919 if ((client
= calloc(1, sizeof(ippeve_client_t
))) == NULL
)
921 perror("Unable to allocate memory for client");
925 client
->printer
= printer
;
928 * Accept the client and get the remote address...
931 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
933 perror("Unable to accept client connection");
940 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
943 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
950 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
954 static ippeve_job_t
* /* O - Job */
955 create_job(ippeve_client_t
*client
) /* I - Client */
957 ippeve_job_t
*job
; /* Job */
958 ipp_attribute_t
*attr
; /* Job attribute */
959 char uri
[1024], /* job-uri value */
960 uuid
[64]; /* job-uuid value */
963 _cupsRWLockWrite(&(client
->printer
->rwlock
));
964 if (client
->printer
->active_job
&&
965 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
968 * Only accept a single job at a time...
971 _cupsRWUnlock(&(client
->printer
->rwlock
));
976 * Allocate and initialize the job object...
979 if ((job
= calloc(1, sizeof(ippeve_job_t
))) == NULL
)
981 perror("Unable to allocate memory for job");
985 job
->printer
= client
->printer
;
986 job
->attrs
= ippNew();
987 job
->state
= IPP_JSTATE_HELD
;
991 * Copy all of the job attributes...
994 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
997 * Get the requesting-user-name, document format, and priority...
1000 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1001 job
->username
= ippGetString(attr
, 0, NULL
);
1003 job
->username
= "anonymous";
1005 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1007 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1009 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1010 job
->format
= ippGetString(attr
, 0, NULL
);
1011 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1012 job
->format
= ippGetString(attr
, 0, NULL
);
1014 job
->format
= "application/octet-stream";
1017 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1018 job
->impressions
= ippGetInteger(attr
, 0);
1020 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1021 job
->name
= ippGetString(attr
, 0, NULL
);
1024 * Add job description attributes and add to the jobs array...
1027 job
->id
= client
->printer
->next_job_id
++;
1029 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1030 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1032 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1033 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1034 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1035 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1036 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1037 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1039 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1040 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1042 cupsArrayAdd(client
->printer
->jobs
, job
);
1043 client
->printer
->active_job
= job
;
1045 _cupsRWUnlock(&(client
->printer
->rwlock
));
1052 * 'create_job_file()' - Create a file for the document in a job.
1055 static int /* O - File descriptor or -1 on error */
1057 ippeve_job_t
*job
, /* I - Job */
1058 char *fname
, /* I - Filename buffer */
1059 size_t fnamesize
, /* I - Size of filename buffer */
1060 const char *directory
, /* I - Directory to store in */
1061 const char *ext
) /* I - Extension (`NULL` for default) */
1063 char name
[256], /* "Safe" filename */
1064 *nameptr
; /* Pointer into filename */
1065 const char *job_name
; /* job-name value */
1069 * Make a name from the job-name attribute...
1072 if ((job_name
= ippGetString(ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
), 0, NULL
)) == NULL
)
1073 job_name
= "untitled";
1075 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1077 if (isalnum(*job_name
& 255) || *job_name
== '-')
1079 *nameptr
++ = (char)tolower(*job_name
& 255);
1085 while (job_name
[1] && !isalnum(job_name
[1] & 255) && job_name
[1] != '-')
1093 * Figure out the extension...
1098 if (!strcasecmp(job
->format
, "image/jpeg"))
1100 else if (!strcasecmp(job
->format
, "image/png"))
1102 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1104 else if (!strcasecmp(job
->format
, "image/urf"))
1106 else if (!strcasecmp(job
->format
, "application/pdf"))
1108 else if (!strcasecmp(job
->format
, "application/postscript"))
1110 else if (!strcasecmp(job
->format
, "application/vnd.hp-pcl"))
1117 * Create a filename with the job-id, job-name, and document-format (extension)...
1120 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", directory
, job
->id
, name
, ext
);
1122 return (open(fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666));
1127 * 'create_listener()' - Create a listener socket.
1130 static int /* O - Listener socket or -1 on error */
1131 create_listener(const char *name
, /* I - Host name (`NULL` for any address) */
1132 int port
, /* I - Port number */
1133 int family
) /* I - Address family */
1135 int sock
; /* Listener socket */
1136 http_addrlist_t
*addrlist
; /* Listen address */
1137 char service
[255]; /* Service port */
1140 snprintf(service
, sizeof(service
), "%d", port
);
1141 if ((addrlist
= httpAddrGetList(name
, family
, service
)) == NULL
)
1144 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1146 httpAddrFreeList(addrlist
);
1153 * 'create_media_col()' - Create a media-col value.
1156 static ipp_t
* /* O - media-col collection */
1157 create_media_col(const char *media
, /* I - Media name */
1158 const char *source
, /* I - Media source, if any */
1159 const char *type
, /* I - Media type, if any */
1160 int width
, /* I - x-dimension in 2540ths */
1161 int length
, /* I - y-dimension in 2540ths */
1162 int bottom
, /* I - Bottom margin in 2540ths */
1163 int left
, /* I - Left margin in 2540ths */
1164 int right
, /* I - Right margin in 2540ths */
1165 int top
) /* I - Top margin in 2540ths */
1167 ipp_t
*media_col
= ippNew(), /* media-col value */
1168 *media_size
= create_media_size(width
, length
);
1169 /* media-size value */
1170 char media_key
[256]; /* media-key value */
1171 const char *media_key_suffix
= ""; /* media-key suffix */
1174 if (bottom
== 0 && left
== 0 && right
== 0 && top
== 0)
1175 media_key_suffix
= "_borderless";
1178 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, media_key_suffix
);
1180 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, media_key_suffix
);
1182 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, media_key_suffix
);
1184 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, media_key_suffix
);
1186 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
, media_key
);
1187 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1188 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1190 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin", bottom
);
1192 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin", left
);
1194 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin", right
);
1196 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin", top
);
1198 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1200 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1202 ippDelete(media_size
);
1209 * 'create_media_size()' - Create a media-size value.
1212 static ipp_t
* /* O - media-col collection */
1213 create_media_size(int width
, /* I - x-dimension in 2540ths */
1214 int length
) /* I - y-dimension in 2540ths */
1216 ipp_t
*media_size
= ippNew(); /* media-size value */
1219 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension", width
);
1220 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension", length
);
1222 return (media_size
);
1227 * 'create_printer()' - Create, register, and listen for connections to a
1231 static ippeve_printer_t
* /* O - Printer */
1233 const char *servername
, /* I - Server hostname (NULL for default) */
1234 int serverport
, /* I - Server port */
1235 const char *name
, /* I - printer-name */
1236 const char *location
, /* I - printer-location */
1237 const char *icon
, /* I - printer-icons */
1238 cups_array_t
*docformats
, /* I - document-format-supported */
1239 const char *subtypes
, /* I - Bonjour service subtype(s) */
1240 const char *directory
, /* I - Spool directory */
1241 const char *command
, /* I - Command to run on job files, if any */
1242 const char *device_uri
, /* I - Output device, if any */
1243 ipp_t
*attrs
) /* I - Capability attributes */
1245 ippeve_printer_t
*printer
; /* Printer */
1246 int i
; /* Looping var */
1248 char path
[1024]; /* Full path to command */
1249 #endif /* !_WIN32 */
1250 char uri
[1024], /* Printer URI */
1252 securi
[1024], /* Secure printer URI */
1253 *uris
[2], /* All URIs */
1254 #endif /* HAVE_SSL */
1255 icons
[1024], /* printer-icons URI */
1256 adminurl
[1024], /* printer-more-info URI */
1257 supplyurl
[1024],/* printer-supply-info-uri URI */
1258 uuid
[128]; /* printer-uuid */
1259 int k_supported
; /* Maximum file size supported */
1260 int num_formats
; /* Number of supported document formats */
1261 const char *formats
[100], /* Supported document formats */
1262 *format
; /* Current format */
1263 int num_job_attrs
; /* Number of supported job attributes */
1264 const char *job_attrs
[100];/* Job attributes */
1265 char xxx_supported
[256];
1266 /* Name of -supported attribute */
1267 _cups_globals_t
*cg
= _cupsGlobals();
1268 /* Global path values */
1270 struct statvfs spoolinfo
; /* FS info for spool directory */
1271 double spoolsize
; /* FS size */
1272 #elif defined(HAVE_STATFS)
1273 struct statfs spoolinfo
; /* FS info for spool directory */
1274 double spoolsize
; /* FS size */
1275 #endif /* HAVE_STATVFS */
1276 static const char * const versions
[] =/* ipp-versions-supported values */
1281 static const char * const features
[] =/* ipp-features-supported values */
1285 static const int ops
[] = /* operations-supported values */
1289 IPP_OP_VALIDATE_JOB
,
1291 IPP_OP_SEND_DOCUMENT
,
1294 IPP_OP_GET_JOB_ATTRIBUTES
,
1296 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1297 IPP_OP_CANCEL_MY_JOBS
,
1299 IPP_OP_IDENTIFY_PRINTER
1301 static const char * const charsets
[] =/* charset-supported values */
1306 static const char * const compressions
[] =/* compression-supported values */
1311 #endif /* HAVE_LIBZ */
1314 static const char * const identify_actions
[] =
1319 static const char * const job_creation
[] =
1320 { /* job-creation-attributes-supported values */
1322 "document-password",
1326 "job-password-encryption",
1327 "orientation-requested",
1332 "print-content-optimize",
1333 "print-rendering-intent",
1335 "printer-resolution",
1338 static const char * const media_col_supported
[] =
1339 { /* media-col-supported values */
1340 "media-bottom-margin",
1341 "media-left-margin",
1342 "media-right-margin",
1349 static const char * const multiple_document_handling
[] =
1350 { /* multiple-document-handling-supported values */
1351 "separate-documents-uncollated-copies",
1352 "separate-documents-collated-copies"
1354 static const char * const reference_uri_schemes_supported
[] =
1355 { /* reference-uri-schemes-supported */
1361 #endif /* HAVE_SSL */
1364 static const char * const uri_authentication_supported
[] =
1365 { /* uri-authentication-supported values */
1369 static const char * const uri_security_supported
[] =
1370 { /* uri-security-supported values */
1374 #endif /* HAVE_SSL */
1375 static const char * const which_jobs
[] =
1376 { /* which-jobs-supported values */
1385 "processing-stopped"
1391 * If a command was specified, make sure it exists and is executable...
1396 if (*command
== '/' || !strncmp(command
, "./", 2))
1398 if (access(command
, X_OK
))
1400 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1406 snprintf(path
, sizeof(path
), "%s/ippeveprinter/%s", cg
->cups_serverbin
, command
);
1408 if (access(command
, X_OK
))
1410 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1417 #endif /* !_WIN32 */
1420 * Allocate memory for the printer...
1423 if ((printer
= calloc(1, sizeof(ippeve_printer_t
))) == NULL
)
1425 _cupsLangPrintError(NULL
, _("Unable to allocate memory for printer"));
1431 printer
->name
= strdup(name
);
1432 printer
->dnssd_name
= strdup(name
);
1433 printer
->command
= command
? strdup(command
) : NULL
;
1434 printer
->device_uri
= device_uri
? strdup(device_uri
) : NULL
;
1435 printer
->directory
= strdup(directory
);
1436 printer
->icon
= icon
? strdup(icon
) : NULL
;
1437 printer
->port
= serverport
;
1438 printer
->start_time
= time(NULL
);
1439 printer
->config_time
= printer
->start_time
;
1440 printer
->state
= IPP_PSTATE_IDLE
;
1441 printer
->state_reasons
= IPPEVE_PREASON_NONE
;
1442 printer
->state_time
= printer
->start_time
;
1443 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1444 printer
->next_job_id
= 1;
1448 printer
->hostname
= strdup(servername
);
1452 char temp
[1024]; /* Temporary string */
1454 printer
->hostname
= strdup(httpGetHostname(NULL
, temp
, sizeof(temp
)));
1457 _cupsRWInit(&(printer
->rwlock
));
1460 * Create the listener sockets...
1463 if ((printer
->ipv4
= create_listener(servername
, printer
->port
, AF_INET
)) < 0)
1465 perror("Unable to create IPv4 listener");
1469 if ((printer
->ipv6
= create_listener(servername
, printer
->port
, AF_INET6
)) < 0)
1471 perror("Unable to create IPv6 listener");
1476 * Prepare URI values for the printer attributes...
1479 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1480 printer
->uri
= strdup(uri
);
1481 printer
->urilen
= strlen(uri
);
1484 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1485 #endif /* HAVE_SSL */
1487 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1488 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1489 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1490 httpAssembleUUID(printer
->hostname
, serverport
, name
, 0, uuid
, sizeof(uuid
));
1494 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1495 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1497 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1499 fprintf(stderr
, "printer-uri=\"%s\",\"%s\"\n", uri
, securi
);
1500 #endif /* HAVE_SSL */
1504 * Get the maximum spool size based on the size of the filesystem used for
1505 * the spool directory. If the host OS doesn't support the statfs call
1506 * or the filesystem is larger than 2TiB, always report INT_MAX.
1510 if (statvfs(printer
->directory
, &spoolinfo
))
1511 k_supported
= INT_MAX
;
1512 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1513 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1514 k_supported
= INT_MAX
;
1516 k_supported
= (int)spoolsize
;
1518 #elif defined(HAVE_STATFS)
1519 if (statfs(printer
->directory
, &spoolinfo
))
1520 k_supported
= INT_MAX
;
1521 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1522 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1523 k_supported
= INT_MAX
;
1525 k_supported
= (int)spoolsize
;
1528 k_supported
= INT_MAX
;
1529 #endif /* HAVE_STATVFS */
1532 * Assemble the final list of document formats...
1535 if (!cupsArrayFind(docformats
, (void *)"application/octet-stream"))
1536 cupsArrayAdd(docformats
, (void *)"application/octet-stream");
1538 for (num_formats
= 0, format
= (const char *)cupsArrayFirst(docformats
); format
&& num_formats
< (int)(sizeof(formats
) / sizeof(formats
[0])); format
= (const char *)cupsArrayNext(docformats
))
1539 formats
[num_formats
++] = format
;
1542 * Get the list of attributes that can be used when creating a job...
1546 job_attrs
[num_job_attrs
++] = "ipp-attribute-fidelity";
1547 job_attrs
[num_job_attrs
++] = "job-name";
1548 job_attrs
[num_job_attrs
++] = "job-priority";
1549 job_attrs
[num_job_attrs
++] = "media";
1550 job_attrs
[num_job_attrs
++] = "media-col";
1551 job_attrs
[num_job_attrs
++] = "multiple-document-handling";
1553 for (i
= 0; i
< (int)(sizeof(job_creation
) / sizeof(job_creation
[0])) && num_job_attrs
< (int)(sizeof(job_attrs
) / sizeof(job_attrs
[0])); i
++)
1555 snprintf(xxx_supported
, sizeof(xxx_supported
), "%s-supported", job_creation
[i
]);
1556 if (ippFindAttribute(attrs
, xxx_supported
, IPP_TAG_ZERO
))
1557 job_attrs
[num_job_attrs
++] = job_creation
[i
];
1561 * Fill out the rest of the printer attributes.
1564 printer
->attrs
= attrs
;
1566 /* charset-configured */
1567 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1569 /* charset-supported */
1570 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1572 /* compression-supported */
1573 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1574 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1576 /* document-format-default */
1577 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_MIMETYPE
), "document-format-default", NULL
, "application/octet-stream");
1579 /* document-format-supported */
1580 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
, "document-format-supported", num_formats
, NULL
, formats
);
1582 /* generated-natural-language-supported */
1583 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1585 /* identify-actions-default */
1586 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1588 /* identify-actions-supported */
1589 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
);
1591 /* ipp-features-supported */
1592 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1594 /* ipp-versions-supported */
1595 if (MaxVersion
== 11)
1596 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", NULL
, "1.1");
1598 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", (int)(sizeof(versions
) / sizeof(versions
[0])), NULL
, versions
);
1600 /* job-creation-attributes-supported */
1601 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-creation-attributes-supported", num_job_attrs
, NULL
, job_attrs
);
1603 /* job-ids-supported */
1604 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1606 /* job-k-octets-supported */
1607 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0, k_supported
);
1609 /* job-priority-default */
1610 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1612 /* job-priority-supported */
1613 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 1);
1615 /* job-sheets-default */
1616 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1618 /* job-sheets-supported */
1619 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1621 /* media-col-supported */
1622 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
);
1624 /* multiple-document-handling-supported */
1625 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
);
1627 /* multiple-document-jobs-supported */
1628 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1630 /* multiple-operation-time-out */
1631 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1633 /* multiple-operation-time-out-action */
1634 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1636 /* natural-language-configured */
1637 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "natural-language-configured", NULL
, "en");
1639 /* operations-supported */
1640 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1642 /* pdl-override-supported */
1643 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1645 /* preferred-attributes-supported */
1646 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1648 /* printer-get-attributes-supported */
1649 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1651 /* printer-geo-location */
1652 ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location");
1655 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-icons", NULL
, icons
);
1657 /* printer-is-accepting-jobs */
1658 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1661 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
1663 /* printer-location */
1664 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location", NULL
, location
);
1666 /* printer-more-info */
1667 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1670 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
1672 /* printer-organization */
1673 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "");
1675 /* printer-organizational-unit */
1676 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "");
1678 /* printer-supply-info-uri */
1679 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1681 /* printer-uri-supported */
1686 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
1689 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1690 #endif /* HAVE_SSL */
1693 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1695 /* reference-uri-scheme-supported */
1696 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
);
1698 /* uri-authentication-supported */
1700 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
1702 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
1703 #endif /* HAVE_SSL */
1705 /* uri-security-supported */
1707 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
1709 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
1710 #endif /* HAVE_SSL */
1712 /* which-jobs-supported */
1713 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
);
1715 debug_attributes("Printer", printer
->attrs
, 0);
1718 * Register the printer with Bonjour...
1721 if (!register_printer(printer
, subtypes
))
1732 * If we get here we were unable to create the printer...
1737 delete_printer(printer
);
1744 * 'debug_attributes()' - Print attributes in a request or response.
1748 debug_attributes(const char *title
, /* I - Title */
1749 ipp_t
*ipp
, /* I - Request/response */
1750 int type
) /* I - 0 = object, 1 = request, 2 = response */
1752 ipp_tag_t group_tag
; /* Current group */
1753 ipp_attribute_t
*attr
; /* Current attribute */
1754 char buffer
[2048]; /* String buffer for value */
1755 int major
, minor
; /* Version */
1761 fprintf(stderr
, "%s:\n", title
);
1762 major
= ippGetVersion(ipp
, &minor
);
1763 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1765 fprintf(stderr
, " operation-id=%s(%04x)\n",
1766 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1768 fprintf(stderr
, " status-code=%s(%04x)\n",
1769 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1770 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1772 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1774 attr
= ippNextAttribute(ipp
))
1776 if (ippGetGroupTag(attr
) != group_tag
)
1778 group_tag
= ippGetGroupTag(attr
);
1779 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1782 if (ippGetName(attr
))
1784 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1785 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1786 ippGetCount(attr
) > 1 ? "1setOf " : "",
1787 ippTagString(ippGetValueTag(attr
)), buffer
);
1794 * 'delete_client()' - Close the socket and free all memory used by a client
1799 delete_client(ippeve_client_t
*client
) /* I - Client */
1802 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1805 * Flush pending writes before closing...
1808 httpFlushWrite(client
->http
);
1814 httpClose(client
->http
);
1816 ippDelete(client
->request
);
1817 ippDelete(client
->response
);
1824 * 'delete_job()' - Remove from the printer and free all memory used by a job
1829 delete_job(ippeve_job_t
*job
) /* I - Job */
1832 fprintf(stderr
, "[Job %d] Removing job from history.\n", job
->id
);
1834 ippDelete(job
->attrs
);
1842 unlink(job
->filename
);
1844 free(job
->filename
);
1852 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1853 * used by a printer object.
1857 delete_printer(ippeve_printer_t
*printer
) /* I - Printer */
1859 if (printer
->ipv4
>= 0)
1860 close(printer
->ipv4
);
1862 if (printer
->ipv6
>= 0)
1863 close(printer
->ipv6
);
1866 if (printer
->printer_ref
)
1867 DNSServiceRefDeallocate(printer
->printer_ref
);
1868 if (printer
->ipp_ref
)
1869 DNSServiceRefDeallocate(printer
->ipp_ref
);
1870 if (printer
->ipps_ref
)
1871 DNSServiceRefDeallocate(printer
->ipps_ref
);
1872 if (printer
->http_ref
)
1873 DNSServiceRefDeallocate(printer
->http_ref
);
1874 #elif defined(HAVE_AVAHI)
1875 avahi_threaded_poll_lock(DNSSDMaster
);
1877 if (printer
->printer_ref
)
1878 avahi_entry_group_free(printer
->printer_ref
);
1879 if (printer
->ipp_ref
)
1880 avahi_entry_group_free(printer
->ipp_ref
);
1881 if (printer
->ipps_ref
)
1882 avahi_entry_group_free(printer
->ipps_ref
);
1883 if (printer
->http_ref
)
1884 avahi_entry_group_free(printer
->http_ref
);
1886 avahi_threaded_poll_unlock(DNSSDMaster
);
1887 #endif /* HAVE_DNSSD */
1889 if (printer
->dnssd_name
)
1890 free(printer
->dnssd_name
);
1892 free(printer
->name
);
1894 free(printer
->icon
);
1895 if (printer
->command
)
1896 free(printer
->command
);
1897 if (printer
->device_uri
)
1898 free(printer
->device_uri
);
1900 if (printer
->ppdfile
)
1901 free(printer
->ppdfile
);
1902 #endif /* !CUPS_LITE */
1903 if (printer
->directory
)
1904 free(printer
->directory
);
1905 if (printer
->hostname
)
1906 free(printer
->hostname
);
1910 ippDelete(printer
->attrs
);
1911 cupsArrayDelete(printer
->jobs
);
1919 * 'dnssd_callback()' - Handle Bonjour registration events.
1922 static void DNSSD_API
1924 DNSServiceRef sdRef
, /* I - Service reference */
1925 DNSServiceFlags flags
, /* I - Status flags */
1926 DNSServiceErrorType errorCode
, /* I - Error, if any */
1927 const char *name
, /* I - Service name */
1928 const char *regtype
, /* I - Service type */
1929 const char *domain
, /* I - Domain for service */
1930 ippeve_printer_t
*printer
) /* I - Printer */
1938 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n", regtype
, (int)errorCode
);
1941 else if (strcasecmp(name
, printer
->dnssd_name
))
1944 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
1946 /* No lock needed since only the main thread accesses/changes this */
1947 free(printer
->dnssd_name
);
1948 printer
->dnssd_name
= strdup(name
);
1953 #elif defined(HAVE_AVAHI)
1955 * 'dnssd_callback()' - Handle Bonjour registration events.
1960 AvahiEntryGroup
*srv
, /* I - Service */
1961 AvahiEntryGroupState state
, /* I - Registration state */
1962 void *context
) /* I - Printer */
1971 * 'dnssd_client_cb()' - Client callback for Avahi.
1973 * Called whenever the client or server state changes...
1978 AvahiClient
*c
, /* I - Client */
1979 AvahiClientState state
, /* I - Current state */
1980 void *userdata
) /* I - User data (unused) */
1990 fprintf(stderr
, "Ignored Avahi state %d.\n", state
);
1993 case AVAHI_CLIENT_FAILURE
:
1994 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
1996 fputs("Avahi server crashed, exiting.\n", stderr
);
2002 #endif /* HAVE_DNSSD */
2006 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2013 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2015 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2019 #elif defined(HAVE_AVAHI)
2020 int error
; /* Error code, if any */
2022 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2024 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2028 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2030 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2034 avahi_threaded_poll_start(DNSSDMaster
);
2035 #endif /* HAVE_DNSSD */
2040 * 'filter_cb()' - Filter printer attributes based on the requested array.
2043 static int /* O - 1 to copy, 0 to ignore */
2044 filter_cb(ippeve_filter_t
*filter
, /* I - Filter parameters */
2045 ipp_t
*dst
, /* I - Destination (unused) */
2046 ipp_attribute_t
*attr
) /* I - Source attribute */
2049 * Filter attributes as needed...
2052 #ifndef _WIN32 /* Avoid MS compiler bug */
2054 #endif /* !_WIN32 */
2056 ipp_tag_t group
= ippGetGroupTag(attr
);
2057 const char *name
= ippGetName(attr
);
2059 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
)))
2062 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2067 * 'find_job()' - Find a job specified in a request.
2070 static ippeve_job_t
* /* O - Job or NULL */
2071 find_job(ippeve_client_t
*client
) /* I - Client */
2073 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2074 ippeve_job_t key
, /* Job search key */
2075 *job
; /* Matching job, if any */
2078 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2080 const char *uri
= ippGetString(attr
, 0, NULL
);
2082 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2083 uri
[client
->printer
->urilen
] == '/')
2084 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2088 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2089 key
.id
= ippGetInteger(attr
, 0);
2091 _cupsRWLockRead(&(client
->printer
->rwlock
));
2092 job
= (ippeve_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2093 _cupsRWUnlock(&(client
->printer
->rwlock
));
2100 * 'finish_document()' - Finish receiving a document file and start processing.
2104 finish_document_data(
2105 ippeve_client_t
*client
, /* I - Client */
2106 ippeve_job_t
*job
) /* I - Job */
2108 char filename
[1024], /* Filename buffer */
2109 buffer
[4096]; /* Copy buffer */
2110 ssize_t bytes
; /* Bytes read */
2111 cups_array_t
*ra
; /* Attributes to send in response */
2112 _cups_thread_t t
; /* Thread */
2116 * Create a file for the request data...
2118 * TODO: Update code to support piping large raster data to the print command.
2121 if ((job
->fd
= create_job_file(job
, filename
, sizeof(filename
), client
->printer
->directory
, NULL
)) < 0)
2123 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2129 fprintf(stderr
, "Created job file \"%s\", format \"%s\".\n", filename
, job
->format
);
2131 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2133 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2135 int error
= errno
; /* Write error */
2142 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2151 * Got an error while reading the print data, so abort this job.
2159 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to read print file.");
2166 int error
= errno
; /* Write error */
2172 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2178 job
->filename
= strdup(filename
);
2179 job
->state
= IPP_JSTATE_PENDING
;
2182 * Process the job...
2185 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
2189 _cupsThreadDetach(t
);
2193 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2198 * Return the job info...
2201 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2203 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2204 cupsArrayAdd(ra
, "job-id");
2205 cupsArrayAdd(ra
, "job-state");
2206 cupsArrayAdd(ra
, "job-state-message");
2207 cupsArrayAdd(ra
, "job-state-reasons");
2208 cupsArrayAdd(ra
, "job-uri");
2210 copy_job_attributes(client
, job
, ra
);
2211 cupsArrayDelete(ra
);
2215 * If we get here we had to abort the job...
2220 job
->state
= IPP_JSTATE_ABORTED
;
2221 job
->completed
= time(NULL
);
2223 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2224 cupsArrayAdd(ra
, "job-id");
2225 cupsArrayAdd(ra
, "job-state");
2226 cupsArrayAdd(ra
, "job-state-reasons");
2227 cupsArrayAdd(ra
, "job-uri");
2229 copy_job_attributes(client
, job
, ra
);
2230 cupsArrayDelete(ra
);
2235 * 'finish_uri()' - Finish fetching a document URI and start processing.
2239 finish_document_uri(
2240 ippeve_client_t
*client
, /* I - Client */
2241 ippeve_job_t
*job
) /* I - Job */
2243 ipp_attribute_t
*uri
; /* document-uri */
2244 char scheme
[256], /* URI scheme */
2245 userpass
[256], /* Username and password info */
2246 hostname
[256], /* Hostname */
2247 resource
[1024]; /* Resource path */
2248 int port
; /* Port number */
2249 http_uri_status_t uri_status
; /* URI decode status */
2250 http_encryption_t encryption
; /* Encryption to use, if any */
2251 http_t
*http
; /* Connection for http/https URIs */
2252 http_status_t status
; /* Access status for http/https URIs */
2253 int infile
; /* Input file for local file URIs */
2254 char filename
[1024], /* Filename buffer */
2255 buffer
[4096]; /* Copy buffer */
2256 ssize_t bytes
; /* Bytes read */
2257 ipp_attribute_t
*attr
; /* Current attribute */
2258 cups_array_t
*ra
; /* Attributes to send in response */
2262 * Do we have a file to print?
2265 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2267 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Unexpected document data following request.");
2273 * Do we have a document URI?
2276 if ((uri
= ippFindAttribute(client
->request
, "document-uri", IPP_TAG_URI
)) == NULL
)
2278 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
2283 if (ippGetCount(uri
) != 1)
2285 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Too many document-uri values.");
2290 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
2291 scheme
, sizeof(scheme
), userpass
,
2292 sizeof(userpass
), hostname
, sizeof(hostname
),
2293 &port
, resource
, sizeof(resource
));
2294 if (uri_status
< HTTP_URI_STATUS_OK
)
2296 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s", httpURIStatusString(uri_status
));
2301 if (strcmp(scheme
, "file") &&
2303 strcmp(scheme
, "https") &&
2304 #endif /* HAVE_SSL */
2305 strcmp(scheme
, "http"))
2307 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
, "URI scheme \"%s\" not supported.", scheme
);
2312 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
2314 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
2320 * Get the document format for the job...
2323 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2325 if ((attr
= ippFindAttribute(job
->attrs
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
2326 job
->format
= ippGetString(attr
, 0, NULL
);
2328 job
->format
= "application/octet-stream";
2331 * Create a file for the request data...
2334 if ((job
->fd
= create_job_file(job
, filename
, sizeof(filename
), client
->printer
->directory
, NULL
)) < 0)
2336 _cupsRWUnlock(&(client
->printer
->rwlock
));
2338 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2343 _cupsRWUnlock(&(client
->printer
->rwlock
));
2345 if (!strcmp(scheme
, "file"))
2347 if ((infile
= open(resource
, O_RDONLY
)) < 0)
2349 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
2356 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
2357 (errno
== EAGAIN
|| errno
== EINTR
))
2361 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2363 int error
= errno
; /* Write error */
2371 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2383 if (port
== 443 || !strcmp(scheme
, "https"))
2384 encryption
= HTTP_ENCRYPTION_ALWAYS
;
2386 #endif /* HAVE_SSL */
2387 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
2389 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
, 1, 30000, NULL
)) == NULL
)
2391 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to connect to %s: %s", hostname
, cupsLastErrorString());
2401 httpClearFields(http
);
2402 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
2403 if (httpGet(http
, resource
))
2405 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", strerror(errno
));
2416 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
2418 if (status
!= HTTP_STATUS_OK
)
2420 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", httpStatus(status
));
2431 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
2433 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2435 int error
= errno
; /* Write error */
2443 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2444 "Unable to write print file: %s", strerror(error
));
2455 int error
= errno
; /* Write error */
2461 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2466 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2469 job
->filename
= strdup(filename
);
2470 job
->state
= IPP_JSTATE_PENDING
;
2472 _cupsRWUnlock(&(client
->printer
->rwlock
));
2475 * Process the job...
2481 * Return the job info...
2484 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2486 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2487 cupsArrayAdd(ra
, "job-id");
2488 cupsArrayAdd(ra
, "job-state");
2489 cupsArrayAdd(ra
, "job-state-reasons");
2490 cupsArrayAdd(ra
, "job-uri");
2492 copy_job_attributes(client
, job
, ra
);
2493 cupsArrayDelete(ra
);
2497 * If we get here we had to abort the job...
2502 job
->state
= IPP_JSTATE_ABORTED
;
2503 job
->completed
= time(NULL
);
2505 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2506 cupsArrayAdd(ra
, "job-id");
2507 cupsArrayAdd(ra
, "job-state");
2508 cupsArrayAdd(ra
, "job-state-reasons");
2509 cupsArrayAdd(ra
, "job-uri");
2511 copy_job_attributes(client
, job
, ra
);
2512 cupsArrayDelete(ra
);
2517 * 'html_escape()' - Write a HTML-safe string.
2521 html_escape(ippeve_client_t
*client
, /* I - Client */
2522 const char *s
, /* I - String to write */
2523 size_t slen
) /* I - Number of characters to write */
2525 const char *start
, /* Start of segment */
2526 *end
; /* End of string */
2530 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2532 while (*s
&& s
< end
)
2534 if (*s
== '&' || *s
== '<')
2537 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2540 httpWrite2(client
->http
, "&", 5);
2542 httpWrite2(client
->http
, "<", 4);
2551 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2556 * 'html_footer()' - Show the web interface footer.
2558 * This function also writes the trailing 0-length chunk.
2562 html_footer(ippeve_client_t
*client
) /* I - Client */
2568 httpWrite2(client
->http
, "", 0);
2573 * 'html_header()' - Show the web interface header and title.
2577 html_header(ippeve_client_t
*client
, /* I - Client */
2578 const char *title
, /* I - Title */
2579 int refresh
) /* I - Refresh timer, if any */
2585 "<title>%s</title>\n"
2586 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2587 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2588 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title
);
2590 html_printf(client
, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh
);
2592 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2594 "body { font-family: sans-serif; margin: 0; }\n"
2595 "div.body { padding: 0px 10px 10px; }\n"
2596 "span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
2597 "span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
2598 "table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
2599 "table.form td, table.form th { padding: 5px 2px; }\n"
2600 "table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
2601 "table.form th { text-align: right; }\n"
2602 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2603 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2604 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2605 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2606 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2607 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2608 "table.nav td { margin: 0; text-align: center; }\n"
2609 "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"
2610 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2611 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2612 "td.nav:hover { background: #666; color: #fff; }\n"
2613 "td.nav:active { background: #000; color: #ff0; }\n"
2617 "<table class=\"nav\"><tr>"
2618 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2619 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2620 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2622 "<div class=\"body\">\n", !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2627 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2631 html_printf(ippeve_client_t
*client
, /* I - Client */
2632 const char *format
, /* I - Printf-style format string */
2633 ...) /* I - Additional arguments as needed */
2635 va_list ap
; /* Pointer to arguments */
2636 const char *start
; /* Start of string */
2637 char size
, /* Size character (h, l, L) */
2638 type
; /* Format type character */
2639 int width
, /* Width of field */
2640 prec
; /* Number of characters of precision */
2641 char tformat
[100], /* Temporary format string for sprintf() */
2642 *tptr
, /* Pointer into temporary format */
2643 temp
[1024]; /* Buffer for formatted numbers */
2644 char *s
; /* Pointer to string */
2648 * Loop through the format string, formatting as needed...
2651 va_start(ap
, format
);
2659 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2662 *tptr
++ = *format
++;
2666 httpWrite2(client
->http
, "%", 1);
2671 else if (strchr(" -+#\'", *format
))
2672 *tptr
++ = *format
++;
2677 * Get width from argument...
2681 width
= va_arg(ap
, int);
2683 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2684 tptr
+= strlen(tptr
);
2690 while (isdigit(*format
& 255))
2692 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2695 width
= width
* 10 + *format
++ - '0';
2701 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2709 * Get precision from argument...
2713 prec
= va_arg(ap
, int);
2715 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2716 tptr
+= strlen(tptr
);
2722 while (isdigit(*format
& 255))
2724 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2727 prec
= prec
* 10 + *format
++ - '0';
2732 if (*format
== 'l' && format
[1] == 'l')
2736 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2744 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2746 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2761 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2770 case 'E' : /* Floating point formats */
2775 if ((size_t)(width
+ 2) > sizeof(temp
))
2778 sprintf(temp
, tformat
, va_arg(ap
, double));
2780 httpWrite2(client
->http
, temp
, strlen(temp
));
2783 case 'B' : /* Integer formats */
2791 if ((size_t)(width
+ 2) > sizeof(temp
))
2794 # ifdef HAVE_LONG_LONG
2796 sprintf(temp
, tformat
, va_arg(ap
, long long));
2798 # endif /* HAVE_LONG_LONG */
2800 sprintf(temp
, tformat
, va_arg(ap
, long));
2802 sprintf(temp
, tformat
, va_arg(ap
, int));
2804 httpWrite2(client
->http
, temp
, strlen(temp
));
2807 case 'p' : /* Pointer value */
2808 if ((size_t)(width
+ 2) > sizeof(temp
))
2811 sprintf(temp
, tformat
, va_arg(ap
, void *));
2813 httpWrite2(client
->http
, temp
, strlen(temp
));
2816 case 'c' : /* Character or character array */
2819 temp
[0] = (char)va_arg(ap
, int);
2821 html_escape(client
, temp
, 1);
2824 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2827 case 's' : /* String */
2828 if ((s
= va_arg(ap
, char *)) == NULL
)
2831 html_escape(client
, s
, strlen(s
));
2840 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2847 * 'ipp_cancel_job()' - Cancel a job.
2851 ipp_cancel_job(ippeve_client_t
*client
) /* I - Client */
2853 ippeve_job_t
*job
; /* Job information */
2860 if ((job
= find_job(client
)) == NULL
)
2862 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2867 * See if the job is already completed, canceled, or aborted; if so,
2868 * we can't cancel...
2873 case IPP_JSTATE_CANCELED
:
2874 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2875 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2878 case IPP_JSTATE_ABORTED
:
2879 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2880 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2883 case IPP_JSTATE_COMPLETED
:
2884 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2885 "Job #%d is already completed - can\'t cancel.", job
->id
);
2893 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2895 if (job
->state
== IPP_JSTATE_PROCESSING
||
2896 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2900 job
->state
= IPP_JSTATE_CANCELED
;
2901 job
->completed
= time(NULL
);
2904 _cupsRWUnlock(&(client
->printer
->rwlock
));
2906 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2913 * 'ipp_close_job()' - Close an open job.
2917 ipp_close_job(ippeve_client_t
*client
) /* I - Client */
2919 ippeve_job_t
*job
; /* Job information */
2926 if ((job
= find_job(client
)) == NULL
)
2928 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2933 * See if the job is already completed, canceled, or aborted; if so,
2934 * we can't cancel...
2939 case IPP_JSTATE_CANCELED
:
2940 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2941 "Job #%d is canceled - can\'t close.", job
->id
);
2944 case IPP_JSTATE_ABORTED
:
2945 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2946 "Job #%d is aborted - can\'t close.", job
->id
);
2949 case IPP_JSTATE_COMPLETED
:
2950 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2951 "Job #%d is completed - can\'t close.", job
->id
);
2954 case IPP_JSTATE_PROCESSING
:
2955 case IPP_JSTATE_STOPPED
:
2956 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2957 "Job #%d is already closed.", job
->id
);
2961 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2968 * 'ipp_create_job()' - Create a job object.
2972 ipp_create_job(ippeve_client_t
*client
) /* I - Client */
2974 ippeve_job_t
*job
; /* New job */
2975 cups_array_t
*ra
; /* Attributes to send in response */
2979 * Validate print job attributes...
2982 if (!valid_job_attributes(client
))
2984 httpFlush(client
->http
);
2989 * Do we have a file to print?
2992 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2994 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2995 "Unexpected document data following request.");
3003 if ((job
= create_job(client
)) == NULL
)
3005 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3006 "Currently printing another job.");
3011 * Return the job info...
3014 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3016 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3017 cupsArrayAdd(ra
, "job-id");
3018 cupsArrayAdd(ra
, "job-state");
3019 cupsArrayAdd(ra
, "job-state-message");
3020 cupsArrayAdd(ra
, "job-state-reasons");
3021 cupsArrayAdd(ra
, "job-uri");
3023 copy_job_attributes(client
, job
, ra
);
3024 cupsArrayDelete(ra
);
3029 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3033 ipp_get_job_attributes(
3034 ippeve_client_t
*client
) /* I - Client */
3036 ippeve_job_t
*job
; /* Job */
3037 cups_array_t
*ra
; /* requested-attributes */
3040 if ((job
= find_job(client
)) == NULL
)
3042 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3046 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3048 ra
= ippCreateRequestedArray(client
->request
);
3049 copy_job_attributes(client
, job
, ra
);
3050 cupsArrayDelete(ra
);
3055 * 'ipp_get_jobs()' - Get a list of job objects.
3059 ipp_get_jobs(ippeve_client_t
*client
) /* I - Client */
3061 ipp_attribute_t
*attr
; /* Current attribute */
3062 const char *which_jobs
= NULL
;
3063 /* which-jobs values */
3064 int job_comparison
; /* Job comparison */
3065 ipp_jstate_t job_state
; /* job-state value */
3066 int first_job_id
, /* First job ID */
3067 limit
, /* Maximum number of jobs to return */
3068 count
; /* Number of jobs that match */
3069 const char *username
; /* Username */
3070 ippeve_job_t
*job
; /* Current job pointer */
3071 cups_array_t
*ra
; /* Requested attributes array */
3075 * See if the "which-jobs" attribute have been specified...
3078 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3079 IPP_TAG_KEYWORD
)) != NULL
)
3081 which_jobs
= ippGetString(attr
, 0, NULL
);
3082 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3085 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3087 job_comparison
= -1;
3088 job_state
= IPP_JSTATE_STOPPED
;
3090 else if (!strcmp(which_jobs
, "completed"))
3093 job_state
= IPP_JSTATE_CANCELED
;
3095 else if (!strcmp(which_jobs
, "aborted"))
3098 job_state
= IPP_JSTATE_ABORTED
;
3100 else if (!strcmp(which_jobs
, "all"))
3103 job_state
= IPP_JSTATE_PENDING
;
3105 else if (!strcmp(which_jobs
, "canceled"))
3108 job_state
= IPP_JSTATE_CANCELED
;
3110 else if (!strcmp(which_jobs
, "pending"))
3113 job_state
= IPP_JSTATE_PENDING
;
3115 else if (!strcmp(which_jobs
, "pending-held"))
3118 job_state
= IPP_JSTATE_HELD
;
3120 else if (!strcmp(which_jobs
, "processing"))
3123 job_state
= IPP_JSTATE_PROCESSING
;
3125 else if (!strcmp(which_jobs
, "processing-stopped"))
3128 job_state
= IPP_JSTATE_STOPPED
;
3132 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3133 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3134 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3135 "which-jobs", NULL
, which_jobs
);
3140 * See if they want to limit the number of jobs reported...
3143 if ((attr
= ippFindAttribute(client
->request
, "limit",
3144 IPP_TAG_INTEGER
)) != NULL
)
3146 limit
= ippGetInteger(attr
, 0);
3148 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3153 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3154 IPP_TAG_INTEGER
)) != NULL
)
3156 first_job_id
= ippGetInteger(attr
, 0);
3158 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
, first_job_id
);
3164 * See if we only want to see jobs for a specific user...
3169 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3170 IPP_TAG_BOOLEAN
)) != NULL
)
3172 int my_jobs
= ippGetBoolean(attr
, 0);
3174 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
, my_jobs
? "true" : "false");
3178 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3179 IPP_TAG_NAME
)) == NULL
)
3181 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3182 "Need requesting-user-name with my-jobs.");
3186 username
= ippGetString(attr
, 0, NULL
);
3188 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n", client
->hostname
, username
);
3193 * OK, build a list of jobs for this printer...
3196 ra
= ippCreateRequestedArray(client
->request
);
3198 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3200 _cupsRWLockRead(&(client
->printer
->rwlock
));
3202 for (count
= 0, job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3203 (limit
<= 0 || count
< limit
) && job
;
3204 job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3207 * Filter out jobs that don't match...
3210 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3211 (job_comparison
== 0 && job
->state
!= job_state
) ||
3212 (job_comparison
> 0 && job
->state
< job_state
) ||
3213 job
->id
< first_job_id
||
3214 (username
&& job
->username
&&
3215 strcasecmp(username
, job
->username
)))
3219 ippAddSeparator(client
->response
);
3222 copy_job_attributes(client
, job
, ra
);
3225 cupsArrayDelete(ra
);
3227 _cupsRWUnlock(&(client
->printer
->rwlock
));
3232 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3236 ipp_get_printer_attributes(
3237 ippeve_client_t
*client
) /* I - Client */
3239 cups_array_t
*ra
; /* Requested attributes array */
3240 ippeve_printer_t
*printer
; /* Printer */
3244 * Send the attributes...
3247 ra
= ippCreateRequestedArray(client
->request
);
3248 printer
= client
->printer
;
3250 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3252 _cupsRWLockRead(&(printer
->rwlock
));
3254 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3255 IPP_TAG_CUPS_CONST
);
3257 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3258 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3260 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3261 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3263 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3264 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3267 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3268 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state", (int)printer
->state
);
3270 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3271 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3273 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3274 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3276 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3278 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3280 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3283 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3285 if (printer
->state_reasons
== IPPEVE_PREASON_NONE
)
3287 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", NULL
, "none");
3291 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3292 ippeve_preason_t bit
; /* Reason bit */
3293 int i
; /* Looping var */
3294 char reason
[32]; /* Reason string */
3296 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
3298 if (printer
->state_reasons
& bit
)
3300 snprintf(reason
, sizeof(reason
), "%s-%s", ippeve_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3302 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3304 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3310 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3311 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3313 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3314 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3316 _cupsRWUnlock(&(printer
->rwlock
));
3318 cupsArrayDelete(ra
);
3323 * 'ipp_identify_printer()' - Beep or display a message.
3327 ipp_identify_printer(
3328 ippeve_client_t
*client
) /* I - Client */
3330 ipp_attribute_t
*actions
, /* identify-actions */
3331 *message
; /* message */
3334 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3335 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3337 if (!actions
|| ippContainsString(actions
, "sound"))
3343 if (ippContainsString(actions
, "display"))
3344 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3346 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3351 * 'ipp_print_job()' - Create a job object with an attached document.
3355 ipp_print_job(ippeve_client_t
*client
) /* I - Client */
3357 ippeve_job_t
*job
; /* New job */
3361 * Validate print job attributes...
3364 if (!valid_job_attributes(client
))
3366 httpFlush(client
->http
);
3371 * Do we have a file to print?
3374 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3376 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3384 if ((job
= create_job(client
)) == NULL
)
3386 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3391 * Then finish getting the document data and process things...
3394 finish_document_data(client
, job
);
3399 * 'ipp_print_uri()' - Create a job object with a referenced document.
3403 ipp_print_uri(ippeve_client_t
*client
) /* I - Client */
3405 ippeve_job_t
*job
; /* New job */
3409 * Validate print job attributes...
3412 if (!valid_job_attributes(client
))
3414 httpFlush(client
->http
);
3422 if ((job
= create_job(client
)) == NULL
)
3424 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3429 * Then finish getting the document data and process things...
3432 finish_document_uri(client
, job
);
3437 * 'ipp_send_document()' - Add an attached document to a job object created with
3443 ippeve_client_t
*client
) /* I - Client */
3445 ippeve_job_t
*job
; /* Job information */
3446 ipp_attribute_t
*attr
; /* Current attribute */
3453 if ((job
= find_job(client
)) == NULL
)
3455 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3456 httpFlush(client
->http
);
3461 * See if we already have a document for this job or the job has already
3462 * in a non-pending state...
3465 if (job
->state
> IPP_JSTATE_HELD
)
3467 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job is not in a pending state.");
3468 httpFlush(client
->http
);
3471 else if (job
->filename
|| job
->fd
>= 0)
3473 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
, "Multiple document jobs are not supported.");
3474 httpFlush(client
->http
);
3479 * Make sure we have the "last-document" operation attribute...
3482 if ((attr
= ippFindAttribute(client
->request
, "last-document", IPP_TAG_ZERO
)) == NULL
)
3484 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing required last-document attribute.");
3485 httpFlush(client
->http
);
3488 else if (ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
3490 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The last-document attribute is not in the operation group.");
3491 httpFlush(client
->http
);
3494 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 || !ippGetBoolean(attr
, 0))
3496 respond_unsupported(client
, attr
);
3497 httpFlush(client
->http
);
3502 * Validate document attributes...
3505 if (!valid_doc_attributes(client
))
3507 httpFlush(client
->http
);
3512 * Then finish getting the document data and process things...
3515 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3517 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3519 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3520 job
->format
= ippGetString(attr
, 0, NULL
);
3521 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3522 job
->format
= ippGetString(attr
, 0, NULL
);
3524 job
->format
= "application/octet-stream";
3526 _cupsRWUnlock(&(client
->printer
->rwlock
));
3528 finish_document_data(client
, job
);
3533 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3538 ipp_send_uri(ippeve_client_t
*client
) /* I - Client */
3540 ippeve_job_t
*job
; /* Job information */
3541 ipp_attribute_t
*attr
; /* Current attribute */
3548 if ((job
= find_job(client
)) == NULL
)
3550 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3551 httpFlush(client
->http
);
3556 * See if we already have a document for this job or the job has already
3557 * in a non-pending state...
3560 if (job
->state
> IPP_JSTATE_HELD
)
3562 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job is not in a pending state.");
3563 httpFlush(client
->http
);
3566 else if (job
->filename
|| job
->fd
>= 0)
3568 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
, "Multiple document jobs are not supported.");
3569 httpFlush(client
->http
);
3573 if ((attr
= ippFindAttribute(client
->request
, "last-document", IPP_TAG_ZERO
)) == NULL
)
3575 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing required last-document attribute.");
3576 httpFlush(client
->http
);
3579 else if (ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
3581 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The last-document attribute is not in the operation group.");
3582 httpFlush(client
->http
);
3585 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 || !ippGetBoolean(attr
, 0))
3587 respond_unsupported(client
, attr
);
3588 httpFlush(client
->http
);
3593 * Validate document attributes...
3596 if (!valid_doc_attributes(client
))
3598 httpFlush(client
->http
);
3603 * Then finish getting the document data and process things...
3606 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3608 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3610 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3611 job
->format
= ippGetString(attr
, 0, NULL
);
3612 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3613 job
->format
= ippGetString(attr
, 0, NULL
);
3615 job
->format
= "application/octet-stream";
3617 _cupsRWUnlock(&(client
->printer
->rwlock
));
3619 finish_document_uri(client
, job
);
3624 * 'ipp_validate_job()' - Validate job creation attributes.
3628 ipp_validate_job(ippeve_client_t
*client
) /* I - Client */
3630 if (valid_job_attributes(client
))
3631 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3636 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
3639 static int /* O - 1 to use, 0 to ignore */
3641 _ipp_file_t
*f
, /* I - IPP file */
3642 void *user_data
, /* I - User data pointer (unused) */
3643 const char *attr
) /* I - Attribute name */
3645 int i
, /* Current element */
3646 result
; /* Result of comparison */
3647 static const char * const ignored
[] =
3648 { /* Ignored attributes */
3649 "attributes-charset",
3650 "attributes-natural-language",
3651 "charset-configured",
3652 "charset-supported",
3653 "device-service-count",
3655 "document-format-varying-attributes",
3656 "generated-natural-language-supported",
3657 "identify-actions-default",
3658 "identify-actions-supported",
3659 "ipp-features-supported",
3660 "ipp-versions-supproted",
3661 "ippget-event-life",
3662 "job-hold-until-supported",
3663 "job-hold-until-time-supported",
3664 "job-ids-supported",
3665 "job-k-octets-supported",
3666 "job-settable-attributes-supported",
3667 "multiple-document-jobs-supported",
3668 "multiple-operation-time-out",
3669 "multiple-operation-time-out-action",
3670 "natural-language-configured",
3671 "notify-attributes-supported",
3672 "notify-events-default",
3673 "notify-events-supported",
3674 "notify-lease-duration-default",
3675 "notify-lease-duration-supported",
3676 "notify-max-events-supported",
3677 "notify-pull-method-supported",
3678 "operations-supported",
3680 "printer-alert-description",
3681 "printer-camera-image-uri",
3682 "printer-charge-info",
3683 "printer-charge-info-uri",
3684 "printer-config-change-date-time",
3685 "printer-config-change-time",
3686 "printer-current-time",
3687 "printer-detailed-status-messages",
3688 "printer-dns-sd-name",
3689 "printer-fax-log-uri",
3690 "printer-get-attributes-supported",
3694 "printer-is-accepting-jobs",
3695 "printer-message-date-time",
3696 "printer-message-from-operator",
3697 "printer-message-time",
3698 "printer-more-info",
3699 "printer-service-type",
3700 "printer-settable-attributes-supported",
3702 "printer-state-message",
3703 "printer-state-reasons",
3704 "printer-static-resource-directory-uri",
3705 "printer-static-resource-k-octets-free",
3706 "printer-static-resource-k-octets-supported",
3707 "printer-strings-languages-supported",
3708 "printer-strings-uri",
3709 "printer-supply-info-uri",
3711 "printer-uri-supported",
3712 "printer-xri-supported",
3714 "reference-uri-scheme-supported",
3715 "uri-authentication-supported",
3716 "uri-security-supported",
3717 "which-jobs-supported",
3718 "xri-authentication-supported",
3719 "xri-security-supported",
3720 "xri-uri-scheme-supported"
3727 for (i
= 0, result
= 1; i
< (int)(sizeof(ignored
) / sizeof(ignored
[0])); i
++)
3729 if ((result
= strcmp(attr
, ignored
[i
])) <= 0)
3733 return (result
!= 0);
3738 * 'ippserver_error_cb()' - Log an error message.
3741 static int /* O - 1 to continue, 0 to stop */
3743 _ipp_file_t
*f
, /* I - IPP file data */
3744 void *user_data
, /* I - User data pointer (unused) */
3745 const char *error
) /* I - Error message */
3750 _cupsLangPrintf(stderr
, "%s\n", error
);
3757 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
3760 static int /* O - 1 to continue, 0 to stop */
3762 _ipp_file_t
*f
, /* I - IPP file data */
3763 _ipp_vars_t
*vars
, /* I - IPP variables */
3764 void *user_data
, /* I - User data pointer (unused) */
3765 const char *token
) /* I - Current token */
3773 * NULL token means do the initial setup - create an empty IPP message and
3777 f
->attrs
= ippNew();
3778 f
->group_tag
= IPP_TAG_PRINTER
;
3782 _cupsLangPrintf(stderr
, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token
, f
->linenum
, f
->filename
);
3790 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
3793 static ipp_t
* /* O - IPP attributes or `NULL` on error */
3794 load_ippserver_attributes(
3795 const char *servername
, /* I - Server name or `NULL` for default */
3796 int serverport
, /* I - Server port number */
3797 const char *filename
, /* I - ippserver attribute filename */
3798 cups_array_t
*docformats
) /* I - document-format-supported values */
3800 ipp_t
*attrs
; /* IPP attributes */
3801 _ipp_vars_t vars
; /* IPP variables */
3802 char temp
[256]; /* Temporary string */
3805 (void)docformats
; /* for now */
3808 * Setup callbacks and variables for the printer configuration file...
3810 * The following additional variables are supported:
3812 * - SERVERNAME: The host name of the server.
3813 * - SERVERPORT: The default port of the server.
3816 _ippVarsInit(&vars
, (_ipp_fattr_cb_t
)ippserver_attr_cb
, (_ipp_ferror_cb_t
)ippserver_error_cb
, (_ipp_ftoken_cb_t
)ippserver_token_cb
);
3820 _ippVarsSet(&vars
, "SERVERNAME", servername
);
3824 httpGetHostname(NULL
, temp
, sizeof(temp
));
3825 _ippVarsSet(&vars
, "SERVERNAME", temp
);
3828 snprintf(temp
, sizeof(temp
), "%d", serverport
);
3829 _ippVarsSet(&vars
, "SERVERPORT", temp
);
3832 * Load attributes and values for the printer...
3835 attrs
= _ippFileParse(&vars
, filename
, NULL
);
3838 * Free memory and return...
3841 _ippVarsDeinit(&vars
);
3848 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
3852 static ipp_t
* /* O - IPP attributes or `NULL` on error */
3853 load_legacy_attributes(
3854 const char *make
, /* I - Manufacturer name */
3855 const char *model
, /* I - Model name */
3856 int ppm
, /* I - pages-per-minute */
3857 int ppm_color
, /* I - pages-per-minute-color */
3858 int duplex
, /* I - Duplex support? */
3859 cups_array_t
*docformats
) /* I - document-format-supported values */
3861 int i
; /* Looping var */
3862 ipp_t
*attrs
, /* IPP attributes */
3863 *col
; /* Collection value */
3864 ipp_attribute_t
*attr
; /* Current attribute */
3865 char device_id
[1024],/* printer-device-id */
3866 *ptr
, /* Pointer into device ID */
3867 make_model
[128];/* printer-make-and-model */
3868 const char *format
, /* Current document format */
3869 *prefix
; /* Prefix for device ID */
3870 int num_media
; /* Number of media */
3871 const char * const *media
; /* List of media */
3872 int num_ready
; /* Number of loaded media */
3873 const char * const *ready
; /* List of loaded media */
3874 pwg_media_t
*pwg
; /* PWG media size information */
3875 static const char * const media_supported
[] =
3876 { /* media-supported values */
3877 "na_letter_8.5x11in", /* Letter */
3878 "na_legal_8.5x14in", /* Legal */
3879 "iso_a4_210x297mm", /* A4 */
3880 "na_number-10_4.125x9.5in", /* #10 Envelope */
3881 "iso_dl_110x220mm" /* DL Envelope */
3883 static const char * const media_supported_color
[] =
3884 { /* media-supported values */
3885 "na_letter_8.5x11in", /* Letter */
3886 "na_legal_8.5x14in", /* Legal */
3887 "iso_a4_210x297mm", /* A4 */
3888 "na_number-10_4.125x9.5in", /* #10 Envelope */
3889 "iso_dl_110x220mm", /* DL Envelope */
3890 "na_index-3x5_3x5in", /* Photo 3x5 */
3891 "oe_photo-l_3.5x5in", /* Photo L */
3892 "na_index-4x6_4x6in", /* Photo 4x6 */
3893 "iso_a6_105x148mm", /* A6 */
3894 "na_5x7_5x7in" /* Photo 5x7 aka 2L */
3895 "iso_a5_148x210mm", /* A5 */
3897 static const char * const media_ready
[] =
3898 { /* media-ready values */
3899 "na_letter_8.5x11in", /* Letter */
3900 "na_number-10_4.125x9.5in" /* #10 */
3902 static const char * const media_ready_color
[] =
3903 { /* media-ready values */
3904 "na_letter_8.5x11in", /* Letter */
3905 "na_index-4x6_4x6in" /* Photo 4x6 */
3907 static const char * const media_source_supported
[] =
3908 { /* media-source-supported values */
3912 "by-pass-tray" /* AKA multi-purpose tray */
3914 static const char * const media_source_supported_color
[] =
3915 { /* media-source-supported values */
3920 static const char * const media_type_supported
[] =
3921 { /* media-type-supported values */
3928 "stationery-letterhead",
3931 static const char * const media_type_supported_color
[] =
3932 { /* media-type-supported values */
3939 "stationery-letterhead",
3941 "photographic-glossy",
3942 "photographic-high-gloss",
3943 "photographic-matte",
3944 "photographic-satin",
3945 "photographic-semi-gloss"
3947 static const int media_bottom_margin_supported
[] =
3948 { /* media-bottom-margin-supported values */
3951 static const int media_bottom_margin_supported_color
[] =
3952 { /* media-bottom/top-margin-supported values */
3954 1168 /* 0.46" (common HP inkjet bottom margin) */
3956 static const int media_lr_margin_supported
[] =
3957 { /* media-left/right-margin-supported values */
3958 340, /* 3.4mm (historical HP PCL A4 margin) */
3961 static const int media_lr_margin_supported_color
[] =
3962 { /* media-left/right-margin-supported values */
3964 340, /* 3.4mm (historical HP PCL A4 margin) */
3967 static const int media_top_margin_supported
[] =
3968 { /* media-top-margin-supported values */
3971 static const int media_top_margin_supported_color
[] =
3972 { /* media-top/top-margin-supported values */
3974 102 /* 0.04" (common HP inkjet top margin */
3976 static const int orientation_requested_supported
[4] =
3977 { /* orientation-requested-supported values */
3978 IPP_ORIENT_PORTRAIT
,
3979 IPP_ORIENT_LANDSCAPE
,
3980 IPP_ORIENT_REVERSE_LANDSCAPE
,
3981 IPP_ORIENT_REVERSE_PORTRAIT
3983 static const char * const overrides_supported
[] =
3984 { /* overrides-supported values */
3988 "orientation-requested",
3991 static const char * const print_color_mode_supported
[] =
3992 { /* print-color-mode-supported values */
3995 static const char * const print_color_mode_supported_color
[] =
3996 { /* print-color-mode-supported values */
4001 static const int print_quality_supported
[] =
4002 { /* print-quality-supported values */
4007 static const char * const printer_input_tray
[] =
4008 { /* printer-input-tray values */
4009 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4010 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
4011 "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
4012 "type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
4014 static const char * const printer_input_tray_color
[] =
4015 { /* printer-input-tray values */
4016 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4017 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
4018 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
4020 static const char * const printer_supply
[] =
4021 { /* printer-supply values */
4022 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4023 "maxcapacity=100;level=25;colorantname=unknown;",
4024 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4025 "maxcapacity=100;level=75;colorantname=black;"
4027 static const char * const printer_supply_color
[] =
4028 { /* printer-supply values */
4029 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4030 "maxcapacity=100;level=25;colorantname=unknown;",
4031 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4032 "maxcapacity=100;level=75;colorantname=black;",
4033 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4034 "maxcapacity=100;level=50;colorantname=cyan;",
4035 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4036 "maxcapacity=100;level=33;colorantname=magenta;",
4037 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4038 "maxcapacity=100;level=67;colorantname=yellow;"
4040 static const char * const printer_supply_description
[] =
4041 { /* printer-supply-description values */
4045 static const char * const printer_supply_description_color
[] =
4046 { /* printer-supply-description values */
4053 static const int pwg_raster_document_resolution_supported
[] =
4058 static const char * const pwg_raster_document_type_supported
[] =
4063 static const char * const pwg_raster_document_type_supported_color
[] =
4070 static const char * const sides_supported
[] =
4071 { /* sides-supported values */
4073 "two-sided-long-edge",
4074 "two-sided-short-edge"
4076 static const char * const urf_supported
[] =
4077 { /* urf-supported values */
4085 static const char * const urf_supported_color
[] =
4086 { /* urf-supported values */
4089 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4095 static const char * const urf_supported_color_duplex
[] =
4096 { /* urf-supported values */
4099 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4106 static const char * const urf_supported_duplex
[] =
4107 { /* urf-supported values */
4122 num_media
= (int)(sizeof(media_supported_color
) / sizeof(media_supported_color
[0]));
4123 media
= media_supported_color
;
4124 num_ready
= (int)(sizeof(media_ready_color
) / sizeof(media_ready_color
[0]));
4125 ready
= media_ready_color
;
4129 num_media
= (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
4130 media
= media_supported
;
4131 num_ready
= (int)(sizeof(media_ready
) / sizeof(media_ready
[0]));
4132 ready
= media_ready
;
4135 /* color-supported */
4136 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
4138 /* copies-default */
4139 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
4141 /* copies-supported */
4142 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, (cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
|| cupsArrayFind(docformats
, (void *)"image/jpeg") != NULL
) ? 999 : 1);
4144 /* document-password-supported */
4145 if (cupsArrayFind(docformats
, (void *)"application/pdf"))
4146 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 1023);
4148 /* finishings-default */
4149 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
4151 /* finishings-supported */
4152 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
4154 /* media-bottom-margin-supported */
4156 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported
) / sizeof(media_bottom_margin_supported
[0])), media_bottom_margin_supported
);
4158 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported_color
) / sizeof(media_bottom_margin_supported_color
[0])), media_bottom_margin_supported_color
);
4160 /* media-col-database and media-col-default */
4161 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", num_media
, NULL
);
4162 for (i
= 0; i
< num_media
; i
++)
4164 int bottom
, left
, /* media-xxx-margins */
4166 const char *source
; /* media-source, if any */
4168 pwg
= pwgMediaForPWG(media
[i
]);
4170 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4172 source
= "photo"; /* Photo size media from photo tray */
4173 bottom
= /* Borderless margins */
4178 else if (pwg
->width
< 21000)
4180 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4181 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4182 left
= /* Left/right margins are standard */
4183 right
= media_lr_margin_supported
[1];
4184 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4186 else if (pwg
->width
== 21000)
4188 source
= NULL
; /* A4 from any tray */
4189 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4190 left
= /* Left/right margins are reduced */
4191 right
= media_lr_margin_supported
[0];
4192 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4196 source
= NULL
; /* Other size media from any tray */
4197 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4198 left
= /* Left/right margins are standard */
4199 right
= media_lr_margin_supported
[1];
4200 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4203 col
= create_media_col(media
[i
], source
, NULL
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4204 ippSetCollection(attrs
, &attr
, i
, col
);
4209 /* media-col-default */
4210 pwg
= pwgMediaForPWG(ready
[0]);
4212 if (pwg
->width
== 21000)
4213 col
= create_media_col(ready
[0], "main", "stationery", pwg
->width
, pwg
->length
, ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0], media_lr_margin_supported
[0], media_lr_margin_supported
[0], ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0]);
4215 col
= create_media_col(ready
[0], "main", "stationery", pwg
->width
, pwg
->length
, ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0], media_lr_margin_supported
[1], media_lr_margin_supported
[1], ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0]);
4217 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default", col
);
4221 /* media-col-ready */
4222 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, NULL
);
4223 for (i
= 0; i
< num_ready
; i
++)
4225 int bottom
, left
, /* media-xxx-margins */
4227 const char *source
, /* media-source */
4228 *type
; /* media-type */
4230 pwg
= pwgMediaForPWG(ready
[i
]);
4232 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4234 source
= "photo"; /* Photo size media from photo tray */
4235 type
= "photographic-glossy"; /* Glossy photo paper */
4236 bottom
= /* Borderless margins */
4241 else if (pwg
->width
< 21000)
4243 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4244 type
= "envelope"; /* Envelope */
4245 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4246 left
= /* Left/right margins are standard */
4247 right
= media_lr_margin_supported
[1];
4248 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4250 else if (pwg
->width
== 21000)
4252 source
= "main"; /* A4 from main tray */
4253 type
= "stationery"; /* Plain paper */
4254 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4255 left
= /* Left/right margins are reduced */
4256 right
= media_lr_margin_supported
[0];
4257 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4261 source
= "main"; /* A4 from main tray */
4262 type
= "stationery"; /* Plain paper */
4263 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4264 left
= /* Left/right margins are standard */
4265 right
= media_lr_margin_supported
[1];
4266 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4269 col
= create_media_col(ready
[i
], source
, type
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4270 ippSetCollection(attrs
, &attr
, i
, col
);
4275 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media
[0]);
4277 /* media-left/right-margin-supported */
4280 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported_color
) / sizeof(media_lr_margin_supported_color
[0])), media_lr_margin_supported_color
);
4281 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported_color
) / sizeof(media_lr_margin_supported_color
[0])), media_lr_margin_supported_color
);
4285 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported
) / sizeof(media_lr_margin_supported
[0])), media_lr_margin_supported
);
4286 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported
) / sizeof(media_lr_margin_supported
[0])), media_lr_margin_supported
);
4290 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", num_ready
, NULL
, ready
);
4292 /* media-supported */
4293 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-supported", num_media
, NULL
, media
);
4295 /* media-size-supported */
4296 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", num_media
, NULL
);
4297 for (i
= 0; i
< num_media
; i
++)
4299 pwg
= pwgMediaForPWG(media
[i
]);
4300 col
= create_media_size(pwg
->width
, pwg
->length
);
4302 ippSetCollection(attrs
, &attr
, i
, col
);
4306 /* media-source-supported */
4308 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-source-supported", (int)(sizeof(media_source_supported_color
) / sizeof(media_source_supported_color
[0])), NULL
, media_source_supported_color
);
4310 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-source-supported", (int)(sizeof(media_source_supported
) / sizeof(media_source_supported
[0])), NULL
, media_source_supported
);
4312 /* media-top-margin-supported */
4314 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported
) / sizeof(media_top_margin_supported
[0])), media_top_margin_supported
);
4316 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported_color
) / sizeof(media_top_margin_supported_color
[0])), media_top_margin_supported_color
);
4318 /* media-type-supported */
4320 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-type-supported", (int)(sizeof(media_type_supported_color
) / sizeof(media_type_supported_color
[0])), NULL
, media_type_supported_color
);
4322 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-type-supported", (int)(sizeof(media_type_supported
) / sizeof(media_type_supported
[0])), NULL
, media_type_supported
);
4324 /* orientation-requested-default */
4325 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-default", IPP_ORIENT_PORTRAIT
);
4327 /* orientation-requested-supported */
4328 if (cupsArrayFind(docformats
, (void *)"application/pdf") || cupsArrayFind(docformats
, (void *)"image/jpeg"))
4329 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported
) / sizeof(orientation_requested_supported
[0])), orientation_requested_supported
);
4331 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", IPP_ORIENT_PORTRAIT
);
4333 /* output-bin-default */
4335 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-up");
4337 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
4339 /* output-bin-supported */
4341 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-up");
4343 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
4345 /* overrides-supported */
4346 if (cupsArrayFind(docformats
, (void *)"application/pdf"))
4347 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "overrides-supported", (int)(sizeof(overrides_supported
) / sizeof(overrides_supported
[0])), NULL
, overrides_supported
);
4349 /* page-ranges-supported */
4350 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
);
4352 /* pages-per-minute */
4353 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute", ppm
);
4355 /* pages-per-minute-color */
4357 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute-color", ppm_color
);
4359 /* print-color-mode-default */
4360 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, ppm_color
> 0 ? "auto" : "monochrome");
4362 /* print-color-mode-supported */
4364 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color
) / sizeof(print_color_mode_supported_color
[0])), NULL
, print_color_mode_supported_color
);
4366 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported
) / sizeof(print_color_mode_supported
[0])), NULL
, print_color_mode_supported
);
4368 /* print-content-optimize-default */
4369 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
4371 /* print-content-optimize-supported */
4372 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
4374 /* print-quality-default */
4375 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
4377 /* print-quality-supported */
4378 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
4380 /* print-rendering-intent-default */
4381 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
4383 /* print-rendering-intent-supported */
4384 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
4386 /* printer-device-id */
4387 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
4388 ptr
= device_id
+ strlen(device_id
);
4390 for (format
= (const char *)cupsArrayFirst(docformats
); format
; format
= (const char *)cupsArrayNext(docformats
))
4392 if (!strcasecmp(format
, "application/pdf"))
4393 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
4394 else if (!strcasecmp(format
, "application/postscript"))
4395 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
4396 else if (!strcasecmp(format
, "application/vnd.hp-PCL"))
4397 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
4398 else if (!strcasecmp(format
, "image/jpeg"))
4399 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
4400 else if (!strcasecmp(format
, "image/png"))
4401 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
4402 else if (!strcasecmp(format
, "image/pwg-raster"))
4403 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPWG", prefix
);
4404 else if (!strcasecmp(format
, "image/urf"))
4405 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sURF", prefix
);
4412 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
4417 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
4419 /* printer-input-tray */
4422 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray_color
[0], strlen(printer_input_tray_color
[0]));
4423 for (i
= 1; i
< (int)(sizeof(printer_input_tray_color
) / sizeof(printer_input_tray_color
[0])); i
++)
4424 ippSetOctetString(attrs
, &attr
, i
, printer_input_tray_color
[i
], strlen(printer_input_tray_color
[i
]));
4428 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray
[0], strlen(printer_input_tray
[0]));
4429 for (i
= 1; i
< (int)(sizeof(printer_input_tray
) / sizeof(printer_input_tray
[0])); i
++)
4430 ippSetOctetString(attrs
, &attr
, i
, printer_input_tray
[i
], strlen(printer_input_tray
[i
]));
4433 /* printer-make-and-model */
4434 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4435 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, make_model
);
4437 /* printer-resolution-default */
4438 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
4440 /* printer-resolution-supported */
4441 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
4443 /* printer-supply and printer-supply-description */
4446 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply_color
[0], strlen(printer_supply_color
[0]));
4447 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
4448 ippSetOctetString(attrs
, &attr
, i
, printer_supply_color
[i
], strlen(printer_supply_color
[i
]));
4450 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-supply-description", (int)(sizeof(printer_supply_description_color
) / sizeof(printer_supply_description_color
[0])), NULL
, printer_supply_description_color
);
4454 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply
[0], strlen(printer_supply
[0]));
4455 for (i
= 1; i
< (int)(sizeof(printer_supply
) / sizeof(printer_supply
[0])); i
++)
4456 ippSetOctetString(attrs
, &attr
, i
, printer_supply
[i
], strlen(printer_supply
[i
]));
4458 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-supply-description", (int)(sizeof(printer_supply_description
) / sizeof(printer_supply_description
[0])), NULL
, printer_supply_description
);
4461 /* pwg-raster-document-xxx-supported */
4462 if (cupsArrayFind(docformats
, (void *)"image/pwg-raster"))
4464 ippAddResolutions(attrs
, IPP_TAG_PRINTER
, "pwg-raster-document-resolution-supported", (int)(sizeof(pwg_raster_document_resolution_supported
) / sizeof(pwg_raster_document_resolution_supported
[0])), IPP_RES_PER_INCH
, pwg_raster_document_resolution_supported
, pwg_raster_document_resolution_supported
);
4466 if (ppm_color
> 0 && duplex
)
4467 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "rotated");
4469 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "normal");
4472 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color
) / sizeof(pwg_raster_document_type_supported_color
[0])), NULL
, pwg_raster_document_type_supported_color
);
4474 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(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
);
4478 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
4480 /* sides-supported */
4482 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", (int)(sizeof(sides_supported
) / sizeof(sides_supported
[0])), NULL
, sides_supported
);
4484 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", NULL
, "one-sided");
4487 if (cupsArrayFind(docformats
, (void *)"image/urf"))
4492 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_color_duplex
) / sizeof(urf_supported_color_duplex
[0])), NULL
, urf_supported_color_duplex
);
4494 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_color
) / sizeof(urf_supported_color
[0])), NULL
, urf_supported_color
);
4498 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_duplex
) / sizeof(urf_supported_duplex
[0])), NULL
, urf_supported_duplex
);
4502 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])), NULL
, urf_supported
);
4512 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4515 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4516 load_ppd_attributes(
4517 const char *ppdfile
, /* I - PPD filename */
4518 cups_array_t
*docformats
) /* I - document-format-supported values */
4520 int i
, j
; /* Looping vars */
4521 ipp_t
*attrs
; /* Attributes */
4522 ipp_attribute_t
*attr
; /* Current attribute */
4523 ipp_t
*col
; /* Current collection value */
4524 ppd_file_t
*ppd
; /* PPD data */
4525 ppd_attr_t
*ppd_attr
; /* PPD attribute */
4526 ppd_choice_t
*ppd_choice
; /* PPD choice */
4527 ppd_size_t
*ppd_size
; /* Default PPD size */
4528 pwg_size_t
*pwg_size
, /* Current PWG size */
4529 *default_size
= NULL
; /* Default PWG size */
4530 const char *default_source
= NULL
, /* Default media source */
4531 *default_type
= NULL
; /* Default media type */
4532 pwg_map_t
*pwg_map
; /* Mapping from PWG to PPD keywords */
4533 _ppd_cache_t
*pc
; /* PPD cache */
4534 _pwg_finishings_t
*finishings
; /* Current finishings value */
4535 const char *template; /* Current finishings-template value */
4536 int num_margins
; /* Number of media-xxx-margin-supported values */
4537 int margins
[10]; /* media-xxx-margin-supported values */
4538 int xres
, /* Default horizontal resolution */
4539 yres
; /* Default vertical resolution */
4540 int num_urf
; /* Number of urf-supported values */
4541 const char *urf
[10]; /* urf-supported values */
4542 char urf_rs
[32]; /* RS value */
4543 static const int orientation_requested_supported
[4] =
4544 { /* orientation-requested-supported values */
4545 IPP_ORIENT_PORTRAIT
,
4546 IPP_ORIENT_LANDSCAPE
,
4547 IPP_ORIENT_REVERSE_LANDSCAPE
,
4548 IPP_ORIENT_REVERSE_PORTRAIT
4550 static const char * const overrides_supported
[] =
4551 { /* overrides-supported */
4555 "orientation-requested",
4558 static const char * const print_color_mode_supported
[] =
4559 { /* print-color-mode-supported values */
4562 static const char * const print_color_mode_supported_color
[] =
4563 { /* print-color-mode-supported values */
4568 static const int print_quality_supported
[] =
4569 { /* print-quality-supported values */
4574 static const char * const printer_supply
[] =
4575 { /* printer-supply values */
4576 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4577 "maxcapacity=100;level=25;colorantname=unknown;",
4578 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4579 "maxcapacity=100;level=75;colorantname=black;"
4581 static const char * const printer_supply_color
[] =
4582 { /* printer-supply values */
4583 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4584 "maxcapacity=100;level=25;colorantname=unknown;",
4585 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4586 "maxcapacity=100;level=75;colorantname=black;",
4587 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4588 "maxcapacity=100;level=50;colorantname=cyan;",
4589 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4590 "maxcapacity=100;level=33;colorantname=magenta;",
4591 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4592 "maxcapacity=100;level=67;colorantname=yellow;"
4594 static const char * const printer_supply_description
[] =
4595 { /* printer-supply-description values */
4599 static const char * const printer_supply_description_color
[] =
4600 { /* printer-supply-description values */
4607 static const char * const pwg_raster_document_type_supported
[] =
4612 static const char * const pwg_raster_document_type_supported_color
[] =
4619 static const char * const sides_supported
[] =
4620 { /* sides-supported values */
4622 "two-sided-long-edge",
4623 "two-sided-short-edge"
4628 * Open the PPD file...
4631 if ((ppd
= ppdOpenFile(ppdfile
)) == NULL
)
4633 ppd_status_t status
; /* Load error */
4635 status
= ppdLastError(&i
);
4636 _cupsLangPrintf(stderr
, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile
, ppdErrorString(status
), i
);
4640 ppdMarkDefaults(ppd
);
4642 pc
= _ppdCacheCreateWithPPD(ppd
);
4644 if ((ppd_size
= ppdPageSize(ppd
, NULL
)) != NULL
)
4647 * Look up default size...
4650 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4652 if (!strcmp(pwg_size
->map
.ppd
, ppd_size
->name
))
4654 default_size
= pwg_size
;
4663 * Default to A4 or Letter...
4666 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4668 if (!strcmp(pwg_size
->map
.ppd
, "Letter") || !strcmp(pwg_size
->map
.ppd
, "A4"))
4670 default_size
= pwg_size
;
4676 default_size
= pc
->sizes
; /* Last resort: first size */
4679 if ((ppd_choice
= ppdFindMarkedChoice(ppd
, "InputSlot")) != NULL
)
4680 default_source
= _ppdCacheGetSource(pc
, ppd_choice
->choice
);
4682 if ((ppd_choice
= ppdFindMarkedChoice(ppd
, "MediaType")) != NULL
)
4683 default_source
= _ppdCacheGetType(pc
, ppd_choice
->choice
);
4685 if ((ppd_attr
= ppdFindAttr(ppd
, "DefaultResolution", NULL
)) != NULL
)
4688 * Use the PPD-defined default resolution...
4691 if ((i
= sscanf(ppd_attr
->value
, "%dx%d", &xres
, &yres
)) == 1)
4699 * Use default of 300dpi...
4705 snprintf(urf_rs
, sizeof(urf_rs
), "RS%d", yres
< xres
? yres
: xres
);
4708 urf
[num_urf
++] = "V1.4";
4709 urf
[num_urf
++] = "CP1";
4710 urf
[num_urf
++] = urf_rs
;
4711 urf
[num_urf
++] = "W8";
4712 if (pc
->sides_2sided_long
)
4713 urf
[num_urf
++] = "DM1";
4714 if (ppd
->color_device
)
4715 urf
[num_urf
++] = "SRGB24";
4718 * PostScript printers accept PDF via one of the CUPS PDF to PostScript
4719 * filters, along with PostScript (of course) and JPEG...
4722 cupsArrayAdd(docformats
, "application/pdf");
4723 cupsArrayAdd(docformats
, "application/postscript");
4724 cupsArrayAdd(docformats
, "image/jpeg");
4727 * Create the attributes...
4732 /* color-supported */
4733 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", (char)ppd
->color_device
);
4735 /* copies-default */
4736 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
4738 /* copies-supported */
4739 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
4741 /* document-password-supported */
4742 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
4744 /* finishing-template-supported */
4745 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template-supported", cupsArrayCount(pc
->templates
) + 1, NULL
, NULL
);
4746 ippSetString(attrs
, &attr
, 0, "none");
4747 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4748 ippSetString(attrs
, &attr
, i
, template);
4750 /* finishings-col-database */
4751 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "finishings-col-database", cupsArrayCount(pc
->templates
) + 1, NULL
);
4754 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4755 ippSetCollection(attrs
, &attr
, 0, col
);
4758 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4761 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, template);
4762 ippSetCollection(attrs
, &attr
, i
, col
);
4766 /* finishings-col-default */
4768 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4769 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "finishings-col-default", col
);
4772 /* finishings-col-ready */
4773 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "finishings-col-ready", cupsArrayCount(pc
->templates
) + 1, NULL
);
4776 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4777 ippSetCollection(attrs
, &attr
, 0, col
);
4780 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4783 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, template);
4784 ippSetCollection(attrs
, &attr
, i
, col
);
4788 /* finishings-col-supported */
4789 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishings-col-supported", NULL
, "finishing-template");
4791 /* finishings-default */
4792 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
4794 /* finishings-ready */
4795 attr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-ready", cupsArrayCount(pc
->finishings
) + 1, NULL
);
4796 ippSetInteger(attrs
, &attr
, 0, IPP_FINISHINGS_NONE
);
4797 for (i
= 1, finishings
= (_pwg_finishings_t
*)cupsArrayFirst(pc
->finishings
); finishings
; i
++, finishings
= (_pwg_finishings_t
*)cupsArrayNext(pc
->finishings
))
4798 ippSetInteger(attrs
, &attr
, i
, finishings
->value
);
4800 /* finishings-supported */
4801 attr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", cupsArrayCount(pc
->finishings
) + 1, NULL
);
4802 ippSetInteger(attrs
, &attr
, 0, IPP_FINISHINGS_NONE
);
4803 for (i
= 1, finishings
= (_pwg_finishings_t
*)cupsArrayFirst(pc
->finishings
); finishings
; i
++, finishings
= (_pwg_finishings_t
*)cupsArrayNext(pc
->finishings
))
4804 ippSetInteger(attrs
, &attr
, i
, finishings
->value
);
4806 /* media-bottom-margin-supported */
4807 for (i
= 0, num_margins
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
&& num_margins
< (int)(sizeof(margins
) / sizeof(margins
[0])); i
++, pwg_size
++)
4809 for (j
= 0; j
< num_margins
; j
++)
4811 if (margins
[j
] == pwg_size
->bottom
)
4815 if (j
>= num_margins
)
4816 margins
[num_margins
++] = pwg_size
->bottom
;
4819 for (i
= 0; i
< (num_margins
- 1); i
++)
4821 for (j
= i
+ 1; j
< num_margins
; j
++)
4823 if (margins
[i
] > margins
[j
])
4825 int mtemp
= margins
[i
];
4827 margins
[i
] = margins
[j
];
4833 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin-supported", num_margins
, margins
);
4835 /* media-col-database */
4836 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", pc
->num_sizes
, NULL
);
4837 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4839 col
= create_media_col(pwg_size
->map
.pwg
, NULL
, NULL
, pwg_size
->width
, pwg_size
->length
, pwg_size
->bottom
, pwg_size
->left
, pwg_size
->right
, pwg_size
->top
);
4840 ippSetCollection(attrs
, &attr
, i
, col
);
4844 /* media-col-default */
4845 col
= create_media_col(default_size
->map
.pwg
, default_source
, default_type
, default_size
->width
, default_size
->length
, default_size
->bottom
, default_size
->left
, default_size
->right
, default_size
->top
);
4846 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default", col
);
4849 /* media-col-ready */
4850 col
= create_media_col(default_size
->map
.pwg
, default_source
, default_type
, default_size
->width
, default_size
->length
, default_size
->bottom
, default_size
->left
, default_size
->right
, default_size
->top
);
4851 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-ready", col
);
4855 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-default", NULL
, default_size
->map
.pwg
);
4857 /* media-left-margin-supported */
4858 for (i
= 0, num_margins
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
&& num_margins
< (int)(sizeof(margins
) / sizeof(margins
[0])); i
++, pwg_size
++)
4860 for (j
= 0; j
< num_margins
; j
++)
4862 if (margins
[j
] == pwg_size
->left
)
4866 if (j
>= num_margins
)
4867 margins
[num_margins
++] = pwg_size
->left
;
4870 for (i
= 0; i
< (num_margins
- 1); i
++)
4872 for (j
= i
+ 1; j
< num_margins
; j
++)
4874 if (margins
[i
] > margins
[j
])
4876 int mtemp
= margins
[i
];
4878 margins
[i
] = margins
[j
];
4884 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin-supported", num_margins
, margins
);
4887 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", NULL
, default_size
->map
.pwg
);
4889 /* media-right-margin-supported */
4890 for (i
= 0, num_margins
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
&& num_margins
< (int)(sizeof(margins
) / sizeof(margins
[0])); i
++, pwg_size
++)
4892 for (j
= 0; j
< num_margins
; j
++)
4894 if (margins
[j
] == pwg_size
->right
)
4898 if (j
>= num_margins
)
4899 margins
[num_margins
++] = pwg_size
->right
;
4902 for (i
= 0; i
< (num_margins
- 1); i
++)
4904 for (j
= i
+ 1; j
< num_margins
; j
++)
4906 if (margins
[i
] > margins
[j
])
4908 int mtemp
= margins
[i
];
4910 margins
[i
] = margins
[j
];
4916 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin-supported", num_margins
, margins
);
4918 /* media-supported */
4919 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-supported", pc
->num_sizes
, NULL
, NULL
);
4920 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4921 ippSetString(attrs
, &attr
, i
, pwg_size
->map
.pwg
);
4923 /* media-size-supported */
4924 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", pc
->num_sizes
, NULL
);
4925 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4927 col
= create_media_size(pwg_size
->width
, pwg_size
->length
);
4928 ippSetCollection(attrs
, &attr
, i
, col
);
4932 /* media-source-supported */
4933 if (pc
->num_sources
> 0)
4935 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source-supported", pc
->num_sources
, NULL
, NULL
);
4936 for (i
= 0, pwg_map
= pc
->sources
; i
< pc
->num_sources
; i
++, pwg_map
++)
4937 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
4941 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-source-supported", NULL
, "auto");
4944 /* media-top-margin-supported */
4945 for (i
= 0, num_margins
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
&& num_margins
< (int)(sizeof(margins
) / sizeof(margins
[0])); i
++, pwg_size
++)
4947 for (j
= 0; j
< num_margins
; j
++)
4949 if (margins
[j
] == pwg_size
->top
)
4953 if (j
>= num_margins
)
4954 margins
[num_margins
++] = pwg_size
->top
;
4957 for (i
= 0; i
< (num_margins
- 1); i
++)
4959 for (j
= i
+ 1; j
< num_margins
; j
++)
4961 if (margins
[i
] > margins
[j
])
4963 int mtemp
= margins
[i
];
4965 margins
[i
] = margins
[j
];
4971 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin-supported", num_margins
, margins
);
4973 /* media-type-supported */
4974 if (pc
->num_types
> 0)
4976 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type-supported", pc
->num_types
, NULL
, NULL
);
4977 for (i
= 0, pwg_map
= pc
->types
; i
< pc
->num_types
; i
++, pwg_map
++)
4978 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
4982 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-type-supported", NULL
, "auto");
4985 /* orientation-requested-default */
4986 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-default", IPP_ORIENT_PORTRAIT
);
4988 /* orientation-requested-supported */
4989 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported
) / sizeof(orientation_requested_supported
[0])), orientation_requested_supported
);
4991 /* output-bin-default */
4992 if (pc
->num_bins
> 0)
4993 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "output-bin-default", NULL
, pc
->bins
->pwg
);
4995 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
4997 /* output-bin-supported */
4998 if (pc
->num_bins
> 0)
5000 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "output-bin-supported", pc
->num_bins
, NULL
, NULL
);
5001 for (i
= 0, pwg_map
= pc
->bins
; i
< pc
->num_bins
; i
++, pwg_map
++)
5002 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
5006 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
5009 /* overrides-supported */
5010 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides_supported
) / sizeof(overrides_supported
[0])), NULL
, overrides_supported
);
5012 /* page-ranges-supported */
5013 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
5015 /* pages-per-minute */
5016 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute", ppd
->throughput
);
5018 /* pages-per-minute-color */
5019 if (ppd
->color_device
)
5020 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute-color", ppd
->throughput
);
5022 /* print-color-mode-default */
5023 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, ppd
->color_device
? "auto" : "monochrome");
5025 /* print-color-mode-supported */
5026 if (ppd
->color_device
)
5027 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color
) / sizeof(print_color_mode_supported_color
[0])), NULL
, print_color_mode_supported_color
);
5029 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported
) / sizeof(print_color_mode_supported
[0])), NULL
, print_color_mode_supported
);
5031 /* print-content-optimize-default */
5032 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
5034 /* print-content-optimize-supported */
5035 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
5037 /* print-quality-default */
5038 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
5040 /* print-quality-supported */
5041 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
5043 /* print-rendering-intent-default */
5044 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
5046 /* print-rendering-intent-supported */
5047 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
5049 /* printer-device-id */
5050 if ((ppd_attr
= ppdFindAttr(ppd
, "1284DeviceId", NULL
)) != NULL
)
5053 * Use the device ID string from the PPD...
5056 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, ppd_attr
->value
);
5061 * Synthesize a device ID string...
5064 char device_id
[1024]; /* Device ID string */
5066 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;CMD:PS;", ppd
->manufacturer
, ppd
->modelname
);
5068 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
5071 /* printer-input-tray */
5072 if (pc
->num_sources
> 0)
5074 for (i
= 0, attr
= NULL
; i
< pc
->num_sources
; i
++)
5076 char input_tray
[1024]; /* printer-input-tray value */
5078 if (!strcmp(pc
->sources
[i
].pwg
, "manual") || strstr(pc
->sources
[i
].pwg
, "-man") != NULL
)
5079 snprintf(input_tray
, sizeof(input_tray
), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc
->sources
[i
].pwg
);
5081 snprintf(input_tray
, sizeof(input_tray
), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc
->sources
[i
].pwg
);
5084 ippSetOctetString(attrs
, &attr
, i
, input_tray
, (int)strlen(input_tray
));
5086 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", input_tray
, (int)strlen(input_tray
));
5091 static const char *printer_input_tray
= "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto";
5093 ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray
, (int)strlen(printer_input_tray
));
5096 /* printer-make-and-model */
5097 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, ppd
->nickname
);
5099 /* printer-resolution-default */
5100 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, xres
, yres
);
5102 /* printer-resolution-supported */
5103 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, xres
, yres
);
5105 /* printer-supply and printer-supply-description */
5106 if (ppd
->color_device
)
5108 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply_color
[0], strlen(printer_supply_color
[0]));
5109 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
5110 ippSetOctetString(attrs
, &attr
, i
, printer_supply_color
[i
], strlen(printer_supply_color
[i
]));
5112 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-supply-description", (int)(sizeof(printer_supply_description_color
) / sizeof(printer_supply_description_color
[0])), NULL
, printer_supply_description_color
);
5116 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply
[0], strlen(printer_supply
[0]));
5117 for (i
= 1; i
< (int)(sizeof(printer_supply
) / sizeof(printer_supply
[0])); i
++)
5118 ippSetOctetString(attrs
, &attr
, i
, printer_supply
[i
], strlen(printer_supply
[i
]));
5120 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-supply-description", (int)(sizeof(printer_supply_description
) / sizeof(printer_supply_description
[0])), NULL
, printer_supply_description
);
5123 /* pwg-raster-document-xxx-supported */
5124 if (cupsArrayFind(docformats
, (void *)"image/pwg-raster"))
5126 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH
, xres
, yres
);
5128 if (pc
->sides_2sided_long
)
5129 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "normal");
5131 if (ppd
->color_device
)
5132 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color
) / sizeof(pwg_raster_document_type_supported_color
[0])), NULL
, pwg_raster_document_type_supported_color
);
5134 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(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
);
5138 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
5140 /* sides-supported */
5141 if (pc
->sides_2sided_long
)
5142 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", (int)(sizeof(sides_supported
) / sizeof(sides_supported
[0])), NULL
, sides_supported
);
5144 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", NULL
, "one-sided");
5147 if (cupsArrayFind(docformats
, (void *)"image/urf"))
5148 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", num_urf
, NULL
, urf
);
5151 * Free the PPD file and return the attributes...
5154 _ppdCacheDestroy(pc
);
5160 #endif /* !CUPS_LITE */
5164 * 'parse_options()' - Parse URL options into CUPS options.
5166 * The client->options string is destroyed by this function.
5169 static int /* O - Number of options */
5170 parse_options(ippeve_client_t
*client
, /* I - Client */
5171 cups_option_t
**options
)/* O - Options */
5173 char *name
, /* Name */
5175 *next
; /* Next name=value pair */
5176 int num_options
= 0; /* Number of options */
5181 for (name
= client
->options
; name
&& *name
; name
= next
)
5183 if ((value
= strchr(name
, '=')) == NULL
)
5187 if ((next
= strchr(value
, '&')) != NULL
)
5190 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5193 return (num_options
);
5198 * 'process_attr_message()' - Process an ATTR: message from a command.
5202 process_attr_message(
5203 ippeve_job_t
*job
, /* I - Job */
5204 char *message
) /* I - Message */
5206 int i
, /* Looping var */
5207 num_options
= 0; /* Number of name=value pairs */
5208 cups_option_t
*options
= NULL
, /* name=value pairs from message */
5209 *option
; /* Current option */
5210 ipp_attribute_t
*attr
; /* Current attribute */
5214 * Grab attributes from the message line...
5217 num_options
= cupsParseOptions(message
+ 5, num_options
, &options
);
5220 * Loop through the options and record them in the printer or job objects...
5223 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
5225 if (!strcmp(option
->name
, "job-impressions"))
5228 * Update job-impressions attribute...
5231 job
->impressions
= atoi(option
->value
);
5233 else if (!strcmp(option
->name
, "job-impressions-completed"))
5236 * Update job-impressions-completed attribute...
5239 job
->impcompleted
= atoi(option
->value
);
5241 else if (!strncmp(option
->name
, "marker-", 7) || !strcmp(option
->name
, "printer-alert") || !strcmp(option
->name
, "printer-alert-description") || !strcmp(option
->name
, "printer-supply") || !strcmp(option
->name
, "printer-supply-description"))
5244 * Update Printer Status attribute...
5247 _cupsRWLockWrite(&job
->printer
->rwlock
);
5249 if ((attr
= ippFindAttribute(job
->printer
->attrs
, option
->name
, IPP_TAG_ZERO
)) != NULL
)
5250 ippDeleteAttribute(job
->printer
->attrs
, attr
);
5252 cupsEncodeOption(job
->printer
->attrs
, IPP_TAG_PRINTER
, option
->name
, option
->value
);
5254 _cupsRWUnlock(&job
->printer
->rwlock
);
5259 * Something else that isn't currently supported...
5262 fprintf(stderr
, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job
->id
, option
->name
, option
->value
);
5266 cupsFreeOptions(num_options
, options
);
5271 * 'process_client()' - Process client requests on a thread.
5274 static void * /* O - Exit status */
5275 process_client(ippeve_client_t
*client
) /* I - Client */
5278 * Loop until we are out of requests or timeout (30 seconds)...
5282 int first_time
= 1; /* First time request? */
5283 #endif /* HAVE_SSL */
5285 while (httpWait(client
->http
, 30000))
5291 * See if we need to negotiate a TLS connection...
5294 char buf
[1]; /* First byte from client */
5296 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5298 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5300 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5302 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5306 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5311 #endif /* HAVE_SSL */
5313 if (!process_http(client
))
5318 * Close the conection to the client and return...
5321 delete_client(client
);
5328 * 'process_http()' - Process a HTTP request.
5331 int /* O - 1 on success, 0 on failure */
5332 process_http(ippeve_client_t
*client
) /* I - Client connection */
5334 char uri
[1024]; /* URI */
5335 http_state_t http_state
; /* HTTP state */
5336 http_status_t http_status
; /* HTTP status */
5337 ipp_state_t ipp_state
; /* State of IPP transfer */
5338 char scheme
[32], /* Method/scheme */
5339 userpass
[128], /* Username:password */
5340 hostname
[HTTP_MAX_HOST
];
5342 int port
; /* Port number */
5343 static const char * const http_states
[] =
5344 { /* Strings for logging HTTP method */
5365 * Clear state variables...
5368 ippDelete(client
->request
);
5369 ippDelete(client
->response
);
5371 client
->request
= NULL
;
5372 client
->response
= NULL
;
5373 client
->operation
= HTTP_STATE_WAITING
;
5376 * Read a request from the connection...
5379 while ((http_state
= httpReadRequest(client
->http
, uri
,
5380 sizeof(uri
))) == HTTP_STATE_WAITING
)
5384 * Parse the request line...
5387 if (http_state
== HTTP_STATE_ERROR
)
5389 if (httpError(client
->http
) == EPIPE
)
5390 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5392 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
, strerror(httpError(client
->http
)));
5396 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5398 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5399 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5402 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5404 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5405 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5409 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
], uri
);
5412 * Separate the URI into its components...
5415 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5416 userpass
, sizeof(userpass
),
5417 hostname
, sizeof(hostname
), &port
,
5418 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5419 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5421 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5422 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5426 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5427 *(client
->options
)++ = '\0';
5430 * Process the request...
5433 client
->start
= time(NULL
);
5434 client
->operation
= httpGetState(client
->http
);
5437 * Parse incoming parameters until the status changes...
5440 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5442 if (http_status
!= HTTP_STATUS_OK
)
5444 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5448 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5449 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5452 * HTTP/1.1 and higher require the "Host:" field...
5455 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5460 * Handle HTTP Upgrade...
5463 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5467 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5469 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5472 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5474 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5476 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5480 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5483 #endif /* HAVE_SSL */
5485 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5490 * Handle HTTP Expect...
5493 if (httpGetExpect(client
->http
) &&
5494 (client
->operation
== HTTP_STATE_POST
||
5495 client
->operation
== HTTP_STATE_PUT
))
5497 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5500 * Send 100-continue header...
5503 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5509 * Send 417-expectation-failed header...
5512 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5518 * Handle new transfers...
5521 switch (client
->operation
)
5523 case HTTP_STATE_OPTIONS
:
5525 * Do OPTIONS command...
5528 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5530 case HTTP_STATE_HEAD
:
5531 if (!strcmp(client
->uri
, "/icon.png"))
5532 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5533 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5534 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5536 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5538 case HTTP_STATE_GET
:
5539 if (!strcmp(client
->uri
, "/icon.png"))
5542 * Send PNG icon file.
5545 if (client
->printer
->icon
)
5547 int fd
; /* Icon file */
5548 struct stat fileinfo
; /* Icon file information */
5549 char buffer
[4096]; /* Copy buffer */
5550 ssize_t bytes
; /* Bytes */
5552 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5554 if (!stat(client
->printer
->icon
, &fileinfo
) && (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5556 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", (size_t)fileinfo
.st_size
))
5562 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5563 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5565 httpFlushWrite(client
->http
);
5570 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5574 fputs("Icon file is internal printer.png.\n", stderr
);
5576 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", sizeof(printer_png
)))
5579 httpWrite2(client
->http
, (const char *)printer_png
, sizeof(printer_png
));
5580 httpFlushWrite(client
->http
);
5583 else if (!strcmp(client
->uri
, "/"))
5586 * Show web status page...
5589 return (show_status(client
));
5591 else if (!strcmp(client
->uri
, "/media"))
5594 * Show web media page...
5597 return (show_media(client
));
5599 else if (!strcmp(client
->uri
, "/supplies"))
5602 * Show web supplies page...
5605 return (show_supplies(client
));
5608 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5611 case HTTP_STATE_POST
:
5612 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5616 * Not an IPP request...
5619 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5623 * Read the IPP request...
5626 client
->request
= ippNew();
5628 while ((ipp_state
= ippRead(client
->http
,
5629 client
->request
)) != IPP_STATE_DATA
)
5631 if (ipp_state
== IPP_STATE_ERROR
)
5633 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
, cupsLastErrorString());
5634 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5640 * Now that we have the IPP request, process the request...
5643 return (process_ipp(client
));
5646 break; /* Anti-compiler-warning-code */
5654 * 'process_ipp()' - Process an IPP request.
5657 static int /* O - 1 on success, 0 on error */
5658 process_ipp(ippeve_client_t
*client
) /* I - Client */
5660 ipp_tag_t group
; /* Current group tag */
5661 ipp_attribute_t
*attr
; /* Current attribute */
5662 ipp_attribute_t
*charset
; /* Character set attribute */
5663 ipp_attribute_t
*language
; /* Language attribute */
5664 ipp_attribute_t
*uri
; /* Printer URI attribute */
5665 int major
, minor
; /* Version number */
5666 const char *name
; /* Name of attribute */
5669 debug_attributes("Request", client
->request
, 1);
5672 * First build an empty response message for this request...
5675 client
->operation_id
= ippGetOperation(client
->request
);
5676 client
->response
= ippNewResponse(client
->request
);
5679 * Then validate the request header and required attributes...
5682 major
= ippGetVersion(client
->request
, &minor
);
5684 if (major
< 1 || major
> 2)
5687 * Return an error, since we only support IPP 1.x and 2.x.
5690 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
5692 else if ((major
* 10 + minor
) > MaxVersion
)
5694 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5695 httpFlush(client
->http
); /* Flush trailing (junk) data */
5697 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5700 else if (ippGetRequestId(client
->request
) <= 0)
5702 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
5704 else if (!ippFirstAttribute(client
->request
))
5706 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
5711 * Make sure that the attributes are provided in the correct order and
5712 * don't repeat groups...
5715 for (attr
= ippFirstAttribute(client
->request
),
5716 group
= ippGetGroupTag(attr
);
5718 attr
= ippNextAttribute(client
->request
))
5720 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5723 * Out of order; return an error...
5726 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5727 "Attribute groups are out of order (%x < %x).",
5728 ippGetGroupTag(attr
), group
);
5732 group
= ippGetGroupTag(attr
);
5738 * Then make sure that the first three attributes are:
5740 * attributes-charset
5741 * attributes-natural-language
5742 * printer-uri/job-uri
5745 attr
= ippFirstAttribute(client
->request
);
5746 name
= ippGetName(attr
);
5747 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5748 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5753 attr
= ippNextAttribute(client
->request
);
5754 name
= ippGetName(attr
);
5756 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5757 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5762 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5763 IPP_TAG_URI
)) != NULL
)
5765 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5766 IPP_TAG_URI
)) != NULL
)
5772 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5773 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5776 * Bad character set...
5779 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5780 "Unsupported character set \"%s\".",
5781 ippGetString(charset
, 0, NULL
));
5783 else if (!charset
|| !language
|| !uri
)
5786 * Return an error, since attributes-charset,
5787 * attributes-natural-language, and printer-uri/job-uri are required
5788 * for all operations.
5791 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5792 "Missing required attributes.");
5796 char scheme
[32], /* URI scheme */
5797 userpass
[32], /* Username/password in URI */
5798 host
[256], /* Host name in URI */
5799 resource
[256]; /* Resource path in URI */
5800 int port
; /* Port number in URI */
5802 name
= ippGetName(uri
);
5804 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5805 scheme
, sizeof(scheme
),
5806 userpass
, sizeof(userpass
),
5807 host
, sizeof(host
), &port
,
5808 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5809 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5810 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5811 else if ((!strcmp(name
, "job-uri") &&
5812 strncmp(resource
, "/ipp/print/", 11)) ||
5813 (!strcmp(name
, "printer-uri") &&
5814 strcmp(resource
, "/ipp/print")))
5815 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5816 name
, ippGetString(uri
, 0, NULL
));
5820 * Try processing the operation...
5823 switch (ippGetOperation(client
->request
))
5825 case IPP_OP_PRINT_JOB
:
5826 ipp_print_job(client
);
5829 case IPP_OP_PRINT_URI
:
5830 ipp_print_uri(client
);
5833 case IPP_OP_VALIDATE_JOB
:
5834 ipp_validate_job(client
);
5837 case IPP_OP_CREATE_JOB
:
5838 ipp_create_job(client
);
5841 case IPP_OP_SEND_DOCUMENT
:
5842 ipp_send_document(client
);
5845 case IPP_OP_SEND_URI
:
5846 ipp_send_uri(client
);
5849 case IPP_OP_CANCEL_JOB
:
5850 ipp_cancel_job(client
);
5853 case IPP_OP_GET_JOB_ATTRIBUTES
:
5854 ipp_get_job_attributes(client
);
5857 case IPP_OP_GET_JOBS
:
5858 ipp_get_jobs(client
);
5861 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5862 ipp_get_printer_attributes(client
);
5865 case IPP_OP_CLOSE_JOB
:
5866 ipp_close_job(client
);
5869 case IPP_OP_IDENTIFY_PRINTER
:
5870 ipp_identify_printer(client
);
5874 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5875 "Operation not supported.");
5884 * Send the HTTP header and return...
5887 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5888 httpFlush(client
->http
); /* Flush trailing (junk) data */
5890 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5891 ippLength(client
->response
)));
5896 * 'process_job()' - Process a print job.
5899 static void * /* O - Thread exit status */
5900 process_job(ippeve_job_t
*job
) /* I - Job */
5902 job
->state
= IPP_JSTATE_PROCESSING
;
5903 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5904 job
->processing
= time(NULL
);
5906 while (job
->printer
->state_reasons
& IPPEVE_PREASON_MEDIA_EMPTY
)
5908 job
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
5913 job
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_NEEDED
;
5915 if (job
->printer
->command
)
5918 * Execute a command with the job spool file and wait for it to complete...
5921 int pid
, /* Process ID */
5922 status
; /* Exit status */
5923 struct timeval start
, /* Start time */
5925 char *myargv
[3], /* Command-line arguments */
5926 *myenvp
[400]; /* Environment variables */
5927 int myenvc
; /* Number of environment variables */
5928 ipp_attribute_t
*attr
; /* Job attribute */
5929 char val
[1280], /* IPP_NAME=value */
5930 *valptr
; /* Pointer into string */
5932 int mystdout
= -1; /* File for stdout */
5933 int mypipe
[2]; /* Pipe for stderr */
5934 char line
[2048], /* Line from stderr */
5935 *ptr
, /* Pointer into line */
5936 *endptr
; /* End of line */
5937 ssize_t bytes
; /* Bytes read */
5938 #endif /* !_WIN32 */
5940 fprintf(stderr
, "[Job %d] Running command \"%s %s\".\n", job
->id
, job
->printer
->command
, job
->filename
);
5941 gettimeofday(&start
, NULL
);
5944 * Setup the command-line arguments...
5947 myargv
[0] = job
->printer
->command
;
5948 myargv
[1] = job
->filename
;
5952 * Copy the current environment, then add environment variables for every
5953 * Job attribute and Printer -default attributes...
5956 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5957 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5959 if (myenvc
> (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 32))
5961 fprintf(stderr
, "[Job %d] Too many environment variables to process job.\n", job
->id
);
5962 job
->state
= IPP_JSTATE_ABORTED
;
5966 if (asprintf(myenvp
+ myenvc
, "CONTENT_TYPE=%s", job
->format
) > 0)
5969 if (job
->printer
->device_uri
&& asprintf(myenvp
+ myenvc
, "DEVICE_URI=%s", job
->printer
->device_uri
) > 0)
5973 if (job
->printer
->ppdfile
&& asprintf(myenvp
+ myenvc
, "PPD=%s", job
->printer
->ppdfile
) > 0)
5975 #endif /* !CUPS_LITE */
5977 for (attr
= ippFirstAttribute(job
->printer
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->printer
->attrs
))
5980 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
5981 * then add the value(s) from the attribute.
5984 const char *name
= ippGetName(attr
),
5985 /* Attribute name */
5986 *suffix
= strstr(name
, "-default");
5987 /* Suffix on attribute name */
5989 if (!suffix
|| suffix
[8])
5997 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6002 *valptr
++ = (char)toupper(*name
& 255);
6007 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6009 myenvp
[myenvc
++] = strdup(val
);
6012 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6015 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6016 * value(s) from the attribute.
6019 const char *name
= ippGetName(attr
);
6020 /* Attribute name */
6030 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6035 *valptr
++ = (char)toupper(*name
& 255);
6040 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6042 myenvp
[myenvc
++] = strdup(val
);
6047 fprintf(stderr
, "[Job %d] Too many environment variables to process job.\n", job
->id
);
6048 job
->state
= IPP_JSTATE_ABORTED
;
6052 myenvp
[myenvc
] = NULL
;
6055 * Now run the program...
6059 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6062 if (job
->printer
->device_uri
)
6064 char scheme
[32], /* URI scheme */
6065 userpass
[256], /* username:password (unused) */
6066 host
[256], /* Hostname or IP address */
6067 resource
[256]; /* Resource path */
6068 int port
; /* Port number */
6071 if (httpSeparateURI(HTTP_URI_CODING_ALL
, job
->printer
->device_uri
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), host
, sizeof(host
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
6073 fprintf(stderr
, "[Job %d] Bad device URI \"%s\".\n", job
->id
, job
->printer
->device_uri
);
6075 else if (!strcmp(scheme
, "file"))
6077 struct stat fileinfo
; /* See if this is a file or directory... */
6079 if (stat(resource
, &fileinfo
))
6081 if (errno
== ENOENT
)
6083 if ((mystdout
= open(resource
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) >= 0)
6084 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6086 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6089 fprintf(stderr
, "[Job %d] Unable to access \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6091 else if (S_ISDIR(fileinfo
.st_mode
))
6093 if ((mystdout
= create_job_file(job
, line
, sizeof(line
), resource
, "prn")) >= 0)
6094 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, line
);
6096 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, line
, strerror(errno
));
6098 else if (!S_ISREG(fileinfo
.st_mode
))
6100 if ((mystdout
= open(resource
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) >= 0)
6101 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6103 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6105 else if ((mystdout
= open(resource
, O_WRONLY
)) >= 0)
6106 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6108 fprintf(stderr
, "[Job %d] Unable to open \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6110 else if (!strcmp(scheme
, "socket"))
6112 http_addrlist_t
*addrlist
; /* List of addresses */
6113 char service
[32]; /* Service number */
6115 snprintf(service
, sizeof(service
), "%d", port
);
6117 if ((addrlist
= httpAddrGetList(host
, AF_UNSPEC
, service
)) == NULL
)
6118 fprintf(stderr
, "[Job %d] Unable to find \"%s\": %s\n", job
->id
, host
, cupsLastErrorString());
6119 else if (!httpAddrConnect2(addrlist
, &mystdout
, 30000, &(job
->cancel
)))
6120 fprintf(stderr
, "[Job %d] Unable to connect to \"%s\": %s\n", job
->id
, host
, cupsLastErrorString());
6122 httpAddrFreeList(addrlist
);
6126 fprintf(stderr
, "[Job %d] Unsupported device URI scheme \"%s\".\n", job
->id
, scheme
);
6129 else if ((mystdout
= create_job_file(job
, line
, sizeof(line
), job
->printer
->directory
, "prn")) >= 0)
6131 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, line
);
6135 mystdout
= open("/dev/null", O_WRONLY
);
6139 fprintf(stderr
, "[Job %d] Unable to create pipe for stderr: %s\n", job
->id
, strerror(errno
));
6140 mypipe
[0] = mypipe
[1] = -1;
6143 if ((pid
= fork()) == 0)
6146 * Child comes here...
6158 execve(job
->printer
->command
, myargv
, myenvp
);
6164 * Unable to fork process...
6167 fprintf(stderr
, "[Job %d] Unable to start job processing command: %s\n", job
->id
, strerror(errno
));
6175 * Free memory used for environment...
6179 free(myenvp
[-- myenvc
]);
6184 * Free memory used for environment...
6188 free(myenvp
[-- myenvc
]);
6191 * Close the output file in the parent process...
6197 * If the pipe exists, read from it until EOF...
6205 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6210 while ((ptr
= strchr(line
, '\n')) != NULL
)
6212 int level
= 3; /* Message log level */
6216 if (!strncmp(line
, "ATTR:", 5))
6219 * Process job/printer attribute updates.
6222 process_attr_message(job
, line
);
6224 else if (!strncmp(line
, "DEBUG:", 6))
6232 else if (!strncmp(line
, "ERROR:", 6))
6239 job
->message
= strdup(line
+ 6);
6242 else if (!strncmp(line
, "INFO:", 5))
6245 * Informational/progress message...
6251 job
->message
= strdup(line
+ 5);
6255 else if (!strncmp(line
, "STATE:", 6))
6258 * Process printer-state-reasons keywords.
6261 process_state_message(job
, line
);
6264 if (Verbosity
>= level
)
6265 fprintf(stderr
, "[Job %d] Command - %s\n", job
->id
, line
);
6269 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6279 * Wait for child to complete...
6282 # ifdef HAVE_WAITPID
6283 while (waitpid(pid
, &status
, 0) < 0);
6285 while (wait(&status
) < 0);
6286 # endif /* HAVE_WAITPID */
6293 if (WIFEXITED(status
))
6294 #endif /* !_WIN32 */
6295 fprintf(stderr
, "[Job %d] Command \"%s\" exited with status %d.\n", job
->id
, job
->printer
->command
, WEXITSTATUS(status
));
6298 fprintf(stderr
, "[Job %d] Command \"%s\" terminated with signal %d.\n", job
->id
, job
->printer
->command
, WTERMSIG(status
));
6299 #endif /* !_WIN32 */
6300 job
->state
= IPP_JSTATE_ABORTED
;
6302 else if (status
< 0)
6303 job
->state
= IPP_JSTATE_ABORTED
;
6305 fprintf(stderr
, "[Job %d] Command \"%s\" completed successfully.\n", job
->id
, job
->printer
->command
);
6308 * Report the total processing time...
6311 gettimeofday(&end
, NULL
);
6313 fprintf(stderr
, "[Job %d] Processing time was %.3f seconds.\n", job
->id
, end
.tv_sec
- start
.tv_sec
+ 0.000001 * (end
.tv_usec
- start
.tv_usec
));
6318 * Sleep for a random amount of time to simulate job processing.
6321 sleep((unsigned)(5 + (rand() % 11)));
6325 job
->state
= IPP_JSTATE_CANCELED
;
6326 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6327 job
->state
= IPP_JSTATE_COMPLETED
;
6331 job
->completed
= time(NULL
);
6332 job
->printer
->state
= IPP_PSTATE_IDLE
;
6333 job
->printer
->active_job
= NULL
;
6340 * 'process_state_message()' - Process a STATE: message from a command.
6344 process_state_message(
6345 ippeve_job_t
*job
, /* I - Job */
6346 char *message
) /* I - Message */
6348 int i
; /* Looping var */
6349 ippeve_preason_t state_reasons
, /* printer-state-reasons values */
6350 bit
; /* Current reason bit */
6351 char *ptr
, /* Pointer into message */
6352 *next
; /* Next keyword in message */
6353 int remove
; /* Non-zero if we are removing keywords */
6357 * Skip leading "STATE:" and any whitespace...
6360 for (message
+= 6; *message
; message
++)
6361 if (*message
!= ' ' && *message
!= '\t')
6365 * Support the following forms of message:
6367 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6369 * "-keyword[,keyword,...]" to remove keywords.
6371 * "+keyword[,keyword,...]" to add keywords.
6373 * Keywords may or may not have a suffix (-report, -warning, -error) per
6377 if (*message
== '-')
6380 state_reasons
= job
->printer
->state_reasons
;
6383 else if (*message
== '+')
6386 state_reasons
= job
->printer
->state_reasons
;
6392 state_reasons
= IPPEVE_PREASON_NONE
;
6397 if ((next
= strchr(message
, ',')) != NULL
)
6400 if ((ptr
= strstr(message
, "-error")) != NULL
)
6402 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6404 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6407 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
6409 if (!strcmp(message
, ippeve_preason_strings
[i
]))
6412 state_reasons
&= ~bit
;
6414 state_reasons
|= bit
;
6424 job
->printer
->state_reasons
= state_reasons
;
6429 * 'register_printer()' - Register a printer object via Bonjour.
6432 static int /* O - 1 on success, 0 on error */
6434 ippeve_printer_t
*printer
, /* I - Printer */
6435 const char *subtypes
) /* I - Service subtype(s) */
6437 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6438 ippeve_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6439 int i
, /* Looping var */
6440 count
; /* Number of values */
6441 ipp_attribute_t
*color_supported
,
6442 *document_format_supported
,
6444 *printer_make_and_model
,
6448 *urf_supported
; /* Printer attributes */
6449 const char *value
; /* Value string */
6450 char formats
[252], /* List of supported formats */
6451 urf
[252], /* List of supported URF values */
6452 *ptr
; /* Pointer into string */
6454 color_supported
= ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_BOOLEAN
);
6455 document_format_supported
= ippFindAttribute(printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
);
6456 printer_location
= ippFindAttribute(printer
->attrs
, "printer-location", IPP_TAG_TEXT
);
6457 printer_make_and_model
= ippFindAttribute(printer
->attrs
, "printer-make-and-model", IPP_TAG_TEXT
);
6458 printer_more_info
= ippFindAttribute(printer
->attrs
, "printer-more-info", IPP_TAG_URI
);
6459 printer_uuid
= ippFindAttribute(printer
->attrs
, "printer-uuid", IPP_TAG_URI
);
6460 sides_supported
= ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
);
6461 urf_supported
= ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_KEYWORD
);
6463 for (i
= 0, count
= ippGetCount(document_format_supported
), ptr
= formats
; i
< count
; i
++)
6465 value
= ippGetString(document_format_supported
, i
, NULL
);
6467 if (!strcasecmp(value
, "application/octet-stream"))
6470 if (ptr
> formats
&& ptr
< (formats
+ sizeof(formats
) - 1))
6473 strlcpy(ptr
, value
, sizeof(formats
) - (size_t)(ptr
- formats
));
6476 if (ptr
>= (formats
+ sizeof(formats
) - 1))
6481 for (i
= 0, count
= ippGetCount(urf_supported
), ptr
= urf
; i
< count
; i
++)
6483 value
= ippGetString(urf_supported
, i
, NULL
);
6485 if (ptr
> urf
&& ptr
< (urf
+ sizeof(urf
) - 1))
6488 strlcpy(ptr
, value
, sizeof(urf
) - (size_t)(ptr
- urf
));
6491 if (ptr
>= (urf
+ sizeof(urf
) - 1))
6495 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6497 DNSServiceErrorType error
; /* Error from Bonjour */
6498 char regtype
[256]; /* Bonjour service type */
6502 * Build the TXT record for IPP...
6505 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6506 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6507 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
6508 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(value
), value
);
6509 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
6510 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(value
), value
);
6511 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
6512 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(value
), value
);
6513 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
), formats
);
6514 TXTRecordSetValue(&ipp_txt
, "Color", 1, ippGetBoolean(color_supported
, 0) ? "T" : "F");
6515 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, ippGetCount(sides_supported
) > 1 ? "T" : "F");
6516 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
6517 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(value
) - 9, value
+ 9);
6519 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6520 # endif /* HAVE_SSL */
6522 TXTRecordSetValue(&ipp_txt
, "URF", (uint8_t)strlen(urf
), urf
);
6523 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6524 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6527 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6528 * defend our service name but not actually support LPD...
6531 printer
->printer_ref
= DNSSDMaster
;
6533 if ((error
= DNSServiceRegister(&(printer
->printer_ref
), kDNSServiceFlagsShareConnection
, 0 /* interfaceIndex */, printer
->dnssd_name
, "_printer._tcp", NULL
/* domain */, NULL
/* host */, 0 /* port */, 0 /* txtLen */, NULL
/* txtRecord */, (DNSServiceRegisterReply
)dnssd_callback
, printer
)) != kDNSServiceErr_NoError
)
6535 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_printer._tcp", error
);
6540 * Then register the _ipp._tcp (IPP) service type with the real port number to
6541 * advertise our IPP printer...
6544 printer
->ipp_ref
= DNSSDMaster
;
6546 if (subtypes
&& *subtypes
)
6547 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtypes
);
6549 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6551 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
), kDNSServiceFlagsShareConnection
, 0 /* interfaceIndex */, printer
->dnssd_name
, regtype
, NULL
/* domain */, NULL
/* host */, htons(printer
->port
), TXTRecordGetLength(&ipp_txt
), TXTRecordGetBytesPtr(&ipp_txt
), (DNSServiceRegisterReply
)dnssd_callback
, printer
)) != kDNSServiceErr_NoError
)
6553 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
6559 * Then register the _ipps._tcp (IPP) service type with the real port number to
6560 * advertise our IPPS printer...
6563 printer
->ipps_ref
= DNSSDMaster
;
6565 if (subtypes
&& *subtypes
)
6566 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtypes
);
6568 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6570 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
), kDNSServiceFlagsShareConnection
, 0 /* interfaceIndex */, printer
->dnssd_name
, regtype
, NULL
/* domain */, NULL
/* host */, htons(printer
->port
), TXTRecordGetLength(&ipp_txt
), TXTRecordGetBytesPtr(&ipp_txt
), (DNSServiceRegisterReply
)dnssd_callback
, printer
)) != kDNSServiceErr_NoError
)
6572 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
6575 # endif /* HAVE_SSL */
6578 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6579 * real port number to advertise our IPP printer...
6582 printer
->http_ref
= DNSSDMaster
;
6584 if ((error
= DNSServiceRegister(&(printer
->http_ref
), kDNSServiceFlagsShareConnection
, 0 /* interfaceIndex */, printer
->dnssd_name
, "_http._tcp,_printer", NULL
/* domain */, NULL
/* host */, htons(printer
->port
), 0 /* txtLen */, NULL
/* txtRecord */, (DNSServiceRegisterReply
)dnssd_callback
, printer
)) != kDNSServiceErr_NoError
)
6586 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_http._tcp,_printer", error
);
6590 TXTRecordDeallocate(&ipp_txt
);
6592 #elif defined(HAVE_AVAHI)
6593 char temp
[256]; /* Subtype service string */
6596 * Create the TXT record...
6600 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6601 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
6602 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s", value
);
6603 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
6604 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", value
);
6605 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
6606 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", value
);
6607 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6608 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", ippGetBoolean(color_supported
, 0) ? "T" : "F");
6609 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", ippGetCount(sides_supported
) > 1 ? "T" : "F");
6610 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
6611 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", value
+ 9);
6613 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6614 # endif /* HAVE_SSL */
6616 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "URF=%s", urf
);
6617 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "txtvers=1");
6618 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "qtotal=1");
6621 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6624 avahi_threaded_poll_lock(DNSSDMaster
);
6626 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6628 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
);
6631 * Then register the _ipp._tcp (IPP)...
6634 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
);
6635 if (subtypes
&& *subtypes
)
6637 char *temptypes
= strdup(subtypes
), *start
, *end
;
6639 for (start
= temptypes
; *start
; start
= end
)
6641 if ((end
= strchr(start
, ',')) != NULL
)
6644 end
= start
+ strlen(start
);
6646 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", start
);
6647 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6655 * _ipps._tcp (IPPS) for secure printing...
6658 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
);
6659 if (subtypes
&& *subtypes
)
6661 char *temptypes
= strdup(subtypes
), *start
, *end
;
6663 for (start
= temptypes
; *start
; start
= end
)
6665 if ((end
= strchr(start
, ',')) != NULL
)
6668 end
= start
+ strlen(start
);
6670 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", start
);
6671 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6676 #endif /* HAVE_SSL */
6679 * Finally _http.tcp (HTTP) for the web interface...
6682 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
);
6683 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");
6689 avahi_entry_group_commit(printer
->ipp_ref
);
6690 avahi_threaded_poll_unlock(DNSSDMaster
);
6692 avahi_string_list_free(ipp_txt
);
6693 #endif /* HAVE_DNSSD */
6700 * 'respond_http()' - Send a HTTP response.
6703 int /* O - 1 on success, 0 on failure */
6705 ippeve_client_t
*client
, /* I - Client */
6706 http_status_t code
, /* I - HTTP status of response */
6707 const char *content_encoding
, /* I - Content-Encoding of response */
6708 const char *type
, /* I - MIME media type of response */
6709 size_t length
) /* I - Length of response */
6711 char message
[1024]; /* Text message */
6714 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6716 if (code
== HTTP_STATUS_CONTINUE
)
6719 * 100-continue doesn't send any headers...
6722 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6726 * Format an error message...
6729 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6731 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6733 type
= "text/plain";
6734 length
= strlen(message
);
6740 * Send the HTTP response header...
6743 httpClearFields(client
->http
);
6745 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6746 client
->operation
== HTTP_STATE_OPTIONS
)
6747 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6751 if (!strcmp(type
, "text/html"))
6752 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6753 "text/html; charset=utf-8");
6755 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6757 if (content_encoding
)
6758 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6761 httpSetLength(client
->http
, length
);
6763 if (httpWriteResponse(client
->http
, code
) < 0)
6767 * Send the response data...
6773 * Send a plain text message.
6776 if (httpPrintf(client
->http
, "%s", message
) < 0)
6779 if (httpWrite2(client
->http
, "", 0) < 0)
6782 else if (client
->response
)
6785 * Send an IPP response...
6788 debug_attributes("Response", client
->response
, 2);
6790 ippSetState(client
->response
, IPP_STATE_IDLE
);
6792 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6801 * 'respond_ipp()' - Send an IPP response.
6805 respond_ipp(ippeve_client_t
*client
, /* I - Client */
6806 ipp_status_t status
, /* I - status-code */
6807 const char *message
, /* I - printf-style status-message */
6808 ...) /* I - Additional args as needed */
6810 const char *formatted
= NULL
; /* Formatted message */
6813 ippSetStatusCode(client
->response
, status
);
6817 va_list ap
; /* Pointer to additional args */
6818 ipp_attribute_t
*attr
; /* New status-message attribute */
6820 va_start(ap
, message
);
6821 if ((attr
= ippFindAttribute(client
->response
, "status-message", IPP_TAG_TEXT
)) != NULL
)
6822 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6824 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
, "status-message", NULL
, message
, ap
);
6827 formatted
= ippGetString(attr
, 0, NULL
);
6831 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
, ippOpString(client
->operation_id
), ippErrorString(status
), formatted
);
6833 fprintf(stderr
, "%s %s %s\n", client
->hostname
, ippOpString(client
->operation_id
), ippErrorString(status
));
6838 * 'respond_unsupported()' - Respond with an unsupported attribute.
6842 respond_unsupported(
6843 ippeve_client_t
*client
, /* I - Client */
6844 ipp_attribute_t
*attr
) /* I - Atribute */
6846 ipp_attribute_t
*temp
; /* Copy of attribute */
6849 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, "Unsupported %s %s%s value.", ippGetName(attr
), ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)));
6851 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6852 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6857 * 'run_printer()' - Run the printer service.
6861 run_printer(ippeve_printer_t
*printer
) /* I - Printer */
6863 int num_fds
; /* Number of file descriptors */
6864 struct pollfd polldata
[3]; /* poll() data */
6865 int timeout
; /* Timeout for poll() */
6866 ippeve_client_t
*client
; /* New client */
6870 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6873 polldata
[0].fd
= printer
->ipv4
;
6874 polldata
[0].events
= POLLIN
;
6876 polldata
[1].fd
= printer
->ipv6
;
6877 polldata
[1].events
= POLLIN
;
6882 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6883 polldata
[num_fds
++].events
= POLLIN
;
6884 #endif /* HAVE_DNSSD */
6887 * Loop until we are killed or have a hard error...
6892 if (cupsArrayCount(printer
->jobs
))
6897 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6899 perror("poll() failed");
6903 if (polldata
[0].revents
& POLLIN
)
6905 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6907 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6911 _cupsThreadDetach(t
);
6915 perror("Unable to create client thread");
6916 delete_client(client
);
6921 if (polldata
[1].revents
& POLLIN
)
6923 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6925 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6929 _cupsThreadDetach(t
);
6933 perror("Unable to create client thread");
6934 delete_client(client
);
6940 if (polldata
[2].revents
& POLLIN
)
6941 DNSServiceProcessResult(DNSSDMaster
);
6942 #endif /* HAVE_DNSSD */
6945 * Clean out old jobs...
6948 clean_jobs(printer
);
6954 * 'show_media()' - Show media load state.
6957 static int /* O - 1 on success, 0 on failure */
6958 show_media(ippeve_client_t
*client
) /* I - Client connection */
6960 ippeve_printer_t
*printer
= client
->printer
;
6962 int i
, j
, /* Looping vars */
6963 num_ready
, /* Number of ready media */
6964 num_sizes
, /* Number of media sizes */
6965 num_sources
, /* Number of media sources */
6966 num_types
; /* Number of media types */
6967 ipp_attribute_t
*media_col_ready
,/* media-col-ready attribute */
6968 *media_ready
, /* media-ready attribute */
6969 *media_sizes
, /* media-supported attribute */
6970 *media_sources
, /* media-source-supported attribute */
6971 *media_types
, /* media-type-supported attribute */
6972 *input_tray
; /* printer-input-tray attribute */
6973 ipp_t
*media_col
; /* media-col value */
6974 const char *media_size
, /* media value */
6975 *media_source
, /* media-source value */
6976 *media_type
, /* media-type value */
6977 *ready_size
, /* media-col-ready media-size[-name] value */
6978 *ready_source
, /* media-col-ready media-source value */
6979 *ready_tray
, /* printer-input-tray value */
6980 *ready_type
; /* media-col-ready media-type value */
6981 char tray_str
[1024], /* printer-input-tray string value */
6982 *tray_ptr
; /* Pointer into value */
6983 int tray_len
; /* Length of printer-input-tray value */
6984 int ready_sheets
; /* printer-input-tray sheets value */
6985 int num_options
; /* Number of form options */
6986 cups_option_t
*options
; /* Form options */
6987 static const int sheets
[] = /* Number of sheets */
6999 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7002 html_header(client
, printer
->name
, 0);
7004 if ((media_col_ready
= ippFindAttribute(printer
->attrs
, "media-col-ready", IPP_TAG_BEGIN_COLLECTION
)) == NULL
)
7006 html_printf(client
, "<p>Error: No media-col-ready defined for printer.</p>\n");
7007 html_footer(client
);
7011 media_ready
= ippFindAttribute(printer
->attrs
, "media-ready", IPP_TAG_ZERO
);
7013 if ((media_sizes
= ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
)) == NULL
)
7015 html_printf(client
, "<p>Error: No media-supported defined for printer.</p>\n");
7016 html_footer(client
);
7020 if ((media_sources
= ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
)) == NULL
)
7022 html_printf(client
, "<p>Error: No media-source-supported defined for printer.</p>\n");
7023 html_footer(client
);
7027 if ((media_types
= ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
)) == NULL
)
7029 html_printf(client
, "<p>Error: No media-type-supported defined for printer.</p>\n");
7030 html_footer(client
);
7034 if ((input_tray
= ippFindAttribute(printer
->attrs
, "printer-input-tray", IPP_TAG_STRING
)) == NULL
)
7036 html_printf(client
, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7037 html_footer(client
);
7041 num_ready
= ippGetCount(media_col_ready
);
7042 num_sizes
= ippGetCount(media_sizes
);
7043 num_sources
= ippGetCount(media_sources
);
7044 num_types
= ippGetCount(media_types
);
7046 if (num_sources
!= ippGetCount(input_tray
))
7048 html_printf(client
, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7049 html_footer(client
);
7054 * Process form data if present...
7057 if (printer
->web_forms
)
7058 num_options
= parse_options(client
, &options
);
7062 if (num_options
> 0)
7065 * WARNING: A real printer/server implementation MUST NOT implement
7066 * media updates via a GET request - GET requests are supposed to be
7067 * idempotent (without side-effects) and we obviously are not
7068 * authenticating access here. This form is provided solely to
7069 * enable testing and development!
7072 char name
[255]; /* Form name */
7073 const char *val
; /* Form value */
7074 pwg_media_t
*media
; /* Media info */
7076 _cupsRWLockWrite(&printer
->rwlock
);
7078 ippDeleteAttribute(printer
->attrs
, media_col_ready
);
7079 media_col_ready
= NULL
;
7083 ippDeleteAttribute(printer
->attrs
, media_ready
);
7087 printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MEDIA_LOW
| IPPEVE_PREASON_MEDIA_EMPTY
| IPPEVE_PREASON_MEDIA_NEEDED
);
7089 for (i
= 0; i
< num_sources
; i
++)
7091 media_source
= ippGetString(media_sources
, i
, NULL
);
7093 if (!strcmp(media_source
, "auto") || !strcmp(media_source
, "manual") || strstr(media_source
, "-man") != NULL
)
7096 snprintf(name
, sizeof(name
), "size%d", i
);
7097 if ((media_size
= cupsGetOption(name
, num_options
, options
)) != NULL
&& (media
= pwgMediaForPWG(media_size
)) != NULL
)
7099 snprintf(name
, sizeof(name
), "type%d", i
);
7100 if ((media_type
= cupsGetOption(name
, num_options
, options
)) != NULL
&& !*media_type
)
7104 ippSetString(printer
->attrs
, &media_ready
, ippGetCount(media_ready
), media_size
);
7106 media_ready
= ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", NULL
, media_size
);
7108 media_col
= create_media_col(media_size
, media_source
, media_type
, media
->width
, media
->length
, -1, -1, -1, -1);
7110 if (media_col_ready
)
7111 ippSetCollection(printer
->attrs
, &media_col_ready
, ippGetCount(media_col_ready
), media_col
);
7113 media_col_ready
= ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-ready", media_col
);
7114 ippDelete(media_col
);
7119 snprintf(name
, sizeof(name
), "level%d", i
);
7120 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
7121 ready_sheets
= atoi(val
);
7125 snprintf(tray_str
, sizeof(tray_str
), "type=sheetFeedAuto%sRemovableTray;mediafeed=%d;mediaxfeed=%d;maxcapacity=%d;level=%d;status=0;name=%s;", !strcmp(media_source
, "by-pass-tray") ? "Non" : "", media
? media
->length
: 0, media
? media
->width
: 0, strcmp(media_source
, "by-pass-tray") ? 250 : 25, ready_sheets
, media_source
);
7127 ippSetOctetString(printer
->attrs
, &input_tray
, i
, tray_str
, (int)strlen(tray_str
));
7129 if (ready_sheets
== 0)
7131 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_EMPTY
;
7132 if (printer
->active_job
)
7133 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
7135 else if (ready_sheets
< 25 && ready_sheets
> 0)
7136 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_LOW
;
7139 if (!media_col_ready
)
7140 media_col_ready
= ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
7143 media_ready
= ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
7145 _cupsRWUnlock(&printer
->rwlock
);
7148 if (printer
->web_forms
)
7149 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
7151 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
7152 for (i
= 0; i
< num_sources
; i
++)
7154 media_source
= ippGetString(media_sources
, i
, NULL
);
7156 if (!strcmp(media_source
, "auto") || !strcmp(media_source
, "manual") || strstr(media_source
, "-man") != NULL
)
7159 for (j
= 0, ready_size
= NULL
, ready_type
= NULL
; j
< num_ready
; j
++)
7161 media_col
= ippGetCollection(media_col_ready
, j
);
7162 ready_size
= ippGetString(ippFindAttribute(media_col
, "media-size-name", IPP_TAG_ZERO
), 0, NULL
);
7163 ready_source
= ippGetString(ippFindAttribute(media_col
, "media-source", IPP_TAG_ZERO
), 0, NULL
);
7164 ready_type
= ippGetString(ippFindAttribute(media_col
, "media-type", IPP_TAG_ZERO
), 0, NULL
);
7166 if (ready_source
&& !strcmp(ready_source
, media_source
))
7169 ready_source
= NULL
;
7174 html_printf(client
, "<tr><th>%s:</th>", media_source
);
7180 if (printer
->web_forms
)
7182 html_printf(client
, "<td><select name=\"size%d\"><option value=\"\">None</option>", i
);
7183 for (j
= 0; j
< num_sizes
; j
++)
7185 media_size
= ippGetString(media_sizes
, j
, NULL
);
7187 html_printf(client
, "<option%s>%s</option>", (ready_size
&& !strcmp(ready_size
, media_size
)) ? " selected" : "", media_size
);
7189 html_printf(client
, "</select>");
7192 html_printf(client
, "<td>%s", ready_size
);
7198 if (printer
->web_forms
)
7200 html_printf(client
, " <select name=\"type%d\"><option value=\"\">None</option>", i
);
7201 for (j
= 0; j
< num_types
; j
++)
7203 media_type
= ippGetString(media_types
, j
, NULL
);
7205 html_printf(client
, "<option%s>%s</option>", (ready_type
&& !strcmp(ready_type
, media_type
)) ? " selected" : "", media_type
);
7207 html_printf(client
, "</select>");
7210 html_printf(client
, ", %s", ready_type
);
7213 * Level/sheets loaded...
7216 if ((ready_tray
= ippGetOctetString(input_tray
, i
, &tray_len
)) != NULL
)
7218 if (tray_len
> (int)(sizeof(tray_str
) - 1))
7219 tray_len
= (int)sizeof(tray_str
) - 1;
7220 memcpy(tray_str
, ready_tray
, (size_t)tray_len
);
7221 tray_str
[tray_len
] = '\0';
7223 if ((tray_ptr
= strstr(tray_str
, "level=")) != NULL
)
7224 ready_sheets
= atoi(tray_ptr
+ 6);
7231 if (printer
->web_forms
)
7233 html_printf(client
, " <select name=\"level%d\">", i
);
7234 for (j
= 0; j
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); j
++)
7236 if (!strcmp(media_source
, "by-pass-tray") && sheets
[j
] > 25)
7240 html_printf(client
, "<option value=\"%d\"%s>Unknown</option>", sheets
[j
], sheets
[j
] == ready_sheets
? " selected" : "");
7242 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[j
], sheets
[j
] == ready_sheets
? " selected" : "", sheets
[j
]);
7244 html_printf(client
, "</select></td></tr>\n");
7246 else if (ready_sheets
> 0)
7247 html_printf(client
, ", %d sheets</td></tr>\n", ready_sheets
);
7249 html_printf(client
, "</td></tr>\n");
7252 if (printer
->web_forms
)
7254 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
7255 if (num_options
> 0)
7256 html_printf(client
, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
7257 html_printf(client
, "</td></tr></table></form>\n");
7259 if (num_options
> 0)
7260 html_printf(client
, "<script>\n"
7261 "setTimeout(hide_status, 3000);\n"
7262 "function hide_status() {\n"
7263 " var status = document.getElementById('status');\n"
7264 " status.style.display = 'none';\n"
7269 html_printf(client
, "</table>\n");
7271 html_footer(client
);
7278 * 'show_status()' - Show printer/system state.
7281 static int /* O - 1 on success, 0 on failure */
7282 show_status(ippeve_client_t
*client
) /* I - Client connection */
7284 ippeve_printer_t
*printer
= client
->printer
;
7286 ippeve_job_t
*job
; /* Current job */
7287 int i
; /* Looping var */
7288 ippeve_preason_t reason
; /* Current reason */
7289 static const char * const reasons
[] = /* Reason strings */
7293 "Input Tray Missing",
7294 "Marker Supply Empty",
7295 "Marker Supply Low",
7296 "Marker Waste Almost Full",
7297 "Marker Waste Full",
7308 static const char * const state_colors
[] =
7309 { /* State colors */
7311 "#EE0", /* Processing */
7312 "#C00" /* Stopped */
7316 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7319 html_header(client
, printer
->name
, printer
->state
== IPP_PSTATE_PROCESSING
? 5 : 15);
7320 html_printf(client
, "<h1><img style=\"background: %s; border-radius: 10px; float: left; margin-right: 10px; padding: 10px;\" src=\"/icon.png\" width=\"64\" height=\"64\">%s Jobs</h1>\n", state_colors
[printer
->state
- IPP_PSTATE_IDLE
], printer
->name
);
7321 html_printf(client
, "<p>%s, %d job(s).", printer
->state
== IPP_PSTATE_IDLE
? "Idle" : printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(printer
->jobs
));
7322 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
7323 if (printer
->state_reasons
& reason
)
7324 html_printf(client
, "\n<br> %s", reasons
[i
]);
7325 html_printf(client
, "</p>\n");
7327 if (cupsArrayCount(printer
->jobs
) > 0)
7329 _cupsRWLockRead(&(printer
->rwlock
));
7331 html_printf(client
, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>Status</th></tr></thead><tbody>\n");
7332 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
); job
; job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
7334 char when
[256], /* When job queued/started/finished */
7335 hhmmss
[64]; /* Time HH:MM:SS */
7339 case IPP_JSTATE_PENDING
:
7340 case IPP_JSTATE_HELD
:
7341 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
7343 case IPP_JSTATE_PROCESSING
:
7344 case IPP_JSTATE_STOPPED
:
7345 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
7347 case IPP_JSTATE_ABORTED
:
7348 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7350 case IPP_JSTATE_CANCELED
:
7351 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7353 case IPP_JSTATE_COMPLETED
:
7354 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7358 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
);
7360 html_printf(client
, "</tbody></table>\n");
7362 _cupsRWUnlock(&(printer
->rwlock
));
7365 html_footer(client
);
7372 * 'show_supplies()' - Show printer supplies.
7375 static int /* O - 1 on success, 0 on failure */
7377 ippeve_client_t
*client
) /* I - Client connection */
7379 ippeve_printer_t
*printer
= client
->printer
;
7381 int i
, /* Looping var */
7382 num_supply
; /* Number of supplies */
7383 ipp_attribute_t
*supply
, /* printer-supply attribute */
7384 *supply_desc
; /* printer-supply-description attribute */
7385 int num_options
; /* Number of form options */
7386 cups_option_t
*options
; /* Form options */
7387 int supply_len
, /* Length of supply value */
7388 level
; /* Supply level */
7389 const char *supply_value
; /* Supply value */
7390 char supply_text
[1024], /* Supply string */
7391 *supply_ptr
; /* Pointer into supply string */
7392 static const char * const printer_supply
[] =
7393 { /* printer-supply values */
7394 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
7395 "maxcapacity=100;level=%d;colorantname=unknown;",
7396 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
7397 "maxcapacity=100;level=%d;colorantname=black;",
7398 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
7399 "maxcapacity=100;level=%d;colorantname=cyan;",
7400 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
7401 "maxcapacity=100;level=%d;colorantname=magenta;",
7402 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
7403 "maxcapacity=100;level=%d;colorantname=yellow;"
7405 static const char * const backgrounds
[] =
7406 { /* Background colors for the supply-level bars */
7407 "#777 linear-gradient(#333,#777)",
7408 "#000 linear-gradient(#666,#000)",
7409 "#0FF linear-gradient(#6FF,#0FF)",
7410 "#F0F linear-gradient(#F6F,#F0F)",
7411 "#CC0 linear-gradient(#EE6,#EE0)"
7413 static const char * const colors
[] = /* Text colors for the supply-level bars */
7423 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7426 html_header(client
, printer
->name
, 0);
7428 if ((supply
= ippFindAttribute(printer
->attrs
, "printer-supply", IPP_TAG_STRING
)) == NULL
)
7430 html_printf(client
, "<p>Error: No printer-supply defined for printer.</p>\n");
7431 html_footer(client
);
7435 num_supply
= ippGetCount(supply
);
7437 if ((supply_desc
= ippFindAttribute(printer
->attrs
, "printer-supply-description", IPP_TAG_TEXT
)) == NULL
)
7439 html_printf(client
, "<p>Error: No printer-supply-description defined for printer.</p>\n");
7440 html_footer(client
);
7444 if (num_supply
!= ippGetCount(supply_desc
))
7446 html_printf(client
, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
7447 html_footer(client
);
7451 if (printer
->web_forms
)
7452 num_options
= parse_options(client
, &options
);
7456 if (num_options
> 0)
7459 * WARNING: A real printer/server implementation MUST NOT implement
7460 * supply updates via a GET request - GET requests are supposed to be
7461 * idempotent (without side-effects) and we obviously are not
7462 * authenticating access here. This form is provided solely to
7463 * enable testing and development!
7466 char name
[64]; /* Form field */
7467 const char *val
; /* Form value */
7469 _cupsRWLockWrite(&printer
->rwlock
);
7471 ippDeleteAttribute(printer
->attrs
, supply
);
7474 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
);
7476 for (i
= 0; i
< num_supply
; i
++)
7478 snprintf(name
, sizeof(name
), "supply%d", i
);
7479 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
7481 level
= atoi(val
); /* New level */
7483 snprintf(supply_text
, sizeof(supply_text
), printer_supply
[i
], level
);
7485 ippSetOctetString(printer
->attrs
, &supply
, ippGetCount(supply
), supply_text
, (int)strlen(supply_text
));
7487 supply
= ippAddOctetString(printer
->attrs
, IPP_TAG_PRINTER
, "printer-supply", supply_text
, (int)strlen(supply_text
));
7492 printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_FULL
;
7493 else if (level
> 90)
7494 printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
;
7499 printer
->state_reasons
|= IPPEVE_PREASON_TONER_EMPTY
;
7500 else if (level
< 10)
7501 printer
->state_reasons
|= IPPEVE_PREASON_TONER_LOW
;
7506 _cupsRWUnlock(&printer
->rwlock
);
7509 if (printer
->web_forms
)
7510 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
7512 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
7513 for (i
= 0; i
< num_supply
; i
++)
7515 supply_value
= ippGetOctetString(supply
, i
, &supply_len
);
7516 if (supply_len
> (int)(sizeof(supply_text
) - 1))
7517 supply_len
= (int)sizeof(supply_text
) - 1;
7519 memcpy(supply_text
, supply_value
, (size_t)supply_len
);
7520 supply_text
[supply_len
] = '\0';
7522 if ((supply_ptr
= strstr(supply_text
, "level=")) != NULL
)
7523 level
= atoi(supply_ptr
+ 6);
7527 if (printer
->web_forms
)
7528 html_printf(client
, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc
, i
, NULL
), i
, level
);
7530 html_printf(client
, "<tr><th>%s:</th>", ippGetString(supply_desc
, i
, NULL
));
7533 html_printf(client
, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; padding: 5px %dpx;\"></span> %d%%</td></tr>\n", backgrounds
[i
], level
* 2, level
);
7535 html_printf(client
, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; color: %s; padding: 5px %dpx;\">%d%%</span></td></tr>\n", backgrounds
[i
], colors
[i
], level
* 2, level
);
7538 if (printer
->web_forms
)
7540 html_printf(client
, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
7541 if (num_options
> 0)
7542 html_printf(client
, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
7543 html_printf(client
, "</td></tr>\n</table>\n</form>\n");
7545 if (num_options
> 0)
7546 html_printf(client
, "<script>\n"
7547 "setTimeout(hide_status, 3000);\n"
7548 "function hide_status() {\n"
7549 " var status = document.getElementById('status');\n"
7550 " status.style.display = 'none';\n"
7555 html_printf(client
, "</table>\n");
7557 html_footer(client
);
7564 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7568 time_string(time_t tv
, /* I - Time value */
7569 char *buffer
, /* I - Buffer */
7570 size_t bufsize
) /* I - Size of buffer */
7572 struct tm
*curtime
= localtime(&tv
);
7575 strftime(buffer
, bufsize
, "%X", curtime
);
7581 * 'usage()' - Show program usage.
7585 usage(int status
) /* O - Exit status */
7587 _cupsLangPuts(stdout
, _("Usage: ippeveprinter [options] \"name\""));
7588 _cupsLangPuts(stdout
, _("Options:"));
7589 _cupsLangPuts(stderr
, _("--help Show program help"));
7590 _cupsLangPuts(stderr
, _("--no-web-forms Disable web forms for media and supplies"));
7591 _cupsLangPuts(stderr
, _("--version Show program version"));
7592 _cupsLangPuts(stdout
, _("-2 Set 2-sided printing support (default=1-sided)"));
7593 _cupsLangPuts(stdout
, _("-D device-uri Set the device URI for the printer"));
7595 _cupsLangPuts(stdout
, _("-K keypath Set location of server X.509 certificates and keys."));
7596 #endif /* HAVE_SSL */
7597 _cupsLangPuts(stdout
, _("-M manufacturer Set manufacturer name (default=Test)"));
7598 _cupsLangPuts(stdout
, _("-P filename.ppd Load printer attributes from PPD file"));
7599 _cupsLangPuts(stdout
, _("-V version Set default IPP version"));
7600 _cupsLangPuts(stdout
, _("-a filename.conf Load printer attributes from conf file"));
7601 _cupsLangPuts(stdout
, _("-c command Set print command"));
7602 _cupsLangPuts(stdout
, _("-d spool-directory Set spool directory"));
7603 _cupsLangPuts(stdout
, _("-f type/subtype[,...] Set supported file types"));
7604 _cupsLangPuts(stdout
, _("-i iconfile.png Set icon file"));
7605 _cupsLangPuts(stdout
, _("-k Keep job spool files"));
7606 _cupsLangPuts(stdout
, _("-l location Set location of printer"));
7607 _cupsLangPuts(stdout
, _("-m model Set model name (default=Printer)"));
7608 _cupsLangPuts(stdout
, _("-n hostname Set hostname for printer"));
7609 _cupsLangPuts(stdout
, _("-p port Set port number for printer"));
7610 _cupsLangPuts(stdout
, _("-r subtype,[subtype] Set DNS-SD service subtype"));
7611 _cupsLangPuts(stdout
, _("-s speed[,color-speed] Set speed in pages per minute"));
7612 _cupsLangPuts(stderr
, _("-v Be verbose"));
7619 * 'valid_doc_attributes()' - Determine whether the document attributes are
7622 * When one or more document attributes are invalid, this function adds a
7623 * suitable response and attributes to the unsupported group.
7626 static int /* O - 1 if valid, 0 if not */
7627 valid_doc_attributes(
7628 ippeve_client_t
*client
) /* I - Client */
7630 int valid
= 1; /* Valid attributes? */
7631 ipp_op_t op
= ippGetOperation(client
->request
);
7633 const char *op_name
= ippOpString(op
);
7634 /* IPP operation name */
7635 ipp_attribute_t
*attr
, /* Current attribute */
7636 *supported
; /* xxx-supported attribute */
7637 const char *compression
= NULL
,
7638 /* compression value */
7639 *format
= NULL
; /* document-format value */
7643 * Check operation attributes...
7646 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
7649 * If compression is specified, only accept a supported value in a Print-Job
7650 * or Send-Document request...
7653 compression
= ippGetString(attr
, 0, NULL
);
7654 supported
= ippFindAttribute(client
->printer
->attrs
,
7655 "compression-supported", IPP_TAG_KEYWORD
);
7657 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7658 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
7659 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
7660 op
!= IPP_OP_VALIDATE_JOB
) ||
7661 !ippContainsString(supported
, compression
))
7663 respond_unsupported(client
, attr
);
7668 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
7670 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
7672 if (strcmp(compression
, "none"))
7675 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
7676 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
7682 * Is it a format we support?
7685 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
7687 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
7688 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
7690 respond_unsupported(client
, attr
);
7695 format
= ippGetString(attr
, 0, NULL
);
7697 fprintf(stderr
, "%s %s document-format=\"%s\"\n", client
->hostname
, op_name
, format
);
7699 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
7704 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
7706 format
= "application/octet-stream"; /* Should never happen */
7708 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
7711 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7714 * Auto-type the file using the first 8 bytes of the file...
7717 unsigned char header
[8]; /* First 8 bytes of file */
7719 memset(header
, 0, sizeof(header
));
7720 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7722 if (!memcmp(header
, "%PDF", 4))
7723 format
= "application/pdf";
7724 else if (!memcmp(header
, "%!", 2))
7725 format
= "application/postscript";
7726 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7727 format
= "image/jpeg";
7728 else if (!memcmp(header
, "\211PNG", 4))
7729 format
= "image/png";
7730 else if (!memcmp(header
, "RAS2", 4))
7731 format
= "image/pwg-raster";
7732 else if (!memcmp(header
, "UNIRAST", 8))
7733 format
= "image/urf";
7739 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n", client
->hostname
, op_name
, format
);
7741 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7745 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7747 respond_unsupported(client
, attr
);
7755 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7756 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7763 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7765 * When one or more job attributes are invalid, this function adds a suitable
7766 * response and attributes to the unsupported group.
7769 static int /* O - 1 if valid, 0 if not */
7770 valid_job_attributes(
7771 ippeve_client_t
*client
) /* I - Client */
7773 int i
, /* Looping var */
7774 count
, /* Number of values */
7775 valid
= 1; /* Valid attributes? */
7776 ipp_attribute_t
*attr
, /* Current attribute */
7777 *supported
; /* xxx-supported attribute */
7781 * Check operation attributes...
7784 valid
= valid_doc_attributes(client
);
7787 * Check the various job template attributes...
7790 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7792 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7793 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7795 respond_unsupported(client
, attr
);
7800 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7802 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7804 respond_unsupported(client
, attr
);
7809 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7811 if (ippGetCount(attr
) != 1 ||
7812 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7813 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7814 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7815 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7817 respond_unsupported(client
, attr
);
7822 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7824 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7826 respond_unsupported(client
, attr
);
7831 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7833 if (ippGetCount(attr
) != 1 ||
7834 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7835 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7837 respond_unsupported(client
, attr
);
7841 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7844 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7846 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7848 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7849 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7851 respond_unsupported(client
, attr
);
7856 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7858 if (ippGetCount(attr
) != 1 ||
7859 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7860 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7861 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7862 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7864 respond_unsupported(client
, attr
);
7869 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7871 if (ippGetCount(attr
) != 1 ||
7872 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7873 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7874 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7876 respond_unsupported(client
, attr
);
7881 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7883 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7885 respond_unsupported(client
, attr
);
7891 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7893 ipp_t
*col
, /* media-col collection */
7894 *size
; /* media-size collection */
7895 ipp_attribute_t
*member
, /* Member attribute */
7896 *x_dim
, /* x-dimension */
7897 *y_dim
; /* y-dimension */
7898 int x_value
, /* y-dimension value */
7899 y_value
; /* x-dimension value */
7901 if (ippGetCount(attr
) != 1 ||
7902 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7904 respond_unsupported(client
, attr
);
7908 col
= ippGetCollection(attr
, 0);
7910 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7912 if (ippGetCount(member
) != 1 ||
7913 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7914 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7915 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7917 respond_unsupported(client
, attr
);
7922 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7924 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7926 respond_unsupported(client
, attr
);
7931 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7933 if (ippGetCount(member
) != 1)
7935 respond_unsupported(client
, attr
);
7940 size
= ippGetCollection(member
, 0);
7942 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7943 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
7945 respond_unsupported(client
, attr
);
7950 x_value
= ippGetInteger(x_dim
, 0);
7951 y_value
= ippGetInteger(y_dim
, 0);
7952 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
7953 count
= ippGetCount(supported
);
7955 for (i
= 0; i
< count
; i
++)
7957 size
= ippGetCollection(supported
, i
);
7958 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
7959 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
7961 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
7967 respond_unsupported(client
, attr
);
7975 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7977 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7978 (strcmp(ippGetString(attr
, 0, NULL
),
7979 "separate-documents-uncollated-copies") &&
7980 strcmp(ippGetString(attr
, 0, NULL
),
7981 "separate-documents-collated-copies")))
7983 respond_unsupported(client
, attr
);
7988 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7990 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7991 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7992 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7994 respond_unsupported(client
, attr
);
7999 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
8001 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
8003 respond_unsupported(client
, attr
);
8008 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
8010 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
8011 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
8012 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
8014 respond_unsupported(client
, attr
);
8019 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
8021 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
8023 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
8026 respond_unsupported(client
, attr
);
8031 int xdpi
, /* Horizontal resolution for job template attribute */
8032 ydpi
, /* Vertical resolution for job template attribute */
8033 sydpi
; /* Vertical resolution for supported value */
8034 ipp_res_t units
, /* Units for job template attribute */
8035 sunits
; /* Units for supported value */
8037 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
8038 count
= ippGetCount(supported
);
8040 for (i
= 0; i
< count
; i
++)
8042 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
8048 respond_unsupported(client
, attr
);
8054 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
8056 const char *sides
= ippGetString(attr
, 0, NULL
);
8057 /* "sides" value... */
8059 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
8061 respond_unsupported(client
, attr
);
8064 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
8066 if (!ippContainsString(supported
, sides
))
8068 respond_unsupported(client
, attr
);
8072 else if (strcmp(sides
, "one-sided"))
8074 respond_unsupported(client
, attr
);