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>
22 #include <cups/debug-private.h>
24 # include <cups/ppd-private.h>
25 #endif /* !CUPS_LITE */
34 # define WEXITSTATUS(s) (s)
35 # include <winsock2.h>
39 extern char **environ
;
41 # include <sys/fcntl.h>
42 # include <sys/wait.h>
48 #elif defined(HAVE_AVAHI)
49 # include <avahi-client/client.h>
50 # include <avahi-client/publish.h>
51 # include <avahi-common/error.h>
52 # include <avahi-common/thread-watch.h>
53 #endif /* HAVE_DNSSD */
54 #ifdef HAVE_SYS_MOUNT_H
55 # include <sys/mount.h>
56 #endif /* HAVE_SYS_MOUNT_H */
57 #ifdef HAVE_SYS_STATFS_H
58 # include <sys/statfs.h>
59 #endif /* HAVE_SYS_STATFS_H */
60 #ifdef HAVE_SYS_STATVFS_H
61 # include <sys/statvfs.h>
62 #endif /* HAVE_SYS_STATVFS_H */
65 #endif /* HAVE_SYS_VFS_H */
67 #include "printer-png.h"
74 enum ippeve_preason_e
/* printer-state-reasons bit values */
76 IPPEVE_PREASON_NONE
= 0x0000, /* none */
77 IPPEVE_PREASON_OTHER
= 0x0001, /* other */
78 IPPEVE_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
79 IPPEVE_PREASON_INPUT_TRAY_MISSING
= 0x0004,
80 /* input-tray-missing */
81 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
82 /* marker-supply-empty */
83 IPPEVE_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
84 /* marker-supply-low */
85 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
86 /* marker-waste-almost-full */
87 IPPEVE_PREASON_MARKER_WASTE_FULL
= 0x0040,
88 /* marker-waste-full */
89 IPPEVE_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
90 IPPEVE_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
91 IPPEVE_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
92 IPPEVE_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
93 IPPEVE_PREASON_MOVING_TO_PAUSED
= 0x0800,
94 /* moving-to-paused */
95 IPPEVE_PREASON_PAUSED
= 0x1000, /* paused */
96 IPPEVE_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
97 IPPEVE_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
98 IPPEVE_PREASON_TONER_LOW
= 0x8000 /* toner-low */
100 typedef unsigned int ippeve_preason_t
; /* Bitfield for printer-state-reasons */
101 static const char * const ippeve_preason_strings
[] =
102 { /* Strings for each bit */
103 /* "none" is implied for no bits set */
106 "input-tray-missing",
107 "marker-supply-empty",
109 "marker-waste-almost-full",
124 * URL scheme for web resources...
128 # define WEB_SCHEME "https"
130 # define WEB_SCHEME "http"
131 #endif /* HAVE_SSL */
139 typedef DNSServiceRef ippeve_srv_t
; /* Service reference */
140 typedef TXTRecordRef ippeve_txt_t
; /* TXT record */
142 #elif defined(HAVE_AVAHI)
143 typedef AvahiEntryGroup
*ippeve_srv_t
; /* Service reference */
144 typedef AvahiStringList
*ippeve_txt_t
; /* TXT record */
147 typedef void *ippeve_srv_t
; /* Service reference */
148 typedef void *ippeve_txt_t
; /* TXT record */
149 #endif /* HAVE_DNSSD */
151 typedef struct ippeve_filter_s
/**** Attribute filter ****/
153 cups_array_t
*ra
; /* Requested attributes */
154 ipp_tag_t group_tag
; /* Group to copy */
157 typedef struct ippeve_job_s ippeve_job_t
;
159 typedef struct ippeve_printer_s
/**** Printer data ****/
161 /* TODO: One IPv4 and one IPv6 listener are really not sufficient */
162 int ipv4
, /* IPv4 listener */
163 ipv6
; /* IPv6 listener */
164 ippeve_srv_t ipp_ref
, /* Bonjour IPP service */
165 ipps_ref
, /* Bonjour IPPS service */
166 http_ref
, /* Bonjour HTTP service */
167 printer_ref
; /* Bonjour LPD service */
168 char *dnssd_name
, /* printer-dnssd-name */
169 *name
, /* printer-name */
170 *icon
, /* Icon filename */
171 *directory
, /* Spool directory */
172 *hostname
, /* Hostname */
173 *uri
, /* printer-uri-supported */
174 *device_uri
, /* Device URI (if any) */
176 *ppdfile
, /* PPD file (if any) */
177 #endif /* !CUPS_LITE */
178 *command
; /* Command to run with job file */
180 int web_forms
; /* Enable web interface forms? */
181 size_t urilen
; /* Length of printer URI */
182 ipp_t
*attrs
; /* Static attributes */
183 time_t start_time
; /* Startup time */
184 time_t config_time
; /* printer-config-change-time */
185 ipp_pstate_t state
; /* printer-state value */
186 ippeve_preason_t state_reasons
; /* printer-state-reasons values */
187 time_t state_time
; /* printer-state-change-time */
188 cups_array_t
*jobs
; /* Jobs */
189 ippeve_job_t
*active_job
; /* Current active/pending job */
190 int next_job_id
; /* Next job-id value */
191 _cups_rwlock_t rwlock
; /* Printer lock */
194 struct ippeve_job_s
/**** Job data ****/
197 const char *name
, /* job-name */
198 *username
, /* job-originating-user-name */
199 *format
; /* document-format */
200 ipp_jstate_t state
; /* job-state value */
201 char *message
; /* job-state-message value */
202 int msglevel
; /* job-state-message log level (0=error, 1=info) */
203 time_t created
, /* time-at-creation value */
204 processing
, /* time-at-processing value */
205 completed
; /* time-at-completed value */
206 int impressions
, /* job-impressions value */
207 impcompleted
; /* job-impressions-completed value */
208 ipp_t
*attrs
; /* Static attributes */
209 int cancel
; /* Non-zero when job canceled */
210 char *filename
; /* Print file name */
211 int fd
; /* Print file descriptor */
212 ippeve_printer_t
*printer
; /* Printer */
215 typedef struct ippeve_client_s
/**** Client data ****/
217 http_t
*http
; /* HTTP connection */
218 ipp_t
*request
, /* IPP request */
219 *response
; /* IPP response */
220 time_t start
; /* Request start time */
221 http_state_t operation
; /* Request operation */
222 ipp_op_t operation_id
; /* IPP operation-id */
223 char uri
[1024], /* Request URI */
224 *options
; /* URI options */
225 http_addr_t addr
; /* Client address */
226 char hostname
[256]; /* Client hostname */
227 ippeve_printer_t
*printer
; /* Printer */
228 ippeve_job_t
*job
; /* Current job, if any */
236 static void clean_jobs(ippeve_printer_t
*printer
);
237 static int compare_jobs(ippeve_job_t
*a
, ippeve_job_t
*b
);
238 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
, ipp_tag_t group_tag
, int quickcopy
);
239 static void copy_job_attributes(ippeve_client_t
*client
, ippeve_job_t
*job
, cups_array_t
*ra
);
240 static ippeve_client_t
*create_client(ippeve_printer_t
*printer
, int sock
);
241 static ippeve_job_t
*create_job(ippeve_client_t
*client
);
242 static int create_job_file(ippeve_job_t
*job
, char *fname
, size_t fnamesize
, const char *dir
, const char *ext
);
243 static int create_listener(const char *name
, int port
, int family
);
244 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
);
245 static ipp_t
*create_media_size(int width
, int length
);
246 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
);
247 static void debug_attributes(const char *title
, ipp_t
*ipp
, int response
);
248 static void delete_client(ippeve_client_t
*client
);
249 static void delete_job(ippeve_job_t
*job
);
250 static void delete_printer(ippeve_printer_t
*printer
);
252 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
);
253 #elif defined(HAVE_AVAHI)
254 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
255 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
256 #endif /* HAVE_DNSSD */
257 static void dnssd_init(void);
258 static int filter_cb(ippeve_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
259 static ippeve_job_t
*find_job(ippeve_client_t
*client
);
260 static void finish_document_data(ippeve_client_t
*client
, ippeve_job_t
*job
);
261 static void finish_document_uri(ippeve_client_t
*client
, ippeve_job_t
*job
);
262 static void html_escape(ippeve_client_t
*client
, const char *s
, size_t slen
);
263 static void html_footer(ippeve_client_t
*client
);
264 static void html_header(ippeve_client_t
*client
, const char *title
, int refresh
);
265 static void html_printf(ippeve_client_t
*client
, const char *format
, ...) _CUPS_FORMAT(2, 3);
266 static void ipp_cancel_job(ippeve_client_t
*client
);
267 static void ipp_close_job(ippeve_client_t
*client
);
268 static void ipp_create_job(ippeve_client_t
*client
);
269 static void ipp_get_job_attributes(ippeve_client_t
*client
);
270 static void ipp_get_jobs(ippeve_client_t
*client
);
271 static void ipp_get_printer_attributes(ippeve_client_t
*client
);
272 static void ipp_identify_printer(ippeve_client_t
*client
);
273 static void ipp_print_job(ippeve_client_t
*client
);
274 static void ipp_print_uri(ippeve_client_t
*client
);
275 static void ipp_send_document(ippeve_client_t
*client
);
276 static void ipp_send_uri(ippeve_client_t
*client
);
277 static void ipp_validate_job(ippeve_client_t
*client
);
278 static ipp_t
*load_ippserver_attributes(const char *servername
, int serverport
, const char *filename
, cups_array_t
*docformats
);
279 static ipp_t
*load_legacy_attributes(const char *make
, const char *model
, int ppm
, int ppm_color
, int duplex
, cups_array_t
*docformats
);
281 static ipp_t
*load_ppd_attributes(const char *ppdfile
, cups_array_t
*docformats
);
282 #endif /* !CUPS_LITE */
283 static int parse_options(ippeve_client_t
*client
, cups_option_t
**options
);
284 static void process_attr_message(ippeve_job_t
*job
, char *message
);
285 static void *process_client(ippeve_client_t
*client
);
286 static int process_http(ippeve_client_t
*client
);
287 static int process_ipp(ippeve_client_t
*client
);
288 static void *process_job(ippeve_job_t
*job
);
289 static void process_state_message(ippeve_job_t
*job
, char *message
);
290 static int register_printer(ippeve_printer_t
*printer
, const char *subtypes
);
291 static int respond_http(ippeve_client_t
*client
, http_status_t code
, const char *content_coding
, const char *type
, size_t length
);
292 static void respond_ipp(ippeve_client_t
*client
, ipp_status_t status
, const char *message
, ...) _CUPS_FORMAT(3, 4);
293 static void respond_unsupported(ippeve_client_t
*client
, ipp_attribute_t
*attr
);
294 static void run_printer(ippeve_printer_t
*printer
);
295 static int show_media(ippeve_client_t
*client
);
296 static int show_status(ippeve_client_t
*client
);
297 static int show_supplies(ippeve_client_t
*client
);
298 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
299 static void usage(int status
) _CUPS_NORETURN
;
300 static int valid_doc_attributes(ippeve_client_t
*client
);
301 static int valid_job_attributes(ippeve_client_t
*client
);
309 static DNSServiceRef DNSSDMaster
= NULL
;
310 #elif defined(HAVE_AVAHI)
311 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
312 static AvahiClient
*DNSSDClient
= NULL
;
313 #endif /* HAVE_DNSSD */
315 static int KeepFiles
= 0, /* Keep spooled job files? */
316 MaxVersion
= 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
317 Verbosity
= 0; /* Verbosity level */
321 * 'main()' - Main entry to the sample server.
324 int /* O - Exit status */
325 main(int argc
, /* I - Number of command-line args */
326 char *argv
[]) /* I - Command-line arguments */
328 int i
; /* Looping var */
329 const char *opt
, /* Current option character */
330 *attrfile
= NULL
, /* ippserver attributes file */
331 *command
= NULL
, /* Command to run with job files */
332 *device_uri
= NULL
, /* Device URI */
333 *icon
= NULL
, /* Icon file */
335 *keypath
= NULL
, /* Keychain path */
336 #endif /* HAVE_SSL */
337 *location
= "", /* Location of printer */
338 *make
= "Example", /* Manufacturer */
339 *model
= "Printer", /* Model */
340 *name
= NULL
, /* Printer name */
342 *ppdfile
= NULL
, /* PPD file */
343 #endif /* !CUPS_LITE */
344 *subtypes
= "_print"; /* DNS-SD service subtype */
345 int legacy
= 0, /* Legacy mode? */
346 duplex
= 0, /* Duplex mode */
347 ppm
= 10, /* Pages per minute for mono */
348 ppm_color
= 0, /* Pages per minute for color */
349 web_forms
= 1; /* Enable web site forms? */
350 ipp_t
*attrs
= NULL
; /* Printer attributes */
351 char directory
[1024] = ""; /* Spool directory */
352 cups_array_t
*docformats
= NULL
; /* Supported formats */
353 const char *servername
= NULL
; /* Server host name */
354 int serverport
= 0; /* Server port number (0 = auto) */
355 ippeve_printer_t
*printer
; /* Printer object */
359 * Parse command-line arguments...
362 for (i
= 1; i
< argc
; i
++)
364 if (!strcmp(argv
[i
], "--help"))
368 else if (!strcmp(argv
[i
], "--no-web-forms"))
372 else if (!strcmp(argv
[i
], "--version"))
377 else if (!strncmp(argv
[i
], "--", 2))
379 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
382 else if (argv
[i
][0] == '-')
384 for (opt
= argv
[i
] + 1; *opt
; opt
++)
388 case '2' : /* -2 (enable 2-sided printing) */
393 case 'D' : /* -D device-uri */
398 device_uri
= argv
[i
];
402 case 'K' : /* -K keypath */
409 #endif /* HAVE_SSL */
411 case 'M' : /* -M manufacturer */
421 case 'P' : /* -P filename.ppd */
428 #endif /* !CUPS_LITE */
430 case 'V' : /* -V max-version */
435 if (!strcmp(argv
[i
], "2.0"))
437 else if (!strcmp(argv
[i
], "1.1"))
443 case 'a' : /* -a attributes-file */
451 case 'c' : /* -c command */
459 case 'd' : /* -d spool-directory */
464 strlcpy(directory
, argv
[i
], sizeof(directory
));
467 case 'f' : /* -f type/subtype[,...] */
472 docformats
= _cupsArrayNewStrings(argv
[i
], ',');
476 case 'i' : /* -i icon.png */
484 case 'k' : /* -k (keep files) */
488 case 'l' : /* -l location */
496 case 'm' : /* -m model */
505 case 'n' : /* -n hostname */
510 servername
= argv
[i
];
513 case 'p' : /* -p port */
515 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
518 serverport
= atoi(argv
[i
]);
521 case 'r' : /* -r subtype */
529 case 's' : /* -s speed[,color-speed] */
534 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
540 case 'v' : /* -v (be verbose) */
544 default : /* Unknown */
545 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), argv
[0], *opt
);
556 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
565 if (attrfile
!= NULL
&& legacy
)
568 if (((ppdfile
!= NULL
) + (attrfile
!= NULL
) + legacy
) > 1)
570 #endif /* CUPS_LITE */
573 * Apply defaults as needed...
580 * Windows is almost always used as a single user system, so use a default
581 * port number of 8631.
588 * Use 8000 + UID mod 1000 for the default port number...
591 serverport
= 8000 + ((int)getuid() % 1000);
594 _cupsLangPrintf(stderr
, _("Listening on port %d."), serverport
);
599 const char *tmpdir
; /* Temporary directory */
602 if ((tmpdir
= getenv("TEMP")) == NULL
)
604 #elif defined(__APPLE__) && TARGET_OS_OSX
605 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
606 tmpdir
= "/private/tmp";
608 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
612 snprintf(directory
, sizeof(directory
), "%s/ippeveprinter.%d", tmpdir
, (int)getpid());
614 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
616 _cupsLangPrintf(stderr
, _("Unable to create spool directory \"%s\": %s"), directory
, strerror(errno
));
621 _cupsLangPrintf(stderr
, _("Using spool directory \"%s\"."), directory
);
625 cupsSetServerCredentials(keypath
, servername
, 1);
626 #endif /* HAVE_SSL */
629 * Initialize DNS-SD...
635 * Create the printer...
639 docformats
= _cupsArrayNewStrings(ppm_color
> 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ',');
642 attrs
= load_ippserver_attributes(servername
, serverport
, attrfile
, docformats
);
646 attrs
= load_ppd_attributes(ppdfile
, docformats
);
649 command
= "ippeveps";
651 #endif /* !CUPS_LITE */
653 attrs
= load_legacy_attributes(make
, model
, ppm
, ppm_color
, duplex
, docformats
);
655 if ((printer
= create_printer(servername
, serverport
, name
, location
, icon
, docformats
, subtypes
, directory
, command
, device_uri
, attrs
)) == NULL
)
658 printer
->web_forms
= web_forms
;
662 printer
->ppdfile
= strdup(ppdfile
);
663 #endif /* !CUPS_LITE */
666 * Run the print service...
669 run_printer(printer
);
672 * Destroy the printer and exit...
675 delete_printer(printer
);
682 * 'clean_jobs()' - Clean out old (completed) jobs.
686 clean_jobs(ippeve_printer_t
*printer
) /* I - Printer */
688 ippeve_job_t
*job
; /* Current job */
689 time_t cleantime
; /* Clean time */
692 if (cupsArrayCount(printer
->jobs
) == 0)
695 cleantime
= time(NULL
) - 60;
697 _cupsRWLockWrite(&(printer
->rwlock
));
698 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
);
700 job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
701 if (job
->completed
&& job
->completed
< cleantime
)
703 cupsArrayRemove(printer
->jobs
, job
);
708 _cupsRWUnlock(&(printer
->rwlock
));
713 * 'compare_jobs()' - Compare two jobs.
716 static int /* O - Result of comparison */
717 compare_jobs(ippeve_job_t
*a
, /* I - First job */
718 ippeve_job_t
*b
) /* I - Second job */
720 return (b
->id
- a
->id
);
725 * 'copy_attributes()' - Copy attributes from one request to another.
729 copy_attributes(ipp_t
*to
, /* I - Destination request */
730 ipp_t
*from
, /* I - Source request */
731 cups_array_t
*ra
, /* I - Requested attributes */
732 ipp_tag_t group_tag
, /* I - Group to copy */
733 int quickcopy
) /* I - Do a quick copy? */
735 ippeve_filter_t filter
; /* Filter data */
739 filter
.group_tag
= group_tag
;
741 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
746 * 'copy_job_attrs()' - Copy job attributes to the response.
751 ippeve_client_t
*client
, /* I - Client */
752 ippeve_job_t
*job
, /* I - Job */
753 cups_array_t
*ra
) /* I - requested-attributes */
755 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
757 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
760 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
762 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
765 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
768 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
770 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
773 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
774 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
776 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
777 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
779 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
780 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
782 if (!ra
|| cupsArrayFind(ra
, "job-state"))
783 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state", (int)job
->state
);
785 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
789 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_TAG_TEXT
, "job-state-message", NULL
, job
->message
);
795 case IPP_JSTATE_PENDING
:
796 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
799 case IPP_JSTATE_HELD
:
801 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
802 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
803 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
805 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
808 case IPP_JSTATE_PROCESSING
:
810 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
812 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
815 case IPP_JSTATE_STOPPED
:
816 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
819 case IPP_JSTATE_CANCELED
:
820 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
823 case IPP_JSTATE_ABORTED
:
824 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
827 case IPP_JSTATE_COMPLETED
:
828 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
834 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
838 case IPP_JSTATE_PENDING
:
839 ippAddString(client
->response
, IPP_TAG_JOB
,
840 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
844 case IPP_JSTATE_HELD
:
846 ippAddString(client
->response
, IPP_TAG_JOB
,
847 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
848 "job-state-reasons", NULL
, "job-incoming");
849 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
850 ippAddString(client
->response
, IPP_TAG_JOB
,
851 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
852 "job-state-reasons", NULL
, "job-hold-until-specified");
854 ippAddString(client
->response
, IPP_TAG_JOB
,
855 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
856 "job-state-reasons", NULL
, "job-data-insufficient");
859 case IPP_JSTATE_PROCESSING
:
861 ippAddString(client
->response
, IPP_TAG_JOB
,
862 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
863 "job-state-reasons", NULL
, "processing-to-stop-point");
865 ippAddString(client
->response
, IPP_TAG_JOB
,
866 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
867 "job-state-reasons", NULL
, "job-printing");
870 case IPP_JSTATE_STOPPED
:
871 ippAddString(client
->response
, IPP_TAG_JOB
,
872 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
873 NULL
, "job-stopped");
876 case IPP_JSTATE_CANCELED
:
877 ippAddString(client
->response
, IPP_TAG_JOB
,
878 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
879 NULL
, "job-canceled-by-user");
882 case IPP_JSTATE_ABORTED
:
883 ippAddString(client
->response
, IPP_TAG_JOB
,
884 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
885 NULL
, "aborted-by-system");
888 case IPP_JSTATE_COMPLETED
:
889 ippAddString(client
->response
, IPP_TAG_JOB
,
890 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
891 NULL
, "job-completed-successfully");
896 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
897 ippAddInteger(client
->response
, IPP_TAG_JOB
,
898 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
899 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
901 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
902 ippAddInteger(client
->response
, IPP_TAG_JOB
,
903 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
904 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
909 * 'create_client()' - Accept a new network connection and create a client
913 static ippeve_client_t
* /* O - Client */
914 create_client(ippeve_printer_t
*printer
, /* I - Printer */
915 int sock
) /* I - Listen socket */
917 ippeve_client_t
*client
; /* Client */
920 if ((client
= calloc(1, sizeof(ippeve_client_t
))) == NULL
)
922 perror("Unable to allocate memory for client");
926 client
->printer
= printer
;
929 * Accept the client and get the remote address...
932 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
934 perror("Unable to accept client connection");
941 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
944 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
951 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
955 static ippeve_job_t
* /* O - Job */
956 create_job(ippeve_client_t
*client
) /* I - Client */
958 ippeve_job_t
*job
; /* Job */
959 ipp_attribute_t
*attr
; /* Job attribute */
960 char uri
[1024], /* job-uri value */
961 uuid
[64]; /* job-uuid value */
964 _cupsRWLockWrite(&(client
->printer
->rwlock
));
965 if (client
->printer
->active_job
&&
966 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
969 * Only accept a single job at a time...
972 _cupsRWUnlock(&(client
->printer
->rwlock
));
977 * Allocate and initialize the job object...
980 if ((job
= calloc(1, sizeof(ippeve_job_t
))) == NULL
)
982 perror("Unable to allocate memory for job");
986 job
->printer
= client
->printer
;
987 job
->attrs
= ippNew();
988 job
->state
= IPP_JSTATE_HELD
;
992 * Copy all of the job attributes...
995 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
998 * Get the requesting-user-name, document format, and priority...
1001 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1002 job
->username
= ippGetString(attr
, 0, NULL
);
1004 job
->username
= "anonymous";
1006 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1008 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1010 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1011 job
->format
= ippGetString(attr
, 0, NULL
);
1012 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1013 job
->format
= ippGetString(attr
, 0, NULL
);
1015 job
->format
= "application/octet-stream";
1018 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1019 job
->impressions
= ippGetInteger(attr
, 0);
1021 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1022 job
->name
= ippGetString(attr
, 0, NULL
);
1025 * Add job description attributes and add to the jobs array...
1028 job
->id
= client
->printer
->next_job_id
++;
1030 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1031 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1033 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1034 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1035 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1036 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1037 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1038 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1040 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1041 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1043 cupsArrayAdd(client
->printer
->jobs
, job
);
1044 client
->printer
->active_job
= job
;
1046 _cupsRWUnlock(&(client
->printer
->rwlock
));
1053 * 'create_job_file()' - Create a file for the document in a job.
1056 static int /* O - File descriptor or -1 on error */
1058 ippeve_job_t
*job
, /* I - Job */
1059 char *fname
, /* I - Filename buffer */
1060 size_t fnamesize
, /* I - Size of filename buffer */
1061 const char *directory
, /* I - Directory to store in */
1062 const char *ext
) /* I - Extension (`NULL` for default) */
1064 char name
[256], /* "Safe" filename */
1065 *nameptr
; /* Pointer into filename */
1066 const char *job_name
; /* job-name value */
1070 * Make a name from the job-name attribute...
1073 if ((job_name
= ippGetString(ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
), 0, NULL
)) == NULL
)
1074 job_name
= "untitled";
1076 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1078 if (isalnum(*job_name
& 255) || *job_name
== '-')
1080 *nameptr
++ = (char)tolower(*job_name
& 255);
1086 while (job_name
[1] && !isalnum(job_name
[1] & 255) && job_name
[1] != '-')
1094 * Figure out the extension...
1099 if (!strcasecmp(job
->format
, "image/jpeg"))
1101 else if (!strcasecmp(job
->format
, "image/png"))
1103 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1105 else if (!strcasecmp(job
->format
, "image/urf"))
1107 else if (!strcasecmp(job
->format
, "application/pdf"))
1109 else if (!strcasecmp(job
->format
, "application/postscript"))
1111 else if (!strcasecmp(job
->format
, "application/vnd.hp-pcl"))
1118 * Create a filename with the job-id, job-name, and document-format (extension)...
1121 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", directory
, job
->id
, name
, ext
);
1123 return (open(fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666));
1128 * 'create_listener()' - Create a listener socket.
1131 static int /* O - Listener socket or -1 on error */
1132 create_listener(const char *name
, /* I - Host name (`NULL` for any address) */
1133 int port
, /* I - Port number */
1134 int family
) /* I - Address family */
1136 int sock
; /* Listener socket */
1137 http_addrlist_t
*addrlist
; /* Listen address */
1138 char service
[255]; /* Service port */
1141 snprintf(service
, sizeof(service
), "%d", port
);
1142 if ((addrlist
= httpAddrGetList(name
, family
, service
)) == NULL
)
1145 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1147 httpAddrFreeList(addrlist
);
1154 * 'create_media_col()' - Create a media-col value.
1157 static ipp_t
* /* O - media-col collection */
1158 create_media_col(const char *media
, /* I - Media name */
1159 const char *source
, /* I - Media source, if any */
1160 const char *type
, /* I - Media type, if any */
1161 int width
, /* I - x-dimension in 2540ths */
1162 int length
, /* I - y-dimension in 2540ths */
1163 int bottom
, /* I - Bottom margin in 2540ths */
1164 int left
, /* I - Left margin in 2540ths */
1165 int right
, /* I - Right margin in 2540ths */
1166 int top
) /* I - Top margin in 2540ths */
1168 ipp_t
*media_col
= ippNew(), /* media-col value */
1169 *media_size
= create_media_size(width
, length
);
1170 /* media-size value */
1171 char media_key
[256]; /* media-key value */
1172 const char *media_key_suffix
= ""; /* media-key suffix */
1175 if (bottom
== 0 && left
== 0 && right
== 0 && top
== 0)
1176 media_key_suffix
= "_borderless";
1179 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, media_key_suffix
);
1181 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, media_key_suffix
);
1183 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, media_key_suffix
);
1185 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, media_key_suffix
);
1187 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
, media_key
);
1188 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1189 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1191 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin", bottom
);
1193 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin", left
);
1195 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin", right
);
1197 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin", top
);
1199 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1201 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1203 ippDelete(media_size
);
1210 * 'create_media_size()' - Create a media-size value.
1213 static ipp_t
* /* O - media-col collection */
1214 create_media_size(int width
, /* I - x-dimension in 2540ths */
1215 int length
) /* I - y-dimension in 2540ths */
1217 ipp_t
*media_size
= ippNew(); /* media-size value */
1220 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension", width
);
1221 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension", length
);
1223 return (media_size
);
1228 * 'create_printer()' - Create, register, and listen for connections to a
1232 static ippeve_printer_t
* /* O - Printer */
1234 const char *servername
, /* I - Server hostname (NULL for default) */
1235 int serverport
, /* I - Server port */
1236 const char *name
, /* I - printer-name */
1237 const char *location
, /* I - printer-location */
1238 const char *icon
, /* I - printer-icons */
1239 cups_array_t
*docformats
, /* I - document-format-supported */
1240 const char *subtypes
, /* I - Bonjour service subtype(s) */
1241 const char *directory
, /* I - Spool directory */
1242 const char *command
, /* I - Command to run on job files, if any */
1243 const char *device_uri
, /* I - Output device, if any */
1244 ipp_t
*attrs
) /* I - Capability attributes */
1246 ippeve_printer_t
*printer
; /* Printer */
1247 int i
; /* Looping var */
1249 char path
[1024]; /* Full path to command */
1250 #endif /* !_WIN32 */
1251 char uri
[1024], /* Printer URI */
1253 securi
[1024], /* Secure printer URI */
1254 *uris
[2], /* All URIs */
1255 #endif /* HAVE_SSL */
1256 icons
[1024], /* printer-icons URI */
1257 adminurl
[1024], /* printer-more-info URI */
1258 supplyurl
[1024],/* printer-supply-info-uri URI */
1259 uuid
[128]; /* printer-uuid */
1260 int k_supported
; /* Maximum file size supported */
1261 int num_formats
; /* Number of supported document formats */
1262 const char *formats
[100], /* Supported document formats */
1263 *format
; /* Current format */
1264 int num_sup_attrs
; /* Number of supported attributes */
1265 const char *sup_attrs
[100];/* Supported attributes */
1266 char xxx_supported
[256];
1267 /* Name of -supported attribute */
1268 _cups_globals_t
*cg
= _cupsGlobals();
1269 /* Global path values */
1271 struct statvfs spoolinfo
; /* FS info for spool directory */
1272 double spoolsize
; /* FS size */
1273 #elif defined(HAVE_STATFS)
1274 struct statfs spoolinfo
; /* FS info for spool directory */
1275 double spoolsize
; /* FS size */
1276 #endif /* HAVE_STATVFS */
1277 static const char * const versions
[] =/* ipp-versions-supported values */
1282 static const char * const features
[] =/* ipp-features-supported values */
1286 static const int ops
[] = /* operations-supported values */
1290 IPP_OP_VALIDATE_JOB
,
1292 IPP_OP_SEND_DOCUMENT
,
1295 IPP_OP_GET_JOB_ATTRIBUTES
,
1297 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1298 IPP_OP_CANCEL_MY_JOBS
,
1300 IPP_OP_IDENTIFY_PRINTER
1302 static const char * const charsets
[] =/* charset-supported values */
1307 static const char * const compressions
[] =/* compression-supported values */
1312 #endif /* HAVE_LIBZ */
1315 static const char * const identify_actions
[] =
1320 static const char * const job_creation
[] =
1321 { /* job-creation-attributes-supported values */
1327 "document-metadata",
1329 "document-natural-language",
1330 "document-password",
1333 "ipp-attribute-fidelity",
1336 "job-accouunting-sheets",
1337 "job-accounting-user-id",
1338 "job-authorization-uri",
1342 "job-hold-until-time",
1343 "job-mandatory-attributes",
1344 "job-message-to-operator",
1346 "job-pages-per-set",
1348 "job-password-encryption",
1351 "job-recipient-name",
1353 "job-sheet-message",
1358 "multiple-document-handling",
1360 "orientation-requested",
1366 "presentation-direction-number-up",
1368 "print-content-optimize",
1370 "print-rendering-intent",
1372 "printer-resolution",
1378 "x-side1-image-shift",
1379 "x-side2-image-shift",
1382 "y-side1-image-shift",
1383 "y-side2-image-shift"
1385 static const char * const media_col_supported
[] =
1386 { /* media-col-supported values */
1387 "media-bottom-margin",
1388 "media-left-margin",
1389 "media-right-margin",
1396 static const char * const multiple_document_handling
[] =
1397 { /* multiple-document-handling-supported values */
1398 "separate-documents-uncollated-copies",
1399 "separate-documents-collated-copies"
1401 static const char * const reference_uri_schemes_supported
[] =
1402 { /* reference-uri-schemes-supported */
1408 #endif /* HAVE_SSL */
1411 static const char * const uri_authentication_supported
[] =
1412 { /* uri-authentication-supported values */
1416 static const char * const uri_security_supported
[] =
1417 { /* uri-security-supported values */
1421 #endif /* HAVE_SSL */
1422 static const char * const which_jobs
[] =
1423 { /* which-jobs-supported values */
1432 "processing-stopped"
1438 * If a command was specified, make sure it exists and is executable...
1443 if (*command
== '/' || !strncmp(command
, "./", 2))
1445 if (access(command
, X_OK
))
1447 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1453 snprintf(path
, sizeof(path
), "%s/command/%s", cg
->cups_serverbin
, command
);
1455 if (access(command
, X_OK
))
1457 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1464 #endif /* !_WIN32 */
1467 * Allocate memory for the printer...
1470 if ((printer
= calloc(1, sizeof(ippeve_printer_t
))) == NULL
)
1472 _cupsLangPrintError(NULL
, _("Unable to allocate memory for printer"));
1478 printer
->name
= strdup(name
);
1479 printer
->dnssd_name
= strdup(name
);
1480 printer
->command
= command
? strdup(command
) : NULL
;
1481 printer
->device_uri
= device_uri
? strdup(device_uri
) : NULL
;
1482 printer
->directory
= strdup(directory
);
1483 printer
->icon
= icon
? strdup(icon
) : NULL
;
1484 printer
->port
= serverport
;
1485 printer
->start_time
= time(NULL
);
1486 printer
->config_time
= printer
->start_time
;
1487 printer
->state
= IPP_PSTATE_IDLE
;
1488 printer
->state_reasons
= IPPEVE_PREASON_NONE
;
1489 printer
->state_time
= printer
->start_time
;
1490 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1491 printer
->next_job_id
= 1;
1495 printer
->hostname
= strdup(servername
);
1499 char temp
[1024]; /* Temporary string */
1501 printer
->hostname
= strdup(httpGetHostname(NULL
, temp
, sizeof(temp
)));
1504 _cupsRWInit(&(printer
->rwlock
));
1507 * Create the listener sockets...
1510 if ((printer
->ipv4
= create_listener(servername
, printer
->port
, AF_INET
)) < 0)
1512 perror("Unable to create IPv4 listener");
1516 if ((printer
->ipv6
= create_listener(servername
, printer
->port
, AF_INET6
)) < 0)
1518 perror("Unable to create IPv6 listener");
1523 * Prepare URI values for the printer attributes...
1526 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1527 printer
->uri
= strdup(uri
);
1528 printer
->urilen
= strlen(uri
);
1531 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1532 #endif /* HAVE_SSL */
1534 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1535 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1536 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1537 httpAssembleUUID(printer
->hostname
, serverport
, name
, 0, uuid
, sizeof(uuid
));
1541 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1542 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1544 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1546 fprintf(stderr
, "printer-uri=\"%s\",\"%s\"\n", uri
, securi
);
1547 #endif /* HAVE_SSL */
1551 * Get the maximum spool size based on the size of the filesystem used for
1552 * the spool directory. If the host OS doesn't support the statfs call
1553 * or the filesystem is larger than 2TiB, always report INT_MAX.
1557 if (statvfs(printer
->directory
, &spoolinfo
))
1558 k_supported
= INT_MAX
;
1559 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1560 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1561 k_supported
= INT_MAX
;
1563 k_supported
= (int)spoolsize
;
1565 #elif defined(HAVE_STATFS)
1566 if (statfs(printer
->directory
, &spoolinfo
))
1567 k_supported
= INT_MAX
;
1568 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1569 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1570 k_supported
= INT_MAX
;
1572 k_supported
= (int)spoolsize
;
1575 k_supported
= INT_MAX
;
1576 #endif /* HAVE_STATVFS */
1579 * Assemble the final list of document formats...
1582 if (!cupsArrayFind(docformats
, (void *)"application/octet-stream"))
1583 cupsArrayAdd(docformats
, (void *)"application/octet-stream");
1585 for (num_formats
= 0, format
= (const char *)cupsArrayFirst(docformats
); format
&& num_formats
< (int)(sizeof(formats
) / sizeof(formats
[0])); format
= (const char *)cupsArrayNext(docformats
))
1586 formats
[num_formats
++] = format
;
1589 * Get the list of attributes that can be used when creating a job...
1593 sup_attrs
[num_sup_attrs
++] = "document-access";
1594 sup_attrs
[num_sup_attrs
++] = "document-charset";
1595 sup_attrs
[num_sup_attrs
++] = "document-format";
1596 sup_attrs
[num_sup_attrs
++] = "document-message";
1597 sup_attrs
[num_sup_attrs
++] = "document-metadata";
1598 sup_attrs
[num_sup_attrs
++] = "document-name";
1599 sup_attrs
[num_sup_attrs
++] = "document-natural-language";
1600 sup_attrs
[num_sup_attrs
++] = "ipp-attribute-fidelity";
1601 sup_attrs
[num_sup_attrs
++] = "job-name";
1602 sup_attrs
[num_sup_attrs
++] = "job-priority";
1604 for (i
= 0; i
< (int)(sizeof(job_creation
) / sizeof(job_creation
[0])) && num_sup_attrs
< (int)(sizeof(sup_attrs
) / sizeof(sup_attrs
[0])); i
++)
1606 snprintf(xxx_supported
, sizeof(xxx_supported
), "%s-supported", job_creation
[i
]);
1607 if (ippFindAttribute(attrs
, xxx_supported
, IPP_TAG_ZERO
))
1608 sup_attrs
[num_sup_attrs
++] = job_creation
[i
];
1612 * Fill out the rest of the printer attributes.
1615 printer
->attrs
= attrs
;
1617 /* charset-configured */
1618 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1620 /* charset-supported */
1621 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1623 /* compression-supported */
1624 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1625 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1627 /* document-format-default */
1628 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_MIMETYPE
), "document-format-default", NULL
, "application/octet-stream");
1630 /* document-format-supported */
1631 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
, "document-format-supported", num_formats
, NULL
, formats
);
1633 /* generated-natural-language-supported */
1634 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1636 /* identify-actions-default */
1637 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1639 /* identify-actions-supported */
1640 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
);
1642 /* ipp-features-supported */
1643 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1645 /* ipp-versions-supported */
1646 if (MaxVersion
== 11)
1647 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", NULL
, "1.1");
1649 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", (int)(sizeof(versions
) / sizeof(versions
[0])), NULL
, versions
);
1651 /* job-creation-attributes-supported */
1652 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-creation-attributes-supported", num_sup_attrs
, NULL
, sup_attrs
);
1654 /* job-ids-supported */
1655 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1657 /* job-k-octets-supported */
1658 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0, k_supported
);
1660 /* job-priority-default */
1661 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1663 /* job-priority-supported */
1664 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 1);
1666 /* job-sheets-default */
1667 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1669 /* job-sheets-supported */
1670 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1672 /* media-col-supported */
1673 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
);
1675 /* multiple-document-handling-supported */
1676 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
);
1678 /* multiple-document-jobs-supported */
1679 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1681 /* multiple-operation-time-out */
1682 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1684 /* multiple-operation-time-out-action */
1685 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1687 /* natural-language-configured */
1688 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "natural-language-configured", NULL
, "en");
1690 /* operations-supported */
1691 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1693 /* pdl-override-supported */
1694 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1696 /* preferred-attributes-supported */
1697 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1699 /* printer-get-attributes-supported */
1700 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1702 /* printer-geo-location */
1703 ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location");
1706 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-icons", NULL
, icons
);
1708 /* printer-is-accepting-jobs */
1709 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1712 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
1714 /* printer-location */
1715 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location", NULL
, location
);
1717 /* printer-more-info */
1718 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1721 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
1723 /* printer-organization */
1724 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "");
1726 /* printer-organizational-unit */
1727 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "");
1729 /* printer-supply-info-uri */
1730 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1732 /* printer-uri-supported */
1737 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
1740 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1741 #endif /* HAVE_SSL */
1744 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1746 /* reference-uri-scheme-supported */
1747 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
);
1749 /* uri-authentication-supported */
1751 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
1753 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
1754 #endif /* HAVE_SSL */
1756 /* uri-security-supported */
1758 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
1760 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
1761 #endif /* HAVE_SSL */
1763 /* which-jobs-supported */
1764 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
);
1766 debug_attributes("Printer", printer
->attrs
, 0);
1769 * Register the printer with Bonjour...
1772 if (!register_printer(printer
, subtypes
))
1783 * If we get here we were unable to create the printer...
1788 delete_printer(printer
);
1795 * 'debug_attributes()' - Print attributes in a request or response.
1799 debug_attributes(const char *title
, /* I - Title */
1800 ipp_t
*ipp
, /* I - Request/response */
1801 int type
) /* I - 0 = object, 1 = request, 2 = response */
1803 ipp_tag_t group_tag
; /* Current group */
1804 ipp_attribute_t
*attr
; /* Current attribute */
1805 char buffer
[2048]; /* String buffer for value */
1806 int major
, minor
; /* Version */
1812 fprintf(stderr
, "%s:\n", title
);
1813 major
= ippGetVersion(ipp
, &minor
);
1814 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1816 fprintf(stderr
, " operation-id=%s(%04x)\n",
1817 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1819 fprintf(stderr
, " status-code=%s(%04x)\n",
1820 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1821 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1823 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1825 attr
= ippNextAttribute(ipp
))
1827 if (ippGetGroupTag(attr
) != group_tag
)
1829 group_tag
= ippGetGroupTag(attr
);
1830 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1833 if (ippGetName(attr
))
1835 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1836 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1837 ippGetCount(attr
) > 1 ? "1setOf " : "",
1838 ippTagString(ippGetValueTag(attr
)), buffer
);
1845 * 'delete_client()' - Close the socket and free all memory used by a client
1850 delete_client(ippeve_client_t
*client
) /* I - Client */
1853 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1856 * Flush pending writes before closing...
1859 httpFlushWrite(client
->http
);
1865 httpClose(client
->http
);
1867 ippDelete(client
->request
);
1868 ippDelete(client
->response
);
1875 * 'delete_job()' - Remove from the printer and free all memory used by a job
1880 delete_job(ippeve_job_t
*job
) /* I - Job */
1883 fprintf(stderr
, "[Job %d] Removing job from history.\n", job
->id
);
1885 ippDelete(job
->attrs
);
1893 unlink(job
->filename
);
1895 free(job
->filename
);
1903 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1904 * used by a printer object.
1908 delete_printer(ippeve_printer_t
*printer
) /* I - Printer */
1910 if (printer
->ipv4
>= 0)
1911 close(printer
->ipv4
);
1913 if (printer
->ipv6
>= 0)
1914 close(printer
->ipv6
);
1917 if (printer
->printer_ref
)
1918 DNSServiceRefDeallocate(printer
->printer_ref
);
1919 if (printer
->ipp_ref
)
1920 DNSServiceRefDeallocate(printer
->ipp_ref
);
1921 if (printer
->ipps_ref
)
1922 DNSServiceRefDeallocate(printer
->ipps_ref
);
1923 if (printer
->http_ref
)
1924 DNSServiceRefDeallocate(printer
->http_ref
);
1925 #elif defined(HAVE_AVAHI)
1926 avahi_threaded_poll_lock(DNSSDMaster
);
1928 if (printer
->printer_ref
)
1929 avahi_entry_group_free(printer
->printer_ref
);
1930 if (printer
->ipp_ref
)
1931 avahi_entry_group_free(printer
->ipp_ref
);
1932 if (printer
->ipps_ref
)
1933 avahi_entry_group_free(printer
->ipps_ref
);
1934 if (printer
->http_ref
)
1935 avahi_entry_group_free(printer
->http_ref
);
1937 avahi_threaded_poll_unlock(DNSSDMaster
);
1938 #endif /* HAVE_DNSSD */
1940 if (printer
->dnssd_name
)
1941 free(printer
->dnssd_name
);
1943 free(printer
->name
);
1945 free(printer
->icon
);
1946 if (printer
->command
)
1947 free(printer
->command
);
1948 if (printer
->device_uri
)
1949 free(printer
->device_uri
);
1951 if (printer
->ppdfile
)
1952 free(printer
->ppdfile
);
1953 #endif /* !CUPS_LITE */
1954 if (printer
->directory
)
1955 free(printer
->directory
);
1956 if (printer
->hostname
)
1957 free(printer
->hostname
);
1961 ippDelete(printer
->attrs
);
1962 cupsArrayDelete(printer
->jobs
);
1970 * 'dnssd_callback()' - Handle Bonjour registration events.
1973 static void DNSSD_API
1975 DNSServiceRef sdRef
, /* I - Service reference */
1976 DNSServiceFlags flags
, /* I - Status flags */
1977 DNSServiceErrorType errorCode
, /* I - Error, if any */
1978 const char *name
, /* I - Service name */
1979 const char *regtype
, /* I - Service type */
1980 const char *domain
, /* I - Domain for service */
1981 ippeve_printer_t
*printer
) /* I - Printer */
1989 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n", regtype
, (int)errorCode
);
1992 else if (strcasecmp(name
, printer
->dnssd_name
))
1995 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
1997 /* No lock needed since only the main thread accesses/changes this */
1998 free(printer
->dnssd_name
);
1999 printer
->dnssd_name
= strdup(name
);
2004 #elif defined(HAVE_AVAHI)
2006 * 'dnssd_callback()' - Handle Bonjour registration events.
2011 AvahiEntryGroup
*srv
, /* I - Service */
2012 AvahiEntryGroupState state
, /* I - Registration state */
2013 void *context
) /* I - Printer */
2022 * 'dnssd_client_cb()' - Client callback for Avahi.
2024 * Called whenever the client or server state changes...
2029 AvahiClient
*c
, /* I - Client */
2030 AvahiClientState state
, /* I - Current state */
2031 void *userdata
) /* I - User data (unused) */
2041 fprintf(stderr
, "Ignored Avahi state %d.\n", state
);
2044 case AVAHI_CLIENT_FAILURE
:
2045 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2047 fputs("Avahi server crashed, exiting.\n", stderr
);
2053 #endif /* HAVE_DNSSD */
2057 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2064 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2066 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2070 #elif defined(HAVE_AVAHI)
2071 int error
; /* Error code, if any */
2073 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2075 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2079 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2081 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2085 avahi_threaded_poll_start(DNSSDMaster
);
2086 #endif /* HAVE_DNSSD */
2091 * 'filter_cb()' - Filter printer attributes based on the requested array.
2094 static int /* O - 1 to copy, 0 to ignore */
2095 filter_cb(ippeve_filter_t
*filter
, /* I - Filter parameters */
2096 ipp_t
*dst
, /* I - Destination (unused) */
2097 ipp_attribute_t
*attr
) /* I - Source attribute */
2100 * Filter attributes as needed...
2103 #ifndef _WIN32 /* Avoid MS compiler bug */
2105 #endif /* !_WIN32 */
2107 ipp_tag_t group
= ippGetGroupTag(attr
);
2108 const char *name
= ippGetName(attr
);
2110 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
)))
2113 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2118 * 'find_job()' - Find a job specified in a request.
2121 static ippeve_job_t
* /* O - Job or NULL */
2122 find_job(ippeve_client_t
*client
) /* I - Client */
2124 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2125 ippeve_job_t key
, /* Job search key */
2126 *job
; /* Matching job, if any */
2129 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2131 const char *uri
= ippGetString(attr
, 0, NULL
);
2133 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2134 uri
[client
->printer
->urilen
] == '/')
2135 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2139 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2140 key
.id
= ippGetInteger(attr
, 0);
2142 _cupsRWLockRead(&(client
->printer
->rwlock
));
2143 job
= (ippeve_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2144 _cupsRWUnlock(&(client
->printer
->rwlock
));
2151 * 'finish_document()' - Finish receiving a document file and start processing.
2155 finish_document_data(
2156 ippeve_client_t
*client
, /* I - Client */
2157 ippeve_job_t
*job
) /* I - Job */
2159 char filename
[1024], /* Filename buffer */
2160 buffer
[4096]; /* Copy buffer */
2161 ssize_t bytes
; /* Bytes read */
2162 cups_array_t
*ra
; /* Attributes to send in response */
2163 _cups_thread_t t
; /* Thread */
2167 * Create a file for the request data...
2169 * TODO: Update code to support piping large raster data to the print command.
2172 if ((job
->fd
= create_job_file(job
, filename
, sizeof(filename
), client
->printer
->directory
, NULL
)) < 0)
2174 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2180 fprintf(stderr
, "Created job file \"%s\", format \"%s\".\n", filename
, job
->format
);
2182 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2184 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2186 int error
= errno
; /* Write error */
2193 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2202 * Got an error while reading the print data, so abort this job.
2210 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to read print file.");
2217 int error
= errno
; /* Write error */
2223 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2229 job
->filename
= strdup(filename
);
2230 job
->state
= IPP_JSTATE_PENDING
;
2233 * Process the job...
2236 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
2240 _cupsThreadDetach(t
);
2244 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2249 * Return the job info...
2252 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2254 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2255 cupsArrayAdd(ra
, "job-id");
2256 cupsArrayAdd(ra
, "job-state");
2257 cupsArrayAdd(ra
, "job-state-message");
2258 cupsArrayAdd(ra
, "job-state-reasons");
2259 cupsArrayAdd(ra
, "job-uri");
2261 copy_job_attributes(client
, job
, ra
);
2262 cupsArrayDelete(ra
);
2266 * If we get here we had to abort the job...
2271 job
->state
= IPP_JSTATE_ABORTED
;
2272 job
->completed
= time(NULL
);
2274 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2275 cupsArrayAdd(ra
, "job-id");
2276 cupsArrayAdd(ra
, "job-state");
2277 cupsArrayAdd(ra
, "job-state-reasons");
2278 cupsArrayAdd(ra
, "job-uri");
2280 copy_job_attributes(client
, job
, ra
);
2281 cupsArrayDelete(ra
);
2286 * 'finish_uri()' - Finish fetching a document URI and start processing.
2290 finish_document_uri(
2291 ippeve_client_t
*client
, /* I - Client */
2292 ippeve_job_t
*job
) /* I - Job */
2294 ipp_attribute_t
*uri
; /* document-uri */
2295 char scheme
[256], /* URI scheme */
2296 userpass
[256], /* Username and password info */
2297 hostname
[256], /* Hostname */
2298 resource
[1024]; /* Resource path */
2299 int port
; /* Port number */
2300 http_uri_status_t uri_status
; /* URI decode status */
2301 http_encryption_t encryption
; /* Encryption to use, if any */
2302 http_t
*http
; /* Connection for http/https URIs */
2303 http_status_t status
; /* Access status for http/https URIs */
2304 int infile
; /* Input file for local file URIs */
2305 char filename
[1024], /* Filename buffer */
2306 buffer
[4096]; /* Copy buffer */
2307 ssize_t bytes
; /* Bytes read */
2308 ipp_attribute_t
*attr
; /* Current attribute */
2309 cups_array_t
*ra
; /* Attributes to send in response */
2313 * Do we have a file to print?
2316 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2318 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Unexpected document data following request.");
2324 * Do we have a document URI?
2327 if ((uri
= ippFindAttribute(client
->request
, "document-uri", IPP_TAG_URI
)) == NULL
)
2329 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
2334 if (ippGetCount(uri
) != 1)
2336 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Too many document-uri values.");
2341 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
2342 scheme
, sizeof(scheme
), userpass
,
2343 sizeof(userpass
), hostname
, sizeof(hostname
),
2344 &port
, resource
, sizeof(resource
));
2345 if (uri_status
< HTTP_URI_STATUS_OK
)
2347 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s", httpURIStatusString(uri_status
));
2352 if (strcmp(scheme
, "file") &&
2354 strcmp(scheme
, "https") &&
2355 #endif /* HAVE_SSL */
2356 strcmp(scheme
, "http"))
2358 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
, "URI scheme \"%s\" not supported.", scheme
);
2363 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
2365 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
2371 * Get the document format for the job...
2374 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2376 if ((attr
= ippFindAttribute(job
->attrs
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
2377 job
->format
= ippGetString(attr
, 0, NULL
);
2379 job
->format
= "application/octet-stream";
2382 * Create a file for the request data...
2385 if ((job
->fd
= create_job_file(job
, filename
, sizeof(filename
), client
->printer
->directory
, NULL
)) < 0)
2387 _cupsRWUnlock(&(client
->printer
->rwlock
));
2389 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2394 _cupsRWUnlock(&(client
->printer
->rwlock
));
2396 if (!strcmp(scheme
, "file"))
2398 if ((infile
= open(resource
, O_RDONLY
)) < 0)
2400 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
2407 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
2408 (errno
== EAGAIN
|| errno
== EINTR
))
2412 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2414 int error
= errno
; /* Write error */
2422 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2434 if (port
== 443 || !strcmp(scheme
, "https"))
2435 encryption
= HTTP_ENCRYPTION_ALWAYS
;
2437 #endif /* HAVE_SSL */
2438 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
2440 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
, 1, 30000, NULL
)) == NULL
)
2442 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to connect to %s: %s", hostname
, cupsLastErrorString());
2452 httpClearFields(http
);
2453 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
2454 if (httpGet(http
, resource
))
2456 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", strerror(errno
));
2467 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
2469 if (status
!= HTTP_STATUS_OK
)
2471 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", httpStatus(status
));
2482 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
2484 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2486 int error
= errno
; /* Write error */
2494 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2495 "Unable to write print file: %s", strerror(error
));
2506 int error
= errno
; /* Write error */
2512 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2517 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2520 job
->filename
= strdup(filename
);
2521 job
->state
= IPP_JSTATE_PENDING
;
2523 _cupsRWUnlock(&(client
->printer
->rwlock
));
2526 * Process the job...
2532 * Return the job info...
2535 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2537 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2538 cupsArrayAdd(ra
, "job-id");
2539 cupsArrayAdd(ra
, "job-state");
2540 cupsArrayAdd(ra
, "job-state-reasons");
2541 cupsArrayAdd(ra
, "job-uri");
2543 copy_job_attributes(client
, job
, ra
);
2544 cupsArrayDelete(ra
);
2548 * If we get here we had to abort the job...
2553 job
->state
= IPP_JSTATE_ABORTED
;
2554 job
->completed
= time(NULL
);
2556 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2557 cupsArrayAdd(ra
, "job-id");
2558 cupsArrayAdd(ra
, "job-state");
2559 cupsArrayAdd(ra
, "job-state-reasons");
2560 cupsArrayAdd(ra
, "job-uri");
2562 copy_job_attributes(client
, job
, ra
);
2563 cupsArrayDelete(ra
);
2568 * 'html_escape()' - Write a HTML-safe string.
2572 html_escape(ippeve_client_t
*client
, /* I - Client */
2573 const char *s
, /* I - String to write */
2574 size_t slen
) /* I - Number of characters to write */
2576 const char *start
, /* Start of segment */
2577 *end
; /* End of string */
2581 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2583 while (*s
&& s
< end
)
2585 if (*s
== '&' || *s
== '<')
2588 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2591 httpWrite2(client
->http
, "&", 5);
2593 httpWrite2(client
->http
, "<", 4);
2602 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2607 * 'html_footer()' - Show the web interface footer.
2609 * This function also writes the trailing 0-length chunk.
2613 html_footer(ippeve_client_t
*client
) /* I - Client */
2619 httpWrite2(client
->http
, "", 0);
2624 * 'html_header()' - Show the web interface header and title.
2628 html_header(ippeve_client_t
*client
, /* I - Client */
2629 const char *title
, /* I - Title */
2630 int refresh
) /* I - Refresh timer, if any */
2636 "<title>%s</title>\n"
2637 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2638 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2639 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title
);
2641 html_printf(client
, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh
);
2643 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2645 "body { font-family: sans-serif; margin: 0; }\n"
2646 "div.body { padding: 0px 10px 10px; }\n"
2647 "span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
2648 "span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
2649 "table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
2650 "table.form td, table.form th { padding: 5px 2px; }\n"
2651 "table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
2652 "table.form th { text-align: right; }\n"
2653 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2654 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2655 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2656 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2657 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2658 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2659 "table.nav td { margin: 0; text-align: center; }\n"
2660 "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"
2661 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2662 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2663 "td.nav:hover { background: #666; color: #fff; }\n"
2664 "td.nav:active { background: #000; color: #ff0; }\n"
2668 "<table class=\"nav\"><tr>"
2669 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2670 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2671 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2673 "<div class=\"body\">\n", !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2678 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2682 html_printf(ippeve_client_t
*client
, /* I - Client */
2683 const char *format
, /* I - Printf-style format string */
2684 ...) /* I - Additional arguments as needed */
2686 va_list ap
; /* Pointer to arguments */
2687 const char *start
; /* Start of string */
2688 char size
, /* Size character (h, l, L) */
2689 type
; /* Format type character */
2690 int width
, /* Width of field */
2691 prec
; /* Number of characters of precision */
2692 char tformat
[100], /* Temporary format string for sprintf() */
2693 *tptr
, /* Pointer into temporary format */
2694 temp
[1024]; /* Buffer for formatted numbers */
2695 char *s
; /* Pointer to string */
2699 * Loop through the format string, formatting as needed...
2702 va_start(ap
, format
);
2710 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2713 *tptr
++ = *format
++;
2717 httpWrite2(client
->http
, "%", 1);
2722 else if (strchr(" -+#\'", *format
))
2723 *tptr
++ = *format
++;
2728 * Get width from argument...
2732 width
= va_arg(ap
, int);
2734 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2735 tptr
+= strlen(tptr
);
2741 while (isdigit(*format
& 255))
2743 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2746 width
= width
* 10 + *format
++ - '0';
2752 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2760 * Get precision from argument...
2764 prec
= va_arg(ap
, int);
2766 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2767 tptr
+= strlen(tptr
);
2773 while (isdigit(*format
& 255))
2775 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2778 prec
= prec
* 10 + *format
++ - '0';
2783 if (*format
== 'l' && format
[1] == 'l')
2787 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2795 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2797 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2812 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2821 case 'E' : /* Floating point formats */
2826 if ((size_t)(width
+ 2) > sizeof(temp
))
2829 sprintf(temp
, tformat
, va_arg(ap
, double));
2831 httpWrite2(client
->http
, temp
, strlen(temp
));
2834 case 'B' : /* Integer formats */
2842 if ((size_t)(width
+ 2) > sizeof(temp
))
2845 # ifdef HAVE_LONG_LONG
2847 sprintf(temp
, tformat
, va_arg(ap
, long long));
2849 # endif /* HAVE_LONG_LONG */
2851 sprintf(temp
, tformat
, va_arg(ap
, long));
2853 sprintf(temp
, tformat
, va_arg(ap
, int));
2855 httpWrite2(client
->http
, temp
, strlen(temp
));
2858 case 'p' : /* Pointer value */
2859 if ((size_t)(width
+ 2) > sizeof(temp
))
2862 sprintf(temp
, tformat
, va_arg(ap
, void *));
2864 httpWrite2(client
->http
, temp
, strlen(temp
));
2867 case 'c' : /* Character or character array */
2870 temp
[0] = (char)va_arg(ap
, int);
2872 html_escape(client
, temp
, 1);
2875 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2878 case 's' : /* String */
2879 if ((s
= va_arg(ap
, char *)) == NULL
)
2882 html_escape(client
, s
, strlen(s
));
2891 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2898 * 'ipp_cancel_job()' - Cancel a job.
2902 ipp_cancel_job(ippeve_client_t
*client
) /* I - Client */
2904 ippeve_job_t
*job
; /* Job information */
2911 if ((job
= find_job(client
)) == NULL
)
2913 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2918 * See if the job is already completed, canceled, or aborted; if so,
2919 * we can't cancel...
2924 case IPP_JSTATE_CANCELED
:
2925 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2926 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2929 case IPP_JSTATE_ABORTED
:
2930 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2931 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2934 case IPP_JSTATE_COMPLETED
:
2935 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2936 "Job #%d is already completed - can\'t cancel.", job
->id
);
2944 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2946 if (job
->state
== IPP_JSTATE_PROCESSING
||
2947 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2951 job
->state
= IPP_JSTATE_CANCELED
;
2952 job
->completed
= time(NULL
);
2955 _cupsRWUnlock(&(client
->printer
->rwlock
));
2957 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2964 * 'ipp_close_job()' - Close an open job.
2968 ipp_close_job(ippeve_client_t
*client
) /* I - Client */
2970 ippeve_job_t
*job
; /* Job information */
2977 if ((job
= find_job(client
)) == NULL
)
2979 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2984 * See if the job is already completed, canceled, or aborted; if so,
2985 * we can't cancel...
2990 case IPP_JSTATE_CANCELED
:
2991 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2992 "Job #%d is canceled - can\'t close.", job
->id
);
2995 case IPP_JSTATE_ABORTED
:
2996 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2997 "Job #%d is aborted - can\'t close.", job
->id
);
3000 case IPP_JSTATE_COMPLETED
:
3001 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3002 "Job #%d is completed - can\'t close.", job
->id
);
3005 case IPP_JSTATE_PROCESSING
:
3006 case IPP_JSTATE_STOPPED
:
3007 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3008 "Job #%d is already closed.", job
->id
);
3012 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3019 * 'ipp_create_job()' - Create a job object.
3023 ipp_create_job(ippeve_client_t
*client
) /* I - Client */
3025 ippeve_job_t
*job
; /* New job */
3026 cups_array_t
*ra
; /* Attributes to send in response */
3030 * Validate print job attributes...
3033 if (!valid_job_attributes(client
))
3035 httpFlush(client
->http
);
3040 * Do we have a file to print?
3043 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3045 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3046 "Unexpected document data following request.");
3054 if ((job
= create_job(client
)) == NULL
)
3056 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3057 "Currently printing another job.");
3062 * Return the job info...
3065 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3067 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3068 cupsArrayAdd(ra
, "job-id");
3069 cupsArrayAdd(ra
, "job-state");
3070 cupsArrayAdd(ra
, "job-state-message");
3071 cupsArrayAdd(ra
, "job-state-reasons");
3072 cupsArrayAdd(ra
, "job-uri");
3074 copy_job_attributes(client
, job
, ra
);
3075 cupsArrayDelete(ra
);
3080 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3084 ipp_get_job_attributes(
3085 ippeve_client_t
*client
) /* I - Client */
3087 ippeve_job_t
*job
; /* Job */
3088 cups_array_t
*ra
; /* requested-attributes */
3091 if ((job
= find_job(client
)) == NULL
)
3093 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3097 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3099 ra
= ippCreateRequestedArray(client
->request
);
3100 copy_job_attributes(client
, job
, ra
);
3101 cupsArrayDelete(ra
);
3106 * 'ipp_get_jobs()' - Get a list of job objects.
3110 ipp_get_jobs(ippeve_client_t
*client
) /* I - Client */
3112 ipp_attribute_t
*attr
; /* Current attribute */
3113 const char *which_jobs
= NULL
;
3114 /* which-jobs values */
3115 int job_comparison
; /* Job comparison */
3116 ipp_jstate_t job_state
; /* job-state value */
3117 int first_job_id
, /* First job ID */
3118 limit
, /* Maximum number of jobs to return */
3119 count
; /* Number of jobs that match */
3120 const char *username
; /* Username */
3121 ippeve_job_t
*job
; /* Current job pointer */
3122 cups_array_t
*ra
; /* Requested attributes array */
3126 * See if the "which-jobs" attribute have been specified...
3129 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3130 IPP_TAG_KEYWORD
)) != NULL
)
3132 which_jobs
= ippGetString(attr
, 0, NULL
);
3133 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3136 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3138 job_comparison
= -1;
3139 job_state
= IPP_JSTATE_STOPPED
;
3141 else if (!strcmp(which_jobs
, "completed"))
3144 job_state
= IPP_JSTATE_CANCELED
;
3146 else if (!strcmp(which_jobs
, "aborted"))
3149 job_state
= IPP_JSTATE_ABORTED
;
3151 else if (!strcmp(which_jobs
, "all"))
3154 job_state
= IPP_JSTATE_PENDING
;
3156 else if (!strcmp(which_jobs
, "canceled"))
3159 job_state
= IPP_JSTATE_CANCELED
;
3161 else if (!strcmp(which_jobs
, "pending"))
3164 job_state
= IPP_JSTATE_PENDING
;
3166 else if (!strcmp(which_jobs
, "pending-held"))
3169 job_state
= IPP_JSTATE_HELD
;
3171 else if (!strcmp(which_jobs
, "processing"))
3174 job_state
= IPP_JSTATE_PROCESSING
;
3176 else if (!strcmp(which_jobs
, "processing-stopped"))
3179 job_state
= IPP_JSTATE_STOPPED
;
3183 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3184 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3185 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3186 "which-jobs", NULL
, which_jobs
);
3191 * See if they want to limit the number of jobs reported...
3194 if ((attr
= ippFindAttribute(client
->request
, "limit",
3195 IPP_TAG_INTEGER
)) != NULL
)
3197 limit
= ippGetInteger(attr
, 0);
3199 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3204 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3205 IPP_TAG_INTEGER
)) != NULL
)
3207 first_job_id
= ippGetInteger(attr
, 0);
3209 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
, first_job_id
);
3215 * See if we only want to see jobs for a specific user...
3220 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3221 IPP_TAG_BOOLEAN
)) != NULL
)
3223 int my_jobs
= ippGetBoolean(attr
, 0);
3225 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
, my_jobs
? "true" : "false");
3229 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3230 IPP_TAG_NAME
)) == NULL
)
3232 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3233 "Need requesting-user-name with my-jobs.");
3237 username
= ippGetString(attr
, 0, NULL
);
3239 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n", client
->hostname
, username
);
3244 * OK, build a list of jobs for this printer...
3247 ra
= ippCreateRequestedArray(client
->request
);
3249 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3251 _cupsRWLockRead(&(client
->printer
->rwlock
));
3253 for (count
= 0, job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3254 (limit
<= 0 || count
< limit
) && job
;
3255 job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3258 * Filter out jobs that don't match...
3261 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3262 (job_comparison
== 0 && job
->state
!= job_state
) ||
3263 (job_comparison
> 0 && job
->state
< job_state
) ||
3264 job
->id
< first_job_id
||
3265 (username
&& job
->username
&&
3266 strcasecmp(username
, job
->username
)))
3270 ippAddSeparator(client
->response
);
3273 copy_job_attributes(client
, job
, ra
);
3276 cupsArrayDelete(ra
);
3278 _cupsRWUnlock(&(client
->printer
->rwlock
));
3283 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3287 ipp_get_printer_attributes(
3288 ippeve_client_t
*client
) /* I - Client */
3290 cups_array_t
*ra
; /* Requested attributes array */
3291 ippeve_printer_t
*printer
; /* Printer */
3295 * Send the attributes...
3298 ra
= ippCreateRequestedArray(client
->request
);
3299 printer
= client
->printer
;
3301 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3303 _cupsRWLockRead(&(printer
->rwlock
));
3305 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3306 IPP_TAG_CUPS_CONST
);
3308 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3309 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3311 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3312 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3314 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3315 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3318 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3319 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state", (int)printer
->state
);
3321 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3322 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3324 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3325 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3327 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3329 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3331 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3334 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3336 if (printer
->state_reasons
== IPPEVE_PREASON_NONE
)
3338 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", NULL
, "none");
3342 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3343 ippeve_preason_t bit
; /* Reason bit */
3344 int i
; /* Looping var */
3345 char reason
[32]; /* Reason string */
3347 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
3349 if (printer
->state_reasons
& bit
)
3351 snprintf(reason
, sizeof(reason
), "%s-%s", ippeve_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3353 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3355 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3361 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3362 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3364 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3365 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3367 _cupsRWUnlock(&(printer
->rwlock
));
3369 cupsArrayDelete(ra
);
3374 * 'ipp_identify_printer()' - Beep or display a message.
3378 ipp_identify_printer(
3379 ippeve_client_t
*client
) /* I - Client */
3381 ipp_attribute_t
*actions
, /* identify-actions */
3382 *message
; /* message */
3385 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3386 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3388 if (!actions
|| ippContainsString(actions
, "sound"))
3394 if (ippContainsString(actions
, "display"))
3395 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3397 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3402 * 'ipp_print_job()' - Create a job object with an attached document.
3406 ipp_print_job(ippeve_client_t
*client
) /* I - Client */
3408 ippeve_job_t
*job
; /* New job */
3412 * Validate print job attributes...
3415 if (!valid_job_attributes(client
))
3417 httpFlush(client
->http
);
3422 * Do we have a file to print?
3425 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3427 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3435 if ((job
= create_job(client
)) == NULL
)
3437 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3442 * Then finish getting the document data and process things...
3445 finish_document_data(client
, job
);
3450 * 'ipp_print_uri()' - Create a job object with a referenced document.
3454 ipp_print_uri(ippeve_client_t
*client
) /* I - Client */
3456 ippeve_job_t
*job
; /* New job */
3460 * Validate print job attributes...
3463 if (!valid_job_attributes(client
))
3465 httpFlush(client
->http
);
3473 if ((job
= create_job(client
)) == NULL
)
3475 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3480 * Then finish getting the document data and process things...
3483 finish_document_uri(client
, job
);
3488 * 'ipp_send_document()' - Add an attached document to a job object created with
3494 ippeve_client_t
*client
) /* I - Client */
3496 ippeve_job_t
*job
; /* Job information */
3497 ipp_attribute_t
*attr
; /* Current attribute */
3504 if ((job
= find_job(client
)) == NULL
)
3506 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3507 httpFlush(client
->http
);
3512 * See if we already have a document for this job or the job has already
3513 * in a non-pending state...
3516 if (job
->state
> IPP_JSTATE_HELD
)
3518 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job is not in a pending state.");
3519 httpFlush(client
->http
);
3522 else if (job
->filename
|| job
->fd
>= 0)
3524 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
, "Multiple document jobs are not supported.");
3525 httpFlush(client
->http
);
3530 * Make sure we have the "last-document" operation attribute...
3533 if ((attr
= ippFindAttribute(client
->request
, "last-document", IPP_TAG_ZERO
)) == NULL
)
3535 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing required last-document attribute.");
3536 httpFlush(client
->http
);
3539 else if (ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
3541 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The last-document attribute is not in the operation group.");
3542 httpFlush(client
->http
);
3545 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 || !ippGetBoolean(attr
, 0))
3547 respond_unsupported(client
, attr
);
3548 httpFlush(client
->http
);
3553 * Validate document attributes...
3556 if (!valid_doc_attributes(client
))
3558 httpFlush(client
->http
);
3563 * Then finish getting the document data and process things...
3566 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3568 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3570 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3571 job
->format
= ippGetString(attr
, 0, NULL
);
3572 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3573 job
->format
= ippGetString(attr
, 0, NULL
);
3575 job
->format
= "application/octet-stream";
3577 _cupsRWUnlock(&(client
->printer
->rwlock
));
3579 finish_document_data(client
, job
);
3584 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3589 ipp_send_uri(ippeve_client_t
*client
) /* I - Client */
3591 ippeve_job_t
*job
; /* Job information */
3592 ipp_attribute_t
*attr
; /* Current attribute */
3599 if ((job
= find_job(client
)) == NULL
)
3601 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3602 httpFlush(client
->http
);
3607 * See if we already have a document for this job or the job has already
3608 * in a non-pending state...
3611 if (job
->state
> IPP_JSTATE_HELD
)
3613 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job is not in a pending state.");
3614 httpFlush(client
->http
);
3617 else if (job
->filename
|| job
->fd
>= 0)
3619 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
, "Multiple document jobs are not supported.");
3620 httpFlush(client
->http
);
3624 if ((attr
= ippFindAttribute(client
->request
, "last-document", IPP_TAG_ZERO
)) == NULL
)
3626 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing required last-document attribute.");
3627 httpFlush(client
->http
);
3630 else if (ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
3632 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The last-document attribute is not in the operation group.");
3633 httpFlush(client
->http
);
3636 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 || !ippGetBoolean(attr
, 0))
3638 respond_unsupported(client
, attr
);
3639 httpFlush(client
->http
);
3644 * Validate document attributes...
3647 if (!valid_doc_attributes(client
))
3649 httpFlush(client
->http
);
3654 * Then finish getting the document data and process things...
3657 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3659 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3661 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3662 job
->format
= ippGetString(attr
, 0, NULL
);
3663 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3664 job
->format
= ippGetString(attr
, 0, NULL
);
3666 job
->format
= "application/octet-stream";
3668 _cupsRWUnlock(&(client
->printer
->rwlock
));
3670 finish_document_uri(client
, job
);
3675 * 'ipp_validate_job()' - Validate job creation attributes.
3679 ipp_validate_job(ippeve_client_t
*client
) /* I - Client */
3681 if (valid_job_attributes(client
))
3682 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3687 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
3690 static int /* O - 1 to use, 0 to ignore */
3692 _ipp_file_t
*f
, /* I - IPP file */
3693 void *user_data
, /* I - User data pointer (unused) */
3694 const char *attr
) /* I - Attribute name */
3696 int i
, /* Current element */
3697 result
; /* Result of comparison */
3698 static const char * const ignored
[] =
3699 { /* Ignored attributes */
3700 "attributes-charset",
3701 "attributes-natural-language",
3702 "charset-configured",
3703 "charset-supported",
3704 "device-service-count",
3706 "document-format-varying-attributes",
3707 "generated-natural-language-supported",
3708 "identify-actions-default",
3709 "identify-actions-supported",
3710 "ipp-features-supported",
3711 "ipp-versions-supproted",
3712 "ippget-event-life",
3713 "job-hold-until-supported",
3714 "job-hold-until-time-supported",
3715 "job-ids-supported",
3716 "job-k-octets-supported",
3717 "job-settable-attributes-supported",
3718 "multiple-document-jobs-supported",
3719 "multiple-operation-time-out",
3720 "multiple-operation-time-out-action",
3721 "natural-language-configured",
3722 "notify-attributes-supported",
3723 "notify-events-default",
3724 "notify-events-supported",
3725 "notify-lease-duration-default",
3726 "notify-lease-duration-supported",
3727 "notify-max-events-supported",
3728 "notify-pull-method-supported",
3729 "operations-supported",
3731 "printer-alert-description",
3732 "printer-camera-image-uri",
3733 "printer-charge-info",
3734 "printer-charge-info-uri",
3735 "printer-config-change-date-time",
3736 "printer-config-change-time",
3737 "printer-current-time",
3738 "printer-detailed-status-messages",
3739 "printer-dns-sd-name",
3740 "printer-fax-log-uri",
3741 "printer-get-attributes-supported",
3745 "printer-is-accepting-jobs",
3746 "printer-message-date-time",
3747 "printer-message-from-operator",
3748 "printer-message-time",
3749 "printer-more-info",
3750 "printer-service-type",
3751 "printer-settable-attributes-supported",
3753 "printer-state-message",
3754 "printer-state-reasons",
3755 "printer-static-resource-directory-uri",
3756 "printer-static-resource-k-octets-free",
3757 "printer-static-resource-k-octets-supported",
3758 "printer-strings-languages-supported",
3759 "printer-strings-uri",
3760 "printer-supply-info-uri",
3762 "printer-uri-supported",
3763 "printer-xri-supported",
3765 "reference-uri-scheme-supported",
3766 "uri-authentication-supported",
3767 "uri-security-supported",
3768 "which-jobs-supported",
3769 "xri-authentication-supported",
3770 "xri-security-supported",
3771 "xri-uri-scheme-supported"
3778 for (i
= 0, result
= 1; i
< (int)(sizeof(ignored
) / sizeof(ignored
[0])); i
++)
3780 if ((result
= strcmp(attr
, ignored
[i
])) <= 0)
3784 return (result
!= 0);
3789 * 'ippserver_error_cb()' - Log an error message.
3792 static int /* O - 1 to continue, 0 to stop */
3794 _ipp_file_t
*f
, /* I - IPP file data */
3795 void *user_data
, /* I - User data pointer (unused) */
3796 const char *error
) /* I - Error message */
3801 _cupsLangPrintf(stderr
, "%s\n", error
);
3808 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
3811 static int /* O - 1 to continue, 0 to stop */
3813 _ipp_file_t
*f
, /* I - IPP file data */
3814 _ipp_vars_t
*vars
, /* I - IPP variables */
3815 void *user_data
, /* I - User data pointer (unused) */
3816 const char *token
) /* I - Current token */
3824 * NULL token means do the initial setup - create an empty IPP message and
3828 f
->attrs
= ippNew();
3829 f
->group_tag
= IPP_TAG_PRINTER
;
3833 _cupsLangPrintf(stderr
, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token
, f
->linenum
, f
->filename
);
3841 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
3844 static ipp_t
* /* O - IPP attributes or `NULL` on error */
3845 load_ippserver_attributes(
3846 const char *servername
, /* I - Server name or `NULL` for default */
3847 int serverport
, /* I - Server port number */
3848 const char *filename
, /* I - ippserver attribute filename */
3849 cups_array_t
*docformats
) /* I - document-format-supported values */
3851 ipp_t
*attrs
; /* IPP attributes */
3852 _ipp_vars_t vars
; /* IPP variables */
3853 char temp
[256]; /* Temporary string */
3856 (void)docformats
; /* for now */
3859 * Setup callbacks and variables for the printer configuration file...
3861 * The following additional variables are supported:
3863 * - SERVERNAME: The host name of the server.
3864 * - SERVERPORT: The default port of the server.
3867 _ippVarsInit(&vars
, (_ipp_fattr_cb_t
)ippserver_attr_cb
, (_ipp_ferror_cb_t
)ippserver_error_cb
, (_ipp_ftoken_cb_t
)ippserver_token_cb
);
3871 _ippVarsSet(&vars
, "SERVERNAME", servername
);
3875 httpGetHostname(NULL
, temp
, sizeof(temp
));
3876 _ippVarsSet(&vars
, "SERVERNAME", temp
);
3879 snprintf(temp
, sizeof(temp
), "%d", serverport
);
3880 _ippVarsSet(&vars
, "SERVERPORT", temp
);
3883 * Load attributes and values for the printer...
3886 attrs
= _ippFileParse(&vars
, filename
, NULL
);
3889 * Free memory and return...
3892 _ippVarsDeinit(&vars
);
3899 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
3903 static ipp_t
* /* O - IPP attributes or `NULL` on error */
3904 load_legacy_attributes(
3905 const char *make
, /* I - Manufacturer name */
3906 const char *model
, /* I - Model name */
3907 int ppm
, /* I - pages-per-minute */
3908 int ppm_color
, /* I - pages-per-minute-color */
3909 int duplex
, /* I - Duplex support? */
3910 cups_array_t
*docformats
) /* I - document-format-supported values */
3912 int i
; /* Looping var */
3913 ipp_t
*attrs
, /* IPP attributes */
3914 *col
; /* Collection value */
3915 ipp_attribute_t
*attr
; /* Current attribute */
3916 char device_id
[1024],/* printer-device-id */
3917 *ptr
, /* Pointer into device ID */
3918 make_model
[128];/* printer-make-and-model */
3919 const char *format
, /* Current document format */
3920 *prefix
; /* Prefix for device ID */
3921 int num_media
; /* Number of media */
3922 const char * const *media
; /* List of media */
3923 int num_ready
; /* Number of loaded media */
3924 const char * const *ready
; /* List of loaded media */
3925 pwg_media_t
*pwg
; /* PWG media size information */
3926 static const char * const media_supported
[] =
3927 { /* media-supported values */
3928 "na_letter_8.5x11in", /* Letter */
3929 "na_legal_8.5x14in", /* Legal */
3930 "iso_a4_210x297mm", /* A4 */
3931 "na_number-10_4.125x9.5in", /* #10 Envelope */
3932 "iso_dl_110x220mm" /* DL Envelope */
3934 static const char * const media_supported_color
[] =
3935 { /* media-supported values */
3936 "na_letter_8.5x11in", /* Letter */
3937 "na_legal_8.5x14in", /* Legal */
3938 "iso_a4_210x297mm", /* A4 */
3939 "na_number-10_4.125x9.5in", /* #10 Envelope */
3940 "iso_dl_110x220mm", /* DL Envelope */
3941 "na_index-3x5_3x5in", /* Photo 3x5 */
3942 "oe_photo-l_3.5x5in", /* Photo L */
3943 "na_index-4x6_4x6in", /* Photo 4x6 */
3944 "iso_a6_105x148mm", /* A6 */
3945 "na_5x7_5x7in" /* Photo 5x7 aka 2L */
3946 "iso_a5_148x210mm", /* A5 */
3948 static const char * const media_ready
[] =
3949 { /* media-ready values */
3950 "na_letter_8.5x11in", /* Letter */
3951 "na_number-10_4.125x9.5in" /* #10 */
3953 static const char * const media_ready_color
[] =
3954 { /* media-ready values */
3955 "na_letter_8.5x11in", /* Letter */
3956 "na_index-4x6_4x6in" /* Photo 4x6 */
3958 static const char * const media_source_supported
[] =
3959 { /* media-source-supported values */
3963 "by-pass-tray" /* AKA multi-purpose tray */
3965 static const char * const media_source_supported_color
[] =
3966 { /* media-source-supported values */
3971 static const char * const media_type_supported
[] =
3972 { /* media-type-supported values */
3979 "stationery-letterhead",
3982 static const char * const media_type_supported_color
[] =
3983 { /* media-type-supported values */
3990 "stationery-letterhead",
3992 "photographic-glossy",
3993 "photographic-high-gloss",
3994 "photographic-matte",
3995 "photographic-satin",
3996 "photographic-semi-gloss"
3998 static const int media_bottom_margin_supported
[] =
3999 { /* media-bottom-margin-supported values */
4002 static const int media_bottom_margin_supported_color
[] =
4003 { /* media-bottom/top-margin-supported values */
4005 1168 /* 0.46" (common HP inkjet bottom margin) */
4007 static const int media_lr_margin_supported
[] =
4008 { /* media-left/right-margin-supported values */
4009 340, /* 3.4mm (historical HP PCL A4 margin) */
4012 static const int media_lr_margin_supported_color
[] =
4013 { /* media-left/right-margin-supported values */
4015 340, /* 3.4mm (historical HP PCL A4 margin) */
4018 static const int media_top_margin_supported
[] =
4019 { /* media-top-margin-supported values */
4022 static const int media_top_margin_supported_color
[] =
4023 { /* media-top/top-margin-supported values */
4025 102 /* 0.04" (common HP inkjet top margin */
4027 static const int orientation_requested_supported
[4] =
4028 { /* orientation-requested-supported values */
4029 IPP_ORIENT_PORTRAIT
,
4030 IPP_ORIENT_LANDSCAPE
,
4031 IPP_ORIENT_REVERSE_LANDSCAPE
,
4032 IPP_ORIENT_REVERSE_PORTRAIT
4034 static const char * const overrides_supported
[] =
4035 { /* overrides-supported values */
4039 "orientation-requested",
4042 static const char * const print_color_mode_supported
[] =
4043 { /* print-color-mode-supported values */
4046 static const char * const print_color_mode_supported_color
[] =
4047 { /* print-color-mode-supported values */
4052 static const int print_quality_supported
[] =
4053 { /* print-quality-supported values */
4058 static const char * const printer_input_tray
[] =
4059 { /* printer-input-tray values */
4060 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4061 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
4062 "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
4063 "type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
4065 static const char * const printer_input_tray_color
[] =
4066 { /* printer-input-tray values */
4067 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4068 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
4069 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
4071 static const char * const printer_supply
[] =
4072 { /* printer-supply values */
4073 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4074 "maxcapacity=100;level=25;colorantname=unknown;",
4075 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4076 "maxcapacity=100;level=75;colorantname=black;"
4078 static const char * const printer_supply_color
[] =
4079 { /* printer-supply values */
4080 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4081 "maxcapacity=100;level=25;colorantname=unknown;",
4082 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4083 "maxcapacity=100;level=75;colorantname=black;",
4084 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4085 "maxcapacity=100;level=50;colorantname=cyan;",
4086 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4087 "maxcapacity=100;level=33;colorantname=magenta;",
4088 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4089 "maxcapacity=100;level=67;colorantname=yellow;"
4091 static const char * const printer_supply_description
[] =
4092 { /* printer-supply-description values */
4096 static const char * const printer_supply_description_color
[] =
4097 { /* printer-supply-description values */
4104 static const int pwg_raster_document_resolution_supported
[] =
4109 static const char * const pwg_raster_document_type_supported
[] =
4114 static const char * const pwg_raster_document_type_supported_color
[] =
4121 static const char * const sides_supported
[] =
4122 { /* sides-supported values */
4124 "two-sided-long-edge",
4125 "two-sided-short-edge"
4127 static const char * const urf_supported
[] =
4128 { /* urf-supported values */
4136 static const char * const urf_supported_color
[] =
4137 { /* urf-supported values */
4140 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4146 static const char * const urf_supported_color_duplex
[] =
4147 { /* urf-supported values */
4150 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4157 static const char * const urf_supported_duplex
[] =
4158 { /* urf-supported values */
4173 num_media
= (int)(sizeof(media_supported_color
) / sizeof(media_supported_color
[0]));
4174 media
= media_supported_color
;
4175 num_ready
= (int)(sizeof(media_ready_color
) / sizeof(media_ready_color
[0]));
4176 ready
= media_ready_color
;
4180 num_media
= (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
4181 media
= media_supported
;
4182 num_ready
= (int)(sizeof(media_ready
) / sizeof(media_ready
[0]));
4183 ready
= media_ready
;
4186 /* color-supported */
4187 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
4189 /* copies-default */
4190 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
4192 /* copies-supported */
4193 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, (cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
|| cupsArrayFind(docformats
, (void *)"image/jpeg") != NULL
) ? 999 : 1);
4195 /* document-password-supported */
4196 if (cupsArrayFind(docformats
, (void *)"application/pdf"))
4197 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 1023);
4199 /* finishings-default */
4200 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
4202 /* finishings-supported */
4203 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
4205 /* media-bottom-margin-supported */
4207 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
);
4209 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
);
4211 /* media-col-database and media-col-default */
4212 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", num_media
, NULL
);
4213 for (i
= 0; i
< num_media
; i
++)
4215 int bottom
, left
, /* media-xxx-margins */
4217 const char *source
; /* media-source, if any */
4219 pwg
= pwgMediaForPWG(media
[i
]);
4221 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4223 source
= "photo"; /* Photo size media from photo tray */
4224 bottom
= /* Borderless margins */
4229 else if (pwg
->width
< 21000)
4231 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4232 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4233 left
= /* Left/right margins are standard */
4234 right
= media_lr_margin_supported
[1];
4235 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4237 else if (pwg
->width
== 21000)
4239 source
= NULL
; /* A4 from any tray */
4240 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4241 left
= /* Left/right margins are reduced */
4242 right
= media_lr_margin_supported
[0];
4243 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4247 source
= NULL
; /* Other size media from any tray */
4248 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4249 left
= /* Left/right margins are standard */
4250 right
= media_lr_margin_supported
[1];
4251 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4254 col
= create_media_col(media
[i
], source
, NULL
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4255 ippSetCollection(attrs
, &attr
, i
, col
);
4260 /* media-col-default */
4261 pwg
= pwgMediaForPWG(ready
[0]);
4263 if (pwg
->width
== 21000)
4264 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]);
4266 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]);
4268 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default", col
);
4272 /* media-col-ready */
4273 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, NULL
);
4274 for (i
= 0; i
< num_ready
; i
++)
4276 int bottom
, left
, /* media-xxx-margins */
4278 const char *source
, /* media-source */
4279 *type
; /* media-type */
4281 pwg
= pwgMediaForPWG(ready
[i
]);
4283 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4285 source
= "photo"; /* Photo size media from photo tray */
4286 type
= "photographic-glossy"; /* Glossy photo paper */
4287 bottom
= /* Borderless margins */
4292 else if (pwg
->width
< 21000)
4294 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4295 type
= "envelope"; /* Envelope */
4296 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4297 left
= /* Left/right margins are standard */
4298 right
= media_lr_margin_supported
[1];
4299 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4301 else if (pwg
->width
== 21000)
4303 source
= "main"; /* A4 from main tray */
4304 type
= "stationery"; /* Plain paper */
4305 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4306 left
= /* Left/right margins are reduced */
4307 right
= media_lr_margin_supported
[0];
4308 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4312 source
= "main"; /* A4 from main tray */
4313 type
= "stationery"; /* Plain paper */
4314 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4315 left
= /* Left/right margins are standard */
4316 right
= media_lr_margin_supported
[1];
4317 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4320 col
= create_media_col(ready
[i
], source
, type
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4321 ippSetCollection(attrs
, &attr
, i
, col
);
4326 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media
[0]);
4328 /* media-left/right-margin-supported */
4331 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
);
4332 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
);
4336 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
);
4337 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
);
4341 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", num_ready
, NULL
, ready
);
4343 /* media-supported */
4344 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-supported", num_media
, NULL
, media
);
4346 /* media-size-supported */
4347 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", num_media
, NULL
);
4348 for (i
= 0; i
< num_media
; i
++)
4350 pwg
= pwgMediaForPWG(media
[i
]);
4351 col
= create_media_size(pwg
->width
, pwg
->length
);
4353 ippSetCollection(attrs
, &attr
, i
, col
);
4357 /* media-source-supported */
4359 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
);
4361 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
);
4363 /* media-top-margin-supported */
4365 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
);
4367 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
);
4369 /* media-type-supported */
4371 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
);
4373 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
);
4375 /* orientation-requested-default */
4376 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-default", IPP_ORIENT_PORTRAIT
);
4378 /* orientation-requested-supported */
4379 if (cupsArrayFind(docformats
, (void *)"application/pdf") || cupsArrayFind(docformats
, (void *)"image/jpeg"))
4380 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported
) / sizeof(orientation_requested_supported
[0])), orientation_requested_supported
);
4382 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", IPP_ORIENT_PORTRAIT
);
4384 /* output-bin-default */
4386 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-up");
4388 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
4390 /* output-bin-supported */
4392 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-up");
4394 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
4396 /* overrides-supported */
4397 if (cupsArrayFind(docformats
, (void *)"application/pdf"))
4398 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "overrides-supported", (int)(sizeof(overrides_supported
) / sizeof(overrides_supported
[0])), NULL
, overrides_supported
);
4400 /* page-ranges-supported */
4401 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
);
4403 /* pages-per-minute */
4404 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute", ppm
);
4406 /* pages-per-minute-color */
4408 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute-color", ppm_color
);
4410 /* print-color-mode-default */
4411 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, ppm_color
> 0 ? "auto" : "monochrome");
4413 /* print-color-mode-supported */
4415 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
);
4417 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
);
4419 /* print-content-optimize-default */
4420 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
4422 /* print-content-optimize-supported */
4423 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
4425 /* print-quality-default */
4426 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
4428 /* print-quality-supported */
4429 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
4431 /* print-rendering-intent-default */
4432 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
4434 /* print-rendering-intent-supported */
4435 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
4437 /* printer-device-id */
4438 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
4439 ptr
= device_id
+ strlen(device_id
);
4441 for (format
= (const char *)cupsArrayFirst(docformats
); format
; format
= (const char *)cupsArrayNext(docformats
))
4443 if (!strcasecmp(format
, "application/pdf"))
4444 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
4445 else if (!strcasecmp(format
, "application/postscript"))
4446 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
4447 else if (!strcasecmp(format
, "application/vnd.hp-PCL"))
4448 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
4449 else if (!strcasecmp(format
, "image/jpeg"))
4450 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
4451 else if (!strcasecmp(format
, "image/png"))
4452 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
4453 else if (!strcasecmp(format
, "image/pwg-raster"))
4454 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPWG", prefix
);
4455 else if (!strcasecmp(format
, "image/urf"))
4456 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sURF", prefix
);
4463 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
4468 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
4470 /* printer-input-tray */
4473 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray_color
[0], (int)strlen(printer_input_tray_color
[0]));
4474 for (i
= 1; i
< (int)(sizeof(printer_input_tray_color
) / sizeof(printer_input_tray_color
[0])); i
++)
4475 ippSetOctetString(attrs
, &attr
, i
, printer_input_tray_color
[i
], (int)strlen(printer_input_tray_color
[i
]));
4479 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray
[0], (int)strlen(printer_input_tray
[0]));
4480 for (i
= 1; i
< (int)(sizeof(printer_input_tray
) / sizeof(printer_input_tray
[0])); i
++)
4481 ippSetOctetString(attrs
, &attr
, i
, printer_input_tray
[i
], (int)strlen(printer_input_tray
[i
]));
4484 /* printer-make-and-model */
4485 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4486 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, make_model
);
4488 /* printer-resolution-default */
4489 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
4491 /* printer-resolution-supported */
4492 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
4494 /* printer-supply and printer-supply-description */
4497 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply_color
[0], (int)strlen(printer_supply_color
[0]));
4498 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
4499 ippSetOctetString(attrs
, &attr
, i
, printer_supply_color
[i
], (int)strlen(printer_supply_color
[i
]));
4501 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
);
4505 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply
[0], (int)strlen(printer_supply
[0]));
4506 for (i
= 1; i
< (int)(sizeof(printer_supply
) / sizeof(printer_supply
[0])); i
++)
4507 ippSetOctetString(attrs
, &attr
, i
, printer_supply
[i
], (int)strlen(printer_supply
[i
]));
4509 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
);
4512 /* pwg-raster-document-xxx-supported */
4513 if (cupsArrayFind(docformats
, (void *)"image/pwg-raster"))
4515 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
);
4517 if (ppm_color
> 0 && duplex
)
4518 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "rotated");
4520 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "normal");
4523 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
);
4525 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
);
4529 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
4531 /* sides-supported */
4533 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", (int)(sizeof(sides_supported
) / sizeof(sides_supported
[0])), NULL
, sides_supported
);
4535 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", NULL
, "one-sided");
4538 if (cupsArrayFind(docformats
, (void *)"image/urf"))
4543 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
);
4545 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_color
) / sizeof(urf_supported_color
[0])), NULL
, urf_supported_color
);
4549 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_duplex
) / sizeof(urf_supported_duplex
[0])), NULL
, urf_supported_duplex
);
4553 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])), NULL
, urf_supported
);
4563 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4566 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4567 load_ppd_attributes(
4568 const char *ppdfile
, /* I - PPD filename */
4569 cups_array_t
*docformats
) /* I - document-format-supported values */
4571 int i
, j
; /* Looping vars */
4572 ipp_t
*attrs
; /* Attributes */
4573 ipp_attribute_t
*attr
; /* Current attribute */
4574 ipp_t
*col
; /* Current collection value */
4575 ppd_file_t
*ppd
; /* PPD data */
4576 ppd_attr_t
*ppd_attr
; /* PPD attribute */
4577 ppd_choice_t
*ppd_choice
; /* PPD choice */
4578 ppd_size_t
*ppd_size
; /* Default PPD size */
4579 pwg_size_t
*pwg_size
, /* Current PWG size */
4580 *default_size
= NULL
; /* Default PWG size */
4581 const char *default_source
= NULL
, /* Default media source */
4582 *default_type
= NULL
; /* Default media type */
4583 pwg_map_t
*pwg_map
; /* Mapping from PWG to PPD keywords */
4584 _ppd_cache_t
*pc
; /* PPD cache */
4585 _pwg_finishings_t
*finishings
; /* Current finishings value */
4586 const char *template; /* Current finishings-template value */
4587 int num_margins
; /* Number of media-xxx-margin-supported values */
4588 int margins
[10]; /* media-xxx-margin-supported values */
4589 int xres
, /* Default horizontal resolution */
4590 yres
; /* Default vertical resolution */
4591 int num_urf
; /* Number of urf-supported values */
4592 const char *urf
[10]; /* urf-supported values */
4593 char urf_rs
[32]; /* RS value */
4594 static const int orientation_requested_supported
[4] =
4595 { /* orientation-requested-supported values */
4596 IPP_ORIENT_PORTRAIT
,
4597 IPP_ORIENT_LANDSCAPE
,
4598 IPP_ORIENT_REVERSE_LANDSCAPE
,
4599 IPP_ORIENT_REVERSE_PORTRAIT
4601 static const char * const overrides_supported
[] =
4602 { /* overrides-supported */
4606 "orientation-requested",
4609 static const char * const print_color_mode_supported
[] =
4610 { /* print-color-mode-supported values */
4613 static const char * const print_color_mode_supported_color
[] =
4614 { /* print-color-mode-supported values */
4619 static const int print_quality_supported
[] =
4620 { /* print-quality-supported values */
4625 static const char * const printer_supply
[] =
4626 { /* printer-supply values */
4627 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4628 "maxcapacity=100;level=25;colorantname=unknown;",
4629 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4630 "maxcapacity=100;level=75;colorantname=black;"
4632 static const char * const printer_supply_color
[] =
4633 { /* printer-supply values */
4634 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4635 "maxcapacity=100;level=25;colorantname=unknown;",
4636 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4637 "maxcapacity=100;level=75;colorantname=black;",
4638 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4639 "maxcapacity=100;level=50;colorantname=cyan;",
4640 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4641 "maxcapacity=100;level=33;colorantname=magenta;",
4642 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4643 "maxcapacity=100;level=67;colorantname=yellow;"
4645 static const char * const printer_supply_description
[] =
4646 { /* printer-supply-description values */
4650 static const char * const printer_supply_description_color
[] =
4651 { /* printer-supply-description values */
4658 static const char * const pwg_raster_document_type_supported
[] =
4663 static const char * const pwg_raster_document_type_supported_color
[] =
4670 static const char * const sides_supported
[] =
4671 { /* sides-supported values */
4673 "two-sided-long-edge",
4674 "two-sided-short-edge"
4679 * Open the PPD file...
4682 if ((ppd
= ppdOpenFile(ppdfile
)) == NULL
)
4684 ppd_status_t status
; /* Load error */
4686 status
= ppdLastError(&i
);
4687 _cupsLangPrintf(stderr
, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile
, ppdErrorString(status
), i
);
4691 ppdMarkDefaults(ppd
);
4693 pc
= _ppdCacheCreateWithPPD(ppd
);
4695 if ((ppd_size
= ppdPageSize(ppd
, NULL
)) != NULL
)
4698 * Look up default size...
4701 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4703 if (!strcmp(pwg_size
->map
.ppd
, ppd_size
->name
))
4705 default_size
= pwg_size
;
4714 * Default to A4 or Letter...
4717 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4719 if (!strcmp(pwg_size
->map
.ppd
, "Letter") || !strcmp(pwg_size
->map
.ppd
, "A4"))
4721 default_size
= pwg_size
;
4727 default_size
= pc
->sizes
; /* Last resort: first size */
4730 if ((ppd_choice
= ppdFindMarkedChoice(ppd
, "InputSlot")) != NULL
)
4731 default_source
= _ppdCacheGetSource(pc
, ppd_choice
->choice
);
4733 if ((ppd_choice
= ppdFindMarkedChoice(ppd
, "MediaType")) != NULL
)
4734 default_source
= _ppdCacheGetType(pc
, ppd_choice
->choice
);
4736 if ((ppd_attr
= ppdFindAttr(ppd
, "DefaultResolution", NULL
)) != NULL
)
4739 * Use the PPD-defined default resolution...
4742 if ((i
= sscanf(ppd_attr
->value
, "%dx%d", &xres
, &yres
)) == 1)
4750 * Use default of 300dpi...
4756 snprintf(urf_rs
, sizeof(urf_rs
), "RS%d", yres
< xres
? yres
: xres
);
4759 urf
[num_urf
++] = "V1.4";
4760 urf
[num_urf
++] = "CP1";
4761 urf
[num_urf
++] = urf_rs
;
4762 urf
[num_urf
++] = "W8";
4763 if (pc
->sides_2sided_long
)
4764 urf
[num_urf
++] = "DM1";
4765 if (ppd
->color_device
)
4766 urf
[num_urf
++] = "SRGB24";
4769 * PostScript printers accept PDF via one of the CUPS PDF to PostScript
4770 * filters, along with PostScript (of course) and JPEG...
4773 cupsArrayAdd(docformats
, "application/pdf");
4774 cupsArrayAdd(docformats
, "application/postscript");
4775 cupsArrayAdd(docformats
, "image/jpeg");
4778 * Create the attributes...
4783 /* color-supported */
4784 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", (char)ppd
->color_device
);
4786 /* copies-default */
4787 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
4789 /* copies-supported */
4790 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
4792 /* document-password-supported */
4793 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
4795 /* finishing-template-supported */
4796 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template-supported", cupsArrayCount(pc
->templates
) + 1, NULL
, NULL
);
4797 ippSetString(attrs
, &attr
, 0, "none");
4798 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4799 ippSetString(attrs
, &attr
, i
, template);
4801 /* finishings-col-database */
4802 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "finishings-col-database", cupsArrayCount(pc
->templates
) + 1, NULL
);
4805 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4806 ippSetCollection(attrs
, &attr
, 0, col
);
4809 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4812 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, template);
4813 ippSetCollection(attrs
, &attr
, i
, col
);
4817 /* finishings-col-default */
4819 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4820 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "finishings-col-default", col
);
4823 /* finishings-col-ready */
4824 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "finishings-col-ready", cupsArrayCount(pc
->templates
) + 1, NULL
);
4827 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4828 ippSetCollection(attrs
, &attr
, 0, col
);
4831 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4834 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, template);
4835 ippSetCollection(attrs
, &attr
, i
, col
);
4839 /* finishings-col-supported */
4840 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishings-col-supported", NULL
, "finishing-template");
4842 /* finishings-default */
4843 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
4845 /* finishings-ready */
4846 attr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-ready", cupsArrayCount(pc
->finishings
) + 1, NULL
);
4847 ippSetInteger(attrs
, &attr
, 0, IPP_FINISHINGS_NONE
);
4848 for (i
= 1, finishings
= (_pwg_finishings_t
*)cupsArrayFirst(pc
->finishings
); finishings
; i
++, finishings
= (_pwg_finishings_t
*)cupsArrayNext(pc
->finishings
))
4849 ippSetInteger(attrs
, &attr
, i
, finishings
->value
);
4851 /* finishings-supported */
4852 attr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", cupsArrayCount(pc
->finishings
) + 1, NULL
);
4853 ippSetInteger(attrs
, &attr
, 0, IPP_FINISHINGS_NONE
);
4854 for (i
= 1, finishings
= (_pwg_finishings_t
*)cupsArrayFirst(pc
->finishings
); finishings
; i
++, finishings
= (_pwg_finishings_t
*)cupsArrayNext(pc
->finishings
))
4855 ippSetInteger(attrs
, &attr
, i
, finishings
->value
);
4857 /* media-bottom-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
->bottom
)
4866 if (j
>= num_margins
)
4867 margins
[num_margins
++] = pwg_size
->bottom
;
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-bottom-margin-supported", num_margins
, margins
);
4886 /* media-col-database */
4887 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", pc
->num_sizes
, NULL
);
4888 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4890 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
);
4891 ippSetCollection(attrs
, &attr
, i
, col
);
4895 /* media-col-default */
4896 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
);
4897 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default", col
);
4900 /* media-col-ready */
4901 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
);
4902 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-ready", col
);
4906 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-default", NULL
, default_size
->map
.pwg
);
4908 /* media-left-margin-supported */
4909 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
++)
4911 for (j
= 0; j
< num_margins
; j
++)
4913 if (margins
[j
] == pwg_size
->left
)
4917 if (j
>= num_margins
)
4918 margins
[num_margins
++] = pwg_size
->left
;
4921 for (i
= 0; i
< (num_margins
- 1); i
++)
4923 for (j
= i
+ 1; j
< num_margins
; j
++)
4925 if (margins
[i
] > margins
[j
])
4927 int mtemp
= margins
[i
];
4929 margins
[i
] = margins
[j
];
4935 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin-supported", num_margins
, margins
);
4938 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", NULL
, default_size
->map
.pwg
);
4940 /* media-right-margin-supported */
4941 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
++)
4943 for (j
= 0; j
< num_margins
; j
++)
4945 if (margins
[j
] == pwg_size
->right
)
4949 if (j
>= num_margins
)
4950 margins
[num_margins
++] = pwg_size
->right
;
4953 for (i
= 0; i
< (num_margins
- 1); i
++)
4955 for (j
= i
+ 1; j
< num_margins
; j
++)
4957 if (margins
[i
] > margins
[j
])
4959 int mtemp
= margins
[i
];
4961 margins
[i
] = margins
[j
];
4967 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin-supported", num_margins
, margins
);
4969 /* media-supported */
4970 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-supported", pc
->num_sizes
, NULL
, NULL
);
4971 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4972 ippSetString(attrs
, &attr
, i
, pwg_size
->map
.pwg
);
4974 /* media-size-supported */
4975 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", pc
->num_sizes
, NULL
);
4976 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4978 col
= create_media_size(pwg_size
->width
, pwg_size
->length
);
4979 ippSetCollection(attrs
, &attr
, i
, col
);
4983 /* media-source-supported */
4984 if (pc
->num_sources
> 0)
4986 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source-supported", pc
->num_sources
, NULL
, NULL
);
4987 for (i
= 0, pwg_map
= pc
->sources
; i
< pc
->num_sources
; i
++, pwg_map
++)
4988 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
4992 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-source-supported", NULL
, "auto");
4995 /* media-top-margin-supported */
4996 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
++)
4998 for (j
= 0; j
< num_margins
; j
++)
5000 if (margins
[j
] == pwg_size
->top
)
5004 if (j
>= num_margins
)
5005 margins
[num_margins
++] = pwg_size
->top
;
5008 for (i
= 0; i
< (num_margins
- 1); i
++)
5010 for (j
= i
+ 1; j
< num_margins
; j
++)
5012 if (margins
[i
] > margins
[j
])
5014 int mtemp
= margins
[i
];
5016 margins
[i
] = margins
[j
];
5022 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin-supported", num_margins
, margins
);
5024 /* media-type-supported */
5025 if (pc
->num_types
> 0)
5027 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type-supported", pc
->num_types
, NULL
, NULL
);
5028 for (i
= 0, pwg_map
= pc
->types
; i
< pc
->num_types
; i
++, pwg_map
++)
5029 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
5033 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-type-supported", NULL
, "auto");
5036 /* orientation-requested-default */
5037 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-default", IPP_ORIENT_PORTRAIT
);
5039 /* orientation-requested-supported */
5040 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported
) / sizeof(orientation_requested_supported
[0])), orientation_requested_supported
);
5042 /* output-bin-default */
5043 if (pc
->num_bins
> 0)
5044 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "output-bin-default", NULL
, pc
->bins
->pwg
);
5046 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
5048 /* output-bin-supported */
5049 if (pc
->num_bins
> 0)
5051 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "output-bin-supported", pc
->num_bins
, NULL
, NULL
);
5052 for (i
= 0, pwg_map
= pc
->bins
; i
< pc
->num_bins
; i
++, pwg_map
++)
5053 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
5057 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
5060 /* overrides-supported */
5061 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides_supported
) / sizeof(overrides_supported
[0])), NULL
, overrides_supported
);
5063 /* page-ranges-supported */
5064 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
5066 /* pages-per-minute */
5067 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute", ppd
->throughput
);
5069 /* pages-per-minute-color */
5070 if (ppd
->color_device
)
5071 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute-color", ppd
->throughput
);
5073 /* print-color-mode-default */
5074 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, ppd
->color_device
? "auto" : "monochrome");
5076 /* print-color-mode-supported */
5077 if (ppd
->color_device
)
5078 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
);
5080 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
);
5082 /* print-content-optimize-default */
5083 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
5085 /* print-content-optimize-supported */
5086 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
5088 /* print-quality-default */
5089 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
5091 /* print-quality-supported */
5092 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
5094 /* print-rendering-intent-default */
5095 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
5097 /* print-rendering-intent-supported */
5098 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
5100 /* printer-device-id */
5101 if ((ppd_attr
= ppdFindAttr(ppd
, "1284DeviceId", NULL
)) != NULL
)
5104 * Use the device ID string from the PPD...
5107 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, ppd_attr
->value
);
5112 * Synthesize a device ID string...
5115 char device_id
[1024]; /* Device ID string */
5117 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;CMD:PS;", ppd
->manufacturer
, ppd
->modelname
);
5119 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
5122 /* printer-input-tray */
5123 if (pc
->num_sources
> 0)
5125 for (i
= 0, attr
= NULL
; i
< pc
->num_sources
; i
++)
5127 char input_tray
[1024]; /* printer-input-tray value */
5129 if (!strcmp(pc
->sources
[i
].pwg
, "manual") || strstr(pc
->sources
[i
].pwg
, "-man") != NULL
)
5130 snprintf(input_tray
, sizeof(input_tray
), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc
->sources
[i
].pwg
);
5132 snprintf(input_tray
, sizeof(input_tray
), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc
->sources
[i
].pwg
);
5135 ippSetOctetString(attrs
, &attr
, i
, input_tray
, (int)strlen(input_tray
));
5137 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", input_tray
, (int)strlen(input_tray
));
5142 static const char *printer_input_tray
= "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto";
5144 ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray
, (int)strlen(printer_input_tray
));
5147 /* printer-make-and-model */
5148 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, ppd
->nickname
);
5150 /* printer-resolution-default */
5151 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, xres
, yres
);
5153 /* printer-resolution-supported */
5154 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, xres
, yres
);
5156 /* printer-supply and printer-supply-description */
5157 if (ppd
->color_device
)
5159 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply_color
[0], (int)strlen(printer_supply_color
[0]));
5160 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
5161 ippSetOctetString(attrs
, &attr
, i
, printer_supply_color
[i
], (int)strlen(printer_supply_color
[i
]));
5163 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
);
5167 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply
[0], (int)strlen(printer_supply
[0]));
5168 for (i
= 1; i
< (int)(sizeof(printer_supply
) / sizeof(printer_supply
[0])); i
++)
5169 ippSetOctetString(attrs
, &attr
, i
, printer_supply
[i
], (int)strlen(printer_supply
[i
]));
5171 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
);
5174 /* pwg-raster-document-xxx-supported */
5175 if (cupsArrayFind(docformats
, (void *)"image/pwg-raster"))
5177 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH
, xres
, yres
);
5179 if (pc
->sides_2sided_long
)
5180 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "normal");
5182 if (ppd
->color_device
)
5183 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
);
5185 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
);
5189 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
5191 /* sides-supported */
5192 if (pc
->sides_2sided_long
)
5193 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", (int)(sizeof(sides_supported
) / sizeof(sides_supported
[0])), NULL
, sides_supported
);
5195 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", NULL
, "one-sided");
5198 if (cupsArrayFind(docformats
, (void *)"image/urf"))
5199 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", num_urf
, NULL
, urf
);
5202 * Free the PPD file and return the attributes...
5205 _ppdCacheDestroy(pc
);
5211 #endif /* !CUPS_LITE */
5215 * 'parse_options()' - Parse URL options into CUPS options.
5217 * The client->options string is destroyed by this function.
5220 static int /* O - Number of options */
5221 parse_options(ippeve_client_t
*client
, /* I - Client */
5222 cups_option_t
**options
)/* O - Options */
5224 char *name
, /* Name */
5226 *next
; /* Next name=value pair */
5227 int num_options
= 0; /* Number of options */
5232 for (name
= client
->options
; name
&& *name
; name
= next
)
5234 if ((value
= strchr(name
, '=')) == NULL
)
5238 if ((next
= strchr(value
, '&')) != NULL
)
5241 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5244 return (num_options
);
5249 * 'process_attr_message()' - Process an ATTR: message from a command.
5253 process_attr_message(
5254 ippeve_job_t
*job
, /* I - Job */
5255 char *message
) /* I - Message */
5257 int i
, /* Looping var */
5258 num_options
= 0; /* Number of name=value pairs */
5259 cups_option_t
*options
= NULL
, /* name=value pairs from message */
5260 *option
; /* Current option */
5261 ipp_attribute_t
*attr
; /* Current attribute */
5265 * Grab attributes from the message line...
5268 num_options
= cupsParseOptions(message
+ 5, num_options
, &options
);
5271 * Loop through the options and record them in the printer or job objects...
5274 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
5276 if (!strcmp(option
->name
, "job-impressions"))
5279 * Update job-impressions attribute...
5282 job
->impressions
= atoi(option
->value
);
5284 else if (!strcmp(option
->name
, "job-impressions-completed"))
5287 * Update job-impressions-completed attribute...
5290 job
->impcompleted
= atoi(option
->value
);
5292 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"))
5295 * Update Printer Status attribute...
5298 _cupsRWLockWrite(&job
->printer
->rwlock
);
5300 if ((attr
= ippFindAttribute(job
->printer
->attrs
, option
->name
, IPP_TAG_ZERO
)) != NULL
)
5301 ippDeleteAttribute(job
->printer
->attrs
, attr
);
5303 cupsEncodeOption(job
->printer
->attrs
, IPP_TAG_PRINTER
, option
->name
, option
->value
);
5305 _cupsRWUnlock(&job
->printer
->rwlock
);
5310 * Something else that isn't currently supported...
5313 fprintf(stderr
, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job
->id
, option
->name
, option
->value
);
5317 cupsFreeOptions(num_options
, options
);
5322 * 'process_client()' - Process client requests on a thread.
5325 static void * /* O - Exit status */
5326 process_client(ippeve_client_t
*client
) /* I - Client */
5329 * Loop until we are out of requests or timeout (30 seconds)...
5333 int first_time
= 1; /* First time request? */
5334 #endif /* HAVE_SSL */
5336 while (httpWait(client
->http
, 30000))
5342 * See if we need to negotiate a TLS connection...
5345 char buf
[1]; /* First byte from client */
5347 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5349 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5351 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5353 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5357 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5362 #endif /* HAVE_SSL */
5364 if (!process_http(client
))
5369 * Close the conection to the client and return...
5372 delete_client(client
);
5379 * 'process_http()' - Process a HTTP request.
5382 int /* O - 1 on success, 0 on failure */
5383 process_http(ippeve_client_t
*client
) /* I - Client connection */
5385 char uri
[1024]; /* URI */
5386 http_state_t http_state
; /* HTTP state */
5387 http_status_t http_status
; /* HTTP status */
5388 ipp_state_t ipp_state
; /* State of IPP transfer */
5389 char scheme
[32], /* Method/scheme */
5390 userpass
[128], /* Username:password */
5391 hostname
[HTTP_MAX_HOST
];
5393 int port
; /* Port number */
5394 static const char * const http_states
[] =
5395 { /* Strings for logging HTTP method */
5416 * Clear state variables...
5419 ippDelete(client
->request
);
5420 ippDelete(client
->response
);
5422 client
->request
= NULL
;
5423 client
->response
= NULL
;
5424 client
->operation
= HTTP_STATE_WAITING
;
5427 * Read a request from the connection...
5430 while ((http_state
= httpReadRequest(client
->http
, uri
,
5431 sizeof(uri
))) == HTTP_STATE_WAITING
)
5435 * Parse the request line...
5438 if (http_state
== HTTP_STATE_ERROR
)
5440 if (httpError(client
->http
) == EPIPE
)
5441 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5443 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
, strerror(httpError(client
->http
)));
5447 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5449 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5450 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5453 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5455 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5456 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5460 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
], uri
);
5463 * Separate the URI into its components...
5466 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5467 userpass
, sizeof(userpass
),
5468 hostname
, sizeof(hostname
), &port
,
5469 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5470 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5472 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5473 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5477 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5478 *(client
->options
)++ = '\0';
5481 * Process the request...
5484 client
->start
= time(NULL
);
5485 client
->operation
= httpGetState(client
->http
);
5488 * Parse incoming parameters until the status changes...
5491 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5493 if (http_status
!= HTTP_STATUS_OK
)
5495 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5499 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5500 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5503 * HTTP/1.1 and higher require the "Host:" field...
5506 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5511 * Handle HTTP Upgrade...
5514 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
5518 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5520 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5523 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5525 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5527 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5531 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5534 #endif /* HAVE_SSL */
5536 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5541 * Handle HTTP Expect...
5544 if (httpGetExpect(client
->http
) &&
5545 (client
->operation
== HTTP_STATE_POST
||
5546 client
->operation
== HTTP_STATE_PUT
))
5548 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
5551 * Send 100-continue header...
5554 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
5560 * Send 417-expectation-failed header...
5563 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
5569 * Handle new transfers...
5572 switch (client
->operation
)
5574 case HTTP_STATE_OPTIONS
:
5576 * Do OPTIONS command...
5579 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5581 case HTTP_STATE_HEAD
:
5582 if (!strcmp(client
->uri
, "/icon.png"))
5583 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5584 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5585 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5587 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5589 case HTTP_STATE_GET
:
5590 if (!strcmp(client
->uri
, "/icon.png"))
5593 * Send PNG icon file.
5596 if (client
->printer
->icon
)
5598 int fd
; /* Icon file */
5599 struct stat fileinfo
; /* Icon file information */
5600 char buffer
[4096]; /* Copy buffer */
5601 ssize_t bytes
; /* Bytes */
5603 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5605 if (!stat(client
->printer
->icon
, &fileinfo
) && (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5607 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", (size_t)fileinfo
.st_size
))
5613 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5614 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5616 httpFlushWrite(client
->http
);
5621 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5625 fputs("Icon file is internal printer.png.\n", stderr
);
5627 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", sizeof(printer_png
)))
5630 httpWrite2(client
->http
, (const char *)printer_png
, sizeof(printer_png
));
5631 httpFlushWrite(client
->http
);
5634 else if (!strcmp(client
->uri
, "/"))
5637 * Show web status page...
5640 return (show_status(client
));
5642 else if (!strcmp(client
->uri
, "/media"))
5645 * Show web media page...
5648 return (show_media(client
));
5650 else if (!strcmp(client
->uri
, "/supplies"))
5653 * Show web supplies page...
5656 return (show_supplies(client
));
5659 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5662 case HTTP_STATE_POST
:
5663 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5667 * Not an IPP request...
5670 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5674 * Read the IPP request...
5677 client
->request
= ippNew();
5679 while ((ipp_state
= ippRead(client
->http
,
5680 client
->request
)) != IPP_STATE_DATA
)
5682 if (ipp_state
== IPP_STATE_ERROR
)
5684 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
, cupsLastErrorString());
5685 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5691 * Now that we have the IPP request, process the request...
5694 return (process_ipp(client
));
5697 break; /* Anti-compiler-warning-code */
5705 * 'process_ipp()' - Process an IPP request.
5708 static int /* O - 1 on success, 0 on error */
5709 process_ipp(ippeve_client_t
*client
) /* I - Client */
5711 ipp_tag_t group
; /* Current group tag */
5712 ipp_attribute_t
*attr
; /* Current attribute */
5713 ipp_attribute_t
*charset
; /* Character set attribute */
5714 ipp_attribute_t
*language
; /* Language attribute */
5715 ipp_attribute_t
*uri
; /* Printer URI attribute */
5716 int major
, minor
; /* Version number */
5717 const char *name
; /* Name of attribute */
5720 debug_attributes("Request", client
->request
, 1);
5723 * First build an empty response message for this request...
5726 client
->operation_id
= ippGetOperation(client
->request
);
5727 client
->response
= ippNewResponse(client
->request
);
5730 * Then validate the request header and required attributes...
5733 major
= ippGetVersion(client
->request
, &minor
);
5735 if (major
< 1 || major
> 2)
5738 * Return an error, since we only support IPP 1.x and 2.x.
5741 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
5743 else if ((major
* 10 + minor
) > MaxVersion
)
5745 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5746 httpFlush(client
->http
); /* Flush trailing (junk) data */
5748 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5751 else if (ippGetRequestId(client
->request
) <= 0)
5753 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
5755 else if (!ippFirstAttribute(client
->request
))
5757 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
5762 * Make sure that the attributes are provided in the correct order and
5763 * don't repeat groups...
5766 for (attr
= ippFirstAttribute(client
->request
),
5767 group
= ippGetGroupTag(attr
);
5769 attr
= ippNextAttribute(client
->request
))
5771 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5774 * Out of order; return an error...
5777 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5778 "Attribute groups are out of order (%x < %x).",
5779 ippGetGroupTag(attr
), group
);
5783 group
= ippGetGroupTag(attr
);
5789 * Then make sure that the first three attributes are:
5791 * attributes-charset
5792 * attributes-natural-language
5793 * printer-uri/job-uri
5796 attr
= ippFirstAttribute(client
->request
);
5797 name
= ippGetName(attr
);
5798 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5799 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5804 attr
= ippNextAttribute(client
->request
);
5805 name
= ippGetName(attr
);
5807 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5808 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5813 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5814 IPP_TAG_URI
)) != NULL
)
5816 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5817 IPP_TAG_URI
)) != NULL
)
5823 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5824 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5827 * Bad character set...
5830 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5831 "Unsupported character set \"%s\".",
5832 ippGetString(charset
, 0, NULL
));
5834 else if (!charset
|| !language
|| !uri
)
5837 * Return an error, since attributes-charset,
5838 * attributes-natural-language, and printer-uri/job-uri are required
5839 * for all operations.
5842 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5843 "Missing required attributes.");
5847 char scheme
[32], /* URI scheme */
5848 userpass
[32], /* Username/password in URI */
5849 host
[256], /* Host name in URI */
5850 resource
[256]; /* Resource path in URI */
5851 int port
; /* Port number in URI */
5853 name
= ippGetName(uri
);
5855 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5856 scheme
, sizeof(scheme
),
5857 userpass
, sizeof(userpass
),
5858 host
, sizeof(host
), &port
,
5859 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5860 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5861 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5862 else if ((!strcmp(name
, "job-uri") &&
5863 strncmp(resource
, "/ipp/print/", 11)) ||
5864 (!strcmp(name
, "printer-uri") &&
5865 strcmp(resource
, "/ipp/print")))
5866 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5867 name
, ippGetString(uri
, 0, NULL
));
5871 * Try processing the operation...
5874 switch (ippGetOperation(client
->request
))
5876 case IPP_OP_PRINT_JOB
:
5877 ipp_print_job(client
);
5880 case IPP_OP_PRINT_URI
:
5881 ipp_print_uri(client
);
5884 case IPP_OP_VALIDATE_JOB
:
5885 ipp_validate_job(client
);
5888 case IPP_OP_CREATE_JOB
:
5889 ipp_create_job(client
);
5892 case IPP_OP_SEND_DOCUMENT
:
5893 ipp_send_document(client
);
5896 case IPP_OP_SEND_URI
:
5897 ipp_send_uri(client
);
5900 case IPP_OP_CANCEL_JOB
:
5901 ipp_cancel_job(client
);
5904 case IPP_OP_GET_JOB_ATTRIBUTES
:
5905 ipp_get_job_attributes(client
);
5908 case IPP_OP_GET_JOBS
:
5909 ipp_get_jobs(client
);
5912 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5913 ipp_get_printer_attributes(client
);
5916 case IPP_OP_CLOSE_JOB
:
5917 ipp_close_job(client
);
5920 case IPP_OP_IDENTIFY_PRINTER
:
5921 ipp_identify_printer(client
);
5925 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5926 "Operation not supported.");
5935 * Send the HTTP header and return...
5938 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5939 httpFlush(client
->http
); /* Flush trailing (junk) data */
5941 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5942 ippLength(client
->response
)));
5947 * 'process_job()' - Process a print job.
5950 static void * /* O - Thread exit status */
5951 process_job(ippeve_job_t
*job
) /* I - Job */
5953 job
->state
= IPP_JSTATE_PROCESSING
;
5954 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5955 job
->processing
= time(NULL
);
5957 while (job
->printer
->state_reasons
& IPPEVE_PREASON_MEDIA_EMPTY
)
5959 job
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
5964 job
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_NEEDED
;
5966 if (job
->printer
->command
)
5969 * Execute a command with the job spool file and wait for it to complete...
5972 int pid
, /* Process ID */
5973 status
; /* Exit status */
5974 struct timeval start
, /* Start time */
5976 char *myargv
[3], /* Command-line arguments */
5977 *myenvp
[400]; /* Environment variables */
5978 int myenvc
; /* Number of environment variables */
5979 ipp_attribute_t
*attr
; /* Job attribute */
5980 char val
[1280], /* IPP_NAME=value */
5981 *valptr
; /* Pointer into string */
5983 int mystdout
= -1; /* File for stdout */
5984 int mypipe
[2]; /* Pipe for stderr */
5985 char line
[2048], /* Line from stderr */
5986 *ptr
, /* Pointer into line */
5987 *endptr
; /* End of line */
5988 ssize_t bytes
; /* Bytes read */
5989 #endif /* !_WIN32 */
5991 fprintf(stderr
, "[Job %d] Running command \"%s %s\".\n", job
->id
, job
->printer
->command
, job
->filename
);
5992 gettimeofday(&start
, NULL
);
5995 * Setup the command-line arguments...
5998 myargv
[0] = job
->printer
->command
;
5999 myargv
[1] = job
->filename
;
6003 * Copy the current environment, then add environment variables for every
6004 * Job attribute and Printer -default attributes...
6007 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6008 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6010 if (myenvc
> (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 32))
6012 fprintf(stderr
, "[Job %d] Too many environment variables to process job.\n", job
->id
);
6013 job
->state
= IPP_JSTATE_ABORTED
;
6017 snprintf(val
, sizeof(val
), "CONTENT_TYPE=%s", job
->format
);
6018 myenvp
[myenvc
++] = strdup(val
);
6020 if (job
->printer
->device_uri
)
6022 snprintf(val
, sizeof(val
), "DEVICE_URI=%s", job
->printer
->device_uri
);
6023 myenvp
[myenvc
++] = strdup(val
);
6027 if (job
->printer
->ppdfile
)
6029 snprintf(val
, sizeof(val
), "PPD=%s", job
->printer
->ppdfile
);
6030 myenvp
[myenvc
++] = strdup(val
);
6032 #endif /* !CUPS_LITE */
6034 for (attr
= ippFirstAttribute(job
->printer
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->printer
->attrs
))
6037 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
6038 * then add the value(s) from the attribute.
6041 const char *name
= ippGetName(attr
),
6042 /* Attribute name */
6043 *suffix
= strstr(name
, "-default");
6044 /* Suffix on attribute name */
6046 if (!suffix
|| suffix
[8])
6054 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6059 *valptr
++ = (char)toupper(*name
& 255);
6064 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6066 myenvp
[myenvc
++] = strdup(val
);
6069 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6072 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6073 * value(s) from the attribute.
6076 const char *name
= ippGetName(attr
);
6077 /* Attribute name */
6087 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6092 *valptr
++ = (char)toupper(*name
& 255);
6097 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6099 myenvp
[myenvc
++] = strdup(val
);
6104 fprintf(stderr
, "[Job %d] Too many environment variables to process job.\n", job
->id
);
6105 job
->state
= IPP_JSTATE_ABORTED
;
6109 myenvp
[myenvc
] = NULL
;
6112 * Now run the program...
6116 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6119 if (job
->printer
->device_uri
)
6121 char scheme
[32], /* URI scheme */
6122 userpass
[256], /* username:password (unused) */
6123 host
[256], /* Hostname or IP address */
6124 resource
[256]; /* Resource path */
6125 int port
; /* Port number */
6128 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
)
6130 fprintf(stderr
, "[Job %d] Bad device URI \"%s\".\n", job
->id
, job
->printer
->device_uri
);
6132 else if (!strcmp(scheme
, "file"))
6134 struct stat fileinfo
; /* See if this is a file or directory... */
6136 if (stat(resource
, &fileinfo
))
6138 if (errno
== ENOENT
)
6140 if ((mystdout
= open(resource
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) >= 0)
6141 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6143 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6146 fprintf(stderr
, "[Job %d] Unable to access \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6148 else if (S_ISDIR(fileinfo
.st_mode
))
6150 if ((mystdout
= create_job_file(job
, line
, sizeof(line
), resource
, "prn")) >= 0)
6151 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, line
);
6153 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, line
, strerror(errno
));
6155 else if (!S_ISREG(fileinfo
.st_mode
))
6157 if ((mystdout
= open(resource
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) >= 0)
6158 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6160 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6162 else if ((mystdout
= open(resource
, O_WRONLY
)) >= 0)
6163 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6165 fprintf(stderr
, "[Job %d] Unable to open \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6167 else if (!strcmp(scheme
, "socket"))
6169 http_addrlist_t
*addrlist
; /* List of addresses */
6170 char service
[32]; /* Service number */
6172 snprintf(service
, sizeof(service
), "%d", port
);
6174 if ((addrlist
= httpAddrGetList(host
, AF_UNSPEC
, service
)) == NULL
)
6175 fprintf(stderr
, "[Job %d] Unable to find \"%s\": %s\n", job
->id
, host
, cupsLastErrorString());
6176 else if (!httpAddrConnect2(addrlist
, &mystdout
, 30000, &(job
->cancel
)))
6177 fprintf(stderr
, "[Job %d] Unable to connect to \"%s\": %s\n", job
->id
, host
, cupsLastErrorString());
6179 httpAddrFreeList(addrlist
);
6183 fprintf(stderr
, "[Job %d] Unsupported device URI scheme \"%s\".\n", job
->id
, scheme
);
6186 else if ((mystdout
= create_job_file(job
, line
, sizeof(line
), job
->printer
->directory
, "prn")) >= 0)
6188 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, line
);
6192 mystdout
= open("/dev/null", O_WRONLY
);
6196 fprintf(stderr
, "[Job %d] Unable to create pipe for stderr: %s\n", job
->id
, strerror(errno
));
6197 mypipe
[0] = mypipe
[1] = -1;
6200 if ((pid
= fork()) == 0)
6203 * Child comes here...
6215 execve(job
->printer
->command
, myargv
, myenvp
);
6221 * Unable to fork process...
6224 fprintf(stderr
, "[Job %d] Unable to start job processing command: %s\n", job
->id
, strerror(errno
));
6232 * Free memory used for environment...
6236 free(myenvp
[-- myenvc
]);
6241 * Free memory used for environment...
6245 free(myenvp
[-- myenvc
]);
6248 * Close the output file in the parent process...
6254 * If the pipe exists, read from it until EOF...
6262 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6267 while ((ptr
= strchr(line
, '\n')) != NULL
)
6269 int level
= 3; /* Message log level */
6273 if (!strncmp(line
, "ATTR:", 5))
6276 * Process job/printer attribute updates.
6279 process_attr_message(job
, line
);
6281 else if (!strncmp(line
, "DEBUG:", 6))
6289 else if (!strncmp(line
, "ERROR:", 6))
6296 job
->message
= strdup(line
+ 6);
6299 else if (!strncmp(line
, "INFO:", 5))
6302 * Informational/progress message...
6308 job
->message
= strdup(line
+ 5);
6312 else if (!strncmp(line
, "STATE:", 6))
6315 * Process printer-state-reasons keywords.
6318 process_state_message(job
, line
);
6321 if (Verbosity
>= level
)
6322 fprintf(stderr
, "[Job %d] Command - %s\n", job
->id
, line
);
6326 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6336 * Wait for child to complete...
6339 # ifdef HAVE_WAITPID
6340 while (waitpid(pid
, &status
, 0) < 0);
6342 while (wait(&status
) < 0);
6343 # endif /* HAVE_WAITPID */
6350 if (WIFEXITED(status
))
6351 #endif /* !_WIN32 */
6352 fprintf(stderr
, "[Job %d] Command \"%s\" exited with status %d.\n", job
->id
, job
->printer
->command
, WEXITSTATUS(status
));
6355 fprintf(stderr
, "[Job %d] Command \"%s\" terminated with signal %d.\n", job
->id
, job
->printer
->command
, WTERMSIG(status
));
6356 #endif /* !_WIN32 */
6357 job
->state
= IPP_JSTATE_ABORTED
;
6359 else if (status
< 0)
6360 job
->state
= IPP_JSTATE_ABORTED
;
6362 fprintf(stderr
, "[Job %d] Command \"%s\" completed successfully.\n", job
->id
, job
->printer
->command
);
6365 * Report the total processing time...
6368 gettimeofday(&end
, NULL
);
6370 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
));
6375 * Sleep for a random amount of time to simulate job processing.
6378 sleep((unsigned)(5 + (CUPS_RAND() % 11)));
6382 job
->state
= IPP_JSTATE_CANCELED
;
6383 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6384 job
->state
= IPP_JSTATE_COMPLETED
;
6388 job
->completed
= time(NULL
);
6389 job
->printer
->state
= IPP_PSTATE_IDLE
;
6390 job
->printer
->active_job
= NULL
;
6397 * 'process_state_message()' - Process a STATE: message from a command.
6401 process_state_message(
6402 ippeve_job_t
*job
, /* I - Job */
6403 char *message
) /* I - Message */
6405 int i
; /* Looping var */
6406 ippeve_preason_t state_reasons
, /* printer-state-reasons values */
6407 bit
; /* Current reason bit */
6408 char *ptr
, /* Pointer into message */
6409 *next
; /* Next keyword in message */
6410 int remove
; /* Non-zero if we are removing keywords */
6414 * Skip leading "STATE:" and any whitespace...
6417 for (message
+= 6; *message
; message
++)
6418 if (*message
!= ' ' && *message
!= '\t')
6422 * Support the following forms of message:
6424 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6426 * "-keyword[,keyword,...]" to remove keywords.
6428 * "+keyword[,keyword,...]" to add keywords.
6430 * Keywords may or may not have a suffix (-report, -warning, -error) per
6434 if (*message
== '-')
6437 state_reasons
= job
->printer
->state_reasons
;
6440 else if (*message
== '+')
6443 state_reasons
= job
->printer
->state_reasons
;
6449 state_reasons
= IPPEVE_PREASON_NONE
;
6454 if ((next
= strchr(message
, ',')) != NULL
)
6457 if ((ptr
= strstr(message
, "-error")) != NULL
)
6459 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6461 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6464 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
6466 if (!strcmp(message
, ippeve_preason_strings
[i
]))
6469 state_reasons
&= ~bit
;
6471 state_reasons
|= bit
;
6481 job
->printer
->state_reasons
= state_reasons
;
6486 * 'register_printer()' - Register a printer object via Bonjour.
6489 static int /* O - 1 on success, 0 on error */
6491 ippeve_printer_t
*printer
, /* I - Printer */
6492 const char *subtypes
) /* I - Service subtype(s) */
6494 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6495 ippeve_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6496 int i
, /* Looping var */
6497 count
; /* Number of values */
6498 ipp_attribute_t
*color_supported
,
6499 *document_format_supported
,
6501 *printer_make_and_model
,
6505 *urf_supported
; /* Printer attributes */
6506 const char *value
; /* Value string */
6507 char formats
[252], /* List of supported formats */
6508 urf
[252], /* List of supported URF values */
6509 *ptr
; /* Pointer into string */
6511 color_supported
= ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_BOOLEAN
);
6512 document_format_supported
= ippFindAttribute(printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
);
6513 printer_location
= ippFindAttribute(printer
->attrs
, "printer-location", IPP_TAG_TEXT
);
6514 printer_make_and_model
= ippFindAttribute(printer
->attrs
, "printer-make-and-model", IPP_TAG_TEXT
);
6515 printer_more_info
= ippFindAttribute(printer
->attrs
, "printer-more-info", IPP_TAG_URI
);
6516 printer_uuid
= ippFindAttribute(printer
->attrs
, "printer-uuid", IPP_TAG_URI
);
6517 sides_supported
= ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
);
6518 urf_supported
= ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_KEYWORD
);
6520 for (i
= 0, count
= ippGetCount(document_format_supported
), ptr
= formats
; i
< count
; i
++)
6522 value
= ippGetString(document_format_supported
, i
, NULL
);
6524 if (!strcasecmp(value
, "application/octet-stream"))
6527 if (ptr
> formats
&& ptr
< (formats
+ sizeof(formats
) - 1))
6530 strlcpy(ptr
, value
, sizeof(formats
) - (size_t)(ptr
- formats
));
6533 if (ptr
>= (formats
+ sizeof(formats
) - 1))
6538 for (i
= 0, count
= ippGetCount(urf_supported
), ptr
= urf
; i
< count
; i
++)
6540 value
= ippGetString(urf_supported
, i
, NULL
);
6542 if (ptr
> urf
&& ptr
< (urf
+ sizeof(urf
) - 1))
6545 strlcpy(ptr
, value
, sizeof(urf
) - (size_t)(ptr
- urf
));
6548 if (ptr
>= (urf
+ sizeof(urf
) - 1))
6552 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6554 DNSServiceErrorType error
; /* Error from Bonjour */
6555 char regtype
[256]; /* Bonjour service type */
6559 * Build the TXT record for IPP...
6562 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6563 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6564 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
6565 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(value
), value
);
6566 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
6567 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(value
), value
);
6568 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
6569 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(value
), value
);
6570 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
), formats
);
6571 TXTRecordSetValue(&ipp_txt
, "Color", 1, ippGetBoolean(color_supported
, 0) ? "T" : "F");
6572 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, ippGetCount(sides_supported
) > 1 ? "T" : "F");
6573 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
6574 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(value
) - 9, value
+ 9);
6576 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6577 # endif /* HAVE_SSL */
6579 TXTRecordSetValue(&ipp_txt
, "URF", (uint8_t)strlen(urf
), urf
);
6580 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6581 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6584 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6585 * defend our service name but not actually support LPD...
6588 printer
->printer_ref
= DNSSDMaster
;
6590 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
)
6592 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_printer._tcp", error
);
6597 * Then register the _ipp._tcp (IPP) service type with the real port number to
6598 * advertise our IPP printer...
6601 printer
->ipp_ref
= DNSSDMaster
;
6603 if (subtypes
&& *subtypes
)
6604 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtypes
);
6606 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6608 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
)
6610 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
6616 * Then register the _ipps._tcp (IPP) service type with the real port number to
6617 * advertise our IPPS printer...
6620 printer
->ipps_ref
= DNSSDMaster
;
6622 if (subtypes
&& *subtypes
)
6623 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtypes
);
6625 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6627 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
)
6629 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
6632 # endif /* HAVE_SSL */
6635 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6636 * real port number to advertise our IPP printer...
6639 printer
->http_ref
= DNSSDMaster
;
6641 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
)
6643 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_http._tcp,_printer", error
);
6647 TXTRecordDeallocate(&ipp_txt
);
6649 #elif defined(HAVE_AVAHI)
6650 char temp
[256]; /* Subtype service string */
6653 * Create the TXT record...
6657 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6658 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
6659 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s", value
);
6660 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
6661 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", value
);
6662 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
6663 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", value
);
6664 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6665 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", ippGetBoolean(color_supported
, 0) ? "T" : "F");
6666 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", ippGetCount(sides_supported
) > 1 ? "T" : "F");
6667 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
6668 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", value
+ 9);
6670 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6671 # endif /* HAVE_SSL */
6673 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "URF=%s", urf
);
6674 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "txtvers=1");
6675 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "qtotal=1");
6678 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6681 avahi_threaded_poll_lock(DNSSDMaster
);
6683 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6685 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
);
6688 * Then register the _ipp._tcp (IPP)...
6691 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
);
6692 if (subtypes
&& *subtypes
)
6694 char *temptypes
= strdup(subtypes
), *start
, *end
;
6696 for (start
= temptypes
; *start
; start
= end
)
6698 if ((end
= strchr(start
, ',')) != NULL
)
6701 end
= start
+ strlen(start
);
6703 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", start
);
6704 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6712 * _ipps._tcp (IPPS) for secure printing...
6715 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
);
6716 if (subtypes
&& *subtypes
)
6718 char *temptypes
= strdup(subtypes
), *start
, *end
;
6720 for (start
= temptypes
; *start
; start
= end
)
6722 if ((end
= strchr(start
, ',')) != NULL
)
6725 end
= start
+ strlen(start
);
6727 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", start
);
6728 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6733 #endif /* HAVE_SSL */
6736 * Finally _http.tcp (HTTP) for the web interface...
6739 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
);
6740 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");
6746 avahi_entry_group_commit(printer
->ipp_ref
);
6747 avahi_threaded_poll_unlock(DNSSDMaster
);
6749 avahi_string_list_free(ipp_txt
);
6750 #endif /* HAVE_DNSSD */
6757 * 'respond_http()' - Send a HTTP response.
6760 int /* O - 1 on success, 0 on failure */
6762 ippeve_client_t
*client
, /* I - Client */
6763 http_status_t code
, /* I - HTTP status of response */
6764 const char *content_encoding
, /* I - Content-Encoding of response */
6765 const char *type
, /* I - MIME media type of response */
6766 size_t length
) /* I - Length of response */
6768 char message
[1024]; /* Text message */
6771 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6773 if (code
== HTTP_STATUS_CONTINUE
)
6776 * 100-continue doesn't send any headers...
6779 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6783 * Format an error message...
6786 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6788 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6790 type
= "text/plain";
6791 length
= strlen(message
);
6797 * Send the HTTP response header...
6800 httpClearFields(client
->http
);
6802 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6803 client
->operation
== HTTP_STATE_OPTIONS
)
6804 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6808 if (!strcmp(type
, "text/html"))
6809 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6810 "text/html; charset=utf-8");
6812 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6814 if (content_encoding
)
6815 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6818 httpSetLength(client
->http
, length
);
6820 if (httpWriteResponse(client
->http
, code
) < 0)
6824 * Send the response data...
6830 * Send a plain text message.
6833 if (httpPrintf(client
->http
, "%s", message
) < 0)
6836 if (httpWrite2(client
->http
, "", 0) < 0)
6839 else if (client
->response
)
6842 * Send an IPP response...
6845 debug_attributes("Response", client
->response
, 2);
6847 ippSetState(client
->response
, IPP_STATE_IDLE
);
6849 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6858 * 'respond_ipp()' - Send an IPP response.
6862 respond_ipp(ippeve_client_t
*client
, /* I - Client */
6863 ipp_status_t status
, /* I - status-code */
6864 const char *message
, /* I - printf-style status-message */
6865 ...) /* I - Additional args as needed */
6867 const char *formatted
= NULL
; /* Formatted message */
6870 ippSetStatusCode(client
->response
, status
);
6874 va_list ap
; /* Pointer to additional args */
6875 ipp_attribute_t
*attr
; /* New status-message attribute */
6877 va_start(ap
, message
);
6878 if ((attr
= ippFindAttribute(client
->response
, "status-message", IPP_TAG_TEXT
)) != NULL
)
6879 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6881 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
, "status-message", NULL
, message
, ap
);
6884 formatted
= ippGetString(attr
, 0, NULL
);
6888 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
, ippOpString(client
->operation_id
), ippErrorString(status
), formatted
);
6890 fprintf(stderr
, "%s %s %s\n", client
->hostname
, ippOpString(client
->operation_id
), ippErrorString(status
));
6895 * 'respond_unsupported()' - Respond with an unsupported attribute.
6899 respond_unsupported(
6900 ippeve_client_t
*client
, /* I - Client */
6901 ipp_attribute_t
*attr
) /* I - Atribute */
6903 ipp_attribute_t
*temp
; /* Copy of attribute */
6906 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, "Unsupported %s %s%s value.", ippGetName(attr
), ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)));
6908 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6909 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6914 * 'run_printer()' - Run the printer service.
6918 run_printer(ippeve_printer_t
*printer
) /* I - Printer */
6920 int num_fds
; /* Number of file descriptors */
6921 struct pollfd polldata
[3]; /* poll() data */
6922 int timeout
; /* Timeout for poll() */
6923 ippeve_client_t
*client
; /* New client */
6927 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6930 polldata
[0].fd
= printer
->ipv4
;
6931 polldata
[0].events
= POLLIN
;
6933 polldata
[1].fd
= printer
->ipv6
;
6934 polldata
[1].events
= POLLIN
;
6939 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6940 polldata
[num_fds
++].events
= POLLIN
;
6941 #endif /* HAVE_DNSSD */
6944 * Loop until we are killed or have a hard error...
6949 if (cupsArrayCount(printer
->jobs
))
6954 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6956 perror("poll() failed");
6960 if (polldata
[0].revents
& POLLIN
)
6962 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6964 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6968 _cupsThreadDetach(t
);
6972 perror("Unable to create client thread");
6973 delete_client(client
);
6978 if (polldata
[1].revents
& POLLIN
)
6980 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6982 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6986 _cupsThreadDetach(t
);
6990 perror("Unable to create client thread");
6991 delete_client(client
);
6997 if (polldata
[2].revents
& POLLIN
)
6998 DNSServiceProcessResult(DNSSDMaster
);
6999 #endif /* HAVE_DNSSD */
7002 * Clean out old jobs...
7005 clean_jobs(printer
);
7011 * 'show_media()' - Show media load state.
7014 static int /* O - 1 on success, 0 on failure */
7015 show_media(ippeve_client_t
*client
) /* I - Client connection */
7017 ippeve_printer_t
*printer
= client
->printer
;
7019 int i
, j
, /* Looping vars */
7020 num_ready
, /* Number of ready media */
7021 num_sizes
, /* Number of media sizes */
7022 num_sources
, /* Number of media sources */
7023 num_types
; /* Number of media types */
7024 ipp_attribute_t
*media_col_ready
,/* media-col-ready attribute */
7025 *media_ready
, /* media-ready attribute */
7026 *media_sizes
, /* media-supported attribute */
7027 *media_sources
, /* media-source-supported attribute */
7028 *media_types
, /* media-type-supported attribute */
7029 *input_tray
; /* printer-input-tray attribute */
7030 ipp_t
*media_col
; /* media-col value */
7031 const char *media_size
, /* media value */
7032 *media_source
, /* media-source value */
7033 *media_type
, /* media-type value */
7034 *ready_size
, /* media-col-ready media-size[-name] value */
7035 *ready_source
, /* media-col-ready media-source value */
7036 *ready_tray
, /* printer-input-tray value */
7037 *ready_type
; /* media-col-ready media-type value */
7038 char tray_str
[1024], /* printer-input-tray string value */
7039 *tray_ptr
; /* Pointer into value */
7040 int tray_len
; /* Length of printer-input-tray value */
7041 int ready_sheets
; /* printer-input-tray sheets value */
7042 int num_options
= 0;/* Number of form options */
7043 cups_option_t
*options
= NULL
;/* Form options */
7044 static const int sheets
[] = /* Number of sheets */
7056 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7059 html_header(client
, printer
->name
, 0);
7061 if ((media_col_ready
= ippFindAttribute(printer
->attrs
, "media-col-ready", IPP_TAG_BEGIN_COLLECTION
)) == NULL
)
7063 html_printf(client
, "<p>Error: No media-col-ready defined for printer.</p>\n");
7064 html_footer(client
);
7068 media_ready
= ippFindAttribute(printer
->attrs
, "media-ready", IPP_TAG_ZERO
);
7070 if ((media_sizes
= ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
)) == NULL
)
7072 html_printf(client
, "<p>Error: No media-supported defined for printer.</p>\n");
7073 html_footer(client
);
7077 if ((media_sources
= ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
)) == NULL
)
7079 html_printf(client
, "<p>Error: No media-source-supported defined for printer.</p>\n");
7080 html_footer(client
);
7084 if ((media_types
= ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
)) == NULL
)
7086 html_printf(client
, "<p>Error: No media-type-supported defined for printer.</p>\n");
7087 html_footer(client
);
7091 if ((input_tray
= ippFindAttribute(printer
->attrs
, "printer-input-tray", IPP_TAG_STRING
)) == NULL
)
7093 html_printf(client
, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7094 html_footer(client
);
7098 num_ready
= ippGetCount(media_col_ready
);
7099 num_sizes
= ippGetCount(media_sizes
);
7100 num_sources
= ippGetCount(media_sources
);
7101 num_types
= ippGetCount(media_types
);
7103 if (num_sources
!= ippGetCount(input_tray
))
7105 html_printf(client
, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7106 html_footer(client
);
7111 * Process form data if present...
7114 if (printer
->web_forms
)
7115 num_options
= parse_options(client
, &options
);
7117 if (num_options
> 0)
7120 * WARNING: A real printer/server implementation MUST NOT implement
7121 * media updates via a GET request - GET requests are supposed to be
7122 * idempotent (without side-effects) and we obviously are not
7123 * authenticating access here. This form is provided solely to
7124 * enable testing and development!
7127 char name
[255]; /* Form name */
7128 const char *val
; /* Form value */
7129 pwg_media_t
*media
; /* Media info */
7131 _cupsRWLockWrite(&printer
->rwlock
);
7133 ippDeleteAttribute(printer
->attrs
, media_col_ready
);
7134 media_col_ready
= NULL
;
7138 ippDeleteAttribute(printer
->attrs
, media_ready
);
7142 printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MEDIA_LOW
| IPPEVE_PREASON_MEDIA_EMPTY
| IPPEVE_PREASON_MEDIA_NEEDED
);
7144 for (i
= 0; i
< num_sources
; i
++)
7146 media_source
= ippGetString(media_sources
, i
, NULL
);
7148 if (!strcmp(media_source
, "auto") || !strcmp(media_source
, "manual") || strstr(media_source
, "-man") != NULL
)
7151 snprintf(name
, sizeof(name
), "size%d", i
);
7152 if ((media_size
= cupsGetOption(name
, num_options
, options
)) != NULL
&& (media
= pwgMediaForPWG(media_size
)) != NULL
)
7154 snprintf(name
, sizeof(name
), "type%d", i
);
7155 if ((media_type
= cupsGetOption(name
, num_options
, options
)) != NULL
&& !*media_type
)
7159 ippSetString(printer
->attrs
, &media_ready
, ippGetCount(media_ready
), media_size
);
7161 media_ready
= ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", NULL
, media_size
);
7163 media_col
= create_media_col(media_size
, media_source
, media_type
, media
->width
, media
->length
, -1, -1, -1, -1);
7165 if (media_col_ready
)
7166 ippSetCollection(printer
->attrs
, &media_col_ready
, ippGetCount(media_col_ready
), media_col
);
7168 media_col_ready
= ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-ready", media_col
);
7169 ippDelete(media_col
);
7174 snprintf(name
, sizeof(name
), "level%d", i
);
7175 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
7176 ready_sheets
= atoi(val
);
7180 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
);
7182 ippSetOctetString(printer
->attrs
, &input_tray
, i
, tray_str
, (int)strlen(tray_str
));
7184 if (ready_sheets
== 0)
7186 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_EMPTY
;
7187 if (printer
->active_job
)
7188 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
7190 else if (ready_sheets
< 25 && ready_sheets
> 0)
7191 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_LOW
;
7194 if (!media_col_ready
)
7195 media_col_ready
= ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
7198 media_ready
= ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
7200 _cupsRWUnlock(&printer
->rwlock
);
7203 if (printer
->web_forms
)
7204 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
7206 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
7207 for (i
= 0; i
< num_sources
; i
++)
7209 media_source
= ippGetString(media_sources
, i
, NULL
);
7211 if (!strcmp(media_source
, "auto") || !strcmp(media_source
, "manual") || strstr(media_source
, "-man") != NULL
)
7214 for (j
= 0, ready_size
= NULL
, ready_type
= NULL
; j
< num_ready
; j
++)
7216 media_col
= ippGetCollection(media_col_ready
, j
);
7217 ready_size
= ippGetString(ippFindAttribute(media_col
, "media-size-name", IPP_TAG_ZERO
), 0, NULL
);
7218 ready_source
= ippGetString(ippFindAttribute(media_col
, "media-source", IPP_TAG_ZERO
), 0, NULL
);
7219 ready_type
= ippGetString(ippFindAttribute(media_col
, "media-type", IPP_TAG_ZERO
), 0, NULL
);
7221 if (ready_source
&& !strcmp(ready_source
, media_source
))
7224 ready_source
= NULL
;
7229 html_printf(client
, "<tr><th>%s:</th>", media_source
);
7235 if (printer
->web_forms
)
7237 html_printf(client
, "<td><select name=\"size%d\"><option value=\"\">None</option>", i
);
7238 for (j
= 0; j
< num_sizes
; j
++)
7240 media_size
= ippGetString(media_sizes
, j
, NULL
);
7242 html_printf(client
, "<option%s>%s</option>", (ready_size
&& !strcmp(ready_size
, media_size
)) ? " selected" : "", media_size
);
7244 html_printf(client
, "</select>");
7247 html_printf(client
, "<td>%s", ready_size
);
7253 if (printer
->web_forms
)
7255 html_printf(client
, " <select name=\"type%d\"><option value=\"\">None</option>", i
);
7256 for (j
= 0; j
< num_types
; j
++)
7258 media_type
= ippGetString(media_types
, j
, NULL
);
7260 html_printf(client
, "<option%s>%s</option>", (ready_type
&& !strcmp(ready_type
, media_type
)) ? " selected" : "", media_type
);
7262 html_printf(client
, "</select>");
7264 else if (ready_type
)
7265 html_printf(client
, ", %s", ready_type
);
7268 * Level/sheets loaded...
7271 if ((ready_tray
= ippGetOctetString(input_tray
, i
, &tray_len
)) != NULL
)
7273 if (tray_len
> (int)(sizeof(tray_str
) - 1))
7274 tray_len
= (int)sizeof(tray_str
) - 1;
7275 memcpy(tray_str
, ready_tray
, (size_t)tray_len
);
7276 tray_str
[tray_len
] = '\0';
7278 if ((tray_ptr
= strstr(tray_str
, "level=")) != NULL
)
7279 ready_sheets
= atoi(tray_ptr
+ 6);
7286 if (printer
->web_forms
)
7288 html_printf(client
, " <select name=\"level%d\">", i
);
7289 for (j
= 0; j
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); j
++)
7291 if (!strcmp(media_source
, "by-pass-tray") && sheets
[j
] > 25)
7295 html_printf(client
, "<option value=\"%d\"%s>Unknown</option>", sheets
[j
], sheets
[j
] == ready_sheets
? " selected" : "");
7297 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[j
], sheets
[j
] == ready_sheets
? " selected" : "", sheets
[j
]);
7299 html_printf(client
, "</select></td></tr>\n");
7301 else if (ready_sheets
== 1)
7302 html_printf(client
, ", 1 sheet</td></tr>\n");
7303 else if (ready_sheets
> 0)
7304 html_printf(client
, ", %d sheets</td></tr>\n", ready_sheets
);
7306 html_printf(client
, "</td></tr>\n");
7309 if (printer
->web_forms
)
7311 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
7312 if (num_options
> 0)
7313 html_printf(client
, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
7314 html_printf(client
, "</td></tr></table></form>\n");
7316 if (num_options
> 0)
7317 html_printf(client
, "<script>\n"
7318 "setTimeout(hide_status, 3000);\n"
7319 "function hide_status() {\n"
7320 " var status = document.getElementById('status');\n"
7321 " status.style.display = 'none';\n"
7326 html_printf(client
, "</table>\n");
7328 html_footer(client
);
7335 * 'show_status()' - Show printer/system state.
7338 static int /* O - 1 on success, 0 on failure */
7339 show_status(ippeve_client_t
*client
) /* I - Client connection */
7341 ippeve_printer_t
*printer
= client
->printer
;
7343 ippeve_job_t
*job
; /* Current job */
7344 int i
; /* Looping var */
7345 ippeve_preason_t reason
; /* Current reason */
7346 static const char * const reasons
[] = /* Reason strings */
7350 "Input Tray Missing",
7351 "Marker Supply Empty",
7352 "Marker Supply Low",
7353 "Marker Waste Almost Full",
7354 "Marker Waste Full",
7365 static const char * const state_colors
[] =
7366 { /* State colors */
7368 "#EE0", /* Processing */
7369 "#C00" /* Stopped */
7373 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7376 html_header(client
, printer
->name
, printer
->state
== IPP_PSTATE_PROCESSING
? 5 : 15);
7377 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
);
7378 html_printf(client
, "<p>%s, %d job(s).", printer
->state
== IPP_PSTATE_IDLE
? "Idle" : printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(printer
->jobs
));
7379 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
7380 if (printer
->state_reasons
& reason
)
7381 html_printf(client
, "\n<br> %s", reasons
[i
]);
7382 html_printf(client
, "</p>\n");
7384 if (cupsArrayCount(printer
->jobs
) > 0)
7386 _cupsRWLockRead(&(printer
->rwlock
));
7388 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");
7389 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
); job
; job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
7391 char when
[256], /* When job queued/started/finished */
7392 hhmmss
[64]; /* Time HH:MM:SS */
7396 case IPP_JSTATE_PENDING
:
7397 case IPP_JSTATE_HELD
:
7398 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
7400 case IPP_JSTATE_PROCESSING
:
7401 case IPP_JSTATE_STOPPED
:
7402 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
7404 case IPP_JSTATE_ABORTED
:
7405 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7407 case IPP_JSTATE_CANCELED
:
7408 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7410 case IPP_JSTATE_COMPLETED
:
7411 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7415 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
);
7417 html_printf(client
, "</tbody></table>\n");
7419 _cupsRWUnlock(&(printer
->rwlock
));
7422 html_footer(client
);
7429 * 'show_supplies()' - Show printer supplies.
7432 static int /* O - 1 on success, 0 on failure */
7434 ippeve_client_t
*client
) /* I - Client connection */
7436 ippeve_printer_t
*printer
= client
->printer
;
7438 int i
, /* Looping var */
7439 num_supply
; /* Number of supplies */
7440 ipp_attribute_t
*supply
, /* printer-supply attribute */
7441 *supply_desc
; /* printer-supply-description attribute */
7442 int num_options
= 0; /* Number of form options */
7443 cups_option_t
*options
= NULL
; /* Form options */
7444 int supply_len
, /* Length of supply value */
7445 level
; /* Supply level */
7446 const char *supply_value
; /* Supply value */
7447 char supply_text
[1024], /* Supply string */
7448 *supply_ptr
; /* Pointer into supply string */
7449 static const char * const printer_supply
[] =
7450 { /* printer-supply values */
7451 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
7452 "maxcapacity=100;level=%d;colorantname=unknown;",
7453 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
7454 "maxcapacity=100;level=%d;colorantname=black;",
7455 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
7456 "maxcapacity=100;level=%d;colorantname=cyan;",
7457 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
7458 "maxcapacity=100;level=%d;colorantname=magenta;",
7459 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
7460 "maxcapacity=100;level=%d;colorantname=yellow;"
7462 static const char * const backgrounds
[] =
7463 { /* Background colors for the supply-level bars */
7464 "#777 linear-gradient(#333,#777)",
7465 "#000 linear-gradient(#666,#000)",
7466 "#0FF linear-gradient(#6FF,#0FF)",
7467 "#F0F linear-gradient(#F6F,#F0F)",
7468 "#CC0 linear-gradient(#EE6,#EE0)"
7470 static const char * const colors
[] = /* Text colors for the supply-level bars */
7480 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7483 html_header(client
, printer
->name
, 0);
7485 if ((supply
= ippFindAttribute(printer
->attrs
, "printer-supply", IPP_TAG_STRING
)) == NULL
)
7487 html_printf(client
, "<p>Error: No printer-supply defined for printer.</p>\n");
7488 html_footer(client
);
7492 num_supply
= ippGetCount(supply
);
7494 if ((supply_desc
= ippFindAttribute(printer
->attrs
, "printer-supply-description", IPP_TAG_TEXT
)) == NULL
)
7496 html_printf(client
, "<p>Error: No printer-supply-description defined for printer.</p>\n");
7497 html_footer(client
);
7501 if (num_supply
!= ippGetCount(supply_desc
))
7503 html_printf(client
, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
7504 html_footer(client
);
7508 if (printer
->web_forms
)
7509 num_options
= parse_options(client
, &options
);
7511 if (num_options
> 0)
7514 * WARNING: A real printer/server implementation MUST NOT implement
7515 * supply updates via a GET request - GET requests are supposed to be
7516 * idempotent (without side-effects) and we obviously are not
7517 * authenticating access here. This form is provided solely to
7518 * enable testing and development!
7521 char name
[64]; /* Form field */
7522 const char *val
; /* Form value */
7524 _cupsRWLockWrite(&printer
->rwlock
);
7526 ippDeleteAttribute(printer
->attrs
, supply
);
7529 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
);
7531 for (i
= 0; i
< num_supply
; i
++)
7533 snprintf(name
, sizeof(name
), "supply%d", i
);
7534 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
7536 level
= atoi(val
); /* New level */
7538 snprintf(supply_text
, sizeof(supply_text
), printer_supply
[i
], level
);
7540 ippSetOctetString(printer
->attrs
, &supply
, ippGetCount(supply
), supply_text
, (int)strlen(supply_text
));
7542 supply
= ippAddOctetString(printer
->attrs
, IPP_TAG_PRINTER
, "printer-supply", supply_text
, (int)strlen(supply_text
));
7547 printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_FULL
;
7548 else if (level
> 90)
7549 printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
;
7554 printer
->state_reasons
|= IPPEVE_PREASON_TONER_EMPTY
;
7555 else if (level
< 10)
7556 printer
->state_reasons
|= IPPEVE_PREASON_TONER_LOW
;
7561 _cupsRWUnlock(&printer
->rwlock
);
7564 if (printer
->web_forms
)
7565 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
7567 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
7568 for (i
= 0; i
< num_supply
; i
++)
7570 supply_value
= ippGetOctetString(supply
, i
, &supply_len
);
7571 if (supply_len
> (int)(sizeof(supply_text
) - 1))
7572 supply_len
= (int)sizeof(supply_text
) - 1;
7574 memcpy(supply_text
, supply_value
, (size_t)supply_len
);
7575 supply_text
[supply_len
] = '\0';
7577 if ((supply_ptr
= strstr(supply_text
, "level=")) != NULL
)
7578 level
= atoi(supply_ptr
+ 6);
7582 if (printer
->web_forms
)
7583 html_printf(client
, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc
, i
, NULL
), i
, level
);
7585 html_printf(client
, "<tr><th>%s:</th>", ippGetString(supply_desc
, i
, NULL
));
7588 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
);
7590 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
);
7593 if (printer
->web_forms
)
7595 html_printf(client
, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
7596 if (num_options
> 0)
7597 html_printf(client
, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
7598 html_printf(client
, "</td></tr>\n</table>\n</form>\n");
7600 if (num_options
> 0)
7601 html_printf(client
, "<script>\n"
7602 "setTimeout(hide_status, 3000);\n"
7603 "function hide_status() {\n"
7604 " var status = document.getElementById('status');\n"
7605 " status.style.display = 'none';\n"
7610 html_printf(client
, "</table>\n");
7612 html_footer(client
);
7619 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7623 time_string(time_t tv
, /* I - Time value */
7624 char *buffer
, /* I - Buffer */
7625 size_t bufsize
) /* I - Size of buffer */
7627 struct tm
*curtime
= localtime(&tv
);
7630 strftime(buffer
, bufsize
, "%X", curtime
);
7636 * 'usage()' - Show program usage.
7640 usage(int status
) /* O - Exit status */
7642 _cupsLangPuts(stdout
, _("Usage: ippeveprinter [options] \"name\""));
7643 _cupsLangPuts(stdout
, _("Options:"));
7644 _cupsLangPuts(stderr
, _("--help Show program help"));
7645 _cupsLangPuts(stderr
, _("--no-web-forms Disable web forms for media and supplies"));
7646 _cupsLangPuts(stderr
, _("--version Show program version"));
7647 _cupsLangPuts(stdout
, _("-2 Set 2-sided printing support (default=1-sided)"));
7648 _cupsLangPuts(stdout
, _("-D device-uri Set the device URI for the printer"));
7650 _cupsLangPuts(stdout
, _("-K keypath Set location of server X.509 certificates and keys."));
7651 #endif /* HAVE_SSL */
7652 _cupsLangPuts(stdout
, _("-M manufacturer Set manufacturer name (default=Test)"));
7653 _cupsLangPuts(stdout
, _("-P filename.ppd Load printer attributes from PPD file"));
7654 _cupsLangPuts(stdout
, _("-V version Set default IPP version"));
7655 _cupsLangPuts(stdout
, _("-a filename.conf Load printer attributes from conf file"));
7656 _cupsLangPuts(stdout
, _("-c command Set print command"));
7657 _cupsLangPuts(stdout
, _("-d spool-directory Set spool directory"));
7658 _cupsLangPuts(stdout
, _("-f type/subtype[,...] Set supported file types"));
7659 _cupsLangPuts(stdout
, _("-i iconfile.png Set icon file"));
7660 _cupsLangPuts(stdout
, _("-k Keep job spool files"));
7661 _cupsLangPuts(stdout
, _("-l location Set location of printer"));
7662 _cupsLangPuts(stdout
, _("-m model Set model name (default=Printer)"));
7663 _cupsLangPuts(stdout
, _("-n hostname Set hostname for printer"));
7664 _cupsLangPuts(stdout
, _("-p port Set port number for printer"));
7665 _cupsLangPuts(stdout
, _("-r subtype,[subtype] Set DNS-SD service subtype"));
7666 _cupsLangPuts(stdout
, _("-s speed[,color-speed] Set speed in pages per minute"));
7667 _cupsLangPuts(stderr
, _("-v Be verbose"));
7674 * 'valid_doc_attributes()' - Determine whether the document attributes are
7677 * When one or more document attributes are invalid, this function adds a
7678 * suitable response and attributes to the unsupported group.
7681 static int /* O - 1 if valid, 0 if not */
7682 valid_doc_attributes(
7683 ippeve_client_t
*client
) /* I - Client */
7685 int valid
= 1; /* Valid attributes? */
7686 ipp_op_t op
= ippGetOperation(client
->request
);
7688 const char *op_name
= ippOpString(op
);
7689 /* IPP operation name */
7690 ipp_attribute_t
*attr
, /* Current attribute */
7691 *supported
; /* xxx-supported attribute */
7692 const char *compression
= NULL
,
7693 /* compression value */
7694 *format
= NULL
; /* document-format value */
7698 * Check operation attributes...
7701 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
7704 * If compression is specified, only accept a supported value in a Print-Job
7705 * or Send-Document request...
7708 compression
= ippGetString(attr
, 0, NULL
);
7709 supported
= ippFindAttribute(client
->printer
->attrs
,
7710 "compression-supported", IPP_TAG_KEYWORD
);
7712 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7713 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
7714 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
7715 op
!= IPP_OP_VALIDATE_JOB
) ||
7716 !ippContainsString(supported
, compression
))
7718 respond_unsupported(client
, attr
);
7723 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
7725 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
7727 if (strcmp(compression
, "none"))
7730 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
7731 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
7737 * Is it a format we support?
7740 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
7742 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
7743 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
7745 respond_unsupported(client
, attr
);
7750 format
= ippGetString(attr
, 0, NULL
);
7752 fprintf(stderr
, "%s %s document-format=\"%s\"\n", client
->hostname
, op_name
, format
);
7754 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
7759 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
7761 format
= "application/octet-stream"; /* Should never happen */
7763 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
7766 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
7769 * Auto-type the file using the first 8 bytes of the file...
7772 unsigned char header
[8]; /* First 8 bytes of file */
7774 memset(header
, 0, sizeof(header
));
7775 httpPeek(client
->http
, (char *)header
, sizeof(header
));
7777 if (!memcmp(header
, "%PDF", 4))
7778 format
= "application/pdf";
7779 else if (!memcmp(header
, "%!", 2))
7780 format
= "application/postscript";
7781 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
7782 format
= "image/jpeg";
7783 else if (!memcmp(header
, "\211PNG", 4))
7784 format
= "image/png";
7785 else if (!memcmp(header
, "RAS2", 4))
7786 format
= "image/pwg-raster";
7787 else if (!memcmp(header
, "UNIRAST", 8))
7788 format
= "image/urf";
7794 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n", client
->hostname
, op_name
, format
);
7796 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
7800 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
7802 respond_unsupported(client
, attr
);
7810 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
7811 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
7818 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
7820 * When one or more job attributes are invalid, this function adds a suitable
7821 * response and attributes to the unsupported group.
7824 static int /* O - 1 if valid, 0 if not */
7825 valid_job_attributes(
7826 ippeve_client_t
*client
) /* I - Client */
7828 int i
, /* Looping var */
7829 count
, /* Number of values */
7830 valid
= 1; /* Valid attributes? */
7831 ipp_attribute_t
*attr
, /* Current attribute */
7832 *supported
; /* xxx-supported attribute */
7836 * Check operation attributes...
7839 valid
= valid_doc_attributes(client
);
7842 * Check the various job template attributes...
7845 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
7847 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7848 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
7850 respond_unsupported(client
, attr
);
7855 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
7857 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
7859 respond_unsupported(client
, attr
);
7864 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
7866 if (ippGetCount(attr
) != 1 ||
7867 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7868 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7869 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7870 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
7872 respond_unsupported(client
, attr
);
7877 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
7879 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
7881 respond_unsupported(client
, attr
);
7886 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
7888 if (ippGetCount(attr
) != 1 ||
7889 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7890 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
7892 respond_unsupported(client
, attr
);
7896 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
7899 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
7901 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
7903 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
7904 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
7906 respond_unsupported(client
, attr
);
7911 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7913 if (ippGetCount(attr
) != 1 ||
7914 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7915 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7916 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7917 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7919 respond_unsupported(client
, attr
);
7924 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7926 if (ippGetCount(attr
) != 1 ||
7927 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7928 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7929 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7931 respond_unsupported(client
, attr
);
7936 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7938 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7940 respond_unsupported(client
, attr
);
7946 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7948 ipp_t
*col
, /* media-col collection */
7949 *size
; /* media-size collection */
7950 ipp_attribute_t
*member
, /* Member attribute */
7951 *x_dim
, /* x-dimension */
7952 *y_dim
; /* y-dimension */
7953 int x_value
, /* y-dimension value */
7954 y_value
; /* x-dimension value */
7956 if (ippGetCount(attr
) != 1 ||
7957 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7959 respond_unsupported(client
, attr
);
7963 col
= ippGetCollection(attr
, 0);
7965 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7967 if (ippGetCount(member
) != 1 ||
7968 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7969 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7970 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7972 respond_unsupported(client
, attr
);
7977 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7979 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7981 respond_unsupported(client
, attr
);
7986 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7988 if (ippGetCount(member
) != 1)
7990 respond_unsupported(client
, attr
);
7995 size
= ippGetCollection(member
, 0);
7997 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7998 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
8000 respond_unsupported(client
, attr
);
8005 x_value
= ippGetInteger(x_dim
, 0);
8006 y_value
= ippGetInteger(y_dim
, 0);
8007 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
8008 count
= ippGetCount(supported
);
8010 for (i
= 0; i
< count
; i
++)
8012 size
= ippGetCollection(supported
, i
);
8013 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
8014 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
8016 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
8022 respond_unsupported(client
, attr
);
8030 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
8032 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
8033 (strcmp(ippGetString(attr
, 0, NULL
),
8034 "separate-documents-uncollated-copies") &&
8035 strcmp(ippGetString(attr
, 0, NULL
),
8036 "separate-documents-collated-copies")))
8038 respond_unsupported(client
, attr
);
8043 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
8045 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
8046 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
8047 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
8049 respond_unsupported(client
, attr
);
8054 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
8056 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
8058 respond_unsupported(client
, attr
);
8063 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
8065 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
8066 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
8067 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
8069 respond_unsupported(client
, attr
);
8074 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
8076 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
8078 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
8081 respond_unsupported(client
, attr
);
8086 int xdpi
, /* Horizontal resolution for job template attribute */
8087 ydpi
, /* Vertical resolution for job template attribute */
8088 sydpi
; /* Vertical resolution for supported value */
8089 ipp_res_t units
, /* Units for job template attribute */
8090 sunits
; /* Units for supported value */
8092 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
8093 count
= ippGetCount(supported
);
8095 for (i
= 0; i
< count
; i
++)
8097 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
8103 respond_unsupported(client
, attr
);
8109 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
8111 const char *sides
= ippGetString(attr
, 0, NULL
);
8112 /* "sides" value... */
8114 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
8116 respond_unsupported(client
, attr
);
8119 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
8121 if (!ippContainsString(supported
, sides
))
8123 respond_unsupported(client
, attr
);
8127 else if (strcmp(sides
, "one-sided"))
8129 respond_unsupported(client
, attr
);