2 * IPP Everywhere printer application for CUPS.
4 * Copyright © 2010-2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * Note: This program began life as the "ippserver" sample code that first
10 * appeared in CUPS 1.4. The name has been changed in order to distinguish it
11 * from the PWG's much more ambitious "ippserver" program, which supports
12 * different kinds of IPP services and multiple services per instance - the
13 * "ippeveprinter" program exposes a single print service conforming to the
14 * current IPP Everywhere specification, thus the new name.
18 * Include necessary headers...
21 #include <cups/cups-private.h>
23 # include <cups/ppd-private.h>
24 #endif /* !CUPS_LITE */
33 # define WEXITSTATUS(s) (s)
34 # include <winsock2.h>
38 extern char **environ
;
40 # include <sys/fcntl.h>
41 # include <sys/wait.h>
47 #elif defined(HAVE_AVAHI)
48 # include <avahi-client/client.h>
49 # include <avahi-client/publish.h>
50 # include <avahi-common/error.h>
51 # include <avahi-common/thread-watch.h>
52 #endif /* HAVE_DNSSD */
53 #ifdef HAVE_SYS_MOUNT_H
54 # include <sys/mount.h>
55 #endif /* HAVE_SYS_MOUNT_H */
56 #ifdef HAVE_SYS_STATFS_H
57 # include <sys/statfs.h>
58 #endif /* HAVE_SYS_STATFS_H */
59 #ifdef HAVE_SYS_STATVFS_H
60 # include <sys/statvfs.h>
61 #endif /* HAVE_SYS_STATVFS_H */
64 #endif /* HAVE_SYS_VFS_H */
66 #include "printer-png.h"
73 enum ippeve_preason_e
/* printer-state-reasons bit values */
75 IPPEVE_PREASON_NONE
= 0x0000, /* none */
76 IPPEVE_PREASON_OTHER
= 0x0001, /* other */
77 IPPEVE_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
78 IPPEVE_PREASON_INPUT_TRAY_MISSING
= 0x0004,
79 /* input-tray-missing */
80 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
81 /* marker-supply-empty */
82 IPPEVE_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
83 /* marker-supply-low */
84 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
85 /* marker-waste-almost-full */
86 IPPEVE_PREASON_MARKER_WASTE_FULL
= 0x0040,
87 /* marker-waste-full */
88 IPPEVE_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
89 IPPEVE_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
90 IPPEVE_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
91 IPPEVE_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
92 IPPEVE_PREASON_MOVING_TO_PAUSED
= 0x0800,
93 /* moving-to-paused */
94 IPPEVE_PREASON_PAUSED
= 0x1000, /* paused */
95 IPPEVE_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
96 IPPEVE_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
97 IPPEVE_PREASON_TONER_LOW
= 0x8000 /* toner-low */
99 typedef unsigned int ippeve_preason_t
; /* Bitfield for printer-state-reasons */
100 static const char * const ippeve_preason_strings
[] =
101 { /* Strings for each bit */
102 /* "none" is implied for no bits set */
105 "input-tray-missing",
106 "marker-supply-empty",
108 "marker-waste-almost-full",
123 * URL scheme for web resources...
127 # define WEB_SCHEME "https"
129 # define WEB_SCHEME "http"
130 #endif /* HAVE_SSL */
138 typedef DNSServiceRef ippeve_srv_t
; /* Service reference */
139 typedef TXTRecordRef ippeve_txt_t
; /* TXT record */
141 #elif defined(HAVE_AVAHI)
142 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
143 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
146 typedef void *_ipp_srv_t
; /* Service reference */
147 typedef void *_ipp_txt_t
; /* TXT record */
148 #endif /* HAVE_DNSSD */
150 typedef struct ippeve_filter_s
/**** Attribute filter ****/
152 cups_array_t
*ra
; /* Requested attributes */
153 ipp_tag_t group_tag
; /* Group to copy */
156 typedef struct ippeve_job_s ippeve_job_t
;
158 typedef struct ippeve_printer_s
/**** Printer data ****/
160 /* TODO: One IPv4 and one IPv6 listener are really not sufficient */
161 int ipv4
, /* IPv4 listener */
162 ipv6
; /* IPv6 listener */
163 ippeve_srv_t ipp_ref
, /* Bonjour IPP service */
164 ipps_ref
, /* Bonjour IPPS service */
165 http_ref
, /* Bonjour HTTP service */
166 printer_ref
; /* Bonjour LPD service */
167 char *dnssd_name
, /* printer-dnssd-name */
168 *name
, /* printer-name */
169 *icon
, /* Icon filename */
170 *directory
, /* Spool directory */
171 *hostname
, /* Hostname */
172 *uri
, /* printer-uri-supported */
173 *device_uri
, /* Device URI (if any) */
175 *ppdfile
, /* PPD file (if any) */
176 #endif /* !CUPS_LITE */
177 *command
; /* Command to run with job file */
179 size_t urilen
; /* Length of printer URI */
180 ipp_t
*attrs
; /* Static attributes */
181 time_t start_time
; /* Startup time */
182 time_t config_time
; /* printer-config-change-time */
183 ipp_pstate_t state
; /* printer-state value */
184 ippeve_preason_t state_reasons
; /* printer-state-reasons values */
185 time_t state_time
; /* printer-state-change-time */
186 cups_array_t
*jobs
; /* Jobs */
187 ippeve_job_t
*active_job
; /* Current active/pending job */
188 int next_job_id
; /* Next job-id value */
189 _cups_rwlock_t rwlock
; /* Printer lock */
192 struct ippeve_job_s
/**** Job data ****/
195 const char *name
, /* job-name */
196 *username
, /* job-originating-user-name */
197 *format
; /* document-format */
198 ipp_jstate_t state
; /* job-state value */
199 time_t created
, /* time-at-creation value */
200 processing
, /* time-at-processing value */
201 completed
; /* time-at-completed value */
202 int impressions
, /* job-impressions value */
203 impcompleted
; /* job-impressions-completed value */
204 ipp_t
*attrs
; /* Static attributes */
205 int cancel
; /* Non-zero when job canceled */
206 char *filename
; /* Print file name */
207 int fd
; /* Print file descriptor */
208 ippeve_printer_t
*printer
; /* Printer */
211 typedef struct ippeve_client_s
/**** Client data ****/
213 http_t
*http
; /* HTTP connection */
214 ipp_t
*request
, /* IPP request */
215 *response
; /* IPP response */
216 time_t start
; /* Request start time */
217 http_state_t operation
; /* Request operation */
218 ipp_op_t operation_id
; /* IPP operation-id */
219 char uri
[1024], /* Request URI */
220 *options
; /* URI options */
221 http_addr_t addr
; /* Client address */
222 char hostname
[256]; /* Client hostname */
223 ippeve_printer_t
*printer
; /* Printer */
224 ippeve_job_t
*job
; /* Current job, if any */
232 static void clean_jobs(ippeve_printer_t
*printer
);
233 static int compare_jobs(ippeve_job_t
*a
, ippeve_job_t
*b
);
234 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
, ipp_tag_t group_tag
, int quickcopy
);
235 static void copy_job_attributes(ippeve_client_t
*client
, ippeve_job_t
*job
, cups_array_t
*ra
);
236 static ippeve_client_t
*create_client(ippeve_printer_t
*printer
, int sock
);
237 static ippeve_job_t
*create_job(ippeve_client_t
*client
);
238 static int create_job_file(ippeve_job_t
*job
, char *fname
, size_t fnamesize
, const char *dir
, const char *ext
);
239 static int create_listener(const char *name
, int port
, int family
);
240 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
);
241 static ipp_t
*create_media_size(int width
, int length
);
242 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
);
243 static void debug_attributes(const char *title
, ipp_t
*ipp
, int response
);
244 static void delete_client(ippeve_client_t
*client
);
245 static void delete_job(ippeve_job_t
*job
);
246 static void delete_printer(ippeve_printer_t
*printer
);
248 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
);
249 #elif defined(HAVE_AVAHI)
250 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
251 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
252 #endif /* HAVE_DNSSD */
253 static void dnssd_init(void);
254 static int filter_cb(ippeve_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
255 static ippeve_job_t
*find_job(ippeve_client_t
*client
);
256 static void finish_document_data(ippeve_client_t
*client
, ippeve_job_t
*job
);
257 static void finish_document_uri(ippeve_client_t
*client
, ippeve_job_t
*job
);
258 static void html_escape(ippeve_client_t
*client
, const char *s
, size_t slen
);
259 static void html_footer(ippeve_client_t
*client
);
260 static void html_header(ippeve_client_t
*client
, const char *title
, int refresh
);
261 static void html_printf(ippeve_client_t
*client
, const char *format
, ...) _CUPS_FORMAT(2, 3);
262 static void ipp_cancel_job(ippeve_client_t
*client
);
263 static void ipp_close_job(ippeve_client_t
*client
);
264 static void ipp_create_job(ippeve_client_t
*client
);
265 static void ipp_get_job_attributes(ippeve_client_t
*client
);
266 static void ipp_get_jobs(ippeve_client_t
*client
);
267 static void ipp_get_printer_attributes(ippeve_client_t
*client
);
268 static void ipp_identify_printer(ippeve_client_t
*client
);
269 static void ipp_print_job(ippeve_client_t
*client
);
270 static void ipp_print_uri(ippeve_client_t
*client
);
271 static void ipp_send_document(ippeve_client_t
*client
);
272 static void ipp_send_uri(ippeve_client_t
*client
);
273 static void ipp_validate_job(ippeve_client_t
*client
);
274 static ipp_t
*load_ippserver_attributes(const char *servername
, int serverport
, const char *filename
, cups_array_t
*docformats
);
275 static ipp_t
*load_legacy_attributes(const char *make
, const char *model
, int ppm
, int ppm_color
, int duplex
, cups_array_t
*docformats
);
277 static ipp_t
*load_ppd_attributes(const char *ppdfile
, cups_array_t
*docformats
);
278 #endif /* !CUPS_LITE */
279 static int parse_options(ippeve_client_t
*client
, cups_option_t
**options
);
280 static void process_attr_message(ippeve_job_t
*job
, char *message
);
281 static void *process_client(ippeve_client_t
*client
);
282 static int process_http(ippeve_client_t
*client
);
283 static int process_ipp(ippeve_client_t
*client
);
284 static void *process_job(ippeve_job_t
*job
);
285 static void process_state_message(ippeve_job_t
*job
, char *message
);
286 static int register_printer(ippeve_printer_t
*printer
, const char *subtypes
);
287 static int respond_http(ippeve_client_t
*client
, http_status_t code
, const char *content_coding
, const char *type
, size_t length
);
288 static void respond_ipp(ippeve_client_t
*client
, ipp_status_t status
, const char *message
, ...) _CUPS_FORMAT(3, 4);
289 static void respond_unsupported(ippeve_client_t
*client
, ipp_attribute_t
*attr
);
290 static void run_printer(ippeve_printer_t
*printer
);
291 static int show_media(ippeve_client_t
*client
);
292 static int show_status(ippeve_client_t
*client
);
293 static int show_supplies(ippeve_client_t
*client
);
294 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
295 static void usage(int status
) _CUPS_NORETURN
;
296 static int valid_doc_attributes(ippeve_client_t
*client
);
297 static int valid_job_attributes(ippeve_client_t
*client
);
305 static DNSServiceRef DNSSDMaster
= NULL
;
306 #elif defined(HAVE_AVAHI)
307 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
308 static AvahiClient
*DNSSDClient
= NULL
;
309 #endif /* HAVE_DNSSD */
311 static int KeepFiles
= 0, /* Keep spooled job files? */
312 MaxVersion
= 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
313 Verbosity
= 0; /* Verbosity level */
317 * 'main()' - Main entry to the sample server.
320 int /* O - Exit status */
321 main(int argc
, /* I - Number of command-line args */
322 char *argv
[]) /* I - Command-line arguments */
324 int i
; /* Looping var */
325 const char *opt
, /* Current option character */
326 *attrfile
= NULL
, /* ippserver attributes file */
327 *command
= NULL
, /* Command to run with job files */
328 *device_uri
= NULL
, /* Device URI */
329 *icon
= NULL
, /* Icon file */
331 *keypath
= NULL
, /* Keychain path */
332 #endif /* HAVE_SSL */
333 *location
= "", /* Location of printer */
334 *make
= "Test", /* Manufacturer */
335 *model
= "Printer", /* Model */
336 *name
= NULL
, /* Printer name */
338 *ppdfile
= NULL
, /* PPD file */
339 #endif /* !CUPS_LITE */
340 *subtypes
= "_print"; /* DNS-SD service subtype */
341 int legacy
= 0, /* Legacy mode? */
342 duplex
= 0, /* Duplex mode */
343 ppm
= 10, /* Pages per minute for mono */
344 ppm_color
= 0; /* Pages per minute for color */
345 ipp_t
*attrs
= NULL
; /* Printer attributes */
346 char directory
[1024] = ""; /* Spool directory */
347 cups_array_t
*docformats
= NULL
; /* Supported formats */
348 const char *servername
= NULL
; /* Server host name */
349 int serverport
= 0; /* Server port number (0 = auto) */
350 ippeve_printer_t
*printer
; /* Printer object */
354 * Parse command-line arguments...
357 for (i
= 1; i
< argc
; i
++)
359 if (!strcmp(argv
[i
], "--help"))
363 else if (!strcmp(argv
[i
], "--version"))
368 else if (!strncmp(argv
[i
], "--", 2))
370 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
373 else if (argv
[i
][0] == '-')
375 for (opt
= argv
[i
] + 1; *opt
; opt
++)
379 case '2' : /* -2 (enable 2-sided printing) */
384 case 'D' : /* -D device-uri */
389 device_uri
= argv
[i
];
393 case 'K' : /* -K keypath */
400 #endif /* HAVE_SSL */
402 case 'M' : /* -M manufacturer */
412 case 'P' : /* -P filename.ppd */
419 #endif /* !CUPS_LITE */
421 case 'V' : /* -V max-version */
426 if (!strcmp(argv
[i
], "2.0"))
428 else if (!strcmp(argv
[i
], "1.1"))
434 case 'a' : /* -a attributes-file */
442 case 'c' : /* -c command */
450 case 'd' : /* -d spool-directory */
455 strlcpy(directory
, argv
[i
], sizeof(directory
));
458 case 'f' : /* -f type/subtype[,...] */
463 docformats
= _cupsArrayNewStrings(argv
[i
], ',');
467 case 'i' : /* -i icon.png */
475 case 'k' : /* -k (keep files) */
479 case 'l' : /* -l location */
487 case 'm' : /* -m model */
496 case 'n' : /* -n hostname */
501 servername
= argv
[i
];
504 case 'p' : /* -p port */
506 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
509 serverport
= atoi(argv
[i
]);
512 case 'r' : /* -r subtype */
520 case 's' : /* -s speed[,color-speed] */
525 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
531 case 'v' : /* -v (be verbose) */
535 default : /* Unknown */
536 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), argv
[0], *opt
);
547 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
556 if (attrfile
!= NULL
&& legacy
)
559 if (((ppdfile
!= NULL
) + (attrfile
!= NULL
) + legacy
) > 1)
561 #endif /* CUPS_LITE */
564 * Apply defaults as needed...
568 docformats
= _cupsArrayNewStrings("application/pdf,image/jpeg,image/pwg-raster", ',');
574 * Windows is almost always used as a single user system, so use a default
575 * port number of 8631.
582 * Use 8000 + UID mod 1000 for the default port number...
585 serverport
= 8000 + ((int)getuid() % 1000);
588 _cupsLangPrintf(stderr
, _("Listening on port %d."), serverport
);
593 const char *tmpdir
; /* Temporary directory */
596 if ((tmpdir
= getenv("TEMP")) == NULL
)
598 #elif defined(__APPLE__) && TARGET_OS_OSX
599 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
600 tmpdir
= "/private/tmp";
602 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
606 snprintf(directory
, sizeof(directory
), "%s/ippeveprinter.%d", tmpdir
, (int)getpid());
608 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
610 _cupsLangPrintf(stderr
, _("Unable to create spool directory \"%s\": %s"), directory
, strerror(errno
));
615 _cupsLangPrintf(stderr
, _("Using spool directory \"%s\"."), directory
);
619 cupsSetServerCredentials(keypath
, servername
, 1);
620 #endif /* HAVE_SSL */
623 * Initialize DNS-SD...
629 * Create the printer...
633 docformats
= _cupsArrayNewStrings("image/pwg-raster", ',');
636 attrs
= load_ippserver_attributes(servername
, serverport
, attrfile
, docformats
);
639 attrs
= load_ppd_attributes(ppdfile
, docformats
);
640 #endif /* !CUPS_LITE */
642 attrs
= load_legacy_attributes(make
, model
, ppm
, ppm_color
, duplex
, docformats
);
644 if ((printer
= create_printer(servername
, serverport
, name
, location
, icon
, docformats
, subtypes
, directory
, command
, device_uri
, attrs
)) == NULL
)
649 printer
->ppdfile
= strdup(ppdfile
);
650 #endif /* !CUPS_LITE */
653 * Run the print service...
656 run_printer(printer
);
659 * Destroy the printer and exit...
662 delete_printer(printer
);
669 * 'clean_jobs()' - Clean out old (completed) jobs.
673 clean_jobs(ippeve_printer_t
*printer
) /* I - Printer */
675 ippeve_job_t
*job
; /* Current job */
676 time_t cleantime
; /* Clean time */
679 if (cupsArrayCount(printer
->jobs
) == 0)
682 cleantime
= time(NULL
) - 60;
684 _cupsRWLockWrite(&(printer
->rwlock
));
685 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
);
687 job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
688 if (job
->completed
&& job
->completed
< cleantime
)
690 cupsArrayRemove(printer
->jobs
, job
);
695 _cupsRWUnlock(&(printer
->rwlock
));
700 * 'compare_jobs()' - Compare two jobs.
703 static int /* O - Result of comparison */
704 compare_jobs(ippeve_job_t
*a
, /* I - First job */
705 ippeve_job_t
*b
) /* I - Second job */
707 return (b
->id
- a
->id
);
712 * 'copy_attributes()' - Copy attributes from one request to another.
716 copy_attributes(ipp_t
*to
, /* I - Destination request */
717 ipp_t
*from
, /* I - Source request */
718 cups_array_t
*ra
, /* I - Requested attributes */
719 ipp_tag_t group_tag
, /* I - Group to copy */
720 int quickcopy
) /* I - Do a quick copy? */
722 ippeve_filter_t filter
; /* Filter data */
726 filter
.group_tag
= group_tag
;
728 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
733 * 'copy_job_attrs()' - Copy job attributes to the response.
738 ippeve_client_t
*client
, /* I - Client */
739 ippeve_job_t
*job
, /* I - Job */
740 cups_array_t
*ra
) /* I - requested-attributes */
742 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
744 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
747 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
749 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
752 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
755 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
757 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
760 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
761 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
763 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
764 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
766 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
767 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
769 if (!ra
|| cupsArrayFind(ra
, "job-state"))
770 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
771 "job-state", job
->state
);
773 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
777 case IPP_JSTATE_PENDING
:
778 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
781 case IPP_JSTATE_HELD
:
783 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
784 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
785 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
787 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
790 case IPP_JSTATE_PROCESSING
:
792 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
794 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
797 case IPP_JSTATE_STOPPED
:
798 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
801 case IPP_JSTATE_CANCELED
:
802 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
805 case IPP_JSTATE_ABORTED
:
806 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
809 case IPP_JSTATE_COMPLETED
:
810 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
815 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
819 case IPP_JSTATE_PENDING
:
820 ippAddString(client
->response
, IPP_TAG_JOB
,
821 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
825 case IPP_JSTATE_HELD
:
827 ippAddString(client
->response
, IPP_TAG_JOB
,
828 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
829 "job-state-reasons", NULL
, "job-incoming");
830 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
831 ippAddString(client
->response
, IPP_TAG_JOB
,
832 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
833 "job-state-reasons", NULL
, "job-hold-until-specified");
835 ippAddString(client
->response
, IPP_TAG_JOB
,
836 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
837 "job-state-reasons", NULL
, "job-data-insufficient");
840 case IPP_JSTATE_PROCESSING
:
842 ippAddString(client
->response
, IPP_TAG_JOB
,
843 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
844 "job-state-reasons", NULL
, "processing-to-stop-point");
846 ippAddString(client
->response
, IPP_TAG_JOB
,
847 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
848 "job-state-reasons", NULL
, "job-printing");
851 case IPP_JSTATE_STOPPED
:
852 ippAddString(client
->response
, IPP_TAG_JOB
,
853 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
854 NULL
, "job-stopped");
857 case IPP_JSTATE_CANCELED
:
858 ippAddString(client
->response
, IPP_TAG_JOB
,
859 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
860 NULL
, "job-canceled-by-user");
863 case IPP_JSTATE_ABORTED
:
864 ippAddString(client
->response
, IPP_TAG_JOB
,
865 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
866 NULL
, "aborted-by-system");
869 case IPP_JSTATE_COMPLETED
:
870 ippAddString(client
->response
, IPP_TAG_JOB
,
871 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
872 NULL
, "job-completed-successfully");
877 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
878 ippAddInteger(client
->response
, IPP_TAG_JOB
,
879 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
880 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
882 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
883 ippAddInteger(client
->response
, IPP_TAG_JOB
,
884 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
885 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
890 * 'create_client()' - Accept a new network connection and create a client
894 static ippeve_client_t
* /* O - Client */
895 create_client(ippeve_printer_t
*printer
, /* I - Printer */
896 int sock
) /* I - Listen socket */
898 ippeve_client_t
*client
; /* Client */
901 if ((client
= calloc(1, sizeof(ippeve_client_t
))) == NULL
)
903 perror("Unable to allocate memory for client");
907 client
->printer
= printer
;
910 * Accept the client and get the remote address...
913 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
915 perror("Unable to accept client connection");
922 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
925 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
932 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
936 static ippeve_job_t
* /* O - Job */
937 create_job(ippeve_client_t
*client
) /* I - Client */
939 ippeve_job_t
*job
; /* Job */
940 ipp_attribute_t
*attr
; /* Job attribute */
941 char uri
[1024], /* job-uri value */
942 uuid
[64]; /* job-uuid value */
945 _cupsRWLockWrite(&(client
->printer
->rwlock
));
946 if (client
->printer
->active_job
&&
947 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
950 * Only accept a single job at a time...
953 _cupsRWUnlock(&(client
->printer
->rwlock
));
958 * Allocate and initialize the job object...
961 if ((job
= calloc(1, sizeof(ippeve_job_t
))) == NULL
)
963 perror("Unable to allocate memory for job");
967 job
->printer
= client
->printer
;
968 job
->attrs
= ippNew();
969 job
->state
= IPP_JSTATE_HELD
;
973 * Copy all of the job attributes...
976 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
979 * Get the requesting-user-name, document format, and priority...
982 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
983 job
->username
= ippGetString(attr
, 0, NULL
);
985 job
->username
= "anonymous";
987 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
989 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
991 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
992 job
->format
= ippGetString(attr
, 0, NULL
);
993 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
994 job
->format
= ippGetString(attr
, 0, NULL
);
996 job
->format
= "application/octet-stream";
999 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1000 job
->impressions
= ippGetInteger(attr
, 0);
1002 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1003 job
->name
= ippGetString(attr
, 0, NULL
);
1006 * Add job description attributes and add to the jobs array...
1009 job
->id
= client
->printer
->next_job_id
++;
1011 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1012 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1014 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1015 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1016 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1017 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1018 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1019 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1021 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1022 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1024 cupsArrayAdd(client
->printer
->jobs
, job
);
1025 client
->printer
->active_job
= job
;
1027 _cupsRWUnlock(&(client
->printer
->rwlock
));
1034 * 'create_job_file()' - Create a file for the document in a job.
1037 static int /* O - File descriptor or -1 on error */
1039 ippeve_job_t
*job
, /* I - Job */
1040 char *fname
, /* I - Filename buffer */
1041 size_t fnamesize
, /* I - Size of filename buffer */
1042 const char *directory
, /* I - Directory to store in */
1043 const char *ext
) /* I - Extension (`NULL` for default) */
1045 char name
[256], /* "Safe" filename */
1046 *nameptr
; /* Pointer into filename */
1047 const char *job_name
; /* job-name value */
1051 * Make a name from the job-name attribute...
1054 if ((job_name
= ippGetString(ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
), 0, NULL
)) == NULL
)
1055 job_name
= "untitled";
1057 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1059 if (isalnum(*job_name
& 255) || *job_name
== '-')
1061 *nameptr
++ = (char)tolower(*job_name
& 255);
1067 while (job_name
[1] && !isalnum(job_name
[1] & 255) && job_name
[1] != '-')
1075 * Figure out the extension...
1080 if (!strcasecmp(job
->format
, "image/jpeg"))
1082 else if (!strcasecmp(job
->format
, "image/png"))
1084 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1086 else if (!strcasecmp(job
->format
, "image/urf"))
1088 else if (!strcasecmp(job
->format
, "application/pdf"))
1090 else if (!strcasecmp(job
->format
, "application/postscript"))
1092 else if (!strcasecmp(job
->format
, "application/vnd.hp-pcl"))
1099 * Create a filename with the job-id, job-name, and document-format (extension)...
1102 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", directory
, job
->id
, name
, ext
);
1104 return (open(fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666));
1109 * 'create_listener()' - Create a listener socket.
1112 static int /* O - Listener socket or -1 on error */
1113 create_listener(const char *name
, /* I - Host name (`NULL` for any address) */
1114 int port
, /* I - Port number */
1115 int family
) /* I - Address family */
1117 int sock
; /* Listener socket */
1118 http_addrlist_t
*addrlist
; /* Listen address */
1119 char service
[255]; /* Service port */
1122 snprintf(service
, sizeof(service
), "%d", port
);
1123 if ((addrlist
= httpAddrGetList(name
, family
, service
)) == NULL
)
1126 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1128 httpAddrFreeList(addrlist
);
1135 * 'create_media_col()' - Create a media-col value.
1138 static ipp_t
* /* O - media-col collection */
1139 create_media_col(const char *media
, /* I - Media name */
1140 const char *source
, /* I - Media source, if any */
1141 const char *type
, /* I - Media type, if any */
1142 int width
, /* I - x-dimension in 2540ths */
1143 int length
, /* I - y-dimension in 2540ths */
1144 int bottom
, /* I - Bottom margin in 2540ths */
1145 int left
, /* I - Left margin in 2540ths */
1146 int right
, /* I - Right margin in 2540ths */
1147 int top
) /* I - Top margin in 2540ths */
1149 ipp_t
*media_col
= ippNew(), /* media-col value */
1150 *media_size
= create_media_size(width
, length
);
1151 /* media-size value */
1152 char media_key
[256]; /* media-key value */
1153 const char *media_key_suffix
= ""; /* media-key suffix */
1156 if (bottom
== 0 && left
== 0 && right
== 0 && top
== 0)
1157 media_key_suffix
= "_borderless";
1160 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, media_key_suffix
);
1162 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, media_key_suffix
);
1164 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, media_key_suffix
);
1166 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, media_key_suffix
);
1168 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
, media_key
);
1169 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1170 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1172 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin", bottom
);
1174 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin", left
);
1176 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin", right
);
1178 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin", top
);
1180 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1182 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1184 ippDelete(media_size
);
1191 * 'create_media_size()' - Create a media-size value.
1194 static ipp_t
* /* O - media-col collection */
1195 create_media_size(int width
, /* I - x-dimension in 2540ths */
1196 int length
) /* I - y-dimension in 2540ths */
1198 ipp_t
*media_size
= ippNew(); /* media-size value */
1201 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension", width
);
1202 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension", length
);
1204 return (media_size
);
1209 * 'create_printer()' - Create, register, and listen for connections to a
1213 static ippeve_printer_t
* /* O - Printer */
1215 const char *servername
, /* I - Server hostname (NULL for default) */
1216 int serverport
, /* I - Server port */
1217 const char *name
, /* I - printer-name */
1218 const char *location
, /* I - printer-location */
1219 const char *icon
, /* I - printer-icons */
1220 cups_array_t
*docformats
, /* I - document-format-supported */
1221 const char *subtypes
, /* I - Bonjour service subtype(s) */
1222 const char *directory
, /* I - Spool directory */
1223 const char *command
, /* I - Command to run on job files, if any */
1224 const char *device_uri
, /* I - Output device, if any */
1225 ipp_t
*attrs
) /* I - Capability attributes */
1227 ippeve_printer_t
*printer
; /* Printer */
1228 int i
; /* Looping var */
1230 char path
[1024]; /* Full path to command */
1231 #endif /* !_WIN32 */
1232 char uri
[1024], /* Printer URI */
1234 securi
[1024], /* Secure printer URI */
1235 *uris
[2], /* All URIs */
1236 #endif /* HAVE_SSL */
1237 icons
[1024], /* printer-icons URI */
1238 adminurl
[1024], /* printer-more-info URI */
1239 supplyurl
[1024],/* printer-supply-info-uri URI */
1240 uuid
[128]; /* printer-uuid */
1241 int k_supported
; /* Maximum file size supported */
1242 int num_formats
; /* Number of supported document formats */
1243 const char *formats
[100], /* Supported document formats */
1244 *format
; /* Current format */
1245 int num_job_attrs
; /* Number of supported job attributes */
1246 const char *job_attrs
[100];/* Job attributes */
1247 char xxx_supported
[256];
1248 /* Name of -supported attribute */
1249 _cups_globals_t
*cg
= _cupsGlobals();
1250 /* Global path values */
1252 struct statvfs spoolinfo
; /* FS info for spool directory */
1253 double spoolsize
; /* FS size */
1254 #elif defined(HAVE_STATFS)
1255 struct statfs spoolinfo
; /* FS info for spool directory */
1256 double spoolsize
; /* FS size */
1257 #endif /* HAVE_STATVFS */
1258 static const char * const versions
[] =/* ipp-versions-supported values */
1263 static const char * const features
[] =/* ipp-features-supported values */
1267 static const int ops
[] = /* operations-supported values */
1271 IPP_OP_VALIDATE_JOB
,
1273 IPP_OP_SEND_DOCUMENT
,
1276 IPP_OP_GET_JOB_ATTRIBUTES
,
1278 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1279 IPP_OP_CANCEL_MY_JOBS
,
1281 IPP_OP_IDENTIFY_PRINTER
1283 static const char * const charsets
[] =/* charset-supported values */
1288 static const char * const compressions
[] =/* compression-supported values */
1293 #endif /* HAVE_LIBZ */
1296 static const char * const identify_actions
[] =
1301 static const char * const job_creation
[] =
1302 { /* job-creation-attributes-supported values */
1304 "document-password",
1309 "orientation-requested",
1314 "print-content-optimize",
1315 "print-rendering-intent",
1317 "printer-resolution",
1320 static const char * const media_col_supported
[] =
1321 { /* media-col-supported values */
1322 "media-bottom-margin",
1323 "media-left-margin",
1324 "media-right-margin",
1331 static const char * const multiple_document_handling
[] =
1332 { /* multiple-document-handling-supported values */
1333 "separate-documents-uncollated-copies",
1334 "separate-documents-collated-copies"
1336 static const char * const reference_uri_schemes_supported
[] =
1337 { /* reference-uri-schemes-supported */
1343 #endif /* HAVE_SSL */
1346 static const char * const uri_authentication_supported
[] =
1347 { /* uri-authentication-supported values */
1351 static const char * const uri_security_supported
[] =
1352 { /* uri-security-supported values */
1356 #endif /* HAVE_SSL */
1357 static const char * const which_jobs
[] =
1358 { /* which-jobs-supported values */
1367 "processing-stopped"
1373 * If a command was specified, make sure it exists and is executable...
1378 if (*command
== '/' || !strncmp(command
, "./", 2))
1380 if (access(command
, X_OK
))
1382 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1388 snprintf(path
, sizeof(path
), "%s/ippeveprinter/%s", cg
->cups_serverbin
, command
);
1390 if (access(command
, X_OK
))
1392 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1399 #endif /* !_WIN32 */
1402 * Allocate memory for the printer...
1405 if ((printer
= calloc(1, sizeof(ippeve_printer_t
))) == NULL
)
1407 _cupsLangPrintError(NULL
, _("Unable to allocate memory for printer"));
1413 printer
->name
= strdup(name
);
1414 printer
->dnssd_name
= strdup(name
);
1415 printer
->command
= command
? strdup(command
) : NULL
;
1416 printer
->device_uri
= device_uri
? strdup(device_uri
) : NULL
;
1417 printer
->directory
= strdup(directory
);
1418 printer
->icon
= icon
? strdup(icon
) : NULL
;
1419 printer
->port
= serverport
;
1420 printer
->start_time
= time(NULL
);
1421 printer
->config_time
= printer
->start_time
;
1422 printer
->state
= IPP_PSTATE_IDLE
;
1423 printer
->state_reasons
= IPPEVE_PREASON_NONE
;
1424 printer
->state_time
= printer
->start_time
;
1425 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1426 printer
->next_job_id
= 1;
1430 printer
->hostname
= strdup(servername
);
1434 char temp
[1024]; /* Temporary string */
1436 printer
->hostname
= strdup(httpGetHostname(NULL
, temp
, sizeof(temp
)));
1439 _cupsRWInit(&(printer
->rwlock
));
1442 * Create the listener sockets...
1445 if ((printer
->ipv4
= create_listener(servername
, printer
->port
, AF_INET
)) < 0)
1447 perror("Unable to create IPv4 listener");
1451 if ((printer
->ipv6
= create_listener(servername
, printer
->port
, AF_INET6
)) < 0)
1453 perror("Unable to create IPv6 listener");
1458 * Prepare URI values for the printer attributes...
1461 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1462 printer
->uri
= strdup(uri
);
1463 printer
->urilen
= strlen(uri
);
1466 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1467 #endif /* HAVE_SSL */
1469 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1470 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1471 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1472 httpAssembleUUID(printer
->hostname
, serverport
, name
, 0, uuid
, sizeof(uuid
));
1476 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1477 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1479 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1481 fprintf(stderr
, "printer-uri=\"%s\",\"%s\"\n", uri
, securi
);
1482 #endif /* HAVE_SSL */
1486 * Get the maximum spool size based on the size of the filesystem used for
1487 * the spool directory. If the host OS doesn't support the statfs call
1488 * or the filesystem is larger than 2TiB, always report INT_MAX.
1492 if (statvfs(printer
->directory
, &spoolinfo
))
1493 k_supported
= INT_MAX
;
1494 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1495 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1496 k_supported
= INT_MAX
;
1498 k_supported
= (int)spoolsize
;
1500 #elif defined(HAVE_STATFS)
1501 if (statfs(printer
->directory
, &spoolinfo
))
1502 k_supported
= INT_MAX
;
1503 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1504 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1505 k_supported
= INT_MAX
;
1507 k_supported
= (int)spoolsize
;
1510 k_supported
= INT_MAX
;
1511 #endif /* HAVE_STATVFS */
1514 * Assemble the final list of document formats...
1517 if (!cupsArrayFind(docformats
, (void *)"application/octet-stream"))
1518 cupsArrayAdd(docformats
, (void *)"application/octet-stream");
1520 for (num_formats
= 0, format
= (const char *)cupsArrayFirst(docformats
); format
&& num_formats
< (int)(sizeof(formats
) / sizeof(formats
[0])); format
= (const char *)cupsArrayNext(docformats
))
1521 formats
[num_formats
++] = format
;
1524 * Get the list of attributes that can be used when creating a job...
1528 job_attrs
[num_job_attrs
++] = "ipp-attribute-fidelity";
1529 job_attrs
[num_job_attrs
++] = "job-name";
1530 job_attrs
[num_job_attrs
++] = "job-priority";
1531 job_attrs
[num_job_attrs
++] = "multiple-document-handling";
1533 for (i
= 0; i
< (int)(sizeof(job_creation
) / sizeof(job_creation
[0])) && num_job_attrs
< (int)(sizeof(job_attrs
) / sizeof(job_attrs
[0])); i
++)
1535 snprintf(xxx_supported
, sizeof(xxx_supported
), "%s-supported", job_creation
[i
]);
1536 if (ippFindAttribute(printer
->attrs
, xxx_supported
, IPP_TAG_ZERO
))
1537 job_attrs
[num_job_attrs
++] = job_creation
[i
];
1541 * Fill out the rest of the printer attributes.
1544 printer
->attrs
= attrs
;
1546 /* charset-configured */
1547 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1549 /* charset-supported */
1550 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1552 /* compression-supported */
1553 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1554 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1556 /* document-format-default */
1557 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_MIMETYPE
), "document-format-default", NULL
, "application/octet-stream");
1559 /* document-format-supported */
1560 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
, "document-format-supported", num_formats
, NULL
, formats
);
1562 /* generated-natural-language-supported */
1563 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1565 /* identify-actions-default */
1566 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1568 /* identify-actions-supported */
1569 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
);
1571 /* ipp-features-supported */
1572 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1574 /* ipp-versions-supported */
1575 if (MaxVersion
== 11)
1576 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", NULL
, "1.1");
1578 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", (int)(sizeof(versions
) / sizeof(versions
[0])), NULL
, versions
);
1580 /* job-creation-attributes-supported */
1581 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-creation-attributes-supported", num_job_attrs
, NULL
, job_attrs
);
1583 /* job-ids-supported */
1584 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1586 /* job-k-octets-supported */
1587 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0, k_supported
);
1589 /* job-priority-default */
1590 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1592 /* job-priority-supported */
1593 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 1);
1595 /* job-sheets-default */
1596 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1598 /* job-sheets-supported */
1599 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1601 /* media-col-supported */
1602 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
);
1604 /* multiple-document-handling-supported */
1605 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
);
1607 /* multiple-document-jobs-supported */
1608 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1610 /* multiple-operation-time-out */
1611 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1613 /* multiple-operation-time-out-action */
1614 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1616 /* natural-language-configured */
1617 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "natural-language-configured", NULL
, "en");
1619 /* operations-supported */
1620 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1622 /* pdl-override-supported */
1623 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1625 /* preferred-attributes-supported */
1626 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1628 /* printer-get-attributes-supported */
1629 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1631 /* printer-geo-location */
1632 ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location");
1635 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-icons", NULL
, icons
);
1637 /* printer-is-accepting-jobs */
1638 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1641 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
1643 /* printer-location */
1644 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location", NULL
, location
);
1646 /* printer-more-info */
1647 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1650 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
1652 /* printer-organization */
1653 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "");
1655 /* printer-organizational-unit */
1656 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "");
1658 /* printer-supply-info-uri */
1659 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1661 /* printer-uri-supported */
1666 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
1669 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1670 #endif /* HAVE_SSL */
1673 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1675 /* reference-uri-scheme-supported */
1676 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
);
1678 /* uri-authentication-supported */
1680 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
1682 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
1683 #endif /* HAVE_SSL */
1685 /* uri-security-supported */
1687 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
1689 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
1690 #endif /* HAVE_SSL */
1692 /* which-jobs-supported */
1693 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
);
1695 debug_attributes("Printer", printer
->attrs
, 0);
1698 * Register the printer with Bonjour...
1701 if (!register_printer(printer
, subtypes
))
1712 * If we get here we were unable to create the printer...
1717 delete_printer(printer
);
1724 * 'debug_attributes()' - Print attributes in a request or response.
1728 debug_attributes(const char *title
, /* I - Title */
1729 ipp_t
*ipp
, /* I - Request/response */
1730 int type
) /* I - 0 = object, 1 = request, 2 = response */
1732 ipp_tag_t group_tag
; /* Current group */
1733 ipp_attribute_t
*attr
; /* Current attribute */
1734 char buffer
[2048]; /* String buffer for value */
1735 int major
, minor
; /* Version */
1741 fprintf(stderr
, "%s:\n", title
);
1742 major
= ippGetVersion(ipp
, &minor
);
1743 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1745 fprintf(stderr
, " operation-id=%s(%04x)\n",
1746 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1748 fprintf(stderr
, " status-code=%s(%04x)\n",
1749 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1750 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1752 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1754 attr
= ippNextAttribute(ipp
))
1756 if (ippGetGroupTag(attr
) != group_tag
)
1758 group_tag
= ippGetGroupTag(attr
);
1759 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1762 if (ippGetName(attr
))
1764 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1765 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1766 ippGetCount(attr
) > 1 ? "1setOf " : "",
1767 ippTagString(ippGetValueTag(attr
)), buffer
);
1774 * 'delete_client()' - Close the socket and free all memory used by a client
1779 delete_client(ippeve_client_t
*client
) /* I - Client */
1782 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1785 * Flush pending writes before closing...
1788 httpFlushWrite(client
->http
);
1794 httpClose(client
->http
);
1796 ippDelete(client
->request
);
1797 ippDelete(client
->response
);
1804 * 'delete_job()' - Remove from the printer and free all memory used by a job
1809 delete_job(ippeve_job_t
*job
) /* I - Job */
1812 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1814 ippDelete(job
->attrs
);
1819 unlink(job
->filename
);
1821 free(job
->filename
);
1829 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1830 * used by a printer object.
1834 delete_printer(ippeve_printer_t
*printer
) /* I - Printer */
1836 if (printer
->ipv4
>= 0)
1837 close(printer
->ipv4
);
1839 if (printer
->ipv6
>= 0)
1840 close(printer
->ipv6
);
1843 if (printer
->printer_ref
)
1844 DNSServiceRefDeallocate(printer
->printer_ref
);
1845 if (printer
->ipp_ref
)
1846 DNSServiceRefDeallocate(printer
->ipp_ref
);
1847 if (printer
->ipps_ref
)
1848 DNSServiceRefDeallocate(printer
->ipps_ref
);
1849 if (printer
->http_ref
)
1850 DNSServiceRefDeallocate(printer
->http_ref
);
1851 #elif defined(HAVE_AVAHI)
1852 avahi_threaded_poll_lock(DNSSDMaster
);
1854 if (printer
->printer_ref
)
1855 avahi_entry_group_free(printer
->printer_ref
);
1856 if (printer
->ipp_ref
)
1857 avahi_entry_group_free(printer
->ipp_ref
);
1858 if (printer
->ipps_ref
)
1859 avahi_entry_group_free(printer
->ipps_ref
);
1860 if (printer
->http_ref
)
1861 avahi_entry_group_free(printer
->http_ref
);
1863 avahi_threaded_poll_unlock(DNSSDMaster
);
1864 #endif /* HAVE_DNSSD */
1866 if (printer
->dnssd_name
)
1867 free(printer
->dnssd_name
);
1869 free(printer
->name
);
1871 free(printer
->icon
);
1872 if (printer
->command
)
1873 free(printer
->command
);
1874 if (printer
->device_uri
)
1875 free(printer
->device_uri
);
1876 if (printer
->ppdfile
)
1877 free(printer
->ppdfile
);
1878 if (printer
->directory
)
1879 free(printer
->directory
);
1880 if (printer
->hostname
)
1881 free(printer
->hostname
);
1885 ippDelete(printer
->attrs
);
1886 cupsArrayDelete(printer
->jobs
);
1894 * 'dnssd_callback()' - Handle Bonjour registration events.
1897 static void DNSSD_API
1899 DNSServiceRef sdRef
, /* I - Service reference */
1900 DNSServiceFlags flags
, /* I - Status flags */
1901 DNSServiceErrorType errorCode
, /* I - Error, if any */
1902 const char *name
, /* I - Service name */
1903 const char *regtype
, /* I - Service type */
1904 const char *domain
, /* I - Domain for service */
1905 ippeve_printer_t
*printer
) /* I - Printer */
1913 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n", regtype
, (int)errorCode
);
1916 else if (strcasecmp(name
, printer
->dnssd_name
))
1919 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
1921 /* No lock needed since only the main thread accesses/changes this */
1922 free(printer
->dnssd_name
);
1923 printer
->dnssd_name
= strdup(name
);
1928 #elif defined(HAVE_AVAHI)
1930 * 'dnssd_callback()' - Handle Bonjour registration events.
1935 AvahiEntryGroup
*srv
, /* I - Service */
1936 AvahiEntryGroupState state
, /* I - Registration state */
1937 void *context
) /* I - Printer */
1946 * 'dnssd_client_cb()' - Client callback for Avahi.
1948 * Called whenever the client or server state changes...
1953 AvahiClient
*c
, /* I - Client */
1954 AvahiClientState state
, /* I - Current state */
1955 void *userdata
) /* I - User data (unused) */
1965 fprintf(stderr
, "Ignored Avahi state %d.\n", state
);
1968 case AVAHI_CLIENT_FAILURE
:
1969 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
1971 fputs("Avahi server crashed, exiting.\n", stderr
);
1977 #endif /* HAVE_DNSSD */
1981 * 'dnssd_init()' - Initialize the DNS-SD service connections...
1988 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
1990 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
1994 #elif defined(HAVE_AVAHI)
1995 int error
; /* Error code, if any */
1997 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
1999 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2003 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2005 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2009 avahi_threaded_poll_start(DNSSDMaster
);
2010 #endif /* HAVE_DNSSD */
2015 * 'filter_cb()' - Filter printer attributes based on the requested array.
2018 static int /* O - 1 to copy, 0 to ignore */
2019 filter_cb(ippeve_filter_t
*filter
, /* I - Filter parameters */
2020 ipp_t
*dst
, /* I - Destination (unused) */
2021 ipp_attribute_t
*attr
) /* I - Source attribute */
2024 * Filter attributes as needed...
2027 #ifndef _WIN32 /* Avoid MS compiler bug */
2029 #endif /* !_WIN32 */
2031 ipp_tag_t group
= ippGetGroupTag(attr
);
2032 const char *name
= ippGetName(attr
);
2034 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
)))
2037 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2042 * 'find_job()' - Find a job specified in a request.
2045 static ippeve_job_t
* /* O - Job or NULL */
2046 find_job(ippeve_client_t
*client
) /* I - Client */
2048 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2049 ippeve_job_t key
, /* Job search key */
2050 *job
; /* Matching job, if any */
2053 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2055 const char *uri
= ippGetString(attr
, 0, NULL
);
2057 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2058 uri
[client
->printer
->urilen
] == '/')
2059 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2063 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2064 key
.id
= ippGetInteger(attr
, 0);
2066 _cupsRWLockRead(&(client
->printer
->rwlock
));
2067 job
= (ippeve_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2068 _cupsRWUnlock(&(client
->printer
->rwlock
));
2075 * 'finish_document()' - Finish receiving a document file and start processing.
2079 finish_document_data(
2080 ippeve_client_t
*client
, /* I - Client */
2081 ippeve_job_t
*job
) /* I - Job */
2083 char filename
[1024], /* Filename buffer */
2084 buffer
[4096]; /* Copy buffer */
2085 ssize_t bytes
; /* Bytes read */
2086 cups_array_t
*ra
; /* Attributes to send in response */
2087 _cups_thread_t t
; /* Thread */
2091 * Create a file for the request data...
2093 * TODO: Update code to support piping large raster data to the print command.
2096 if ((job
->fd
= create_job_file(job
, filename
, sizeof(filename
), client
->printer
->directory
, NULL
)) < 0)
2098 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2104 fprintf(stderr
, "Created job file \"%s\", format \"%s\".\n", filename
, job
->format
);
2106 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2108 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2110 int error
= errno
; /* Write error */
2117 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2126 * Got an error while reading the print data, so abort this job.
2134 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to read print file.");
2141 int error
= errno
; /* Write error */
2147 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2153 job
->filename
= strdup(filename
);
2154 job
->state
= IPP_JSTATE_PENDING
;
2157 * Process the job...
2160 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
2164 _cupsThreadDetach(t
);
2168 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2173 * Return the job info...
2176 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2178 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2179 cupsArrayAdd(ra
, "job-id");
2180 cupsArrayAdd(ra
, "job-state");
2181 cupsArrayAdd(ra
, "job-state-message");
2182 cupsArrayAdd(ra
, "job-state-reasons");
2183 cupsArrayAdd(ra
, "job-uri");
2185 copy_job_attributes(client
, job
, ra
);
2186 cupsArrayDelete(ra
);
2190 * If we get here we had to abort the job...
2195 job
->state
= IPP_JSTATE_ABORTED
;
2196 job
->completed
= time(NULL
);
2198 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2199 cupsArrayAdd(ra
, "job-id");
2200 cupsArrayAdd(ra
, "job-state");
2201 cupsArrayAdd(ra
, "job-state-reasons");
2202 cupsArrayAdd(ra
, "job-uri");
2204 copy_job_attributes(client
, job
, ra
);
2205 cupsArrayDelete(ra
);
2210 * 'finish_uri()' - Finish fetching a document URI and start processing.
2214 finish_document_uri(
2215 ippeve_client_t
*client
, /* I - Client */
2216 ippeve_job_t
*job
) /* I - Job */
2218 ipp_attribute_t
*uri
; /* document-uri */
2219 char scheme
[256], /* URI scheme */
2220 userpass
[256], /* Username and password info */
2221 hostname
[256], /* Hostname */
2222 resource
[1024]; /* Resource path */
2223 int port
; /* Port number */
2224 http_uri_status_t uri_status
; /* URI decode status */
2225 http_encryption_t encryption
; /* Encryption to use, if any */
2226 http_t
*http
; /* Connection for http/https URIs */
2227 http_status_t status
; /* Access status for http/https URIs */
2228 int infile
; /* Input file for local file URIs */
2229 char filename
[1024], /* Filename buffer */
2230 buffer
[4096]; /* Copy buffer */
2231 ssize_t bytes
; /* Bytes read */
2232 ipp_attribute_t
*attr
; /* Current attribute */
2233 cups_array_t
*ra
; /* Attributes to send in response */
2237 * Do we have a file to print?
2240 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2242 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Unexpected document data following request.");
2248 * Do we have a document URI?
2251 if ((uri
= ippFindAttribute(client
->request
, "document-uri", IPP_TAG_URI
)) == NULL
)
2253 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
2258 if (ippGetCount(uri
) != 1)
2260 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Too many document-uri values.");
2265 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
2266 scheme
, sizeof(scheme
), userpass
,
2267 sizeof(userpass
), hostname
, sizeof(hostname
),
2268 &port
, resource
, sizeof(resource
));
2269 if (uri_status
< HTTP_URI_STATUS_OK
)
2271 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s", httpURIStatusString(uri_status
));
2276 if (strcmp(scheme
, "file") &&
2278 strcmp(scheme
, "https") &&
2279 #endif /* HAVE_SSL */
2280 strcmp(scheme
, "http"))
2282 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
, "URI scheme \"%s\" not supported.", scheme
);
2287 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
2289 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
2295 * Get the document format for the job...
2298 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2300 if ((attr
= ippFindAttribute(job
->attrs
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
2301 job
->format
= ippGetString(attr
, 0, NULL
);
2303 job
->format
= "application/octet-stream";
2306 * Create a file for the request data...
2309 if ((job
->fd
= create_job_file(job
, filename
, sizeof(filename
), client
->printer
->directory
, NULL
)) < 0)
2311 _cupsRWUnlock(&(client
->printer
->rwlock
));
2313 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2318 _cupsRWUnlock(&(client
->printer
->rwlock
));
2320 if (!strcmp(scheme
, "file"))
2322 if ((infile
= open(resource
, O_RDONLY
)) < 0)
2324 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
2331 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
2332 (errno
== EAGAIN
|| errno
== EINTR
))
2336 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2338 int error
= errno
; /* Write error */
2346 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2358 if (port
== 443 || !strcmp(scheme
, "https"))
2359 encryption
= HTTP_ENCRYPTION_ALWAYS
;
2361 #endif /* HAVE_SSL */
2362 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
2364 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
, 1, 30000, NULL
)) == NULL
)
2366 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to connect to %s: %s", hostname
, cupsLastErrorString());
2376 httpClearFields(http
);
2377 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
2378 if (httpGet(http
, resource
))
2380 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", strerror(errno
));
2391 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
2393 if (status
!= HTTP_STATUS_OK
)
2395 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", httpStatus(status
));
2406 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
2408 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2410 int error
= errno
; /* Write error */
2418 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2419 "Unable to write print file: %s", strerror(error
));
2430 int error
= errno
; /* Write error */
2436 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2441 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2444 job
->filename
= strdup(filename
);
2445 job
->state
= IPP_JSTATE_PENDING
;
2447 _cupsRWUnlock(&(client
->printer
->rwlock
));
2450 * Process the job...
2456 * Return the job info...
2459 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2461 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2462 cupsArrayAdd(ra
, "job-id");
2463 cupsArrayAdd(ra
, "job-state");
2464 cupsArrayAdd(ra
, "job-state-reasons");
2465 cupsArrayAdd(ra
, "job-uri");
2467 copy_job_attributes(client
, job
, ra
);
2468 cupsArrayDelete(ra
);
2472 * If we get here we had to abort the job...
2477 job
->state
= IPP_JSTATE_ABORTED
;
2478 job
->completed
= time(NULL
);
2480 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2481 cupsArrayAdd(ra
, "job-id");
2482 cupsArrayAdd(ra
, "job-state");
2483 cupsArrayAdd(ra
, "job-state-reasons");
2484 cupsArrayAdd(ra
, "job-uri");
2486 copy_job_attributes(client
, job
, ra
);
2487 cupsArrayDelete(ra
);
2492 * 'html_escape()' - Write a HTML-safe string.
2496 html_escape(ippeve_client_t
*client
, /* I - Client */
2497 const char *s
, /* I - String to write */
2498 size_t slen
) /* I - Number of characters to write */
2500 const char *start
, /* Start of segment */
2501 *end
; /* End of string */
2505 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2507 while (*s
&& s
< end
)
2509 if (*s
== '&' || *s
== '<')
2512 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2515 httpWrite2(client
->http
, "&", 5);
2517 httpWrite2(client
->http
, "<", 4);
2526 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2531 * 'html_footer()' - Show the web interface footer.
2533 * This function also writes the trailing 0-length chunk.
2537 html_footer(ippeve_client_t
*client
) /* I - Client */
2543 httpWrite2(client
->http
, "", 0);
2548 * 'html_header()' - Show the web interface header and title.
2552 html_header(ippeve_client_t
*client
, /* I - Client */
2553 const char *title
, /* I - Title */
2554 int refresh
) /* I - Refresh timer, if any */
2560 "<title>%s</title>\n"
2561 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2562 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2563 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title
);
2565 html_printf(client
, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh
);
2567 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2569 "body { font-family: sans-serif; margin: 0; }\n"
2570 "div.body { padding: 0px 10px 10px; }\n"
2571 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2572 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2573 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2574 "table.form th { text-align: right; }\n"
2575 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2576 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2577 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2578 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2579 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2580 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2581 "table.nav td { margin: 0; text-align: center; }\n"
2582 "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"
2583 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2584 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2585 "td.nav:hover { background: #666; color: #fff; }\n"
2586 "td.nav:active { background: #000; color: #ff0; }\n"
2590 "<table class=\"nav\"><tr>"
2591 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2592 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2593 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2595 "<div class=\"body\">\n", !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2600 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2604 html_printf(ippeve_client_t
*client
, /* I - Client */
2605 const char *format
, /* I - Printf-style format string */
2606 ...) /* I - Additional arguments as needed */
2608 va_list ap
; /* Pointer to arguments */
2609 const char *start
; /* Start of string */
2610 char size
, /* Size character (h, l, L) */
2611 type
; /* Format type character */
2612 int width
, /* Width of field */
2613 prec
; /* Number of characters of precision */
2614 char tformat
[100], /* Temporary format string for sprintf() */
2615 *tptr
, /* Pointer into temporary format */
2616 temp
[1024]; /* Buffer for formatted numbers */
2617 char *s
; /* Pointer to string */
2621 * Loop through the format string, formatting as needed...
2624 va_start(ap
, format
);
2632 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2635 *tptr
++ = *format
++;
2639 httpWrite2(client
->http
, "%", 1);
2644 else if (strchr(" -+#\'", *format
))
2645 *tptr
++ = *format
++;
2650 * Get width from argument...
2654 width
= va_arg(ap
, int);
2656 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2657 tptr
+= strlen(tptr
);
2663 while (isdigit(*format
& 255))
2665 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2668 width
= width
* 10 + *format
++ - '0';
2674 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2682 * Get precision from argument...
2686 prec
= va_arg(ap
, int);
2688 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2689 tptr
+= strlen(tptr
);
2695 while (isdigit(*format
& 255))
2697 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2700 prec
= prec
* 10 + *format
++ - '0';
2705 if (*format
== 'l' && format
[1] == 'l')
2709 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2717 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2719 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2734 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2743 case 'E' : /* Floating point formats */
2748 if ((size_t)(width
+ 2) > sizeof(temp
))
2751 sprintf(temp
, tformat
, va_arg(ap
, double));
2753 httpWrite2(client
->http
, temp
, strlen(temp
));
2756 case 'B' : /* Integer formats */
2764 if ((size_t)(width
+ 2) > sizeof(temp
))
2767 # ifdef HAVE_LONG_LONG
2769 sprintf(temp
, tformat
, va_arg(ap
, long long));
2771 # endif /* HAVE_LONG_LONG */
2773 sprintf(temp
, tformat
, va_arg(ap
, long));
2775 sprintf(temp
, tformat
, va_arg(ap
, int));
2777 httpWrite2(client
->http
, temp
, strlen(temp
));
2780 case 'p' : /* Pointer value */
2781 if ((size_t)(width
+ 2) > sizeof(temp
))
2784 sprintf(temp
, tformat
, va_arg(ap
, void *));
2786 httpWrite2(client
->http
, temp
, strlen(temp
));
2789 case 'c' : /* Character or character array */
2792 temp
[0] = (char)va_arg(ap
, int);
2794 html_escape(client
, temp
, 1);
2797 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2800 case 's' : /* String */
2801 if ((s
= va_arg(ap
, char *)) == NULL
)
2804 html_escape(client
, s
, strlen(s
));
2813 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2820 * 'ipp_cancel_job()' - Cancel a job.
2824 ipp_cancel_job(ippeve_client_t
*client
) /* I - Client */
2826 ippeve_job_t
*job
; /* Job information */
2833 if ((job
= find_job(client
)) == NULL
)
2835 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2840 * See if the job is already completed, canceled, or aborted; if so,
2841 * we can't cancel...
2846 case IPP_JSTATE_CANCELED
:
2847 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2848 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2851 case IPP_JSTATE_ABORTED
:
2852 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2853 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2856 case IPP_JSTATE_COMPLETED
:
2857 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2858 "Job #%d is already completed - can\'t cancel.", job
->id
);
2866 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2868 if (job
->state
== IPP_JSTATE_PROCESSING
||
2869 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2873 job
->state
= IPP_JSTATE_CANCELED
;
2874 job
->completed
= time(NULL
);
2877 _cupsRWUnlock(&(client
->printer
->rwlock
));
2879 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2886 * 'ipp_close_job()' - Close an open job.
2890 ipp_close_job(ippeve_client_t
*client
) /* I - Client */
2892 ippeve_job_t
*job
; /* Job information */
2899 if ((job
= find_job(client
)) == NULL
)
2901 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2906 * See if the job is already completed, canceled, or aborted; if so,
2907 * we can't cancel...
2912 case IPP_JSTATE_CANCELED
:
2913 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2914 "Job #%d is canceled - can\'t close.", job
->id
);
2917 case IPP_JSTATE_ABORTED
:
2918 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2919 "Job #%d is aborted - can\'t close.", job
->id
);
2922 case IPP_JSTATE_COMPLETED
:
2923 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2924 "Job #%d is completed - can\'t close.", job
->id
);
2927 case IPP_JSTATE_PROCESSING
:
2928 case IPP_JSTATE_STOPPED
:
2929 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2930 "Job #%d is already closed.", job
->id
);
2934 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2941 * 'ipp_create_job()' - Create a job object.
2945 ipp_create_job(ippeve_client_t
*client
) /* I - Client */
2947 ippeve_job_t
*job
; /* New job */
2948 cups_array_t
*ra
; /* Attributes to send in response */
2952 * Validate print job attributes...
2955 if (!valid_job_attributes(client
))
2957 httpFlush(client
->http
);
2962 * Do we have a file to print?
2965 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2967 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2968 "Unexpected document data following request.");
2976 if ((job
= create_job(client
)) == NULL
)
2978 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2979 "Currently printing another job.");
2984 * Return the job info...
2987 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2989 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2990 cupsArrayAdd(ra
, "job-id");
2991 cupsArrayAdd(ra
, "job-state");
2992 cupsArrayAdd(ra
, "job-state-message");
2993 cupsArrayAdd(ra
, "job-state-reasons");
2994 cupsArrayAdd(ra
, "job-uri");
2996 copy_job_attributes(client
, job
, ra
);
2997 cupsArrayDelete(ra
);
3002 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3006 ipp_get_job_attributes(
3007 ippeve_client_t
*client
) /* I - Client */
3009 ippeve_job_t
*job
; /* Job */
3010 cups_array_t
*ra
; /* requested-attributes */
3013 if ((job
= find_job(client
)) == NULL
)
3015 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3019 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3021 ra
= ippCreateRequestedArray(client
->request
);
3022 copy_job_attributes(client
, job
, ra
);
3023 cupsArrayDelete(ra
);
3028 * 'ipp_get_jobs()' - Get a list of job objects.
3032 ipp_get_jobs(ippeve_client_t
*client
) /* I - Client */
3034 ipp_attribute_t
*attr
; /* Current attribute */
3035 const char *which_jobs
= NULL
;
3036 /* which-jobs values */
3037 int job_comparison
; /* Job comparison */
3038 ipp_jstate_t job_state
; /* job-state value */
3039 int first_job_id
, /* First job ID */
3040 limit
, /* Maximum number of jobs to return */
3041 count
; /* Number of jobs that match */
3042 const char *username
; /* Username */
3043 ippeve_job_t
*job
; /* Current job pointer */
3044 cups_array_t
*ra
; /* Requested attributes array */
3048 * See if the "which-jobs" attribute have been specified...
3051 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3052 IPP_TAG_KEYWORD
)) != NULL
)
3054 which_jobs
= ippGetString(attr
, 0, NULL
);
3055 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3058 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3060 job_comparison
= -1;
3061 job_state
= IPP_JSTATE_STOPPED
;
3063 else if (!strcmp(which_jobs
, "completed"))
3066 job_state
= IPP_JSTATE_CANCELED
;
3068 else if (!strcmp(which_jobs
, "aborted"))
3071 job_state
= IPP_JSTATE_ABORTED
;
3073 else if (!strcmp(which_jobs
, "all"))
3076 job_state
= IPP_JSTATE_PENDING
;
3078 else if (!strcmp(which_jobs
, "canceled"))
3081 job_state
= IPP_JSTATE_CANCELED
;
3083 else if (!strcmp(which_jobs
, "pending"))
3086 job_state
= IPP_JSTATE_PENDING
;
3088 else if (!strcmp(which_jobs
, "pending-held"))
3091 job_state
= IPP_JSTATE_HELD
;
3093 else if (!strcmp(which_jobs
, "processing"))
3096 job_state
= IPP_JSTATE_PROCESSING
;
3098 else if (!strcmp(which_jobs
, "processing-stopped"))
3101 job_state
= IPP_JSTATE_STOPPED
;
3105 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3106 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3107 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3108 "which-jobs", NULL
, which_jobs
);
3113 * See if they want to limit the number of jobs reported...
3116 if ((attr
= ippFindAttribute(client
->request
, "limit",
3117 IPP_TAG_INTEGER
)) != NULL
)
3119 limit
= ippGetInteger(attr
, 0);
3121 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3126 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3127 IPP_TAG_INTEGER
)) != NULL
)
3129 first_job_id
= ippGetInteger(attr
, 0);
3131 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
, first_job_id
);
3137 * See if we only want to see jobs for a specific user...
3142 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3143 IPP_TAG_BOOLEAN
)) != NULL
)
3145 int my_jobs
= ippGetBoolean(attr
, 0);
3147 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
, my_jobs
? "true" : "false");
3151 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3152 IPP_TAG_NAME
)) == NULL
)
3154 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3155 "Need requesting-user-name with my-jobs.");
3159 username
= ippGetString(attr
, 0, NULL
);
3161 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n", client
->hostname
, username
);
3166 * OK, build a list of jobs for this printer...
3169 ra
= ippCreateRequestedArray(client
->request
);
3171 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3173 _cupsRWLockRead(&(client
->printer
->rwlock
));
3175 for (count
= 0, job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3176 (limit
<= 0 || count
< limit
) && job
;
3177 job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3180 * Filter out jobs that don't match...
3183 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3184 (job_comparison
== 0 && job
->state
!= job_state
) ||
3185 (job_comparison
> 0 && job
->state
< job_state
) ||
3186 job
->id
< first_job_id
||
3187 (username
&& job
->username
&&
3188 strcasecmp(username
, job
->username
)))
3192 ippAddSeparator(client
->response
);
3195 copy_job_attributes(client
, job
, ra
);
3198 cupsArrayDelete(ra
);
3200 _cupsRWUnlock(&(client
->printer
->rwlock
));
3205 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3209 ipp_get_printer_attributes(
3210 ippeve_client_t
*client
) /* I - Client */
3212 cups_array_t
*ra
; /* Requested attributes array */
3213 ippeve_printer_t
*printer
; /* Printer */
3217 * Send the attributes...
3220 ra
= ippCreateRequestedArray(client
->request
);
3221 printer
= client
->printer
;
3223 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3225 _cupsRWLockRead(&(printer
->rwlock
));
3227 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3228 IPP_TAG_CUPS_CONST
);
3230 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3231 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3233 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3234 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3236 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3237 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3240 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3241 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3242 "printer-state", printer
->state
);
3244 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3245 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3247 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3248 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3250 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3252 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3254 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3257 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3259 if (printer
->state_reasons
== IPPEVE_PREASON_NONE
)
3261 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", NULL
, "none");
3265 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3266 ippeve_preason_t bit
; /* Reason bit */
3267 int i
; /* Looping var */
3268 char reason
[32]; /* Reason string */
3270 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
3272 if (printer
->state_reasons
& bit
)
3274 snprintf(reason
, sizeof(reason
), "%s-%s", ippeve_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3276 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3278 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3284 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3285 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3287 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3288 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3290 _cupsRWUnlock(&(printer
->rwlock
));
3292 cupsArrayDelete(ra
);
3297 * 'ipp_identify_printer()' - Beep or display a message.
3301 ipp_identify_printer(
3302 ippeve_client_t
*client
) /* I - Client */
3304 ipp_attribute_t
*actions
, /* identify-actions */
3305 *message
; /* message */
3308 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3309 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3311 if (!actions
|| ippContainsString(actions
, "sound"))
3317 if (ippContainsString(actions
, "display"))
3318 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3320 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3325 * 'ipp_print_job()' - Create a job object with an attached document.
3329 ipp_print_job(ippeve_client_t
*client
) /* I - Client */
3331 ippeve_job_t
*job
; /* New job */
3335 * Validate print job attributes...
3338 if (!valid_job_attributes(client
))
3340 httpFlush(client
->http
);
3345 * Do we have a file to print?
3348 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3350 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3358 if ((job
= create_job(client
)) == NULL
)
3360 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3365 * Then finish getting the document data and process things...
3368 finish_document_data(client
, job
);
3373 * 'ipp_print_uri()' - Create a job object with a referenced document.
3377 ipp_print_uri(ippeve_client_t
*client
) /* I - Client */
3379 ippeve_job_t
*job
; /* New job */
3383 * Validate print job attributes...
3386 if (!valid_job_attributes(client
))
3388 httpFlush(client
->http
);
3396 if ((job
= create_job(client
)) == NULL
)
3398 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3403 * Then finish getting the document data and process things...
3406 finish_document_uri(client
, job
);
3411 * 'ipp_send_document()' - Add an attached document to a job object created with
3417 ippeve_client_t
*client
) /* I - Client */
3419 ippeve_job_t
*job
; /* Job information */
3420 ipp_attribute_t
*attr
; /* Current attribute */
3427 if ((job
= find_job(client
)) == NULL
)
3429 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3430 httpFlush(client
->http
);
3435 * See if we already have a document for this job or the job has already
3436 * in a non-pending state...
3439 if (job
->state
> IPP_JSTATE_HELD
)
3441 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job is not in a pending state.");
3442 httpFlush(client
->http
);
3445 else if (job
->filename
|| job
->fd
>= 0)
3447 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
, "Multiple document jobs are not supported.");
3448 httpFlush(client
->http
);
3453 * Make sure we have the "last-document" operation attribute...
3456 if ((attr
= ippFindAttribute(client
->request
, "last-document", IPP_TAG_ZERO
)) == NULL
)
3458 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing required last-document attribute.");
3459 httpFlush(client
->http
);
3462 else if (ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
3464 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The last-document attribute is not in the operation group.");
3465 httpFlush(client
->http
);
3468 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 || !ippGetBoolean(attr
, 0))
3470 respond_unsupported(client
, attr
);
3471 httpFlush(client
->http
);
3476 * Validate document attributes...
3479 if (!valid_doc_attributes(client
))
3481 httpFlush(client
->http
);
3486 * Then finish getting the document data and process things...
3489 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3491 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3493 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3494 job
->format
= ippGetString(attr
, 0, NULL
);
3495 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3496 job
->format
= ippGetString(attr
, 0, NULL
);
3498 job
->format
= "application/octet-stream";
3500 _cupsRWUnlock(&(client
->printer
->rwlock
));
3502 finish_document_data(client
, job
);
3507 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3512 ipp_send_uri(ippeve_client_t
*client
) /* I - Client */
3514 ippeve_job_t
*job
; /* Job information */
3515 ipp_attribute_t
*attr
; /* Current attribute */
3522 if ((job
= find_job(client
)) == NULL
)
3524 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3525 httpFlush(client
->http
);
3530 * See if we already have a document for this job or the job has already
3531 * in a non-pending state...
3534 if (job
->state
> IPP_JSTATE_HELD
)
3536 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job is not in a pending state.");
3537 httpFlush(client
->http
);
3540 else if (job
->filename
|| job
->fd
>= 0)
3542 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
, "Multiple document jobs are not supported.");
3543 httpFlush(client
->http
);
3547 if ((attr
= ippFindAttribute(client
->request
, "last-document", IPP_TAG_ZERO
)) == NULL
)
3549 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing required last-document attribute.");
3550 httpFlush(client
->http
);
3553 else if (ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
3555 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The last-document attribute is not in the operation group.");
3556 httpFlush(client
->http
);
3559 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 || !ippGetBoolean(attr
, 0))
3561 respond_unsupported(client
, attr
);
3562 httpFlush(client
->http
);
3567 * Validate document attributes...
3570 if (!valid_doc_attributes(client
))
3572 httpFlush(client
->http
);
3577 * Then finish getting the document data and process things...
3580 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3582 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3584 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3585 job
->format
= ippGetString(attr
, 0, NULL
);
3586 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3587 job
->format
= ippGetString(attr
, 0, NULL
);
3589 job
->format
= "application/octet-stream";
3591 _cupsRWUnlock(&(client
->printer
->rwlock
));
3593 finish_document_uri(client
, job
);
3598 * 'ipp_validate_job()' - Validate job creation attributes.
3602 ipp_validate_job(ippeve_client_t
*client
) /* I - Client */
3604 if (valid_job_attributes(client
))
3605 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3610 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
3613 static int /* O - 1 to use, 0 to ignore */
3615 _ipp_file_t
*f
, /* I - IPP file */
3616 void *user_data
, /* I - User data pointer (unused) */
3617 const char *attr
) /* I - Attribute name */
3619 int i
, /* Current element */
3620 result
; /* Result of comparison */
3621 static const char * const ignored
[] =
3622 { /* Ignored attributes */
3623 "attributes-charset",
3624 "attributes-natural-language",
3625 "charset-configured",
3626 "charset-supported",
3627 "device-service-count",
3629 "document-format-varying-attributes",
3630 "generated-natural-language-supported",
3631 "identify-actions-default",
3632 "identify-actions-supported",
3633 "ipp-features-supported",
3634 "ipp-versions-supproted",
3635 "ippget-event-life",
3636 "job-hold-until-supported",
3637 "job-hold-until-time-supported",
3638 "job-ids-supported",
3639 "job-k-octets-supported",
3640 "job-settable-attributes-supported",
3641 "multiple-document-jobs-supported",
3642 "multiple-operation-time-out",
3643 "multiple-operation-time-out-action",
3644 "natural-language-configured",
3645 "notify-attributes-supported",
3646 "notify-events-default",
3647 "notify-events-supported",
3648 "notify-lease-duration-default",
3649 "notify-lease-duration-supported",
3650 "notify-max-events-supported",
3651 "notify-pull-method-supported",
3652 "operations-supported",
3654 "printer-alert-description",
3655 "printer-camera-image-uri",
3656 "printer-charge-info",
3657 "printer-charge-info-uri",
3658 "printer-config-change-date-time",
3659 "printer-config-change-time",
3660 "printer-current-time",
3661 "printer-detailed-status-messages",
3662 "printer-dns-sd-name",
3663 "printer-fax-log-uri",
3664 "printer-get-attributes-supported",
3668 "printer-is-accepting-jobs",
3669 "printer-message-date-time",
3670 "printer-message-from-operator",
3671 "printer-message-time",
3672 "printer-more-info",
3673 "printer-service-type",
3674 "printer-settable-attributes-supported",
3676 "printer-state-message",
3677 "printer-state-reasons",
3678 "printer-static-resource-directory-uri",
3679 "printer-static-resource-k-octets-free",
3680 "printer-static-resource-k-octets-supported",
3681 "printer-strings-languages-supported",
3682 "printer-strings-uri",
3683 "printer-supply-info-uri",
3685 "printer-uri-supported",
3686 "printer-xri-supported",
3688 "reference-uri-scheme-supported",
3689 "uri-authentication-supported",
3690 "uri-security-supported",
3691 "which-jobs-supported",
3692 "xri-authentication-supported",
3693 "xri-security-supported",
3694 "xri-uri-scheme-supported"
3701 for (i
= 0, result
= 1; i
< (int)(sizeof(ignored
) / sizeof(ignored
[0])); i
++)
3703 if ((result
= strcmp(attr
, ignored
[i
])) <= 0)
3707 return (result
!= 0);
3712 * 'ippserver_error_cb()' - Log an error message.
3715 static int /* O - 1 to continue, 0 to stop */
3717 _ipp_file_t
*f
, /* I - IPP file data */
3718 void *user_data
, /* I - User data pointer (unused) */
3719 const char *error
) /* I - Error message */
3724 _cupsLangPrintf(stderr
, "%s\n", error
);
3731 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
3734 static int /* O - 1 to continue, 0 to stop */
3736 _ipp_file_t
*f
, /* I - IPP file data */
3737 _ipp_vars_t
*vars
, /* I - IPP variables */
3738 void *user_data
, /* I - User data pointer (unused) */
3739 const char *token
) /* I - Current token */
3747 * NULL token means do the initial setup - create an empty IPP message and
3751 f
->attrs
= ippNew();
3752 f
->group_tag
= IPP_TAG_PRINTER
;
3756 _cupsLangPrintf(stderr
, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token
, f
->linenum
, f
->filename
);
3764 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
3767 static ipp_t
* /* O - IPP attributes or `NULL` on error */
3768 load_ippserver_attributes(
3769 const char *servername
, /* I - Server name or `NULL` for default */
3770 int serverport
, /* I - Server port number */
3771 const char *filename
, /* I - ippserver attribute filename */
3772 cups_array_t
*docformats
) /* I - document-format-supported values */
3774 ipp_t
*attrs
; /* IPP attributes */
3775 _ipp_vars_t vars
; /* IPP variables */
3776 char temp
[256]; /* Temporary string */
3780 * Setup callbacks and variables for the printer configuration file...
3782 * The following additional variables are supported:
3784 * - SERVERNAME: The host name of the server.
3785 * - SERVERPORT: The default port of the server.
3788 _ippVarsInit(&vars
, (_ipp_fattr_cb_t
)ippserver_attr_cb
, (_ipp_ferror_cb_t
)ippserver_error_cb
, (_ipp_ftoken_cb_t
)ippserver_token_cb
);
3792 _ippVarsSet(&vars
, "SERVERNAME", servername
);
3796 httpGetHostname(NULL
, temp
, sizeof(temp
));
3797 _ippVarsSet(&vars
, "SERVERNAME", temp
);
3800 snprintf(temp
, sizeof(temp
), "%d", serverport
);
3801 _ippVarsSet(&vars
, "SERVERPORT", temp
);
3804 * Load attributes and values for the printer...
3807 attrs
= _ippFileParse(&vars
, filename
, NULL
);
3810 * Free memory and return...
3813 _ippVarsDeinit(&vars
);
3820 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
3824 static ipp_t
* /* O - IPP attributes or `NULL` on error */
3825 load_legacy_attributes(
3826 const char *make
, /* I - Manufacturer name */
3827 const char *model
, /* I - Model name */
3828 int ppm
, /* I - pages-per-minute */
3829 int ppm_color
, /* I - pages-per-minute-color */
3830 int duplex
, /* I - Duplex support? */
3831 cups_array_t
*docformats
) /* I - document-format-supported values */
3833 int i
; /* Looping var */
3834 ipp_t
*attrs
, /* IPP attributes */
3835 *col
; /* Collection value */
3836 ipp_attribute_t
*attr
; /* Current attribute */
3837 char device_id
[1024],/* printer-device-id */
3838 *ptr
, /* Pointer into device ID */
3839 make_model
[128];/* printer-make-and-model */
3840 const char *format
, /* Current document format */
3841 *prefix
; /* Prefix for device ID */
3842 int num_media
; /* Number of media */
3843 const char * const *media
; /* List of media */
3844 int num_ready
; /* Number of loaded media */
3845 const char * const *ready
; /* List of loaded media */
3846 pwg_media_t
*pwg
; /* PWG media size information */
3847 static const char * const media_supported
[] =
3848 { /* media-supported values */
3849 "na_letter_8.5x11in", /* Letter */
3850 "na_legal_8.5x14in", /* Legal */
3851 "iso_a4_210x297mm", /* A4 */
3852 "na_number-10_4.125x9.5in", /* #10 Envelope */
3853 "iso_dl_110x220mm" /* DL Envelope */
3855 static const char * const media_supported_color
[] =
3856 { /* media-supported values */
3857 "na_letter_8.5x11in", /* Letter */
3858 "na_legal_8.5x14in", /* Legal */
3859 "iso_a4_210x297mm", /* A4 */
3860 "na_number-10_4.125x9.5in", /* #10 Envelope */
3861 "iso_dl_110x220mm", /* DL Envelope */
3862 "na_index-3x5_3x5in", /* Photo 3x5 */
3863 "oe_photo-l_3.5x5in", /* Photo L */
3864 "na_index-4x6_4x6in", /* Photo 4x6 */
3865 "iso_a6_105x148mm", /* A6 */
3866 "na_5x7_5x7in" /* Photo 5x7 aka 2L */
3867 "iso_a5_148x210mm", /* A5 */
3869 static const char * const media_ready
[] =
3870 { /* media-ready values */
3871 "na_letter_8.5x11in", /* Letter */
3872 "na_number-10_4.125x9.5in" /* #10 */
3874 static const char * const media_ready_color
[] =
3875 { /* media-ready values */
3876 "na_letter_8.5x11in", /* Letter */
3877 "na_index-4x6_4x6in" /* Photo 4x6 */
3879 static const char * const media_source_supported
[] =
3880 { /* media-source-supported values */
3884 "by-pass-tray" /* AKA multi-purpose tray */
3886 static const char * const media_source_supported_color
[] =
3887 { /* media-source-supported values */
3892 static const char * const media_type_supported
[] =
3893 { /* media-type-supported values */
3900 "stationery-letterhead",
3903 static const char * const media_type_supported_color
[] =
3904 { /* media-type-supported values */
3911 "stationery-letterhead",
3913 "photographic-glossy",
3914 "photographic-high-gloss",
3915 "photographic-matte",
3916 "photographic-satin",
3917 "photographic-semi-gloss"
3919 static const int media_bottom_margin_supported
[] =
3920 { /* media-bottom-margin-supported values */
3923 static const int media_bottom_margin_supported_color
[] =
3924 { /* media-bottom/top-margin-supported values */
3926 1168 /* 0.46" (common HP inkjet bottom margin) */
3928 static const int media_lr_margin_supported
[] =
3929 { /* media-left/right-margin-supported values */
3930 340, /* 3.4mm (historical HP PCL A4 margin) */
3933 static const int media_lr_margin_supported_color
[] =
3934 { /* media-left/right-margin-supported values */
3936 340, /* 3.4mm (historical HP PCL A4 margin) */
3939 static const int media_top_margin_supported
[] =
3940 { /* media-top-margin-supported values */
3943 static const int media_top_margin_supported_color
[] =
3944 { /* media-top/top-margin-supported values */
3946 102 /* 0.04" (common HP inkjet top margin */
3948 static const int orientation_requested_supported
[4] =
3949 { /* orientation-requested-supported values */
3950 IPP_ORIENT_PORTRAIT
,
3951 IPP_ORIENT_LANDSCAPE
,
3952 IPP_ORIENT_REVERSE_LANDSCAPE
,
3953 IPP_ORIENT_REVERSE_PORTRAIT
3955 static const char * const print_color_mode_supported
[] =
3956 { /* print-color-mode-supported values */
3959 static const char * const print_color_mode_supported_color
[] =
3960 { /* print-color-mode-supported values */
3965 static const int print_quality_supported
[] =
3966 { /* print-quality-supported values */
3971 static const char * const printer_input_tray
[] =
3972 { /* printer-input-tray values */
3973 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
3974 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
3975 "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
3976 "type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
3978 static const char * const printer_input_tray_color
[] =
3979 { /* printer-input-tray values */
3980 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
3981 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
3982 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
3984 static const char * const printer_supply
[] =
3985 { /* printer-supply values */
3986 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
3987 "maxcapacity=100;level=25;colorantname=unknown;",
3988 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
3989 "maxcapacity=100;level=75;colorantname=black;"
3991 static const char * const printer_supply_color
[] =
3992 { /* printer-supply values */
3993 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
3994 "maxcapacity=100;level=25;colorantname=unknown;",
3995 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
3996 "maxcapacity=100;level=75;colorantname=black;",
3997 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
3998 "maxcapacity=100;level=50;colorantname=cyan;",
3999 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4000 "maxcapacity=100;level=33;colorantname=magenta;",
4001 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4002 "maxcapacity=100;level=67;colorantname=yellow;"
4004 static const char * const printer_supply_description
[] =
4005 { /* printer-supply-description values */
4009 static const char * const printer_supply_description_color
[] =
4010 { /* printer-supply-description values */
4017 static const int pwg_raster_document_resolution_supported
[] =
4022 static const char * const pwg_raster_document_type_supported
[] =
4027 static const char * const pwg_raster_document_type_supported_color
[] =
4034 static const char * const sides_supported
[] =
4035 { /* sides-supported values */
4037 "two-sided-long-edge",
4038 "two-sided-short-edge"
4040 static const char * const urf_supported
[] =
4041 { /* urf-supported values */
4049 static const char * const urf_supported_color
[] =
4050 { /* urf-supported values */
4053 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4059 static const char * const urf_supported_color_duplex
[] =
4060 { /* urf-supported values */
4063 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4070 static const char * const urf_supported_duplex
[] =
4071 { /* urf-supported values */
4086 num_media
= (int)(sizeof(media_supported_color
) / sizeof(media_supported_color
[0]));
4087 media
= media_supported_color
;
4088 num_ready
= (int)(sizeof(media_ready_color
) / sizeof(media_ready_color
[0]));
4089 ready
= media_ready_color
;
4093 num_media
= (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
4094 media
= media_supported
;
4095 num_ready
= (int)(sizeof(media_ready
) / sizeof(media_ready
[0]));
4096 ready
= media_ready
;
4099 /* color-supported */
4100 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
4102 /* copies-default */
4103 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
4105 /* copies-supported */
4106 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, (cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
|| cupsArrayFind(docformats
, (void *)"image/jpeg") != NULL
) ? 999 : 1);
4108 /* finishings-default */
4109 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
4111 /* finishings-supported */
4112 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
4114 /* media-bottom-margin-supported */
4116 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
);
4118 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
);
4120 /* media-col-database and media-col-default */
4121 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", num_media
, NULL
);
4122 for (i
= 0; i
< num_media
; i
++)
4124 int bottom
, left
, /* media-xxx-margins */
4126 const char *source
; /* media-source, if any */
4128 pwg
= pwgMediaForPWG(media
[i
]);
4130 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4132 source
= "photo"; /* Photo size media from photo tray */
4133 bottom
= /* Borderless margins */
4138 else if (pwg
->width
< 21000)
4140 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4141 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4142 left
= /* Left/right margins are standard */
4143 right
= media_lr_margin_supported
[1];
4144 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4146 else if (pwg
->width
== 21000)
4148 source
= NULL
; /* A4 from any tray */
4149 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4150 left
= /* Left/right margins are reduced */
4151 right
= media_lr_margin_supported
[0];
4152 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4156 source
= NULL
; /* Other size media from any tray */
4157 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4158 left
= /* Left/right margins are standard */
4159 right
= media_lr_margin_supported
[1];
4160 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4163 col
= create_media_col(media
[i
], source
, NULL
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4164 ippSetCollection(attrs
, &attr
, i
, col
);
4168 /* media-col-default */
4169 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default", col
);
4175 /* media-col-ready */
4176 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, NULL
);
4177 for (i
= 0; i
< num_ready
; i
++)
4179 int bottom
, left
, /* media-xxx-margins */
4181 const char *source
, /* media-source */
4182 *type
; /* media-type */
4184 pwg
= pwgMediaForPWG(ready
[i
]);
4186 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4188 source
= "photo"; /* Photo size media from photo tray */
4189 type
= "photographic-glossy"; /* Glossy photo paper */
4190 bottom
= /* Borderless margins */
4195 else if (pwg
->width
< 21000)
4197 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4198 type
= "envelope"; /* Envelope */
4199 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4200 left
= /* Left/right margins are standard */
4201 right
= media_lr_margin_supported
[1];
4202 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4204 else if (pwg
->width
== 21000)
4206 source
= "main"; /* A4 from main tray */
4207 type
= "stationery"; /* Plain paper */
4208 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4209 left
= /* Left/right margins are reduced */
4210 right
= media_lr_margin_supported
[0];
4211 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4215 source
= "main"; /* A4 from main tray */
4216 type
= "stationery"; /* Plain paper */
4217 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4218 left
= /* Left/right margins are standard */
4219 right
= media_lr_margin_supported
[1];
4220 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4223 col
= create_media_col(media
[i
], source
, NULL
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4224 ippSetCollection(attrs
, &attr
, i
, col
);
4229 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media
[0]);
4231 /* media-left/right-margin-supported */
4234 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
);
4235 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
);
4239 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
);
4240 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
);
4244 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", num_ready
, NULL
, ready
);
4246 /* media-supported */
4247 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-supported", num_media
, NULL
, media
);
4249 /* media-size-supported */
4250 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", num_media
, NULL
);
4251 for (i
= 0; i
< num_media
; i
++)
4253 pwg
= pwgMediaForPWG(media
[i
]);
4254 col
= create_media_size(pwg
->width
, pwg
->length
);
4256 ippSetCollection(attrs
, &attr
, i
, col
);
4260 /* media-source-supported */
4262 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
);
4264 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
);
4266 /* media-top-margin-supported */
4268 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
);
4270 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
);
4272 /* media-type-supported */
4274 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
);
4276 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
);
4278 /* orientation-requested-supported */
4279 if (cupsArrayFind(docformats
, (void *)"application/pdf") || cupsArrayFind(docformats
, (void *)"image/jpeg"))
4280 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported
) / sizeof(orientation_requested_supported
[0])), orientation_requested_supported
);
4282 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", IPP_ORIENT_PORTRAIT
);
4284 /* output-bin-default */
4286 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-up");
4288 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
4290 /* output-bin-supported */
4292 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-up");
4294 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
4296 /* page-ranges-supported */
4297 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
);
4299 /* pages-per-minute */
4300 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute", ppm
);
4302 /* pages-per-minute-color */
4304 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute-color", ppm_color
);
4306 /* print-color-mode-default */
4307 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
4309 /* print-color-mode-supported */
4311 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
);
4313 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
);
4315 /* print-content-optimize-default */
4316 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
4318 /* print-content-optimize-supported */
4319 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
4321 /* print-quality-default */
4322 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
4324 /* print-quality-supported */
4325 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
4327 /* print-rendering-intent-default */
4328 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
4330 /* print-rendering-intent-supported */
4331 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
4333 /* printer-device-id */
4334 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
4335 ptr
= device_id
+ strlen(device_id
);
4337 for (format
= (const char *)cupsArrayFirst(docformats
); format
; format
= (const char *)cupsArrayNext(docformats
))
4339 if (!strcasecmp(format
, "application/pdf"))
4340 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
4341 else if (!strcasecmp(format
, "application/postscript"))
4342 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
4343 else if (!strcasecmp(format
, "application/vnd.hp-PCL"))
4344 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
4345 else if (!strcasecmp(format
, "image/jpeg"))
4346 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
4347 else if (!strcasecmp(format
, "image/png"))
4348 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
4349 else if (!strcasecmp(format
, "image/pwg-raster"))
4350 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPWG", prefix
);
4351 else if (!strcasecmp(format
, "image/urf"))
4352 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sURF", prefix
);
4359 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
4364 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
4366 /* printer-input-tray */
4369 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray_color
[0], strlen(printer_input_tray_color
[0]));
4370 for (i
= 1; i
< (int)(sizeof(printer_input_tray_color
) / sizeof(printer_input_tray_color
[0])); i
++)
4371 ippSetOctetString(attrs
, &attr
, i
, printer_input_tray_color
[i
], strlen(printer_input_tray_color
[i
]));
4375 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray
[0], strlen(printer_input_tray
[0]));
4376 for (i
= 1; i
< (int)(sizeof(printer_input_tray
) / sizeof(printer_input_tray
[0])); i
++)
4377 ippSetOctetString(attrs
, &attr
, i
, printer_input_tray
[i
], strlen(printer_input_tray
[i
]));
4380 /* printer-make-and-model */
4381 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4382 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, make_model
);
4384 /* printer-resolution-default */
4385 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
4387 /* printer-resolution-supported */
4388 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
4390 /* printer-supply and printer-supply-description */
4393 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply_color
[0], strlen(printer_supply_color
[0]));
4394 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
4395 ippSetOctetString(attrs
, &attr
, i
, printer_supply_color
[i
], strlen(printer_supply_color
[i
]));
4397 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
);
4401 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply
[0], strlen(printer_supply
[0]));
4402 for (i
= 1; i
< (int)(sizeof(printer_supply
) / sizeof(printer_supply
[0])); i
++)
4403 ippSetOctetString(attrs
, &attr
, i
, printer_supply
[i
], strlen(printer_supply
[i
]));
4405 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
);
4408 /* pwg-raster-document-xxx-supported */
4409 if (cupsArrayFind(docformats
, (void *)"image/pwg-raster"))
4411 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
);
4413 if (ppm_color
> 0 && duplex
)
4414 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "rotated");
4416 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "normal");
4419 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
);
4421 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
);
4425 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
4427 /* sides-supported */
4429 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", (int)(sizeof(sides_supported
) / sizeof(sides_supported
[0])), NULL
, sides_supported
);
4431 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", NULL
, "one-sided");
4434 if (cupsArrayFind(docformats
, (void *)"image/urf"))
4439 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
);
4441 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_color
) / sizeof(urf_supported_color
[0])), NULL
, urf_supported_color
);
4445 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_duplex
) / sizeof(urf_supported_duplex
[0])), NULL
, urf_supported_duplex
);
4449 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])), NULL
, urf_supported
);
4459 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4462 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4463 load_ppd_attributes(
4464 const char *ppdfile
, /* I - PPD filename */
4465 cups_array_t
*docformats
) /* I - document-format-supported values */
4471 static const char * const overrides
[] =
4472 { /* overrides-supported */
4477 /* document-password-supported */
4478 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
4479 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
4481 /* overrides-supported */
4482 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
4483 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
4485 /* page-ranges-supported */
4486 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
4487 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
4488 /* pages-per-minute */
4489 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
4490 "pages-per-minute", ppm
);
4492 /* pages-per-minute-color */
4494 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
4495 "pages-per-minute-color", ppm_color
);
4500 #endif /* !CUPS_LITE */
4504 * 'parse_options()' - Parse URL options into CUPS options.
4506 * The client->options string is destroyed by this function.
4509 static int /* O - Number of options */
4510 parse_options(ippeve_client_t
*client
, /* I - Client */
4511 cups_option_t
**options
)/* O - Options */
4513 char *name
, /* Name */
4515 *next
; /* Next name=value pair */
4516 int num_options
= 0; /* Number of options */
4521 for (name
= client
->options
; name
&& *name
; name
= next
)
4523 if ((value
= strchr(name
, '=')) == NULL
)
4527 if ((next
= strchr(value
, '&')) != NULL
)
4530 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4533 return (num_options
);
4538 * 'process_attr_message()' - Process an ATTR: message from a command.
4542 process_attr_message(
4543 ippeve_job_t
*job
, /* I - Job */
4544 char *message
) /* I - Message */
4552 * 'process_client()' - Process client requests on a thread.
4555 static void * /* O - Exit status */
4556 process_client(ippeve_client_t
*client
) /* I - Client */
4559 * Loop until we are out of requests or timeout (30 seconds)...
4563 int first_time
= 1; /* First time request? */
4564 #endif /* HAVE_SSL */
4566 while (httpWait(client
->http
, 30000))
4572 * See if we need to negotiate a TLS connection...
4575 char buf
[1]; /* First byte from client */
4577 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4579 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4581 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4583 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4587 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4592 #endif /* HAVE_SSL */
4594 if (!process_http(client
))
4599 * Close the conection to the client and return...
4602 delete_client(client
);
4609 * 'process_http()' - Process a HTTP request.
4612 int /* O - 1 on success, 0 on failure */
4613 process_http(ippeve_client_t
*client
) /* I - Client connection */
4615 char uri
[1024]; /* URI */
4616 http_state_t http_state
; /* HTTP state */
4617 http_status_t http_status
; /* HTTP status */
4618 ipp_state_t ipp_state
; /* State of IPP transfer */
4619 char scheme
[32], /* Method/scheme */
4620 userpass
[128], /* Username:password */
4621 hostname
[HTTP_MAX_HOST
];
4623 int port
; /* Port number */
4624 const char *encoding
; /* Content-Encoding value */
4625 static const char * const http_states
[] =
4626 { /* Strings for logging HTTP method */
4647 * Clear state variables...
4650 ippDelete(client
->request
);
4651 ippDelete(client
->response
);
4653 client
->request
= NULL
;
4654 client
->response
= NULL
;
4655 client
->operation
= HTTP_STATE_WAITING
;
4658 * Read a request from the connection...
4661 while ((http_state
= httpReadRequest(client
->http
, uri
,
4662 sizeof(uri
))) == HTTP_STATE_WAITING
)
4666 * Parse the request line...
4669 if (http_state
== HTTP_STATE_ERROR
)
4671 if (httpError(client
->http
) == EPIPE
)
4672 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4674 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
, strerror(httpError(client
->http
)));
4678 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4680 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4681 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4684 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4686 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4687 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4691 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
], uri
);
4694 * Separate the URI into its components...
4697 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4698 userpass
, sizeof(userpass
),
4699 hostname
, sizeof(hostname
), &port
,
4700 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4701 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4703 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4704 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4708 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4709 *(client
->options
)++ = '\0';
4712 * Process the request...
4715 client
->start
= time(NULL
);
4716 client
->operation
= httpGetState(client
->http
);
4719 * Parse incoming parameters until the status changes...
4722 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4724 if (http_status
!= HTTP_STATUS_OK
)
4726 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4730 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4731 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4734 * HTTP/1.1 and higher require the "Host:" field...
4737 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4742 * Handle HTTP Upgrade...
4745 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4749 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4751 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4754 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4756 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4758 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4762 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4765 #endif /* HAVE_SSL */
4767 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4772 * Handle HTTP Expect...
4775 if (httpGetExpect(client
->http
) &&
4776 (client
->operation
== HTTP_STATE_POST
||
4777 client
->operation
== HTTP_STATE_PUT
))
4779 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4782 * Send 100-continue header...
4785 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4791 * Send 417-expectation-failed header...
4794 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4800 * Handle new transfers...
4803 encoding
= httpGetContentEncoding(client
->http
);
4805 switch (client
->operation
)
4807 case HTTP_STATE_OPTIONS
:
4809 * Do OPTIONS command...
4812 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4814 case HTTP_STATE_HEAD
:
4815 if (!strcmp(client
->uri
, "/icon.png"))
4816 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4817 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4818 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4820 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4822 case HTTP_STATE_GET
:
4823 if (!strcmp(client
->uri
, "/icon.png"))
4826 * Send PNG icon file.
4829 if (client
->printer
->icon
)
4831 int fd
; /* Icon file */
4832 struct stat fileinfo
; /* Icon file information */
4833 char buffer
[4096]; /* Copy buffer */
4834 ssize_t bytes
; /* Bytes */
4836 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4838 if (!stat(client
->printer
->icon
, &fileinfo
) && (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4840 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", (size_t)fileinfo
.st_size
))
4846 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4847 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4849 httpFlushWrite(client
->http
);
4854 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4858 fputs("Icon file is internal printer.png.\n", stderr
);
4860 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", sizeof(printer_png
)))
4863 httpWrite2(client
->http
, (const char *)printer_png
, sizeof(printer_png
));
4864 httpFlushWrite(client
->http
);
4867 else if (!strcmp(client
->uri
, "/"))
4870 * Show web status page...
4873 return (show_status(client
));
4875 else if (!strcmp(client
->uri
, "/media"))
4878 * Show web media page...
4881 return (show_media(client
));
4883 else if (!strcmp(client
->uri
, "/supplies"))
4886 * Show web supplies page...
4889 return (show_supplies(client
));
4892 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4895 case HTTP_STATE_POST
:
4896 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4900 * Not an IPP request...
4903 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4907 * Read the IPP request...
4910 client
->request
= ippNew();
4912 while ((ipp_state
= ippRead(client
->http
,
4913 client
->request
)) != IPP_STATE_DATA
)
4915 if (ipp_state
== IPP_STATE_ERROR
)
4917 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
, cupsLastErrorString());
4918 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4924 * Now that we have the IPP request, process the request...
4927 return (process_ipp(client
));
4930 break; /* Anti-compiler-warning-code */
4938 * 'process_ipp()' - Process an IPP request.
4941 static int /* O - 1 on success, 0 on error */
4942 process_ipp(ippeve_client_t
*client
) /* I - Client */
4944 ipp_tag_t group
; /* Current group tag */
4945 ipp_attribute_t
*attr
; /* Current attribute */
4946 ipp_attribute_t
*charset
; /* Character set attribute */
4947 ipp_attribute_t
*language
; /* Language attribute */
4948 ipp_attribute_t
*uri
; /* Printer URI attribute */
4949 int major
, minor
; /* Version number */
4950 const char *name
; /* Name of attribute */
4953 debug_attributes("Request", client
->request
, 1);
4956 * First build an empty response message for this request...
4959 client
->operation_id
= ippGetOperation(client
->request
);
4960 client
->response
= ippNewResponse(client
->request
);
4963 * Then validate the request header and required attributes...
4966 major
= ippGetVersion(client
->request
, &minor
);
4968 if (major
< 1 || major
> 2)
4971 * Return an error, since we only support IPP 1.x and 2.x.
4974 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
4976 else if ((major
* 10 + minor
) > MaxVersion
)
4978 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
4979 httpFlush(client
->http
); /* Flush trailing (junk) data */
4981 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4984 else if (ippGetRequestId(client
->request
) <= 0)
4986 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
4988 else if (!ippFirstAttribute(client
->request
))
4990 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
4995 * Make sure that the attributes are provided in the correct order and
4996 * don't repeat groups...
4999 for (attr
= ippFirstAttribute(client
->request
),
5000 group
= ippGetGroupTag(attr
);
5002 attr
= ippNextAttribute(client
->request
))
5004 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5007 * Out of order; return an error...
5010 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5011 "Attribute groups are out of order (%x < %x).",
5012 ippGetGroupTag(attr
), group
);
5016 group
= ippGetGroupTag(attr
);
5022 * Then make sure that the first three attributes are:
5024 * attributes-charset
5025 * attributes-natural-language
5026 * printer-uri/job-uri
5029 attr
= ippFirstAttribute(client
->request
);
5030 name
= ippGetName(attr
);
5031 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5032 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5037 attr
= ippNextAttribute(client
->request
);
5038 name
= ippGetName(attr
);
5040 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5041 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5046 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5047 IPP_TAG_URI
)) != NULL
)
5049 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5050 IPP_TAG_URI
)) != NULL
)
5056 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5057 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5060 * Bad character set...
5063 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5064 "Unsupported character set \"%s\".",
5065 ippGetString(charset
, 0, NULL
));
5067 else if (!charset
|| !language
|| !uri
)
5070 * Return an error, since attributes-charset,
5071 * attributes-natural-language, and printer-uri/job-uri are required
5072 * for all operations.
5075 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5076 "Missing required attributes.");
5080 char scheme
[32], /* URI scheme */
5081 userpass
[32], /* Username/password in URI */
5082 host
[256], /* Host name in URI */
5083 resource
[256]; /* Resource path in URI */
5084 int port
; /* Port number in URI */
5086 name
= ippGetName(uri
);
5088 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5089 scheme
, sizeof(scheme
),
5090 userpass
, sizeof(userpass
),
5091 host
, sizeof(host
), &port
,
5092 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5093 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5094 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5095 else if ((!strcmp(name
, "job-uri") &&
5096 strncmp(resource
, "/ipp/print/", 11)) ||
5097 (!strcmp(name
, "printer-uri") &&
5098 strcmp(resource
, "/ipp/print")))
5099 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5100 name
, ippGetString(uri
, 0, NULL
));
5104 * Try processing the operation...
5107 switch (ippGetOperation(client
->request
))
5109 case IPP_OP_PRINT_JOB
:
5110 ipp_print_job(client
);
5113 case IPP_OP_PRINT_URI
:
5114 ipp_print_uri(client
);
5117 case IPP_OP_VALIDATE_JOB
:
5118 ipp_validate_job(client
);
5121 case IPP_OP_CREATE_JOB
:
5122 ipp_create_job(client
);
5125 case IPP_OP_SEND_DOCUMENT
:
5126 ipp_send_document(client
);
5129 case IPP_OP_SEND_URI
:
5130 ipp_send_uri(client
);
5133 case IPP_OP_CANCEL_JOB
:
5134 ipp_cancel_job(client
);
5137 case IPP_OP_GET_JOB_ATTRIBUTES
:
5138 ipp_get_job_attributes(client
);
5141 case IPP_OP_GET_JOBS
:
5142 ipp_get_jobs(client
);
5145 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5146 ipp_get_printer_attributes(client
);
5149 case IPP_OP_CLOSE_JOB
:
5150 ipp_close_job(client
);
5153 case IPP_OP_IDENTIFY_PRINTER
:
5154 ipp_identify_printer(client
);
5158 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5159 "Operation not supported.");
5168 * Send the HTTP header and return...
5171 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5172 httpFlush(client
->http
); /* Flush trailing (junk) data */
5174 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5175 ippLength(client
->response
)));
5180 * 'process_job()' - Process a print job.
5183 static void * /* O - Thread exit status */
5184 process_job(ippeve_job_t
*job
) /* I - Job */
5186 job
->state
= IPP_JSTATE_PROCESSING
;
5187 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5188 job
->processing
= time(NULL
);
5190 while (job
->printer
->state_reasons
& IPPEVE_PREASON_MEDIA_EMPTY
)
5192 job
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
5197 job
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_NEEDED
;
5199 if (job
->printer
->command
)
5202 * Execute a command with the job spool file and wait for it to complete...
5205 int pid
, /* Process ID */
5206 status
; /* Exit status */
5207 time_t start
, /* Start time */
5209 char *myargv
[3], /* Command-line arguments */
5210 *myenvp
[400]; /* Environment variables */
5211 int myenvc
; /* Number of environment variables */
5212 ipp_attribute_t
*attr
; /* Job attribute */
5213 char val
[1280], /* IPP_NAME=value */
5214 *valptr
; /* Pointer into string */
5216 int mystdout
= -1; /* File for stdout */
5217 int mypipe
[2]; /* Pipe for stderr */
5218 char line
[2048], /* Line from stderr */
5219 *ptr
, /* Pointer into line */
5220 *endptr
; /* End of line */
5221 ssize_t bytes
; /* Bytes read */
5222 #endif /* !_WIN32 */
5224 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
, job
->filename
);
5228 * Setup the command-line arguments...
5231 myargv
[0] = job
->printer
->command
;
5232 myargv
[1] = job
->filename
;
5236 * Copy the current environment, then add environment variables for every
5237 * Job attribute and Printer -default attributes...
5240 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5241 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5243 if (myenvc
> (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 32))
5245 fprintf(stderr
, "Too many environment variables to process job #%d.\n", job
->id
);
5246 job
->state
= IPP_JSTATE_ABORTED
;
5250 if (asprintf(myenvp
+ myenvc
, "CONTENT_TYPE=%s", job
->format
) > 0)
5253 if (job
->printer
->device_uri
&& asprintf(myenvp
+ myenvc
, "DEVICE_URI=%s", job
->printer
->device_uri
) > 0)
5257 if (job
->printer
->ppdfile
&& asprintf(myenvp
+ myenvc
, "PPD=%s", job
->printer
->ppdfile
) > 0)
5259 #endif /* !CUPS_LITE */
5261 if ((attr
= ippFindAttribute(job
->attrs
, "document-name", IPP_TAG_NAME
)) != NULL
&& asprintf(myenvp
+ myenvc
, "DOCUMENT_NAME=%s", ippGetString(attr
, 0, NULL
)) > 0)
5264 for (attr
= ippFirstAttribute(job
->printer
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->printer
->attrs
))
5267 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
5268 * then add the value(s) from the attribute.
5271 const char *name
= ippGetName(attr
),
5272 /* Attribute name */
5273 *suffix
= strstr(name
, "-default");
5274 /* Suffix on attribute name */
5276 if (!suffix
|| suffix
[8])
5284 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5289 *valptr
++ = (char)toupper(*name
& 255);
5294 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5296 myenvp
[myenvc
++] = strdup(val
);
5299 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
5302 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5303 * value(s) from the attribute.
5306 const char *name
= ippGetName(attr
);
5307 /* Attribute name */
5317 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5322 *valptr
++ = (char)toupper(*name
& 255);
5327 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5329 myenvp
[myenvc
++] = strdup(val
);
5334 fprintf(stderr
, "Too many environment variables to process job #%d.\n", job
->id
);
5335 job
->state
= IPP_JSTATE_ABORTED
;
5339 myenvp
[myenvc
] = NULL
;
5342 * Now run the program...
5346 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
5349 if (job
->printer
->device_uri
)
5351 char scheme
[32], /* URI scheme */
5352 userpass
[256], /* username:password (unused) */
5353 host
[256], /* Hostname or IP address */
5354 resource
[256]; /* Resource path */
5355 int port
; /* Port number */
5358 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
)
5360 fprintf(stderr
, "Bad device URI \"%s\".\n", job
->printer
->device_uri
);
5362 else if (!strcmp(scheme
, "file"))
5364 struct stat fileinfo
; /* See if this is a file or directory... */
5366 if (stat(resource
, &fileinfo
))
5368 if (errno
== ENOENT
)
5370 if ((mystdout
= open(resource
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) >= 0)
5371 fprintf(stderr
, "Saving print command output to \"%s\".\n", resource
);
5373 fprintf(stderr
, "Unable to create \"%s\": %s\n", resource
, strerror(errno
));
5376 fprintf(stderr
, "Unable to access \"%s\": %s\n", resource
, strerror(errno
));
5378 else if (S_ISDIR(fileinfo
.st_mode
))
5380 if ((mystdout
= create_job_file(job
, line
, sizeof(line
), resource
, "prn")) >= 0)
5381 fprintf(stderr
, "Saving print command output to \"%s\".\n", line
);
5383 fprintf(stderr
, "Unable to create \"%s\": %s\n", line
, strerror(errno
));
5385 else if (!S_ISREG(fileinfo
.st_mode
))
5387 if ((mystdout
= open(resource
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) >= 0)
5388 fprintf(stderr
, "Saving print command output to \"%s\".\n", resource
);
5390 fprintf(stderr
, "Unable to create \"%s\": %s\n", resource
, strerror(errno
));
5392 else if ((mystdout
= open(resource
, O_WRONLY
)) >= 0)
5393 fprintf(stderr
, "Saving print command output to \"%s\".\n", resource
);
5395 fprintf(stderr
, "Unable to open \"%s\": %s\n", resource
, strerror(errno
));
5397 else if (!strcmp(scheme
, "socket"))
5399 http_addrlist_t
*addrlist
; /* List of addresses */
5400 char service
[32]; /* Service number */
5402 snprintf(service
, sizeof(service
), "%d", port
);
5404 if ((addrlist
= httpAddrGetList(host
, AF_UNSPEC
, service
)) == NULL
)
5405 fprintf(stderr
, "Unable to find \"%s\": %s\n", host
, cupsLastErrorString());
5406 else if (!httpAddrConnect2(addrlist
, &mystdout
, 30000, &(job
->cancel
)))
5407 fprintf(stderr
, "Unable to connect to \"%s\": %s\n", host
, cupsLastErrorString());
5409 httpAddrFreeList(addrlist
);
5413 fprintf(stderr
, "Unsupported device URI scheme \"%s\".\n", scheme
);
5416 else if ((mystdout
= create_job_file(job
, line
, sizeof(line
), job
->printer
->directory
, "prn")) >= 0)
5418 fprintf(stderr
, "Saving print command output to \"%s\".\n", line
);
5422 mystdout
= open("/dev/null", O_WRONLY
);
5426 perror("Unable to create pipe for stderr");
5427 mypipe
[0] = mypipe
[1] = -1;
5430 if ((pid
= fork()) == 0)
5433 * Child comes here...
5445 execve(job
->printer
->command
, myargv
, myenvp
);
5451 * Unable to fork process...
5454 perror("Unable to start job processing command");
5462 * Free memory used for environment...
5466 free(myenvp
[-- myenvc
]);
5471 * Free memory used for environment...
5475 free(myenvp
[-- myenvc
]);
5478 * Close the output file in the parent process...
5484 * If the pipe exists, read from it until EOF...
5492 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
5497 while ((ptr
= strchr(line
, '\n')) != NULL
)
5501 if (!strncmp(line
, "STATE:", 6))
5504 * Process printer-state-reasons keywords.
5507 process_state_message(job
, line
);
5509 else if (!strncmp(line
, "ATTR:", 5))
5512 * Process printer attribute update.
5515 process_attr_message(job
, line
);
5517 else if (Verbosity
> 1)
5518 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
5522 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
5532 * Wait for child to complete...
5535 # ifdef HAVE_WAITPID
5536 while (waitpid(pid
, &status
, 0) < 0);
5538 while (wait(&status
) < 0);
5539 # endif /* HAVE_WAITPID */
5546 if (WIFEXITED(status
))
5547 #endif /* !_WIN32 */
5548 fprintf(stderr
, "Command \"%s\" exited with status %d.\n", job
->printer
->command
, WEXITSTATUS(status
));
5551 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n", job
->printer
->command
, WTERMSIG(status
));
5552 #endif /* !_WIN32 */
5553 job
->state
= IPP_JSTATE_ABORTED
;
5555 else if (status
< 0)
5556 job
->state
= IPP_JSTATE_ABORTED
;
5558 fprintf(stderr
, "Command \"%s\" completed successfully.\n", job
->printer
->command
);
5561 * Make sure processing takes at least 5 seconds...
5565 if ((end
- start
) < 5)
5571 * Sleep for a random amount of time to simulate job processing.
5574 sleep((unsigned)(5 + (rand() % 11)));
5578 job
->state
= IPP_JSTATE_CANCELED
;
5579 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5580 job
->state
= IPP_JSTATE_COMPLETED
;
5584 job
->completed
= time(NULL
);
5585 job
->printer
->state
= IPP_PSTATE_IDLE
;
5586 job
->printer
->active_job
= NULL
;
5593 * 'process_state_message()' - Process a STATE: message from a command.
5597 process_state_message(
5598 ippeve_job_t
*job
, /* I - Job */
5599 char *message
) /* I - Message */
5601 int i
; /* Looping var */
5602 ippeve_preason_t state_reasons
, /* printer-state-reasons values */
5603 bit
; /* Current reason bit */
5604 char *ptr
, /* Pointer into message */
5605 *next
; /* Next keyword in message */
5606 int remove
; /* Non-zero if we are removing keywords */
5610 * Skip leading "STATE:" and any whitespace...
5613 for (message
+= 6; *message
; message
++)
5614 if (*message
!= ' ' && *message
!= '\t')
5618 * Support the following forms of message:
5620 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
5622 * "-keyword[,keyword,...]" to remove keywords.
5624 * "+keyword[,keyword,...]" to add keywords.
5626 * Keywords may or may not have a suffix (-report, -warning, -error) per
5630 if (*message
== '-')
5633 state_reasons
= job
->printer
->state_reasons
;
5636 else if (*message
== '+')
5639 state_reasons
= job
->printer
->state_reasons
;
5645 state_reasons
= IPPEVE_PREASON_NONE
;
5650 if ((next
= strchr(message
, ',')) != NULL
)
5653 if ((ptr
= strstr(message
, "-error")) != NULL
)
5655 else if ((ptr
= strstr(message
, "-report")) != NULL
)
5657 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
5660 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
5662 if (!strcmp(message
, ippeve_preason_strings
[i
]))
5665 state_reasons
&= ~bit
;
5667 state_reasons
|= bit
;
5677 job
->printer
->state_reasons
= state_reasons
;
5682 * 'register_printer()' - Register a printer object via Bonjour.
5685 static int /* O - 1 on success, 0 on error */
5687 ippeve_printer_t
*printer
, /* I - Printer */
5688 const char *subtypes
) /* I - Service subtype(s) */
5690 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
5691 ippeve_txt_t ipp_txt
; /* Bonjour IPP TXT record */
5692 int i
, /* Looping var */
5693 count
; /* Number of values */
5694 ipp_attribute_t
*color_supported
,
5695 *document_format_supported
,
5697 *printer_make_and_model
,
5701 *urf_supported
; /* Printer attributes */
5702 const char *value
; /* Value string */
5703 char formats
[252], /* List of supported formats */
5704 urf
[252], /* List of supported URF values */
5705 *ptr
; /* Pointer into string */
5707 color_supported
= ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_BOOLEAN
);
5708 document_format_supported
= ippFindAttribute(printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
);
5709 printer_location
= ippFindAttribute(printer
->attrs
, "printer-location", IPP_TAG_TEXT
);
5710 printer_make_and_model
= ippFindAttribute(printer
->attrs
, "printer-make-and-model", IPP_TAG_TEXT
);
5711 printer_more_info
= ippFindAttribute(printer
->attrs
, "printer-more-info", IPP_TAG_URI
);
5712 printer_uuid
= ippFindAttribute(printer
->attrs
, "printer-uuid", IPP_TAG_URI
);
5713 sides_supported
= ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
);
5714 urf_supported
= ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_KEYWORD
);
5716 for (i
= 0, count
= ippGetCount(document_format_supported
), ptr
= formats
; i
< count
; i
++)
5718 value
= ippGetString(document_format_supported
, i
, NULL
);
5720 if (!strcasecmp(value
, "application/octet-stream"))
5723 if (ptr
> formats
&& ptr
< (formats
+ sizeof(formats
) - 1))
5726 strlcpy(ptr
, value
, sizeof(formats
) - (size_t)(ptr
- formats
));
5729 if (ptr
>= (formats
+ sizeof(formats
) - 1))
5734 for (i
= 0, count
= ippGetCount(urf_supported
), ptr
= urf
; i
< count
; i
++)
5736 value
= ippGetString(urf_supported
, i
, NULL
);
5738 if (ptr
> urf
&& ptr
< (urf
+ sizeof(urf
) - 1))
5741 strlcpy(ptr
, value
, sizeof(urf
) - (size_t)(ptr
- urf
));
5744 if (ptr
>= (urf
+ sizeof(urf
) - 1))
5748 #endif /* HAVE_DNSSD || HAVE_AVAHI */
5750 DNSServiceErrorType error
; /* Error from Bonjour */
5751 char regtype
[256]; /* Bonjour service type */
5755 * Build the TXT record for IPP...
5758 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
5759 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
5760 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
5761 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(value
), value
);
5762 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
5763 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(value
), value
);
5764 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
5765 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(value
), value
);
5766 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
), formats
);
5767 TXTRecordSetValue(&ipp_txt
, "Color", 1, ippGetBoolean(color_supported
, 0) ? "T" : "F");
5768 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, ippGetCount(sides_supported
) > 1 ? "T" : "F");
5769 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
5770 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(value
) - 9, value
+ 9);
5772 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
5773 # endif /* HAVE_SSL */
5775 TXTRecordSetValue(&ipp_txt
, "URF", strlen(urf
), urf
);
5776 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
5777 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
5780 * Register the _printer._tcp (LPD) service type with a port number of 0 to
5781 * defend our service name but not actually support LPD...
5784 printer
->printer_ref
= DNSSDMaster
;
5786 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
)
5788 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_printer._tcp", error
);
5793 * Then register the _ipp._tcp (IPP) service type with the real port number to
5794 * advertise our IPP printer...
5797 printer
->ipp_ref
= DNSSDMaster
;
5799 if (subtypes
&& *subtypes
)
5800 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtypes
);
5802 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
5804 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
)
5806 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
5812 * Then register the _ipps._tcp (IPP) service type with the real port number to
5813 * advertise our IPPS printer...
5816 printer
->ipps_ref
= DNSSDMaster
;
5818 if (subtypes
&& *subtypes
)
5819 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtypes
);
5821 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
5823 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
)
5825 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
5828 # endif /* HAVE_SSL */
5831 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
5832 * real port number to advertise our IPP printer...
5835 printer
->http_ref
= DNSSDMaster
;
5837 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
)
5839 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_http._tcp,_printer", error
);
5843 TXTRecordDeallocate(&ipp_txt
);
5845 #elif defined(HAVE_AVAHI)
5846 char temp
[256]; /* Subtype service string */
5849 * Create the TXT record...
5853 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
5854 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
5855 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s", value
);
5856 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
5857 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", value
);
5858 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
5859 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", value
);
5860 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
5861 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", ippGetBoolean(color_supported
, 0) ? "T" : "F");
5862 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", ippGetCount(sides_supported
) > 1 ? "T" : "F");
5863 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
5864 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", value
+ 9);
5866 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
5867 # endif /* HAVE_SSL */
5869 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "URF=%s", urf
);
5870 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "txtvers=1");
5871 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "qtotal=1");
5874 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
5877 avahi_threaded_poll_lock(DNSSDMaster
);
5879 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
5881 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
);
5884 * Then register the ippeve._tcp (IPP)...
5887 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
);
5888 if (subtypes
&& *subtypes
)
5890 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
5891 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
5896 * ippeves._tcp (IPPS) for secure printing...
5899 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
);
5900 if (subtypes
&& *subtypes
)
5902 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
5903 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
5905 #endif /* HAVE_SSL */
5908 * Finally _http.tcp (HTTP) for the web interface...
5911 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
);
5912 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");
5918 avahi_entry_group_commit(printer
->ipp_ref
);
5919 avahi_threaded_poll_unlock(DNSSDMaster
);
5921 avahi_string_list_free(ipp_txt
);
5922 #endif /* HAVE_DNSSD */
5929 * 'respond_http()' - Send a HTTP response.
5932 int /* O - 1 on success, 0 on failure */
5934 ippeve_client_t
*client
, /* I - Client */
5935 http_status_t code
, /* I - HTTP status of response */
5936 const char *content_encoding
, /* I - Content-Encoding of response */
5937 const char *type
, /* I - MIME media type of response */
5938 size_t length
) /* I - Length of response */
5940 char message
[1024]; /* Text message */
5943 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
5945 if (code
== HTTP_STATUS_CONTINUE
)
5948 * 100-continue doesn't send any headers...
5951 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
5955 * Format an error message...
5958 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
5960 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
5962 type
= "text/plain";
5963 length
= strlen(message
);
5969 * Send the HTTP response header...
5972 httpClearFields(client
->http
);
5974 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
5975 client
->operation
== HTTP_STATE_OPTIONS
)
5976 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
5980 if (!strcmp(type
, "text/html"))
5981 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
5982 "text/html; charset=utf-8");
5984 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
5986 if (content_encoding
)
5987 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
5990 httpSetLength(client
->http
, length
);
5992 if (httpWriteResponse(client
->http
, code
) < 0)
5996 * Send the response data...
6002 * Send a plain text message.
6005 if (httpPrintf(client
->http
, "%s", message
) < 0)
6008 if (httpWrite2(client
->http
, "", 0) < 0)
6011 else if (client
->response
)
6014 * Send an IPP response...
6017 debug_attributes("Response", client
->response
, 2);
6019 ippSetState(client
->response
, IPP_STATE_IDLE
);
6021 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6030 * 'respond_ipp()' - Send an IPP response.
6034 respond_ipp(ippeve_client_t
*client
, /* I - Client */
6035 ipp_status_t status
, /* I - status-code */
6036 const char *message
, /* I - printf-style status-message */
6037 ...) /* I - Additional args as needed */
6039 const char *formatted
= NULL
; /* Formatted message */
6042 ippSetStatusCode(client
->response
, status
);
6046 va_list ap
; /* Pointer to additional args */
6047 ipp_attribute_t
*attr
; /* New status-message attribute */
6049 va_start(ap
, message
);
6050 if ((attr
= ippFindAttribute(client
->response
, "status-message", IPP_TAG_TEXT
)) != NULL
)
6051 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6053 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
, "status-message", NULL
, message
, ap
);
6056 formatted
= ippGetString(attr
, 0, NULL
);
6060 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
, ippOpString(client
->operation_id
), ippErrorString(status
), formatted
);
6062 fprintf(stderr
, "%s %s %s\n", client
->hostname
, ippOpString(client
->operation_id
), ippErrorString(status
));
6067 * 'respond_unsupported()' - Respond with an unsupported attribute.
6071 respond_unsupported(
6072 ippeve_client_t
*client
, /* I - Client */
6073 ipp_attribute_t
*attr
) /* I - Atribute */
6075 ipp_attribute_t
*temp
; /* Copy of attribute */
6078 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, "Unsupported %s %s%s value.", ippGetName(attr
), ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)));
6080 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6081 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6086 * 'run_printer()' - Run the printer service.
6090 run_printer(ippeve_printer_t
*printer
) /* I - Printer */
6092 int num_fds
; /* Number of file descriptors */
6093 struct pollfd polldata
[3]; /* poll() data */
6094 int timeout
; /* Timeout for poll() */
6095 ippeve_client_t
*client
; /* New client */
6099 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6102 polldata
[0].fd
= printer
->ipv4
;
6103 polldata
[0].events
= POLLIN
;
6105 polldata
[1].fd
= printer
->ipv6
;
6106 polldata
[1].events
= POLLIN
;
6111 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6112 polldata
[num_fds
++].events
= POLLIN
;
6113 #endif /* HAVE_DNSSD */
6116 * Loop until we are killed or have a hard error...
6121 if (cupsArrayCount(printer
->jobs
))
6126 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6128 perror("poll() failed");
6132 if (polldata
[0].revents
& POLLIN
)
6134 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6136 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6140 _cupsThreadDetach(t
);
6144 perror("Unable to create client thread");
6145 delete_client(client
);
6150 if (polldata
[1].revents
& POLLIN
)
6152 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6154 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6158 _cupsThreadDetach(t
);
6162 perror("Unable to create client thread");
6163 delete_client(client
);
6169 if (polldata
[2].revents
& POLLIN
)
6170 DNSServiceProcessResult(DNSSDMaster
);
6171 #endif /* HAVE_DNSSD */
6174 * Clean out old jobs...
6177 clean_jobs(printer
);
6183 * 'show_media()' - Show media load state.
6186 static int /* O - 1 on success, 0 on failure */
6187 show_media(ippeve_client_t
*client
) /* I - Client connection */
6189 ippeve_printer_t
*printer
= client
->printer
;
6191 int i
, j
, /* Looping vars */
6192 num_ready
, /* Number of ready media */
6193 num_sizes
, /* Number of media sizes */
6194 num_sources
, /* Number of media sources */
6195 num_types
; /* Number of media types */
6196 ipp_attribute_t
*media_col_ready
,/* media-col-ready attribute */
6197 *media_ready
, /* media-ready attribute */
6198 *media_sizes
, /* media-supported attribute */
6199 *media_sources
, /* media-source-supported attribute */
6200 *media_types
, /* media-type-supported attribute */
6201 *input_tray
; /* printer-input-tray attribute */
6202 ipp_t
*media_col
; /* media-col value */
6203 const char *media_size
, /* media value */
6204 *media_source
, /* media-source value */
6205 *media_type
, /* media-type value */
6206 *ready_size
, /* media-col-ready media-size[-name] value */
6207 *ready_source
, /* media-col-ready media-source value */
6208 *ready_tray
, /* printer-input-tray value */
6209 *ready_type
; /* media-col-ready media-type value */
6210 char tray_str
[1024], /* printer-input-tray string value */
6211 *tray_ptr
; /* Pointer into value */
6212 int tray_len
; /* Length of printer-input-tray value */
6213 int ready_sheets
; /* printer-input-tray sheets value */
6214 int num_options
; /* Number of form options */
6215 cups_option_t
*options
; /* Form options */
6216 static const int sheets
[] = /* Number of sheets */
6227 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
6230 html_header(client
, printer
->name
, 0);
6232 if ((media_col_ready
= ippFindAttribute(printer
->attrs
, "media-col-ready", IPP_TAG_BEGIN_COLLECTION
)) == NULL
)
6234 html_printf(client
, "<p>Error: No media-col-ready defined for printer.</p>\n");
6235 html_footer(client
);
6239 media_ready
= ippFindAttribute(printer
->attrs
, "media-ready", IPP_TAG_ZERO
);
6241 if ((media_sizes
= ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
)) == NULL
)
6243 html_printf(client
, "<p>Error: No media-supported defined for printer.</p>\n");
6244 html_footer(client
);
6248 if ((media_sources
= ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
)) == NULL
)
6250 html_printf(client
, "<p>Error: No media-source-supported defined for printer.</p>\n");
6251 html_footer(client
);
6255 if ((media_types
= ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
)) == NULL
)
6257 html_printf(client
, "<p>Error: No media-type-supported defined for printer.</p>\n");
6258 html_footer(client
);
6262 if ((input_tray
= ippFindAttribute(printer
->attrs
, "printer-input-tray", IPP_TAG_STRING
)) == NULL
)
6264 html_printf(client
, "<p>Error: No printer-input-tray defined for printer.</p>\n");
6265 html_footer(client
);
6269 num_ready
= ippGetCount(media_col_ready
);
6270 num_sizes
= ippGetCount(media_sizes
);
6271 num_sources
= ippGetCount(media_sources
);
6272 num_types
= ippGetCount(media_types
);
6274 if (num_sources
!= ippGetCount(input_tray
))
6276 html_printf(client
, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
6277 html_footer(client
);
6282 * Process form data if present...
6285 if ((num_options
= parse_options(client
, &options
)) > 0)
6288 * WARNING: A real printer/server implementation MUST NOT implement
6289 * media updates via a GET request - GET requests are supposed to be
6290 * idempotent (without side-effects) and we obviously are not
6291 * authenticating access here. This form is provided solely to
6292 * enable testing and development!
6295 char name
[255]; /* Form name */
6296 const char *val
; /* Form value */
6297 pwg_media_t
*media
; /* Media info */
6299 _cupsRWLockWrite(&printer
->rwlock
);
6301 ippDeleteAttribute(printer
->attrs
, media_col_ready
);
6302 media_col_ready
= NULL
;
6306 ippDeleteAttribute(printer
->attrs
, media_ready
);
6310 printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MEDIA_LOW
| IPPEVE_PREASON_MEDIA_EMPTY
| IPPEVE_PREASON_MEDIA_NEEDED
);
6312 for (i
= 0; i
< num_sources
; i
++)
6314 media_source
= ippGetString(media_sources
, i
, NULL
);
6316 if (!strcmp(media_source
, "auto") || !strcmp(media_source
, "manual"))
6319 snprintf(name
, sizeof(name
), "size%d", i
);
6320 if ((media_size
= cupsGetOption(name
, num_options
, options
)) != NULL
&& (media
= pwgMediaForPWG(media_size
)) != NULL
)
6322 snprintf(name
, sizeof(name
), "type%d", i
);
6323 if ((media_type
= cupsGetOption(name
, num_options
, options
)) != NULL
&& !*media_type
)
6327 ippSetString(printer
->attrs
, &media_ready
, ippGetCount(media_ready
), media_size
);
6329 media_ready
= ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", NULL
, media_size
);
6331 media_col
= create_media_col(media_size
, media_source
, media_type
, media
->width
, media
->length
, -1, -1, -1, -1);
6333 if (media_col_ready
)
6334 ippSetCollection(printer
->attrs
, &media_col_ready
, ippGetCount(media_col_ready
), media_col
);
6336 media_col_ready
= ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-ready", media_col
);
6337 ippDelete(media_col
);
6342 snprintf(name
, sizeof(name
), "level%d", i
);
6343 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
6344 ready_sheets
= atoi(val
);
6348 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
, "main") ? 250 : 25, ready_sheets
, media_source
);
6350 ippSetOctetString(printer
->attrs
, &input_tray
, i
, tray_str
, (int)strlen(tray_str
));
6352 if (ready_sheets
== 0)
6354 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_EMPTY
;
6355 if (printer
->active_job
)
6356 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
6358 else if (ready_sheets
< 25 && ready_sheets
> 0)
6359 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_LOW
;
6362 if (!media_col_ready
)
6363 media_col_ready
= ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
6366 media_ready
= ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
6368 _cupsRWUnlock(&printer
->rwlock
);
6370 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
6373 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
6375 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
6376 for (i
= 0; i
< num_sources
; i
++)
6378 media_source
= ippGetString(media_sources
, i
, NULL
);
6380 if (!strcmp(media_source
, "auto") || !strcmp(media_source
, "manual"))
6383 for (j
= 0, ready_size
= NULL
, ready_type
= NULL
; j
< num_ready
; j
++)
6385 media_col
= ippGetCollection(media_col_ready
, j
);
6386 ready_size
= ippGetString(ippFindAttribute(media_col
, "media-size-name", IPP_TAG_ZERO
), 0, NULL
);
6387 ready_source
= ippGetString(ippFindAttribute(media_col
, "media-source", IPP_TAG_ZERO
), 0, NULL
);
6388 ready_type
= ippGetString(ippFindAttribute(media_col
, "media-type", IPP_TAG_ZERO
), 0, NULL
);
6390 if (ready_source
&& !strcmp(ready_source
, media_source
))
6393 ready_source
= NULL
;
6402 html_printf(client
, "<tr><th>%s:</th><td><select name=\"size%d\"><option value=\"\">None</option>", media_source
, i
);
6403 for (j
= 0; j
< num_sizes
; j
++)
6405 media_size
= ippGetString(media_sizes
, j
, NULL
);
6407 html_printf(client
, "<option%s>%s</option>", (ready_size
&& !strcmp(ready_size
, media_size
)) ? " selected" : "", media_size
);
6409 html_printf(client
, "</select>\n");
6415 html_printf(client
, "<select name=\"type%d\"><option value=\"\">None</option>", i
);
6416 for (j
= 0; j
< num_types
; j
++)
6418 media_type
= ippGetString(media_types
, j
, NULL
);
6420 html_printf(client
, "<option%s>%s</option>", (ready_type
&& !strcmp(ready_type
, media_type
)) ? " selected" : "", media_type
);
6422 html_printf(client
, "</select>\n");
6425 * Level/sheets loaded...
6428 if ((ready_tray
= ippGetOctetString(input_tray
, i
, &tray_len
)) != NULL
)
6430 if (tray_len
> (sizeof(tray_str
) - 1))
6431 tray_len
= sizeof(tray_str
) - 1;
6432 memcpy(tray_str
, ready_tray
, (size_t)tray_len
);
6433 tray_str
[tray_len
] = '\0';
6435 if ((tray_ptr
= strstr(tray_str
, "level=")) != NULL
)
6436 ready_sheets
= atoi(tray_ptr
+ 6);
6443 html_printf(client
, "<select name=\"level%d\">", i
);
6444 for (j
= 0; j
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); j
++)
6446 if (strcmp(media_source
, "main") && sheets
[j
] > 25)
6450 html_printf(client
, "<option value=\"%d\"%s>Unknown</option>", sheets
[j
], sheets
[j
] == ready_sheets
? " selected" : "");
6452 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[j
], sheets
[j
] == ready_sheets
? " selected" : "", sheets
[j
]);
6454 html_printf(client
, "</select></td></tr>\n");
6457 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
6458 html_footer(client
);
6465 * 'show_status()' - Show printer/system state.
6468 static int /* O - 1 on success, 0 on failure */
6469 show_status(ippeve_client_t
*client
) /* I - Client connection */
6471 ippeve_printer_t
*printer
= client
->printer
;
6473 ippeve_job_t
*job
; /* Current job */
6474 int i
; /* Looping var */
6475 ippeve_preason_t reason
; /* Current reason */
6476 static const char * const reasons
[] = /* Reason strings */
6480 "Input Tray Missing",
6481 "Marker Supply Empty",
6482 "Marker Supply Low",
6483 "Marker Waste Almost Full",
6484 "Marker Waste Full",
6495 static const char * const state_colors
[] =
6496 { /* State colors */
6498 "#EE0", /* Processing */
6499 "#C00" /* Stopped */
6503 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
6506 html_header(client
, printer
->name
, printer
->state
== IPP_PSTATE_PROCESSING
? 5 : 15);
6507 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
);
6508 html_printf(client
, "<p>%s, %d job(s).", printer
->state
== IPP_PSTATE_IDLE
? "Idle" : printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(printer
->jobs
));
6509 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
6510 if (printer
->state_reasons
& reason
)
6511 html_printf(client
, "\n<br> %s", reasons
[i
]);
6512 html_printf(client
, "</p>\n");
6514 if (cupsArrayCount(printer
->jobs
) > 0)
6516 _cupsRWLockRead(&(printer
->rwlock
));
6518 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");
6519 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
); job
; job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
6521 char when
[256], /* When job queued/started/finished */
6522 hhmmss
[64]; /* Time HH:MM:SS */
6526 case IPP_JSTATE_PENDING
:
6527 case IPP_JSTATE_HELD
:
6528 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
6530 case IPP_JSTATE_PROCESSING
:
6531 case IPP_JSTATE_STOPPED
:
6532 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
6534 case IPP_JSTATE_ABORTED
:
6535 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
6537 case IPP_JSTATE_CANCELED
:
6538 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
6540 case IPP_JSTATE_COMPLETED
:
6541 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
6545 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
);
6547 html_printf(client
, "</tbody></table>\n");
6549 _cupsRWUnlock(&(printer
->rwlock
));
6552 html_footer(client
);
6559 * 'show_supplies()' - Show printer supplies.
6562 static int /* O - 1 on success, 0 on failure */
6564 ippeve_client_t
*client
) /* I - Client connection */
6566 ippeve_printer_t
*printer
= client
->printer
;
6568 int i
, /* Looping var */
6569 num_supply
; /* Number of supplies */
6570 ipp_attribute_t
*supply
, /* printer-supply attribute */
6571 *supply_desc
; /* printer-supply-description attribute */
6572 int num_options
; /* Number of form options */
6573 cups_option_t
*options
; /* Form options */
6574 int supply_len
, /* Length of supply value */
6575 level
; /* Supply level */
6576 const char *supply_value
; /* Supply value */
6577 char supply_text
[1024], /* Supply string */
6578 *supply_ptr
; /* Pointer into supply string */
6579 static const char * const printer_supply
[] =
6580 { /* printer-supply values */
6581 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
6582 "maxcapacity=100;level=%d;colorantname=unknown;",
6583 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
6584 "maxcapacity=100;level=%d;colorantname=black;",
6585 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
6586 "maxcapacity=100;level=%d;colorantname=cyan;",
6587 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
6588 "maxcapacity=100;level=%d;colorantname=magenta;",
6589 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
6590 "maxcapacity=100;level=%d;colorantname=yellow;"
6592 static const char * const colors
[] = /* Colors for the supply-level bars */
6594 "#777 linear-gradient(#333,#777)",
6595 "#000 linear-gradient(#666,#000)",
6596 "#0FF linear-gradient(#6FF,#0FF)",
6597 "#F0F linear-gradient(#F6F,#F0F)",
6598 "#CC0 linear-gradient(#EE6,#EE0)"
6602 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
6605 html_header(client
, printer
->name
, 0);
6607 if ((supply
= ippFindAttribute(printer
->attrs
, "printer-supply", IPP_TAG_STRING
)) == NULL
)
6609 html_printf(client
, "<p>Error: No printer-supply defined for printer.</p>\n");
6610 html_footer(client
);
6614 num_supply
= ippGetCount(supply
);
6616 if ((supply_desc
= ippFindAttribute(printer
->attrs
, "printer-supply-description", IPP_TAG_TEXT
)) == NULL
)
6618 html_printf(client
, "<p>Error: No printer-supply-description defined for printer.</p>\n");
6619 html_footer(client
);
6623 if (num_supply
!= ippGetCount(supply_desc
))
6625 html_printf(client
, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
6626 html_footer(client
);
6630 if ((num_options
= parse_options(client
, &options
)) > 0)
6633 * WARNING: A real printer/server implementation MUST NOT implement
6634 * supply updates via a GET request - GET requests are supposed to be
6635 * idempotent (without side-effects) and we obviously are not
6636 * authenticating access here. This form is provided solely to
6637 * enable testing and development!
6640 char name
[64]; /* Form field */
6641 const char *val
; /* Form value */
6643 _cupsRWLockWrite(&printer
->rwlock
);
6645 ippDeleteAttribute(printer
->attrs
, supply
);
6648 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
);
6650 for (i
= 0; i
< num_supply
; i
++)
6652 snprintf(name
, sizeof(name
), "supply%d", i
);
6653 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
6655 level
= atoi(val
); /* New level */
6657 snprintf(supply_text
, sizeof(supply_text
), printer_supply
[i
], level
);
6659 ippSetOctetString(printer
->attrs
, &supply
, ippGetCount(supply
), supply_text
, (int)strlen(supply_text
));
6661 supply
= ippAddOctetString(printer
->attrs
, IPP_TAG_PRINTER
, "printer-supply", supply_text
, (int)strlen(supply_text
));
6666 printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_FULL
;
6667 else if (level
> 90)
6668 printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
;
6673 printer
->state_reasons
|= IPPEVE_PREASON_TONER_EMPTY
;
6674 else if (level
< 10)
6675 printer
->state_reasons
|= IPPEVE_PREASON_TONER_LOW
;
6680 _cupsRWUnlock(&printer
->rwlock
);
6682 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
6685 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
6687 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
6688 for (i
= 0; i
< num_supply
; i
++)
6690 supply_value
= ippGetOctetString(supply
, i
, &supply_len
);
6691 if (supply_len
> (sizeof(supply_text
) - 1))
6692 supply_len
= sizeof(supply_text
) - 1;
6694 memcpy(supply_text
, supply_value
, (size_t)supply_len
);
6695 supply_text
[supply_len
] = '\0';
6697 if ((supply_ptr
= strstr(supply_text
, "level=")) != NULL
)
6698 level
= atoi(supply_ptr
+ 6);
6702 html_printf(client
, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"><span class=\"bar\" style=\"background: %s; width: %dpx;\"></span></td></tr>\n", ippGetString(supply_desc
, i
, NULL
), i
, level
, colors
[i
], level
* 2);
6704 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
6705 html_footer(client
);
6712 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6716 time_string(time_t tv
, /* I - Time value */
6717 char *buffer
, /* I - Buffer */
6718 size_t bufsize
) /* I - Size of buffer */
6720 struct tm
*curtime
= localtime(&tv
);
6723 strftime(buffer
, bufsize
, "%X", curtime
);
6729 * 'usage()' - Show program usage.
6733 usage(int status
) /* O - Exit status */
6735 _cupsLangPuts(stdout
, _("Usage: ippeveprinter [options] \"name\""));
6736 _cupsLangPuts(stdout
, _("Options:"));
6737 _cupsLangPuts(stderr
, _("--help Show program help"));
6738 _cupsLangPuts(stderr
, _("--version Show program version"));
6739 _cupsLangPuts(stdout
, _("-2 Set 2-sided printing support (default=1-sided)"));
6740 _cupsLangPuts(stdout
, _("-D device-uri Set the device URI for the printer"));
6742 _cupsLangPuts(stdout
, _("-K keypath Set location of server X.509 certificates and keys."));
6743 #endif /* HAVE_SSL */
6744 _cupsLangPuts(stdout
, _("-M manufacturer Set manufacturer name (default=Test)"));
6745 _cupsLangPuts(stdout
, _("-P filename.ppd Load printer attributes from PPD file"));
6746 _cupsLangPuts(stdout
, _("-V version Set default IPP version"));
6747 _cupsLangPuts(stdout
, _("-a filename.conf Load printer attributes from conf file"));
6748 _cupsLangPuts(stdout
, _("-c command Set print command"));
6749 _cupsLangPuts(stdout
, _("-d spool-directory Set spool directory"));
6750 _cupsLangPuts(stdout
, _("-f type/subtype[,...] Set supported file types"));
6751 _cupsLangPuts(stdout
, _("-i iconfile.png Set icon file"));
6752 _cupsLangPuts(stdout
, _("-k Keep job spool files"));
6753 _cupsLangPuts(stdout
, _("-l location Set location of printer"));
6754 _cupsLangPuts(stdout
, _("-m model Set model name (default=Printer)"));
6755 _cupsLangPuts(stdout
, _("-n hostname Set hostname for printer"));
6756 _cupsLangPuts(stdout
, _("-p port Set port number for printer"));
6757 _cupsLangPuts(stdout
, _("-r subtype Set DNS-SD service subtype"));
6758 _cupsLangPuts(stdout
, _("-s speed[,color-speed] Set speed in pages per minute"));
6759 _cupsLangPuts(stderr
, _("-v Be verbose"));
6766 * 'valid_doc_attributes()' - Determine whether the document attributes are
6769 * When one or more document attributes are invalid, this function adds a
6770 * suitable response and attributes to the unsupported group.
6773 static int /* O - 1 if valid, 0 if not */
6774 valid_doc_attributes(
6775 ippeve_client_t
*client
) /* I - Client */
6777 int valid
= 1; /* Valid attributes? */
6778 ipp_op_t op
= ippGetOperation(client
->request
);
6780 const char *op_name
= ippOpString(op
);
6781 /* IPP operation name */
6782 ipp_attribute_t
*attr
, /* Current attribute */
6783 *supported
; /* xxx-supported attribute */
6784 const char *compression
= NULL
,
6785 /* compression value */
6786 *format
= NULL
; /* document-format value */
6790 * Check operation attributes...
6793 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6796 * If compression is specified, only accept a supported value in a Print-Job
6797 * or Send-Document request...
6800 compression
= ippGetString(attr
, 0, NULL
);
6801 supported
= ippFindAttribute(client
->printer
->attrs
,
6802 "compression-supported", IPP_TAG_KEYWORD
);
6804 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6805 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6806 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6807 op
!= IPP_OP_VALIDATE_JOB
) ||
6808 !ippContainsString(supported
, compression
))
6810 respond_unsupported(client
, attr
);
6815 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6817 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6819 if (strcmp(compression
, "none"))
6822 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6823 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6829 * Is it a format we support?
6832 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6834 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6835 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6837 respond_unsupported(client
, attr
);
6842 format
= ippGetString(attr
, 0, NULL
);
6844 fprintf(stderr
, "%s %s document-format=\"%s\"\n", client
->hostname
, op_name
, format
);
6846 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6851 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6853 format
= "application/octet-stream"; /* Should never happen */
6855 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6858 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6861 * Auto-type the file using the first 8 bytes of the file...
6864 unsigned char header
[8]; /* First 8 bytes of file */
6866 memset(header
, 0, sizeof(header
));
6867 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6869 if (!memcmp(header
, "%PDF", 4))
6870 format
= "application/pdf";
6871 else if (!memcmp(header
, "%!", 2))
6872 format
= "application/postscript";
6873 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6874 format
= "image/jpeg";
6875 else if (!memcmp(header
, "\211PNG", 4))
6876 format
= "image/png";
6877 else if (!memcmp(header
, "RAS2", 4))
6878 format
= "image/pwg-raster";
6879 else if (!memcmp(header
, "UNIRAST", 8))
6880 format
= "image/urf";
6886 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n", client
->hostname
, op_name
, format
);
6888 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6892 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6894 respond_unsupported(client
, attr
);
6902 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6903 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6910 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6912 * When one or more job attributes are invalid, this function adds a suitable
6913 * response and attributes to the unsupported group.
6916 static int /* O - 1 if valid, 0 if not */
6917 valid_job_attributes(
6918 ippeve_client_t
*client
) /* I - Client */
6920 int i
, /* Looping var */
6921 count
, /* Number of values */
6922 valid
= 1; /* Valid attributes? */
6923 ipp_attribute_t
*attr
, /* Current attribute */
6924 *supported
; /* xxx-supported attribute */
6928 * Check operation attributes...
6931 valid
= valid_doc_attributes(client
);
6934 * Check the various job template attributes...
6937 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6939 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6940 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6942 respond_unsupported(client
, attr
);
6947 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6949 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6951 respond_unsupported(client
, attr
);
6956 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6958 if (ippGetCount(attr
) != 1 ||
6959 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6960 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6961 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6962 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6964 respond_unsupported(client
, attr
);
6969 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6971 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6973 respond_unsupported(client
, attr
);
6978 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6980 if (ippGetCount(attr
) != 1 ||
6981 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6982 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6984 respond_unsupported(client
, attr
);
6988 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6991 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6993 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6995 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6996 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6998 respond_unsupported(client
, attr
);
7003 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
7005 if (ippGetCount(attr
) != 1 ||
7006 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7007 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7008 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
7009 strcmp(ippGetString(attr
, 0, NULL
), "none"))
7011 respond_unsupported(client
, attr
);
7016 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
7018 if (ippGetCount(attr
) != 1 ||
7019 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
7020 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
7021 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
7023 respond_unsupported(client
, attr
);
7028 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7030 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
7032 respond_unsupported(client
, attr
);
7038 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
7040 ipp_t
*col
, /* media-col collection */
7041 *size
; /* media-size collection */
7042 ipp_attribute_t
*member
, /* Member attribute */
7043 *x_dim
, /* x-dimension */
7044 *y_dim
; /* y-dimension */
7045 int x_value
, /* y-dimension value */
7046 y_value
; /* x-dimension value */
7048 if (ippGetCount(attr
) != 1 ||
7049 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
7051 respond_unsupported(client
, attr
);
7055 col
= ippGetCollection(attr
, 0);
7057 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
7059 if (ippGetCount(member
) != 1 ||
7060 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
7061 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
7062 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
7064 respond_unsupported(client
, attr
);
7069 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
7071 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
7073 respond_unsupported(client
, attr
);
7078 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
7080 if (ippGetCount(member
) != 1)
7082 respond_unsupported(client
, attr
);
7087 size
= ippGetCollection(member
, 0);
7089 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
7090 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
7092 respond_unsupported(client
, attr
);
7097 x_value
= ippGetInteger(x_dim
, 0);
7098 y_value
= ippGetInteger(y_dim
, 0);
7099 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
7100 count
= ippGetCount(supported
);
7102 for (i
= 0; i
< count
; i
++)
7104 size
= ippGetCollection(supported
, i
);
7105 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
7106 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
7108 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
7114 respond_unsupported(client
, attr
);
7122 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
7124 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7125 (strcmp(ippGetString(attr
, 0, NULL
),
7126 "separate-documents-uncollated-copies") &&
7127 strcmp(ippGetString(attr
, 0, NULL
),
7128 "separate-documents-collated-copies")))
7130 respond_unsupported(client
, attr
);
7135 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
7137 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7138 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
7139 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
7141 respond_unsupported(client
, attr
);
7146 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7148 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7150 respond_unsupported(client
, attr
);
7155 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7157 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7158 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7159 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7161 respond_unsupported(client
, attr
);
7166 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7168 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7170 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7173 respond_unsupported(client
, attr
);
7178 int xdpi
, /* Horizontal resolution for job template attribute */
7179 ydpi
, /* Vertical resolution for job template attribute */
7180 sydpi
; /* Vertical resolution for supported value */
7181 ipp_res_t units
, /* Units for job template attribute */
7182 sunits
; /* Units for supported value */
7184 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7185 count
= ippGetCount(supported
);
7187 for (i
= 0; i
< count
; i
++)
7189 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7195 respond_unsupported(client
, attr
);
7201 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7203 const char *sides
= ippGetString(attr
, 0, NULL
);
7204 /* "sides" value... */
7206 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7208 respond_unsupported(client
, attr
);
7211 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7213 if (!ippContainsString(supported
, sides
))
7215 respond_unsupported(client
, attr
);
7219 else if (strcmp(sides
, "one-sided"))
7221 respond_unsupported(client
, attr
);