2 * IPP Everywhere printer application for CUPS.
4 * Copyright © 2010-2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * Note: This program began life as the "ippserver" sample code that first
10 * appeared in CUPS 1.4. The name has been changed in order to distinguish it
11 * from the PWG's much more ambitious "ippserver" program, which supports
12 * different kinds of IPP services and multiple services per instance - the
13 * "ippeveprinter" program exposes a single print service conforming to the
14 * current IPP Everywhere specification, thus the new name.
18 * Include necessary headers...
21 #include <cups/cups-private.h>
22 #include <cups/ppd-private.h>
30 # define WEXITSTATUS(s) (s)
31 # include <winsock2.h>
35 extern char **environ
;
37 # include <sys/fcntl.h>
38 # include <sys/wait.h>
44 #elif defined(HAVE_AVAHI)
45 # include <avahi-client/client.h>
46 # include <avahi-client/publish.h>
47 # include <avahi-common/error.h>
48 # include <avahi-common/thread-watch.h>
49 #endif /* HAVE_DNSSD */
50 #ifdef HAVE_SYS_MOUNT_H
51 # include <sys/mount.h>
52 #endif /* HAVE_SYS_MOUNT_H */
53 #ifdef HAVE_SYS_STATFS_H
54 # include <sys/statfs.h>
55 #endif /* HAVE_SYS_STATFS_H */
56 #ifdef HAVE_SYS_STATVFS_H
57 # include <sys/statvfs.h>
58 #endif /* HAVE_SYS_STATVFS_H */
61 #endif /* HAVE_SYS_VFS_H */
68 enum ippeve_preason_e
/* printer-state-reasons bit values */
70 IPPEVE_PREASON_NONE
= 0x0000, /* none */
71 IPPEVE_PREASON_OTHER
= 0x0001, /* other */
72 IPPEVE_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
73 IPPEVE_PREASON_INPUT_TRAY_MISSING
= 0x0004,
74 /* input-tray-missing */
75 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
76 /* marker-supply-empty */
77 IPPEVE_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
78 /* marker-supply-low */
79 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
80 /* marker-waste-almost-full */
81 IPPEVE_PREASON_MARKER_WASTE_FULL
= 0x0040,
82 /* marker-waste-full */
83 IPPEVE_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
84 IPPEVE_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
85 IPPEVE_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
86 IPPEVE_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
87 IPPEVE_PREASON_MOVING_TO_PAUSED
= 0x0800,
88 /* moving-to-paused */
89 IPPEVE_PREASON_PAUSED
= 0x1000, /* paused */
90 IPPEVE_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
91 IPPEVE_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
92 IPPEVE_PREASON_TONER_LOW
= 0x8000 /* toner-low */
94 typedef unsigned int ippeve_preason_t
; /* Bitfield for printer-state-reasons */
95 static const char * const ippeve_preason_strings
[] =
96 { /* Strings for each bit */
97 /* "none" is implied for no bits set */
100 "input-tray-missing",
101 "marker-supply-empty",
103 "marker-waste-almost-full",
118 * URL scheme for web resources...
122 # define WEB_SCHEME "https"
124 # define WEB_SCHEME "http"
125 #endif /* HAVE_SSL */
133 typedef DNSServiceRef ippeve_srv_t
; /* Service reference */
134 typedef TXTRecordRef ippeve_txt_t
; /* TXT record */
136 #elif defined(HAVE_AVAHI)
137 typedef AvahiEntryGroup
*_ipp_srv_t
; /* Service reference */
138 typedef AvahiStringList
*_ipp_txt_t
; /* TXT record */
141 typedef void *_ipp_srv_t
; /* Service reference */
142 typedef void *_ipp_txt_t
; /* TXT record */
143 #endif /* HAVE_DNSSD */
145 typedef struct ippeve_filter_s
/**** Attribute filter ****/
147 cups_array_t
*ra
; /* Requested attributes */
148 ipp_tag_t group_tag
; /* Group to copy */
151 typedef struct ippeve_job_s ippeve_job_t
;
153 typedef struct ippeve_printer_s
/**** Printer data ****/
155 int ipv4
, /* IPv4 listener */
156 ipv6
; /* IPv6 listener */
157 ippeve_srv_t ipp_ref
, /* Bonjour IPP service */
158 ipps_ref
, /* Bonjour IPPS service */
159 http_ref
, /* Bonjour HTTP service */
160 printer_ref
; /* Bonjour LPD service */
161 char *dnssd_name
, /* printer-dnssd-name */
162 *name
, /* printer-name */
163 *icon
, /* Icon filename */
164 *directory
, /* Spool directory */
165 *hostname
, /* Hostname */
166 *uri
, /* printer-uri-supported */
167 *device_uri
, /* Device URI (if any) */
168 *ppdfile
, /* PPD file (if any) */
169 *command
; /* Command to run with job file */
171 size_t urilen
; /* Length of printer URI */
172 ipp_t
*attrs
; /* Static attributes */
173 time_t start_time
; /* Startup time */
174 time_t config_time
; /* printer-config-change-time */
175 ipp_pstate_t state
; /* printer-state value */
176 ippeve_preason_t state_reasons
; /* printer-state-reasons values */
177 time_t state_time
; /* printer-state-change-time */
178 cups_array_t
*jobs
; /* Jobs */
179 ippeve_job_t
*active_job
; /* Current active/pending job */
180 int next_job_id
; /* Next job-id value */
181 _cups_rwlock_t rwlock
; /* Printer lock */
184 struct ippeve_job_s
/**** Job data ****/
187 const char *name
, /* job-name */
188 *username
, /* job-originating-user-name */
189 *format
; /* document-format */
190 ipp_jstate_t state
; /* job-state value */
191 time_t created
, /* time-at-creation value */
192 processing
, /* time-at-processing value */
193 completed
; /* time-at-completed value */
194 int impressions
, /* job-impressions value */
195 impcompleted
; /* job-impressions-completed value */
196 ipp_t
*attrs
; /* Static attributes */
197 int cancel
; /* Non-zero when job canceled */
198 char *filename
; /* Print file name */
199 int fd
; /* Print file descriptor */
200 ippeve_printer_t
*printer
; /* Printer */
203 typedef struct ippeve_client_s
/**** Client data ****/
205 http_t
*http
; /* HTTP connection */
206 ipp_t
*request
, /* IPP request */
207 *response
; /* IPP response */
208 time_t start
; /* Request start time */
209 http_state_t operation
; /* Request operation */
210 ipp_op_t operation_id
; /* IPP operation-id */
211 char uri
[1024], /* Request URI */
212 *options
; /* URI options */
213 http_addr_t addr
; /* Client address */
214 char hostname
[256]; /* Client hostname */
215 ippeve_printer_t
*printer
; /* Printer */
216 ippeve_job_t
*job
; /* Current job, if any */
224 static void clean_jobs(ippeve_printer_t
*printer
);
225 static int compare_jobs(ippeve_job_t
*a
, ippeve_job_t
*b
);
226 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
, ipp_tag_t group_tag
, int quickcopy
);
227 static void copy_job_attributes(ippeve_client_t
*client
, ippeve_job_t
*job
, cups_array_t
*ra
);
228 static ippeve_client_t
*create_client(ippeve_printer_t
*printer
, int sock
);
229 static ippeve_job_t
*create_job(ippeve_client_t
*client
);
230 static int create_job_file(ippeve_printer_t
*printer
, ippeve_job_t
*job
, char *fname
, size_t fnamesize
, const char *ext
);
231 static int create_listener(const char *name
, int port
, int family
);
232 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
);
233 static ipp_t
*create_media_size(int width
, int length
);
234 static ippeve_printer_t
*create_printer(const char *servername
, int serverport
, const char *name
, const char *location
, const char *icon
, cups_array_t
*docformats
, const char *subtypes
, const char *directory
, const char *command
, const char *ppdfile
, const char *device_uri
, ipp_t
*attrs
);
235 static void debug_attributes(const char *title
, ipp_t
*ipp
, int response
);
236 static void delete_client(ippeve_client_t
*client
);
237 static void delete_job(ippeve_job_t
*job
);
238 static void delete_printer(ippeve_printer_t
*printer
);
240 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
);
241 #elif defined(HAVE_AVAHI)
242 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
243 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
244 #endif /* HAVE_DNSSD */
245 static void dnssd_init(void);
246 static int filter_cb(ippeve_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
247 static ippeve_job_t
*find_job(ippeve_client_t
*client
);
248 static void html_escape(ippeve_client_t
*client
, const char *s
, size_t slen
);
249 static void html_footer(ippeve_client_t
*client
);
250 static void html_header(ippeve_client_t
*client
, const char *title
);
251 static void html_printf(ippeve_client_t
*client
, const char *format
, ...) _CUPS_FORMAT(2, 3);
252 static void ipp_cancel_job(ippeve_client_t
*client
);
253 static void ipp_close_job(ippeve_client_t
*client
);
254 static void ipp_create_job(ippeve_client_t
*client
);
255 static void ipp_get_job_attributes(ippeve_client_t
*client
);
256 static void ipp_get_jobs(ippeve_client_t
*client
);
257 static void ipp_get_printer_attributes(ippeve_client_t
*client
);
258 static void ipp_identify_printer(ippeve_client_t
*client
);
259 static void ipp_print_job(ippeve_client_t
*client
);
260 static void ipp_print_uri(ippeve_client_t
*client
);
261 static void ipp_send_document(ippeve_client_t
*client
);
262 static void ipp_send_uri(ippeve_client_t
*client
);
263 static void ipp_validate_job(ippeve_client_t
*client
);
264 static ipp_t
*load_ippserver_attributes(const char *servername
, int serverport
, const char *filename
, cups_array_t
*docformats
);
265 static ipp_t
*load_legacy_attributes(const char *make
, const char *model
, int ppm
, int ppm_color
, int duplex
, cups_array_t
*docformats
);
266 static ipp_t
*load_ppd_attributes(const char *ppdfile
, cups_array_t
*docformats
);
267 static int parse_options(ippeve_client_t
*client
, cups_option_t
**options
);
268 static void process_attr_message(ippeve_job_t
*job
, char *message
);
269 static void *process_client(ippeve_client_t
*client
);
270 static int process_http(ippeve_client_t
*client
);
271 static int process_ipp(ippeve_client_t
*client
);
272 static void *process_job(ippeve_job_t
*job
);
273 static void process_state_message(ippeve_job_t
*job
, char *message
);
274 static int register_printer(ippeve_printer_t
*printer
, const char *subtypes
);
275 static int respond_http(ippeve_client_t
*client
, http_status_t code
, const char *content_coding
, const char *type
, size_t length
);
276 static void respond_ipp(ippeve_client_t
*client
, ipp_status_t status
, const char *message
, ...) _CUPS_FORMAT(3, 4);
277 static void respond_unsupported(ippeve_client_t
*client
, ipp_attribute_t
*attr
);
278 static void run_printer(ippeve_printer_t
*printer
);
279 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
280 static void usage(int status
) _CUPS_NORETURN
;
281 static int valid_doc_attributes(ippeve_client_t
*client
);
282 static int valid_job_attributes(ippeve_client_t
*client
);
290 static DNSServiceRef DNSSDMaster
= NULL
;
291 #elif defined(HAVE_AVAHI)
292 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
293 static AvahiClient
*DNSSDClient
= NULL
;
294 #endif /* HAVE_DNSSD */
296 static int KeepFiles
= 0, /* Keep spooled job files? */
297 MaxVersion
= 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
298 Verbosity
= 0; /* Verbosity level */
302 * 'main()' - Main entry to the sample server.
305 int /* O - Exit status */
306 main(int argc
, /* I - Number of command-line args */
307 char *argv
[]) /* I - Command-line arguments */
309 int i
; /* Looping var */
310 const char *opt
, /* Current option character */
311 *attrfile
= NULL
, /* ippserver attributes file */
312 *command
= NULL
, /* Command to run with job files */
313 *device_uri
= NULL
, /* Device URI */
314 *icon
= "printer.png", /* Icon file */
316 *keypath
= NULL
, /* Keychain path */
317 #endif /* HAVE_SSL */
318 *location
= "", /* Location of printer */
319 *make
= "Test", /* Manufacturer */
320 *model
= "Printer", /* Model */
321 *name
= NULL
, /* Printer name */
322 *ppdfile
= NULL
, /* PPD file */
323 *subtypes
= "_print"; /* DNS-SD service subtype */
324 int legacy
= 0, /* Legacy mode? */
325 duplex
= 0, /* Duplex mode */
326 ppm
= 10, /* Pages per minute for mono */
327 ppm_color
= 0; /* Pages per minute for color */
328 ipp_t
*attrs
= NULL
; /* Printer attributes */
329 char directory
[1024] = ""; /* Spool directory */
330 cups_array_t
*docformats
= NULL
; /* Supported formats */
331 const char *servername
= NULL
; /* Server host name */
332 int serverport
= 0; /* Server port number (0 = auto) */
333 ippeve_printer_t
*printer
; /* Printer object */
337 * Parse command-line arguments...
340 for (i
= 1; i
< argc
; i
++)
342 if (!strcmp(argv
[i
], "--help"))
346 else if (!strcmp(argv
[i
], "--version"))
351 else if (!strncmp(argv
[i
], "--", 2))
353 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
356 else if (argv
[i
][0] == '-')
358 for (opt
= argv
[i
] + 1; *opt
; opt
++)
362 case '2' : /* -2 (enable 2-sided printing) */
367 case 'D' : /* -D device-uri */
372 device_uri
= argv
[i
];
376 case 'K' : /* -K keypath */
383 #endif /* HAVE_SSL */
385 case 'M' : /* -M manufacturer */
394 case 'P' : /* -P filename.ppd */
402 case 'V' : /* -V max-version */
407 if (!strcmp(argv
[i
], "2.0"))
409 else if (!strcmp(argv
[i
], "1.1"))
415 case 'a' : /* -a attributes-file */
423 case 'c' : /* -c command */
431 case 'd' : /* -d spool-directory */
436 strlcpy(directory
, argv
[i
], sizeof(directory
));
439 case 'f' : /* -f type/subtype[,...] */
444 docformats
= _cupsArrayNewStrings(argv
[i
], ',');
448 case 'h' : /* -h (show help) */
451 case 'i' : /* -i icon.png */
459 case 'k' : /* -k (keep files) */
463 case 'l' : /* -l location */
471 case 'm' : /* -m model */
480 case 'n' : /* -n hostname */
485 servername
= argv
[i
];
488 case 'p' : /* -p port */
490 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
493 serverport
= atoi(argv
[i
]);
496 case 'r' : /* -r subtype */
504 case 's' : /* -s speed[,color-speed] */
509 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
515 case 'v' : /* -v (be verbose) */
519 default : /* Unknown */
520 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), argv
[0], *opt
);
531 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
539 if (((ppdfile
!= NULL
) + (attrfile
!= NULL
) + legacy
) > 1)
543 * Apply defaults as needed...
547 docformats
= _cupsArrayNewStrings("application/pdf,image/jpeg,image/pwg-raster", ',');
553 * Windows is almost always used as a single user system, so use a default
554 * port number of 8631.
561 * Use 8000 + UID mod 1000 for the default port number...
564 serverport
= 8000 + ((int)getuid() % 1000);
567 _cupsLangPrintf(stderr
, _("Listening on port %d."), serverport
);
572 const char *tmpdir
; /* Temporary directory */
575 if ((tmpdir
= getenv("TEMP")) == NULL
)
577 #elif defined(__APPLE__) && TARGET_OS_OSX
578 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
579 tmpdir
= "/private/tmp";
581 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
585 snprintf(directory
, sizeof(directory
), "%s/ippeveprinter.%d", tmpdir
, (int)getpid());
587 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
589 _cupsLangPrintf(stderr
, _("Unable to create spool directory \"%s\": %s"), directory
, strerror(errno
));
594 _cupsLangPrintf(stderr
, _("Using spool directory \"%s\"."), directory
);
598 cupsSetServerCredentials(keypath
, servername
, 1);
599 #endif /* HAVE_SSL */
602 * Initialize DNS-SD...
608 * Create the printer...
612 docformats
= _cupsArrayNewStrings("image/pwg-raster", ',');
615 attrs
= load_ippserver_attributes(servername
, serverport
, attrfile
, docformats
);
617 attrs
= load_ppd_attributes(ppdfile
, docformats
);
619 attrs
= load_legacy_attributes(make
, model
, ppm
, ppm_color
, duplex
, docformats
);
621 if ((printer
= create_printer(servername
, serverport
, name
, location
, icon
, docformats
, subtypes
, directory
, command
, ppdfile
, device_uri
, attrs
)) == NULL
)
625 * Run the print service...
628 run_printer(printer
);
631 * Destroy the printer and exit...
634 delete_printer(printer
);
641 * 'clean_jobs()' - Clean out old (completed) jobs.
645 clean_jobs(ippeve_printer_t
*printer
) /* I - Printer */
647 ippeve_job_t
*job
; /* Current job */
648 time_t cleantime
; /* Clean time */
651 if (cupsArrayCount(printer
->jobs
) == 0)
654 cleantime
= time(NULL
) - 60;
656 _cupsRWLockWrite(&(printer
->rwlock
));
657 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
);
659 job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
660 if (job
->completed
&& job
->completed
< cleantime
)
662 cupsArrayRemove(printer
->jobs
, job
);
667 _cupsRWUnlock(&(printer
->rwlock
));
672 * 'compare_jobs()' - Compare two jobs.
675 static int /* O - Result of comparison */
676 compare_jobs(ippeve_job_t
*a
, /* I - First job */
677 ippeve_job_t
*b
) /* I - Second job */
679 return (b
->id
- a
->id
);
684 * 'copy_attributes()' - Copy attributes from one request to another.
688 copy_attributes(ipp_t
*to
, /* I - Destination request */
689 ipp_t
*from
, /* I - Source request */
690 cups_array_t
*ra
, /* I - Requested attributes */
691 ipp_tag_t group_tag
, /* I - Group to copy */
692 int quickcopy
) /* I - Do a quick copy? */
694 ippeve_filter_t filter
; /* Filter data */
698 filter
.group_tag
= group_tag
;
700 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
705 * 'copy_job_attrs()' - Copy job attributes to the response.
710 ippeve_client_t
*client
, /* I - Client */
711 ippeve_job_t
*job
, /* I - Job */
712 cups_array_t
*ra
) /* I - requested-attributes */
714 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
716 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
719 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
721 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
724 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
727 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
729 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
732 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
733 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
735 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
736 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
738 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
739 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
741 if (!ra
|| cupsArrayFind(ra
, "job-state"))
742 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
743 "job-state", job
->state
);
745 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
749 case IPP_JSTATE_PENDING
:
750 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
753 case IPP_JSTATE_HELD
:
755 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
756 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
757 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
759 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
762 case IPP_JSTATE_PROCESSING
:
764 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
766 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
769 case IPP_JSTATE_STOPPED
:
770 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
773 case IPP_JSTATE_CANCELED
:
774 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
777 case IPP_JSTATE_ABORTED
:
778 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
781 case IPP_JSTATE_COMPLETED
:
782 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
787 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
791 case IPP_JSTATE_PENDING
:
792 ippAddString(client
->response
, IPP_TAG_JOB
,
793 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
797 case IPP_JSTATE_HELD
:
799 ippAddString(client
->response
, IPP_TAG_JOB
,
800 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
801 "job-state-reasons", NULL
, "job-incoming");
802 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
803 ippAddString(client
->response
, IPP_TAG_JOB
,
804 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
805 "job-state-reasons", NULL
, "job-hold-until-specified");
807 ippAddString(client
->response
, IPP_TAG_JOB
,
808 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
809 "job-state-reasons", NULL
, "job-data-insufficient");
812 case IPP_JSTATE_PROCESSING
:
814 ippAddString(client
->response
, IPP_TAG_JOB
,
815 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
816 "job-state-reasons", NULL
, "processing-to-stop-point");
818 ippAddString(client
->response
, IPP_TAG_JOB
,
819 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
820 "job-state-reasons", NULL
, "job-printing");
823 case IPP_JSTATE_STOPPED
:
824 ippAddString(client
->response
, IPP_TAG_JOB
,
825 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
826 NULL
, "job-stopped");
829 case IPP_JSTATE_CANCELED
:
830 ippAddString(client
->response
, IPP_TAG_JOB
,
831 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
832 NULL
, "job-canceled-by-user");
835 case IPP_JSTATE_ABORTED
:
836 ippAddString(client
->response
, IPP_TAG_JOB
,
837 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
838 NULL
, "aborted-by-system");
841 case IPP_JSTATE_COMPLETED
:
842 ippAddString(client
->response
, IPP_TAG_JOB
,
843 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
844 NULL
, "job-completed-successfully");
849 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
850 ippAddInteger(client
->response
, IPP_TAG_JOB
,
851 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
852 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
854 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
855 ippAddInteger(client
->response
, IPP_TAG_JOB
,
856 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
857 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
862 * 'create_client()' - Accept a new network connection and create a client
866 static ippeve_client_t
* /* O - Client */
867 create_client(ippeve_printer_t
*printer
, /* I - Printer */
868 int sock
) /* I - Listen socket */
870 ippeve_client_t
*client
; /* Client */
873 if ((client
= calloc(1, sizeof(ippeve_client_t
))) == NULL
)
875 perror("Unable to allocate memory for client");
879 client
->printer
= printer
;
882 * Accept the client and get the remote address...
885 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
887 perror("Unable to accept client connection");
894 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
897 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
904 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
908 static ippeve_job_t
* /* O - Job */
909 create_job(ippeve_client_t
*client
) /* I - Client */
911 ippeve_job_t
*job
; /* Job */
912 ipp_attribute_t
*attr
; /* Job attribute */
913 char uri
[1024], /* job-uri value */
914 uuid
[64]; /* job-uuid value */
917 _cupsRWLockWrite(&(client
->printer
->rwlock
));
918 if (client
->printer
->active_job
&&
919 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
922 * Only accept a single job at a time...
925 _cupsRWUnlock(&(client
->printer
->rwlock
));
930 * Allocate and initialize the job object...
933 if ((job
= calloc(1, sizeof(ippeve_job_t
))) == NULL
)
935 perror("Unable to allocate memory for job");
939 job
->printer
= client
->printer
;
940 job
->attrs
= ippNew();
941 job
->state
= IPP_JSTATE_HELD
;
945 * Copy all of the job attributes...
948 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
951 * Get the requesting-user-name, document format, and priority...
954 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
955 job
->username
= ippGetString(attr
, 0, NULL
);
957 job
->username
= "anonymous";
959 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
961 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
963 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
964 job
->format
= ippGetString(attr
, 0, NULL
);
965 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
966 job
->format
= ippGetString(attr
, 0, NULL
);
968 job
->format
= "application/octet-stream";
971 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
972 job
->impressions
= ippGetInteger(attr
, 0);
974 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
975 job
->name
= ippGetString(attr
, 0, NULL
);
978 * Add job description attributes and add to the jobs array...
981 job
->id
= client
->printer
->next_job_id
++;
983 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
984 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
986 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
987 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
988 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
989 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
990 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
991 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
993 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
994 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
996 cupsArrayAdd(client
->printer
->jobs
, job
);
997 client
->printer
->active_job
= job
;
999 _cupsRWUnlock(&(client
->printer
->rwlock
));
1006 * 'create_job_file()' - Create a file for the document in a job.
1009 static int /* O - File descriptor or -1 on error */
1011 ippeve_printer_t
*printer
, /* I - Printer */
1012 ippeve_job_t
*job
, /* I - Job */
1013 char *fname
, /* I - Filename buffer */
1014 size_t fnamesize
, /* I - Size of filename buffer */
1015 const char *ext
) /* I - Extension (`NULL` for default) */
1017 char name
[256], /* "Safe" filename */
1018 *nameptr
; /* Pointer into filename */
1019 const char *job_name
; /* job-name value */
1023 * Make a name from the job-name attribute...
1026 if ((job_name
= ippGetString(ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
), 0, NULL
)) == NULL
)
1027 job_name
= "untitled";
1029 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1031 if (isalnum(*job_name
& 255) || *job_name
== '-')
1033 *nameptr
++ = (char)tolower(*job_name
& 255);
1039 while (job_name
[1] && !isalnum(job_name
[1] & 255) && job_name
[1] != '-')
1047 * Figure out the extension...
1052 if (!strcasecmp(job
->format
, "image/jpeg"))
1054 else if (!strcasecmp(job
->format
, "image/png"))
1056 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1058 else if (!strcasecmp(job
->format
, "image/urf"))
1060 else if (!strcasecmp(job
->format
, "application/pdf"))
1062 else if (!strcasecmp(job
->format
, "application/postscript"))
1064 else if (!strcasecmp(job
->format
, "application/vnd.hp-pcl"))
1071 * Create a filename with the job-id, job-name, and document-format (extension)...
1074 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", printer
->directory
, job
->id
, name
, ext
);
1076 return (open(fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666));
1081 * 'create_listener()' - Create a listener socket.
1084 static int /* O - Listener socket or -1 on error */
1085 create_listener(const char *name
, /* I - Host name (`NULL` for any address) */
1086 int port
, /* I - Port number */
1087 int family
) /* I - Address family */
1089 int sock
; /* Listener socket */
1090 http_addrlist_t
*addrlist
; /* Listen address */
1091 char service
[255]; /* Service port */
1094 snprintf(service
, sizeof(service
), "%d", port
);
1095 if ((addrlist
= httpAddrGetList(name
, family
, service
)) == NULL
)
1098 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1100 httpAddrFreeList(addrlist
);
1107 * 'create_media_col()' - Create a media-col value.
1110 static ipp_t
* /* O - media-col collection */
1111 create_media_col(const char *media
, /* I - Media name */
1112 const char *source
, /* I - Media source, if any */
1113 const char *type
, /* I - Media type, if any */
1114 int width
, /* I - x-dimension in 2540ths */
1115 int length
, /* I - y-dimension in 2540ths */
1116 int bottom
, /* I - Bottom margin in 2540ths */
1117 int left
, /* I - Left margin in 2540ths */
1118 int right
, /* I - Right margin in 2540ths */
1119 int top
) /* I - Top margin in 2540ths */
1121 ipp_t
*media_col
= ippNew(), /* media-col value */
1122 *media_size
= create_media_size(width
, length
);
1123 /* media-size value */
1124 char media_key
[256]; /* media-key value */
1125 const char *media_key_suffix
= ""; /* media-key suffix */
1128 if (bottom
== 0 && left
== 0 && right
== 0 && top
== 0)
1129 media_key_suffix
= "_borderless";
1132 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, media_key_suffix
);
1134 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, media_key_suffix
);
1136 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, media_key_suffix
);
1138 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, media_key_suffix
);
1140 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
, media_key
);
1141 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1142 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1143 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin", bottom
);
1144 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin", left
);
1145 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin", right
);
1146 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin", top
);
1148 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1150 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1152 ippDelete(media_size
);
1159 * 'create_media_size()' - Create a media-size value.
1162 static ipp_t
* /* O - media-col collection */
1163 create_media_size(int width
, /* I - x-dimension in 2540ths */
1164 int length
) /* I - y-dimension in 2540ths */
1166 ipp_t
*media_size
= ippNew(); /* media-size value */
1169 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension", width
);
1170 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension", length
);
1172 return (media_size
);
1177 * 'create_printer()' - Create, register, and listen for connections to a
1181 static ippeve_printer_t
* /* O - Printer */
1183 const char *servername
, /* I - Server hostname (NULL for default) */
1184 int serverport
, /* I - Server port */
1185 const char *name
, /* I - printer-name */
1186 const char *location
, /* I - printer-location */
1187 const char *icon
, /* I - printer-icons */
1188 cups_array_t
*docformats
, /* I - document-format-supported */
1189 const char *subtypes
, /* I - Bonjour service subtype(s) */
1190 const char *directory
, /* I - Spool directory */
1191 const char *command
, /* I - Command to run on job files, if any */
1192 const char *ppdfile
, /* I - PPD file, if any */
1193 const char *device_uri
, /* I - Output device, if any */
1194 ipp_t
*attrs
) /* I - Capability attributes */
1196 ippeve_printer_t
*printer
; /* Printer */
1197 int i
; /* Looping var */
1199 char path
[1024]; /* Full path to command */
1200 #endif /* !_WIN32 */
1201 char uri
[1024], /* Printer URI */
1203 securi
[1024], /* Secure printer URI */
1204 *uris
[2], /* All URIs */
1205 #endif /* HAVE_SSL */
1206 icons
[1024], /* printer-icons URI */
1207 adminurl
[1024], /* printer-more-info URI */
1208 supplyurl
[1024],/* printer-supply-info-uri URI */
1209 uuid
[128]; /* printer-uuid */
1210 int k_supported
; /* Maximum file size supported */
1211 int num_formats
; /* Number of supported document formats */
1212 const char *formats
[100], /* Supported document formats */
1213 *format
; /* Current format */
1214 int num_job_attrs
; /* Number of supported job attributes */
1215 const char *job_attrs
[100];/* Job attributes */
1216 char xxx_supported
[256];
1217 /* Name of -supported attribute */
1218 _cups_globals_t
*cg
= _cupsGlobals();
1219 /* Global path values */
1221 struct statvfs spoolinfo
; /* FS info for spool directory */
1222 double spoolsize
; /* FS size */
1223 #elif defined(HAVE_STATFS)
1224 struct statfs spoolinfo
; /* FS info for spool directory */
1225 double spoolsize
; /* FS size */
1226 #endif /* HAVE_STATVFS */
1227 static const char * const versions
[] =/* ipp-versions-supported values */
1232 static const char * const features
[] =/* ipp-features-supported values */
1236 static const int ops
[] = /* operations-supported values */
1240 IPP_OP_VALIDATE_JOB
,
1242 IPP_OP_SEND_DOCUMENT
,
1245 IPP_OP_GET_JOB_ATTRIBUTES
,
1247 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1248 IPP_OP_CANCEL_MY_JOBS
,
1250 IPP_OP_IDENTIFY_PRINTER
1252 static const char * const charsets
[] =/* charset-supported values */
1257 static const char * const compressions
[] =/* compression-supported values */
1262 #endif /* HAVE_LIBZ */
1265 static const char * const identify_actions
[] =
1270 static const char * const job_creation
[] =
1271 { /* job-creation-attributes-supported values */
1273 "document-password",
1278 "orientation-requested",
1283 "print-content-optimize",
1284 "print-rendering-intent",
1286 "printer-resolution",
1289 static const char * const media_col_supported
[] =
1290 { /* media-col-supported values */
1291 "media-bottom-margin",
1292 "media-left-margin",
1293 "media-right-margin",
1300 static const char * const multiple_document_handling
[] =
1301 { /* multiple-document-handling-supported values */
1302 "separate-documents-uncollated-copies",
1303 "separate-documents-collated-copies"
1305 static const char * const reference_uri_schemes_supported
[] =
1306 { /* reference-uri-schemes-supported */
1312 #endif /* HAVE_SSL */
1315 static const char * const uri_authentication_supported
[] =
1316 { /* uri-authentication-supported values */
1320 static const char * const uri_security_supported
[] =
1321 { /* uri-security-supported values */
1325 #endif /* HAVE_SSL */
1326 static const char * const which_jobs
[] =
1327 { /* which-jobs-supported values */
1336 "processing-stopped"
1342 * If a command was specified, make sure it exists and is executable...
1347 if (*command
== '/' || !strncmp(command
, "./", 2))
1349 if (access(command
, X_OK
))
1351 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1357 snprintf(path
, sizeof(path
), "%s/ippeveprinter/%s", cg
->cups_serverbin
, command
);
1359 if (access(command
, X_OK
))
1361 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1368 #endif /* !_WIN32 */
1371 * Allocate memory for the printer...
1374 if ((printer
= calloc(1, sizeof(ippeve_printer_t
))) == NULL
)
1376 perror("ippserver: Unable to allocate memory for printer");
1382 printer
->name
= strdup(name
);
1383 printer
->dnssd_name
= strdup(name
);
1384 printer
->command
= command
? strdup(command
) : NULL
;
1385 printer
->device_uri
= device_uri
? strdup(device_uri
) : NULL
;
1386 printer
->directory
= strdup(directory
);
1387 printer
->icon
= icon
? strdup(icon
) : NULL
;
1388 printer
->port
= serverport
;
1389 printer
->ppdfile
= ppdfile
? strdup(ppdfile
) : NULL
;
1390 printer
->start_time
= time(NULL
);
1391 printer
->config_time
= printer
->start_time
;
1392 printer
->state
= IPP_PSTATE_IDLE
;
1393 printer
->state_reasons
= IPPEVE_PREASON_NONE
;
1394 printer
->state_time
= printer
->start_time
;
1395 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1396 printer
->next_job_id
= 1;
1400 printer
->hostname
= strdup(servername
);
1404 char temp
[1024]; /* Temporary string */
1406 printer
->hostname
= strdup(httpGetHostname(NULL
, temp
, sizeof(temp
)));
1409 _cupsRWInit(&(printer
->rwlock
));
1412 * Create the listener sockets...
1415 if ((printer
->ipv4
= create_listener(servername
, printer
->port
, AF_INET
)) < 0)
1417 perror("Unable to create IPv4 listener");
1421 if ((printer
->ipv6
= create_listener(servername
, printer
->port
, AF_INET6
)) < 0)
1423 perror("Unable to create IPv6 listener");
1428 * Prepare URI values for the printer attributes...
1431 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1432 printer
->uri
= strdup(uri
);
1433 printer
->urilen
= strlen(uri
);
1436 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1437 #endif /* HAVE_SSL */
1439 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1440 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1441 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1442 httpAssembleUUID(printer
->hostname
, serverport
, name
, 0, uuid
, sizeof(uuid
));
1446 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1447 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1449 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1451 fprintf(stderr
, "printer-uri=\"%s\",\"%s\"\n", uri
, securi
);
1452 #endif /* HAVE_SSL */
1456 * Get the maximum spool size based on the size of the filesystem used for
1457 * the spool directory. If the host OS doesn't support the statfs call
1458 * or the filesystem is larger than 2TiB, always report INT_MAX.
1462 if (statvfs(printer
->directory
, &spoolinfo
))
1463 k_supported
= INT_MAX
;
1464 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1465 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1466 k_supported
= INT_MAX
;
1468 k_supported
= (int)spoolsize
;
1470 #elif defined(HAVE_STATFS)
1471 if (statfs(printer
->directory
, &spoolinfo
))
1472 k_supported
= INT_MAX
;
1473 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1474 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1475 k_supported
= INT_MAX
;
1477 k_supported
= (int)spoolsize
;
1480 k_supported
= INT_MAX
;
1481 #endif /* HAVE_STATVFS */
1484 * Assemble the final list of document formats...
1487 if (!cupsArrayFind(docformats
, (void *)"application/octet-stream"))
1488 cupsArrayAdd(docformats
, (void *)"application/octet-stream");
1490 for (num_formats
= 0, format
= (const char *)cupsArrayFirst(docformats
); format
&& num_formats
< (int)(sizeof(formats
) / sizeof(formats
[0])); format
= (const char *)cupsArrayNext(docformats
))
1491 formats
[num_formats
++] = format
;
1494 * Get the list of attributes that can be used when creating a job...
1498 job_attrs
[num_job_attrs
++] = "ipp-attribute-fidelity";
1499 job_attrs
[num_job_attrs
++] = "job-name";
1500 job_attrs
[num_job_attrs
++] = "job-priority";
1501 job_attrs
[num_job_attrs
++] = "multiple-document-handling";
1503 for (i
= 0; i
< (int)(sizeof(job_creation
) / sizeof(job_creation
[0])) && num_job_attrs
< (int)(sizeof(job_attrs
) / sizeof(job_attrs
[0])); i
++)
1505 snprintf(xxx_supported
, sizeof(xxx_supported
), "%s-supported", job_creation
[i
]);
1506 if (ippFindAttribute(printer
->attrs
, xxx_supported
, IPP_TAG_ZERO
))
1507 job_attrs
[num_job_attrs
++] = job_creation
[i
];
1511 * Fill out the rest of the printer attributes.
1514 printer
->attrs
= attrs
;
1516 /* charset-configured */
1517 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1519 /* charset-supported */
1520 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1522 /* compression-supported */
1523 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1524 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1526 /* document-format-default */
1527 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_MIMETYPE
), "document-format-default", NULL
, "application/octet-stream");
1529 /* document-format-supported */
1530 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
, "document-format-supported", num_formats
, NULL
, formats
);
1532 /* generated-natural-language-supported */
1533 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1535 /* identify-actions-default */
1536 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1538 /* identify-actions-supported */
1539 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
);
1541 /* ipp-features-supported */
1542 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1544 /* ipp-versions-supported */
1545 if (MaxVersion
== 11)
1546 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", NULL
, "1.1");
1548 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", (int)(sizeof(versions
) / sizeof(versions
[0])), NULL
, versions
);
1550 /* job-creation-attributes-supported */
1551 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-creation-attributes-supported", num_job_attrs
, NULL
, job_attrs
);
1553 /* job-ids-supported */
1554 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1556 /* job-k-octets-supported */
1557 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0, k_supported
);
1559 /* job-priority-default */
1560 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1562 /* job-priority-supported */
1563 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 1);
1565 /* job-sheets-default */
1566 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1568 /* job-sheets-supported */
1569 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1571 /* media-col-supported */
1572 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
);
1574 /* multiple-document-handling-supported */
1575 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
);
1577 /* multiple-document-jobs-supported */
1578 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1580 /* multiple-operation-time-out */
1581 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1583 /* multiple-operation-time-out-action */
1584 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1586 /* natural-language-configured */
1587 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "natural-language-configured", NULL
, "en");
1589 /* operations-supported */
1590 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1592 /* pdl-override-supported */
1593 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1595 /* preferred-attributes-supported */
1596 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1598 /* printer-get-attributes-supported */
1599 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1601 /* printer-geo-location */
1602 ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location");
1605 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-icons", NULL
, icons
);
1607 /* printer-is-accepting-jobs */
1608 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1611 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
1613 /* printer-location */
1614 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location", NULL
, location
);
1616 /* printer-more-info */
1617 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1620 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
1622 /* printer-organization */
1623 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "");
1625 /* printer-organizational-unit */
1626 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "");
1628 /* printer-supply-info-uri */
1629 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1631 /* printer-uri-supported */
1636 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
1639 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1640 #endif /* HAVE_SSL */
1643 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1645 /* reference-uri-scheme-supported */
1646 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
);
1648 /* uri-authentication-supported */
1650 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
1652 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
1653 #endif /* HAVE_SSL */
1655 /* uri-security-supported */
1657 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
1659 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
1660 #endif /* HAVE_SSL */
1662 /* which-jobs-supported */
1663 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
);
1665 debug_attributes("Printer", printer
->attrs
, 0);
1668 * Register the printer with Bonjour...
1671 if (!register_printer(printer
, subtypes
))
1682 * If we get here we were unable to create the printer...
1687 delete_printer(printer
);
1694 * 'debug_attributes()' - Print attributes in a request or response.
1698 debug_attributes(const char *title
, /* I - Title */
1699 ipp_t
*ipp
, /* I - Request/response */
1700 int type
) /* I - 0 = object, 1 = request, 2 = response */
1702 ipp_tag_t group_tag
; /* Current group */
1703 ipp_attribute_t
*attr
; /* Current attribute */
1704 char buffer
[2048]; /* String buffer for value */
1705 int major
, minor
; /* Version */
1711 fprintf(stderr
, "%s:\n", title
);
1712 major
= ippGetVersion(ipp
, &minor
);
1713 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1715 fprintf(stderr
, " operation-id=%s(%04x)\n",
1716 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1718 fprintf(stderr
, " status-code=%s(%04x)\n",
1719 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1720 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1722 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1724 attr
= ippNextAttribute(ipp
))
1726 if (ippGetGroupTag(attr
) != group_tag
)
1728 group_tag
= ippGetGroupTag(attr
);
1729 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1732 if (ippGetName(attr
))
1734 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1735 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1736 ippGetCount(attr
) > 1 ? "1setOf " : "",
1737 ippTagString(ippGetValueTag(attr
)), buffer
);
1744 * 'delete_client()' - Close the socket and free all memory used by a client
1749 delete_client(ippeve_client_t
*client
) /* I - Client */
1752 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1755 * Flush pending writes before closing...
1758 httpFlushWrite(client
->http
);
1764 httpClose(client
->http
);
1766 ippDelete(client
->request
);
1767 ippDelete(client
->response
);
1774 * 'delete_job()' - Remove from the printer and free all memory used by a job
1779 delete_job(ippeve_job_t
*job
) /* I - Job */
1782 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1784 ippDelete(job
->attrs
);
1789 unlink(job
->filename
);
1791 free(job
->filename
);
1799 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1800 * used by a printer object.
1804 delete_printer(ippeve_printer_t
*printer
) /* I - Printer */
1806 if (printer
->ipv4
>= 0)
1807 close(printer
->ipv4
);
1809 if (printer
->ipv6
>= 0)
1810 close(printer
->ipv6
);
1813 if (printer
->printer_ref
)
1814 DNSServiceRefDeallocate(printer
->printer_ref
);
1815 if (printer
->ipp_ref
)
1816 DNSServiceRefDeallocate(printer
->ipp_ref
);
1817 if (printer
->ipps_ref
)
1818 DNSServiceRefDeallocate(printer
->ipps_ref
);
1819 if (printer
->http_ref
)
1820 DNSServiceRefDeallocate(printer
->http_ref
);
1821 #elif defined(HAVE_AVAHI)
1822 avahi_threaded_poll_lock(DNSSDMaster
);
1824 if (printer
->printer_ref
)
1825 avahi_entry_group_free(printer
->printer_ref
);
1826 if (printer
->ipp_ref
)
1827 avahi_entry_group_free(printer
->ipp_ref
);
1828 if (printer
->ipps_ref
)
1829 avahi_entry_group_free(printer
->ipps_ref
);
1830 if (printer
->http_ref
)
1831 avahi_entry_group_free(printer
->http_ref
);
1833 avahi_threaded_poll_unlock(DNSSDMaster
);
1834 #endif /* HAVE_DNSSD */
1836 if (printer
->dnssd_name
)
1837 free(printer
->dnssd_name
);
1839 free(printer
->name
);
1841 free(printer
->icon
);
1842 if (printer
->command
)
1843 free(printer
->command
);
1844 if (printer
->directory
)
1845 free(printer
->directory
);
1846 if (printer
->hostname
)
1847 free(printer
->hostname
);
1851 ippDelete(printer
->attrs
);
1852 cupsArrayDelete(printer
->jobs
);
1860 * 'dnssd_callback()' - Handle Bonjour registration events.
1863 static void DNSSD_API
1865 DNSServiceRef sdRef
, /* I - Service reference */
1866 DNSServiceFlags flags
, /* I - Status flags */
1867 DNSServiceErrorType errorCode
, /* I - Error, if any */
1868 const char *name
, /* I - Service name */
1869 const char *regtype
, /* I - Service type */
1870 const char *domain
, /* I - Domain for service */
1871 ippeve_printer_t
*printer
) /* I - Printer */
1879 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
1880 regtype
, (int)errorCode
);
1883 else if (strcasecmp(name
, printer
->dnssd_name
))
1886 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
1888 /* No lock needed since only the main thread accesses/changes this */
1889 free(printer
->dnssd_name
);
1890 printer
->dnssd_name
= strdup(name
);
1895 #elif defined(HAVE_AVAHI)
1897 * 'dnssd_callback()' - Handle Bonjour registration events.
1902 AvahiEntryGroup
*srv
, /* I - Service */
1903 AvahiEntryGroupState state
, /* I - Registration state */
1904 void *context
) /* I - Printer */
1913 * 'dnssd_client_cb()' - Client callback for Avahi.
1915 * Called whenever the client or server state changes...
1920 AvahiClient
*c
, /* I - Client */
1921 AvahiClientState state
, /* I - Current state */
1922 void *userdata
) /* I - User data (unused) */
1932 fprintf(stderr
, "Ignore Avahi state %d.\n", state
);
1935 case AVAHI_CLIENT_FAILURE
:
1936 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
1938 fputs("Avahi server crashed, exiting.\n", stderr
);
1944 #endif /* HAVE_DNSSD */
1948 * 'dnssd_init()' - Initialize the DNS-SD service connections...
1955 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
1957 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
1961 #elif defined(HAVE_AVAHI)
1962 int error
; /* Error code, if any */
1964 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
1966 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
1970 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
1972 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
1976 avahi_threaded_poll_start(DNSSDMaster
);
1977 #endif /* HAVE_DNSSD */
1982 * 'filter_cb()' - Filter printer attributes based on the requested array.
1985 static int /* O - 1 to copy, 0 to ignore */
1986 filter_cb(ippeve_filter_t
*filter
, /* I - Filter parameters */
1987 ipp_t
*dst
, /* I - Destination (unused) */
1988 ipp_attribute_t
*attr
) /* I - Source attribute */
1991 * Filter attributes as needed...
1994 #ifndef _WIN32 /* Avoid MS compiler bug */
1996 #endif /* !_WIN32 */
1998 ipp_tag_t group
= ippGetGroupTag(attr
);
1999 const char *name
= ippGetName(attr
);
2001 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
)))
2004 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2009 * 'find_job()' - Find a job specified in a request.
2012 static ippeve_job_t
* /* O - Job or NULL */
2013 find_job(ippeve_client_t
*client
) /* I - Client */
2015 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2016 ippeve_job_t key
, /* Job search key */
2017 *job
; /* Matching job, if any */
2020 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2022 const char *uri
= ippGetString(attr
, 0, NULL
);
2024 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2025 uri
[client
->printer
->urilen
] == '/')
2026 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2030 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2031 key
.id
= ippGetInteger(attr
, 0);
2033 _cupsRWLockRead(&(client
->printer
->rwlock
));
2034 job
= (ippeve_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2035 _cupsRWUnlock(&(client
->printer
->rwlock
));
2042 * 'html_escape()' - Write a HTML-safe string.
2046 html_escape(ippeve_client_t
*client
, /* I - Client */
2047 const char *s
, /* I - String to write */
2048 size_t slen
) /* I - Number of characters to write */
2050 const char *start
, /* Start of segment */
2051 *end
; /* End of string */
2055 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2057 while (*s
&& s
< end
)
2059 if (*s
== '&' || *s
== '<')
2062 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2065 httpWrite2(client
->http
, "&", 5);
2067 httpWrite2(client
->http
, "<", 4);
2076 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2081 * 'html_footer()' - Show the web interface footer.
2083 * This function also writes the trailing 0-length chunk.
2087 html_footer(ippeve_client_t
*client
) /* I - Client */
2093 httpWrite2(client
->http
, "", 0);
2098 * 'html_header()' - Show the web interface header and title.
2102 html_header(ippeve_client_t
*client
, /* I - Client */
2103 const char *title
) /* I - Title */
2109 "<title>%s</title>\n"
2110 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2111 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2112 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
2113 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2115 "body { font-family: sans-serif; margin: 0; }\n"
2116 "div.body { padding: 0px 10px 10px; }\n"
2117 "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
2118 "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
2119 "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
2120 "table.form th { text-align: right; }\n"
2121 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2122 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2123 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2124 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2125 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2126 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2127 "table.nav td { margin: 0; text-align: center; }\n"
2128 "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"
2129 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2130 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2131 "td.nav:hover { background: #666; color: #fff; }\n"
2132 "td.nav:active { background: #000; color: #ff0; }\n"
2136 "<table class=\"nav\"><tr>"
2137 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2138 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2139 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2141 "<div class=\"body\">\n", title
, !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2146 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2150 html_printf(ippeve_client_t
*client
, /* I - Client */
2151 const char *format
, /* I - Printf-style format string */
2152 ...) /* I - Additional arguments as needed */
2154 va_list ap
; /* Pointer to arguments */
2155 const char *start
; /* Start of string */
2156 char size
, /* Size character (h, l, L) */
2157 type
; /* Format type character */
2158 int width
, /* Width of field */
2159 prec
; /* Number of characters of precision */
2160 char tformat
[100], /* Temporary format string for sprintf() */
2161 *tptr
, /* Pointer into temporary format */
2162 temp
[1024]; /* Buffer for formatted numbers */
2163 char *s
; /* Pointer to string */
2167 * Loop through the format string, formatting as needed...
2170 va_start(ap
, format
);
2178 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2181 *tptr
++ = *format
++;
2185 httpWrite2(client
->http
, "%", 1);
2190 else if (strchr(" -+#\'", *format
))
2191 *tptr
++ = *format
++;
2196 * Get width from argument...
2200 width
= va_arg(ap
, int);
2202 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2203 tptr
+= strlen(tptr
);
2209 while (isdigit(*format
& 255))
2211 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2214 width
= width
* 10 + *format
++ - '0';
2220 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2228 * Get precision from argument...
2232 prec
= va_arg(ap
, int);
2234 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2235 tptr
+= strlen(tptr
);
2241 while (isdigit(*format
& 255))
2243 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2246 prec
= prec
* 10 + *format
++ - '0';
2251 if (*format
== 'l' && format
[1] == 'l')
2255 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2263 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2265 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2280 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2289 case 'E' : /* Floating point formats */
2294 if ((size_t)(width
+ 2) > sizeof(temp
))
2297 sprintf(temp
, tformat
, va_arg(ap
, double));
2299 httpWrite2(client
->http
, temp
, strlen(temp
));
2302 case 'B' : /* Integer formats */
2310 if ((size_t)(width
+ 2) > sizeof(temp
))
2313 # ifdef HAVE_LONG_LONG
2315 sprintf(temp
, tformat
, va_arg(ap
, long long));
2317 # endif /* HAVE_LONG_LONG */
2319 sprintf(temp
, tformat
, va_arg(ap
, long));
2321 sprintf(temp
, tformat
, va_arg(ap
, int));
2323 httpWrite2(client
->http
, temp
, strlen(temp
));
2326 case 'p' : /* Pointer value */
2327 if ((size_t)(width
+ 2) > sizeof(temp
))
2330 sprintf(temp
, tformat
, va_arg(ap
, void *));
2332 httpWrite2(client
->http
, temp
, strlen(temp
));
2335 case 'c' : /* Character or character array */
2338 temp
[0] = (char)va_arg(ap
, int);
2340 html_escape(client
, temp
, 1);
2343 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2346 case 's' : /* String */
2347 if ((s
= va_arg(ap
, char *)) == NULL
)
2350 html_escape(client
, s
, strlen(s
));
2359 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2366 * 'ipp_cancel_job()' - Cancel a job.
2370 ipp_cancel_job(ippeve_client_t
*client
) /* I - Client */
2372 ippeve_job_t
*job
; /* Job information */
2379 if ((job
= find_job(client
)) == NULL
)
2381 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2386 * See if the job is already completed, canceled, or aborted; if so,
2387 * we can't cancel...
2392 case IPP_JSTATE_CANCELED
:
2393 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2394 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2397 case IPP_JSTATE_ABORTED
:
2398 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2399 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2402 case IPP_JSTATE_COMPLETED
:
2403 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2404 "Job #%d is already completed - can\'t cancel.", job
->id
);
2412 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2414 if (job
->state
== IPP_JSTATE_PROCESSING
||
2415 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2419 job
->state
= IPP_JSTATE_CANCELED
;
2420 job
->completed
= time(NULL
);
2423 _cupsRWUnlock(&(client
->printer
->rwlock
));
2425 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2432 * 'ipp_close_job()' - Close an open job.
2436 ipp_close_job(ippeve_client_t
*client
) /* I - Client */
2438 ippeve_job_t
*job
; /* Job information */
2445 if ((job
= find_job(client
)) == NULL
)
2447 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2452 * See if the job is already completed, canceled, or aborted; if so,
2453 * we can't cancel...
2458 case IPP_JSTATE_CANCELED
:
2459 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2460 "Job #%d is canceled - can\'t close.", job
->id
);
2463 case IPP_JSTATE_ABORTED
:
2464 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2465 "Job #%d is aborted - can\'t close.", job
->id
);
2468 case IPP_JSTATE_COMPLETED
:
2469 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2470 "Job #%d is completed - can\'t close.", job
->id
);
2473 case IPP_JSTATE_PROCESSING
:
2474 case IPP_JSTATE_STOPPED
:
2475 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2476 "Job #%d is already closed.", job
->id
);
2480 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2487 * 'ipp_create_job()' - Create a job object.
2491 ipp_create_job(ippeve_client_t
*client
) /* I - Client */
2493 ippeve_job_t
*job
; /* New job */
2494 cups_array_t
*ra
; /* Attributes to send in response */
2498 * Validate print job attributes...
2501 if (!valid_job_attributes(client
))
2503 httpFlush(client
->http
);
2508 * Do we have a file to print?
2511 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2513 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2514 "Unexpected document data following request.");
2522 if ((job
= create_job(client
)) == NULL
)
2524 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2525 "Currently printing another job.");
2530 * Return the job info...
2533 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2535 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2536 cupsArrayAdd(ra
, "job-id");
2537 cupsArrayAdd(ra
, "job-state");
2538 cupsArrayAdd(ra
, "job-state-message");
2539 cupsArrayAdd(ra
, "job-state-reasons");
2540 cupsArrayAdd(ra
, "job-uri");
2542 copy_job_attributes(client
, job
, ra
);
2543 cupsArrayDelete(ra
);
2548 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2552 ipp_get_job_attributes(
2553 ippeve_client_t
*client
) /* I - Client */
2555 ippeve_job_t
*job
; /* Job */
2556 cups_array_t
*ra
; /* requested-attributes */
2559 if ((job
= find_job(client
)) == NULL
)
2561 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2565 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2567 ra
= ippCreateRequestedArray(client
->request
);
2568 copy_job_attributes(client
, job
, ra
);
2569 cupsArrayDelete(ra
);
2574 * 'ipp_get_jobs()' - Get a list of job objects.
2578 ipp_get_jobs(ippeve_client_t
*client
) /* I - Client */
2580 ipp_attribute_t
*attr
; /* Current attribute */
2581 const char *which_jobs
= NULL
;
2582 /* which-jobs values */
2583 int job_comparison
; /* Job comparison */
2584 ipp_jstate_t job_state
; /* job-state value */
2585 int first_job_id
, /* First job ID */
2586 limit
, /* Maximum number of jobs to return */
2587 count
; /* Number of jobs that match */
2588 const char *username
; /* Username */
2589 ippeve_job_t
*job
; /* Current job pointer */
2590 cups_array_t
*ra
; /* Requested attributes array */
2594 * See if the "which-jobs" attribute have been specified...
2597 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2598 IPP_TAG_KEYWORD
)) != NULL
)
2600 which_jobs
= ippGetString(attr
, 0, NULL
);
2601 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2604 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2606 job_comparison
= -1;
2607 job_state
= IPP_JSTATE_STOPPED
;
2609 else if (!strcmp(which_jobs
, "completed"))
2612 job_state
= IPP_JSTATE_CANCELED
;
2614 else if (!strcmp(which_jobs
, "aborted"))
2617 job_state
= IPP_JSTATE_ABORTED
;
2619 else if (!strcmp(which_jobs
, "all"))
2622 job_state
= IPP_JSTATE_PENDING
;
2624 else if (!strcmp(which_jobs
, "canceled"))
2627 job_state
= IPP_JSTATE_CANCELED
;
2629 else if (!strcmp(which_jobs
, "pending"))
2632 job_state
= IPP_JSTATE_PENDING
;
2634 else if (!strcmp(which_jobs
, "pending-held"))
2637 job_state
= IPP_JSTATE_HELD
;
2639 else if (!strcmp(which_jobs
, "processing"))
2642 job_state
= IPP_JSTATE_PROCESSING
;
2644 else if (!strcmp(which_jobs
, "processing-stopped"))
2647 job_state
= IPP_JSTATE_STOPPED
;
2651 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2652 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2653 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2654 "which-jobs", NULL
, which_jobs
);
2659 * See if they want to limit the number of jobs reported...
2662 if ((attr
= ippFindAttribute(client
->request
, "limit",
2663 IPP_TAG_INTEGER
)) != NULL
)
2665 limit
= ippGetInteger(attr
, 0);
2667 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2672 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2673 IPP_TAG_INTEGER
)) != NULL
)
2675 first_job_id
= ippGetInteger(attr
, 0);
2677 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2684 * See if we only want to see jobs for a specific user...
2689 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2690 IPP_TAG_BOOLEAN
)) != NULL
)
2692 int my_jobs
= ippGetBoolean(attr
, 0);
2694 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2695 my_jobs
? "true" : "false");
2699 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2700 IPP_TAG_NAME
)) == NULL
)
2702 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2703 "Need requesting-user-name with my-jobs.");
2707 username
= ippGetString(attr
, 0, NULL
);
2709 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2710 client
->hostname
, username
);
2715 * OK, build a list of jobs for this printer...
2718 ra
= ippCreateRequestedArray(client
->request
);
2720 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2722 _cupsRWLockRead(&(client
->printer
->rwlock
));
2724 for (count
= 0, job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2725 (limit
<= 0 || count
< limit
) && job
;
2726 job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2729 * Filter out jobs that don't match...
2732 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2733 (job_comparison
== 0 && job
->state
!= job_state
) ||
2734 (job_comparison
> 0 && job
->state
< job_state
) ||
2735 job
->id
< first_job_id
||
2736 (username
&& job
->username
&&
2737 strcasecmp(username
, job
->username
)))
2741 ippAddSeparator(client
->response
);
2744 copy_job_attributes(client
, job
, ra
);
2747 cupsArrayDelete(ra
);
2749 _cupsRWUnlock(&(client
->printer
->rwlock
));
2754 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2758 ipp_get_printer_attributes(
2759 ippeve_client_t
*client
) /* I - Client */
2761 cups_array_t
*ra
; /* Requested attributes array */
2762 ippeve_printer_t
*printer
; /* Printer */
2766 * Send the attributes...
2769 ra
= ippCreateRequestedArray(client
->request
);
2770 printer
= client
->printer
;
2772 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2774 _cupsRWLockRead(&(printer
->rwlock
));
2776 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2777 IPP_TAG_CUPS_CONST
);
2779 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
2780 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
2782 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
2783 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
2785 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
2786 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
2789 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2790 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2791 "printer-state", printer
->state
);
2793 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
2794 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
2796 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
2797 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
2799 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
2801 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
2803 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
2806 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2808 if (printer
->state_reasons
== IPPEVE_PREASON_NONE
)
2810 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", NULL
, "none");
2814 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
2815 ippeve_preason_t bit
; /* Reason bit */
2816 int i
; /* Looping var */
2817 char reason
[32]; /* Reason string */
2819 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
2821 if (printer
->state_reasons
& bit
)
2823 snprintf(reason
, sizeof(reason
), "%s-%s", ippeve_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
2825 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
2827 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
2833 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2834 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
2836 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2837 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
2839 _cupsRWUnlock(&(printer
->rwlock
));
2841 cupsArrayDelete(ra
);
2846 * 'ipp_identify_printer()' - Beep or display a message.
2850 ipp_identify_printer(
2851 ippeve_client_t
*client
) /* I - Client */
2853 ipp_attribute_t
*actions
, /* identify-actions */
2854 *message
; /* message */
2857 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
2858 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
2860 if (!actions
|| ippContainsString(actions
, "sound"))
2866 if (ippContainsString(actions
, "display"))
2867 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
2869 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2874 * 'ipp_print_job()' - Create a job object with an attached document.
2878 ipp_print_job(ippeve_client_t
*client
) /* I - Client */
2880 ippeve_job_t
*job
; /* New job */
2881 char filename
[1024], /* Filename buffer */
2882 buffer
[4096]; /* Copy buffer */
2883 ssize_t bytes
; /* Bytes read */
2884 cups_array_t
*ra
; /* Attributes to send in response */
2885 _cups_thread_t t
; /* Thread */
2889 * Validate print job attributes...
2892 if (!valid_job_attributes(client
))
2894 httpFlush(client
->http
);
2899 * Do we have a file to print?
2902 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
2904 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
2912 if ((job
= create_job(client
)) == NULL
)
2914 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
2919 * Create a file for the request data...
2922 if ((job
->fd
= create_job_file(client
->printer
, job
, filename
, sizeof(filename
), NULL
)) < 0)
2924 job
->state
= IPP_JSTATE_ABORTED
;
2926 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2931 fprintf(stderr
, "Created job file \"%s\", format \"%s\".\n", filename
, job
->format
);
2933 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2935 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2937 int error
= errno
; /* Write error */
2939 job
->state
= IPP_JSTATE_ABORTED
;
2946 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2954 * Got an error while reading the print data, so abort this job.
2957 job
->state
= IPP_JSTATE_ABORTED
;
2964 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to read print file.");
2970 int error
= errno
; /* Write error */
2972 job
->state
= IPP_JSTATE_ABORTED
;
2977 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2982 job
->filename
= strdup(filename
);
2983 job
->state
= IPP_JSTATE_PENDING
;
2986 * Process the job...
2989 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
2993 _cupsThreadDetach(t
);
2997 job
->state
= IPP_JSTATE_ABORTED
;
2998 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3003 * Return the job info...
3006 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3008 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3009 cupsArrayAdd(ra
, "job-id");
3010 cupsArrayAdd(ra
, "job-state");
3011 cupsArrayAdd(ra
, "job-state-message");
3012 cupsArrayAdd(ra
, "job-state-reasons");
3013 cupsArrayAdd(ra
, "job-uri");
3015 copy_job_attributes(client
, job
, ra
);
3016 cupsArrayDelete(ra
);
3021 * 'ipp_print_uri()' - Create a job object with a referenced document.
3025 ipp_print_uri(ippeve_client_t
*client
) /* I - Client */
3027 ippeve_job_t
*job
; /* New job */
3028 ipp_attribute_t
*uri
; /* document-uri */
3029 char scheme
[256], /* URI scheme */
3030 userpass
[256], /* Username and password info */
3031 hostname
[256], /* Hostname */
3032 resource
[1024]; /* Resource path */
3033 int port
; /* Port number */
3034 http_uri_status_t uri_status
; /* URI decode status */
3035 http_encryption_t encryption
; /* Encryption to use, if any */
3036 http_t
*http
; /* Connection for http/https URIs */
3037 http_status_t status
; /* Access status for http/https URIs */
3038 int infile
; /* Input file for local file URIs */
3039 char filename
[1024], /* Filename buffer */
3040 buffer
[4096]; /* Copy buffer */
3041 ssize_t bytes
; /* Bytes read */
3042 cups_array_t
*ra
; /* Attributes to send in response */
3046 * Validate print job attributes...
3049 if (!valid_job_attributes(client
))
3051 httpFlush(client
->http
);
3056 * Do we have a file to print?
3059 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3061 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Unexpected document data following request.");
3066 * Do we have a document URI?
3069 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3070 IPP_TAG_URI
)) == NULL
)
3072 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3076 if (ippGetCount(uri
) != 1)
3078 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Too many document-uri values.");
3082 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3083 scheme
, sizeof(scheme
), userpass
,
3084 sizeof(userpass
), hostname
, sizeof(hostname
),
3085 &port
, resource
, sizeof(resource
));
3086 if (uri_status
< HTTP_URI_STATUS_OK
)
3088 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s", httpURIStatusString(uri_status
));
3092 if (strcmp(scheme
, "file") &&
3094 strcmp(scheme
, "https") &&
3095 #endif /* HAVE_SSL */
3096 strcmp(scheme
, "http"))
3098 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
, "URI scheme \"%s\" not supported.", scheme
);
3102 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3104 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
3112 if ((job
= create_job(client
)) == NULL
)
3114 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3119 * Create a file for the request data...
3122 if ((job
->fd
= create_job_file(client
->printer
, job
, filename
, sizeof(filename
), NULL
)) < 0)
3124 job
->state
= IPP_JSTATE_ABORTED
;
3126 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
3130 if (!strcmp(scheme
, "file"))
3132 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3134 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
3140 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 && (errno
== EAGAIN
|| errno
== EINTR
))
3142 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3144 int error
= errno
; /* Write error */
3146 job
->state
= IPP_JSTATE_ABORTED
;
3154 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
3165 if (port
== 443 || !strcmp(scheme
, "https"))
3166 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3168 #endif /* HAVE_SSL */
3169 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3171 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
, 1, 30000, NULL
)) == NULL
)
3173 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to connect to %s: %s", hostname
, cupsLastErrorString());
3174 job
->state
= IPP_JSTATE_ABORTED
;
3183 httpClearFields(http
);
3184 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3185 if (httpGet(http
, resource
))
3187 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", strerror(errno
));
3189 job
->state
= IPP_JSTATE_ABORTED
;
3199 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3201 if (status
!= HTTP_STATUS_OK
)
3203 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", httpStatus(status
));
3205 job
->state
= IPP_JSTATE_ABORTED
;
3215 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3217 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3219 int error
= errno
; /* Write error */
3221 job
->state
= IPP_JSTATE_ABORTED
;
3229 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
3239 int error
= errno
; /* Write error */
3241 job
->state
= IPP_JSTATE_ABORTED
;
3246 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
3251 job
->filename
= strdup(filename
);
3252 job
->state
= IPP_JSTATE_PENDING
;
3255 * Process the job...
3261 * Return the job info...
3264 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3266 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3267 cupsArrayAdd(ra
, "job-id");
3268 cupsArrayAdd(ra
, "job-state");
3269 cupsArrayAdd(ra
, "job-state-reasons");
3270 cupsArrayAdd(ra
, "job-uri");
3272 copy_job_attributes(client
, job
, ra
);
3273 cupsArrayDelete(ra
);
3278 * 'ipp_send_document()' - Add an attached document to a job object created with
3284 ippeve_client_t
*client
) /* I - Client */
3286 ippeve_job_t
*job
; /* Job information */
3287 char filename
[1024], /* Filename buffer */
3288 buffer
[4096]; /* Copy buffer */
3289 ssize_t bytes
; /* Bytes read */
3290 ipp_attribute_t
*attr
; /* Current attribute */
3291 cups_array_t
*ra
; /* Attributes to send in response */
3298 if ((job
= find_job(client
)) == NULL
)
3300 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3301 httpFlush(client
->http
);
3306 * See if we already have a document for this job or the job has already
3307 * in a non-pending state...
3310 if (job
->state
> IPP_JSTATE_HELD
)
3312 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3313 "Job is not in a pending state.");
3314 httpFlush(client
->http
);
3317 else if (job
->filename
|| job
->fd
>= 0)
3319 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3320 "Multiple document jobs are not supported.");
3321 httpFlush(client
->http
);
3325 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3326 IPP_TAG_ZERO
)) == NULL
)
3328 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3329 "Missing required last-document attribute.");
3330 httpFlush(client
->http
);
3333 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3334 !ippGetBoolean(attr
, 0))
3336 respond_unsupported(client
, attr
);
3337 httpFlush(client
->http
);
3342 * Validate document attributes...
3345 if (!valid_doc_attributes(client
))
3347 httpFlush(client
->http
);
3351 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3354 * Get the document format for the job...
3357 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3359 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3360 job
->format
= ippGetString(attr
, 0, NULL
);
3361 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3362 job
->format
= ippGetString(attr
, 0, NULL
);
3364 job
->format
= "application/octet-stream";
3367 * Create a file for the request data...
3370 if ((job
->fd
= create_job_file(client
->printer
, job
, filename
, sizeof(filename
), NULL
)) < 0)
3372 job
->state
= IPP_JSTATE_ABORTED
;
3374 _cupsRWUnlock(&(client
->printer
->rwlock
));
3376 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
3381 fprintf(stderr
, "Created job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3383 _cupsRWUnlock(&(client
->printer
->rwlock
));
3385 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3387 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3389 int error
= errno
; /* Write error */
3391 job
->state
= IPP_JSTATE_ABORTED
;
3398 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
3406 * Got an error while reading the print data, so abort this job.
3409 job
->state
= IPP_JSTATE_ABORTED
;
3416 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to read print file.");
3422 int error
= errno
; /* Write error */
3424 job
->state
= IPP_JSTATE_ABORTED
;
3429 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
3433 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3436 job
->filename
= strdup(filename
);
3437 job
->state
= IPP_JSTATE_PENDING
;
3439 _cupsRWUnlock(&(client
->printer
->rwlock
));
3442 * Process the job...
3448 * Return the job info...
3451 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3453 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3454 cupsArrayAdd(ra
, "job-id");
3455 cupsArrayAdd(ra
, "job-state");
3456 cupsArrayAdd(ra
, "job-state-reasons");
3457 cupsArrayAdd(ra
, "job-uri");
3459 copy_job_attributes(client
, job
, ra
);
3460 cupsArrayDelete(ra
);
3465 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3470 ipp_send_uri(ippeve_client_t
*client
) /* I - Client */
3472 ippeve_job_t
*job
; /* Job information */
3473 ipp_attribute_t
*uri
; /* document-uri */
3474 char scheme
[256], /* URI scheme */
3475 userpass
[256], /* Username and password info */
3476 hostname
[256], /* Hostname */
3477 resource
[1024]; /* Resource path */
3478 int port
; /* Port number */
3479 http_uri_status_t uri_status
; /* URI decode status */
3480 http_encryption_t encryption
; /* Encryption to use, if any */
3481 http_t
*http
; /* Connection for http/https URIs */
3482 http_status_t status
; /* Access status for http/https URIs */
3483 int infile
; /* Input file for local file URIs */
3484 char filename
[1024], /* Filename buffer */
3485 buffer
[4096]; /* Copy buffer */
3486 ssize_t bytes
; /* Bytes read */
3487 ipp_attribute_t
*attr
; /* Current attribute */
3488 cups_array_t
*ra
; /* Attributes to send in response */
3495 if ((job
= find_job(client
)) == NULL
)
3497 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3498 httpFlush(client
->http
);
3503 * See if we already have a document for this job or the job has already
3504 * in a non-pending state...
3507 if (job
->state
> IPP_JSTATE_HELD
)
3509 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3510 "Job is not in a pending state.");
3511 httpFlush(client
->http
);
3514 else if (job
->filename
|| job
->fd
>= 0)
3516 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3517 "Multiple document jobs are not supported.");
3518 httpFlush(client
->http
);
3522 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3523 IPP_TAG_ZERO
)) == NULL
)
3525 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3526 "Missing required last-document attribute.");
3527 httpFlush(client
->http
);
3530 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3531 !ippGetBoolean(attr
, 0))
3533 respond_unsupported(client
, attr
);
3534 httpFlush(client
->http
);
3539 * Validate document attributes...
3542 if (!valid_doc_attributes(client
))
3544 httpFlush(client
->http
);
3549 * Do we have a file to print?
3552 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3554 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3555 "Unexpected document data following request.");
3560 * Do we have a document URI?
3563 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3564 IPP_TAG_URI
)) == NULL
)
3566 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3570 if (ippGetCount(uri
) != 1)
3572 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Too many document-uri values.");
3576 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3577 scheme
, sizeof(scheme
), userpass
,
3578 sizeof(userpass
), hostname
, sizeof(hostname
),
3579 &port
, resource
, sizeof(resource
));
3580 if (uri_status
< HTTP_URI_STATUS_OK
)
3582 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s", httpURIStatusString(uri_status
));
3586 if (strcmp(scheme
, "file") &&
3588 strcmp(scheme
, "https") &&
3589 #endif /* HAVE_SSL */
3590 strcmp(scheme
, "http"))
3592 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
, "URI scheme \"%s\" not supported.", scheme
);
3596 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3598 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
3603 * Get the document format for the job...
3606 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3608 if ((attr
= ippFindAttribute(job
->attrs
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
3609 job
->format
= ippGetString(attr
, 0, NULL
);
3611 job
->format
= "application/octet-stream";
3614 * Create a file for the request data...
3617 if ((job
->fd
= create_job_file(client
->printer
, job
, filename
, sizeof(filename
), NULL
)) < 0)
3619 job
->state
= IPP_JSTATE_ABORTED
;
3621 _cupsRWUnlock(&(client
->printer
->rwlock
));
3623 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3624 "Unable to create print file: %s", strerror(errno
));
3628 _cupsRWUnlock(&(client
->printer
->rwlock
));
3630 if (!strcmp(scheme
, "file"))
3632 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3634 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
3640 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3641 (errno
== EAGAIN
|| errno
== EINTR
))
3643 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3645 int error
= errno
; /* Write error */
3647 job
->state
= IPP_JSTATE_ABORTED
;
3655 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3656 "Unable to write print file: %s", strerror(error
));
3667 if (port
== 443 || !strcmp(scheme
, "https"))
3668 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3670 #endif /* HAVE_SSL */
3671 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3673 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3674 1, 30000, NULL
)) == NULL
)
3676 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to connect to %s: %s", hostname
, cupsLastErrorString());
3677 job
->state
= IPP_JSTATE_ABORTED
;
3686 httpClearFields(http
);
3687 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3688 if (httpGet(http
, resource
))
3690 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3691 "Unable to GET URI: %s", strerror(errno
));
3693 job
->state
= IPP_JSTATE_ABORTED
;
3703 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3705 if (status
!= HTTP_STATUS_OK
)
3707 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3708 "Unable to GET URI: %s", httpStatus(status
));
3710 job
->state
= IPP_JSTATE_ABORTED
;
3720 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3722 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3724 int error
= errno
; /* Write error */
3726 job
->state
= IPP_JSTATE_ABORTED
;
3734 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3735 "Unable to write print file: %s", strerror(error
));
3745 int error
= errno
; /* Write error */
3747 job
->state
= IPP_JSTATE_ABORTED
;
3752 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3753 "Unable to write print file: %s", strerror(error
));
3757 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3760 job
->filename
= strdup(filename
);
3761 job
->state
= IPP_JSTATE_PENDING
;
3763 _cupsRWUnlock(&(client
->printer
->rwlock
));
3766 * Process the job...
3772 * Return the job info...
3775 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3777 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3778 cupsArrayAdd(ra
, "job-id");
3779 cupsArrayAdd(ra
, "job-state");
3780 cupsArrayAdd(ra
, "job-state-reasons");
3781 cupsArrayAdd(ra
, "job-uri");
3783 copy_job_attributes(client
, job
, ra
);
3784 cupsArrayDelete(ra
);
3789 * 'ipp_validate_job()' - Validate job creation attributes.
3793 ipp_validate_job(ippeve_client_t
*client
) /* I - Client */
3795 if (valid_job_attributes(client
))
3796 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3801 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
3804 static int /* O - 1 to use, 0 to ignore */
3806 _ipp_file_t
*f
, /* I - IPP file */
3807 void *user_data
, /* I - User data pointer (unused) */
3808 const char *attr
) /* I - Attribute name */
3810 int i
, /* Current element */
3811 result
; /* Result of comparison */
3812 static const char * const ignored
[] =
3813 { /* Ignored attributes */
3814 "attributes-charset",
3815 "attributes-natural-language",
3816 "charset-configured",
3817 "charset-supported",
3818 "device-service-count",
3820 "document-format-varying-attributes",
3821 "generated-natural-language-supported",
3822 "identify-actions-default",
3823 "identify-actions-supported",
3824 "ipp-features-supported",
3825 "ipp-versions-supproted",
3826 "ippget-event-life",
3827 "job-hold-until-supported",
3828 "job-hold-until-time-supported",
3829 "job-ids-supported",
3830 "job-k-octets-supported",
3831 "job-settable-attributes-supported",
3832 "multiple-document-jobs-supported",
3833 "multiple-operation-time-out",
3834 "multiple-operation-time-out-action",
3835 "natural-language-configured",
3836 "notify-attributes-supported",
3837 "notify-events-default",
3838 "notify-events-supported",
3839 "notify-lease-duration-default",
3840 "notify-lease-duration-supported",
3841 "notify-max-events-supported",
3842 "notify-pull-method-supported",
3843 "operations-supported",
3845 "printer-alert-description",
3846 "printer-camera-image-uri",
3847 "printer-charge-info",
3848 "printer-charge-info-uri",
3849 "printer-config-change-date-time",
3850 "printer-config-change-time",
3851 "printer-current-time",
3852 "printer-detailed-status-messages",
3853 "printer-dns-sd-name",
3854 "printer-fax-log-uri",
3855 "printer-get-attributes-supported",
3859 "printer-is-accepting-jobs",
3860 "printer-message-date-time",
3861 "printer-message-from-operator",
3862 "printer-message-time",
3863 "printer-more-info",
3864 "printer-service-type",
3865 "printer-settable-attributes-supported",
3867 "printer-state-message",
3868 "printer-state-reasons",
3869 "printer-static-resource-directory-uri",
3870 "printer-static-resource-k-octets-free",
3871 "printer-static-resource-k-octets-supported",
3872 "printer-strings-languages-supported",
3873 "printer-strings-uri",
3874 "printer-supply-info-uri",
3876 "printer-uri-supported",
3877 "printer-xri-supported",
3879 "reference-uri-scheme-supported",
3880 "uri-authentication-supported",
3881 "uri-security-supported",
3882 "which-jobs-supported",
3883 "xri-authentication-supported",
3884 "xri-security-supported",
3885 "xri-uri-scheme-supported"
3892 for (i
= 0, result
= 1; i
< (int)(sizeof(ignored
) / sizeof(ignored
[0])); i
++)
3894 if ((result
= strcmp(attr
, ignored
[i
])) <= 0)
3898 return (result
!= 0);
3903 * 'ippserver_error_cb()' - Log an error message.
3906 static int /* O - 1 to continue, 0 to stop */
3908 _ipp_file_t
*f
, /* I - IPP file data */
3909 void *user_data
, /* I - User data pointer (unused) */
3910 const char *error
) /* I - Error message */
3915 _cupsLangPrintf(stderr
, "%s\n", error
);
3922 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
3925 static int /* O - 1 to continue, 0 to stop */
3927 _ipp_file_t
*f
, /* I - IPP file data */
3928 _ipp_vars_t
*vars
, /* I - IPP variables */
3929 void *user_data
, /* I - User data pointer (unused) */
3930 const char *token
) /* I - Current token */
3938 * NULL token means do the initial setup - create an empty IPP message and
3942 f
->attrs
= ippNew();
3943 f
->group_tag
= IPP_TAG_PRINTER
;
3947 _cupsLangPrintf(stderr
, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token
, f
->linenum
, f
->filename
);
3955 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
3958 static ipp_t
* /* O - IPP attributes or `NULL` on error */
3959 load_ippserver_attributes(
3960 const char *servername
, /* I - Server name or `NULL` for default */
3961 int serverport
, /* I - Server port number */
3962 const char *filename
, /* I - ippserver attribute filename */
3963 cups_array_t
*docformats
) /* I - document-format-supported values */
3965 ipp_t
*attrs
; /* IPP attributes */
3966 _ipp_vars_t vars
; /* IPP variables */
3967 char temp
[256]; /* Temporary string */
3971 * Setup callbacks and variables for the printer configuration file...
3973 * The following additional variables are supported:
3975 * - SERVERNAME: The host name of the server.
3976 * - SERVERPORT: The default port of the server.
3979 _ippVarsInit(&vars
, (_ipp_fattr_cb_t
)ippserver_attr_cb
, (_ipp_ferror_cb_t
)ippserver_error_cb
, (_ipp_ftoken_cb_t
)ippserver_token_cb
);
3983 _ippVarsSet(&vars
, "SERVERNAME", servername
);
3987 httpGetHostname(NULL
, temp
, sizeof(temp
));
3988 _ippVarsSet(&vars
, "SERVERNAME", temp
);
3991 snprintf(temp
, sizeof(temp
), "%d", serverport
);
3992 _ippVarsSet(&vars
, "SERVERPORT", temp
);
3995 * Load attributes and values for the printer...
3998 attrs
= _ippFileParse(&vars
, filename
, NULL
);
4001 * Free memory and return...
4004 _ippVarsDeinit(&vars
);
4011 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
4015 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4016 load_legacy_attributes(
4017 const char *make
, /* I - Manufacturer name */
4018 const char *model
, /* I - Model name */
4019 int ppm
, /* I - pages-per-minute */
4020 int ppm_color
, /* I - pages-per-minute-color */
4021 int duplex
, /* I - Duplex support? */
4022 cups_array_t
*docformats
) /* I - document-format-supported values */
4024 int i
; /* Looping var */
4025 ipp_t
*attrs
, /* IPP attributes */
4026 *col
; /* Collection value */
4027 ipp_attribute_t
*attr
; /* Current attribute */
4028 char device_id
[1024],/* printer-device-id */
4029 *ptr
, /* Pointer into device ID */
4030 make_model
[128];/* printer-make-and-model */
4031 const char *format
, /* Current document format */
4032 *prefix
; /* Prefix for device ID */
4033 int num_media
; /* Number of media */
4034 const char * const *media
; /* List of media */
4035 int num_ready
; /* Number of loaded media */
4036 const char * const *ready
; /* List of loaded media */
4037 pwg_media_t
*pwg
; /* PWG media size information */
4038 static const char * const media_supported
[] =
4039 { /* media-supported values */
4040 "na_letter_8.5x11in", /* Letter */
4041 "na_legal_8.5x14in", /* Legal */
4042 "iso_a4_210x297mm", /* A4 */
4043 "na_number-10_4.125x9.5in", /* #10 Envelope */
4044 "iso_dl_110x220mm" /* DL Envelope */
4046 static const char * const media_supported_color
[] =
4047 { /* media-supported values */
4048 "na_letter_8.5x11in", /* Letter */
4049 "na_legal_8.5x14in", /* Legal */
4050 "iso_a4_210x297mm", /* A4 */
4051 "na_number-10_4.125x9.5in", /* #10 Envelope */
4052 "iso_dl_110x220mm", /* DL Envelope */
4053 "na_index-3x5_3x5in", /* Photo 3x5 */
4054 "oe_photo-l_3.5x5in", /* Photo L */
4055 "na_index-4x6_4x6in", /* Photo 4x6 */
4056 "iso_a6_105x148mm", /* A6 */
4057 "na_5x7_5x7in" /* Photo 5x7 aka 2L */
4058 "iso_a5_148x210mm", /* A5 */
4060 static const char * const media_ready
[] =
4061 { /* media-ready values */
4062 "na_letter_8.5x11in", /* Letter */
4063 "na_number-10_4.125x9.5in" /* #10 */
4065 static const char * const media_ready_color
[] =
4066 { /* media-ready values */
4067 "na_letter_8.5x11in", /* Letter */
4068 "na_index-4x6_4x6in" /* Photo 4x6 */
4070 static const char * const media_source_supported
[] =
4071 { /* media-source-supported values */
4075 "by-pass-tray" /* AKA multi-purpose tray */
4077 static const char * const media_source_supported_color
[] =
4078 { /* media-source-supported values */
4082 "by-pass-tray", /* AKA multi-purpose tray */
4085 static const char * const media_type_supported
[] =
4086 { /* media-type-supported values */
4093 "stationery-letterhead",
4096 static const char * const media_type_supported_color
[] =
4097 { /* media-type-supported values */
4104 "stationery-letterhead",
4106 "photographic-glossy",
4107 "photographic-high-gloss",
4108 "photographic-matte",
4109 "photographic-satin",
4110 "photographic-semi-gloss"
4112 static const int media_bottom_margin_supported
[] =
4113 { /* media-bottom-margin-supported values */
4116 static const int media_bottom_margin_supported_color
[] =
4117 { /* media-bottom/top-margin-supported values */
4119 1168 /* 0.46" (common HP inkjet bottom margin) */
4121 static const int media_lr_margin_supported
[] =
4122 { /* media-left/right-margin-supported values */
4123 340, /* 3.4mm (historical HP PCL A4 margin) */
4126 static const int media_lr_margin_supported_color
[] =
4127 { /* media-left/right-margin-supported values */
4129 340, /* 3.4mm (historical HP PCL A4 margin) */
4132 static const int media_top_margin_supported
[] =
4133 { /* media-top-margin-supported values */
4136 static const int media_top_margin_supported_color
[] =
4137 { /* media-top/top-margin-supported values */
4139 102 /* 0.04" (common HP inkjet top margin */
4141 static const int orientation_requested_supported
[4] =
4142 { /* orientation-requested-supported values */
4143 IPP_ORIENT_PORTRAIT
,
4144 IPP_ORIENT_LANDSCAPE
,
4145 IPP_ORIENT_REVERSE_LANDSCAPE
,
4146 IPP_ORIENT_REVERSE_PORTRAIT
4148 static const char * const print_color_mode_supported
[] =
4149 { /* print-color-mode-supported values */
4152 static const char * const print_color_mode_supported_color
[] =
4153 { /* print-color-mode-supported values */
4158 static const int print_quality_supported
[] =
4159 { /* print-quality-supported values */
4164 static const char * const printer_supply
[] =
4165 { /* printer-supply values */
4166 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4167 "maxcapacity=100;level=25;colorantname=unknown;",
4168 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4169 "maxcapacity=100;level=75;colorantname=black;"
4171 static const char * const printer_supply_color
[] =
4172 { /* printer-supply values */
4173 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4174 "maxcapacity=100;level=25;colorantname=unknown;",
4175 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4176 "maxcapacity=100;level=75;colorantname=black;",
4177 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4178 "maxcapacity=100;level=50;colorantname=cyan;",
4179 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4180 "maxcapacity=100;level=33;colorantname=magenta;",
4181 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4182 "maxcapacity=100;level=67;colorantname=yellow;"
4184 static const char * const printer_supply_description
[] =
4185 { /* printer-supply-description values */
4189 static const char * const printer_supply_description_color
[] =
4190 { /* printer-supply-description values */
4197 static const int pwg_raster_document_resolution_supported
[] =
4202 static const char * const pwg_raster_document_type_supported
[] =
4207 static const char * const pwg_raster_document_type_supported_color
[] =
4214 static const char * const sides_supported
[] =
4215 { /* sides-supported values */
4217 "two-sided-long-edge",
4218 "two-sided-short-edge"
4220 static const char * const urf_supported
[] =
4221 { /* urf-supported values */
4229 static const char * const urf_supported_color
[] =
4230 { /* urf-supported values */
4233 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4239 static const char * const urf_supported_color_duplex
[] =
4240 { /* urf-supported values */
4243 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4250 static const char * const urf_supported_duplex
[] =
4251 { /* urf-supported values */
4266 num_media
= (int)(sizeof(media_supported_color
) / sizeof(media_supported_color
[0]));
4267 media
= media_supported_color
;
4268 num_ready
= (int)(sizeof(media_ready_color
) / sizeof(media_ready_color
[0]));
4269 ready
= media_ready_color
;
4273 num_media
= (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
4274 media
= media_supported
;
4275 num_ready
= (int)(sizeof(media_ready
) / sizeof(media_ready
[0]));
4276 ready
= media_ready
;
4279 /* color-supported */
4280 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
4282 /* copies-default */
4283 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
4285 /* copies-supported */
4286 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, (cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
|| cupsArrayFind(docformats
, (void *)"image/jpeg") != NULL
) ? 999 : 1);
4288 /* finishings-default */
4289 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
4291 /* finishings-supported */
4292 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
4294 /* media-bottom-margin-supported */
4296 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
);
4298 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
);
4300 /* media-col-database and media-col-default */
4301 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", num_media
, NULL
);
4302 for (i
= 0; i
< num_media
; i
++)
4304 int bottom
, left
, /* media-xxx-margins */
4306 const char *source
; /* media-source, if any */
4308 pwg
= pwgMediaForPWG(media
[i
]);
4310 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4312 source
= "photo"; /* Photo size media from photo tray */
4313 bottom
= /* Borderless margins */
4318 else if (pwg
->width
< 21000)
4320 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4321 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4322 left
= /* Left/right margins are standard */
4323 right
= media_lr_margin_supported
[1];
4324 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4326 else if (pwg
->width
== 21000)
4328 source
= NULL
; /* A4 from any tray */
4329 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4330 left
= /* Left/right margins are reduced */
4331 right
= media_lr_margin_supported
[0];
4332 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4336 source
= NULL
; /* Other size media from any tray */
4337 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4338 left
= /* Left/right margins are standard */
4339 right
= media_lr_margin_supported
[1];
4340 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4343 col
= create_media_col(media
[i
], source
, NULL
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4344 ippSetCollection(attrs
, &attr
, i
, col
);
4348 /* media-col-default */
4349 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default", col
);
4355 /* media-col-ready */
4356 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", num_ready
, NULL
);
4357 for (i
= 0; i
< num_ready
; i
++)
4359 int bottom
, left
, /* media-xxx-margins */
4361 const char *source
, /* media-source */
4362 *type
; /* media-type */
4364 pwg
= pwgMediaForPWG(ready
[i
]);
4366 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4368 source
= "photo"; /* Photo size media from photo tray */
4369 type
= "photographic-glossy"; /* Glossy photo paper */
4370 bottom
= /* Borderless margins */
4375 else if (pwg
->width
< 21000)
4377 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4378 type
= "envelope"; /* Envelope */
4379 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4380 left
= /* Left/right margins are standard */
4381 right
= media_lr_margin_supported
[1];
4382 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4384 else if (pwg
->width
== 21000)
4386 source
= "main"; /* A4 from main tray */
4387 type
= "stationery"; /* Plain paper */
4388 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4389 left
= /* Left/right margins are reduced */
4390 right
= media_lr_margin_supported
[0];
4391 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4395 source
= "main"; /* A4 from main tray */
4396 type
= "stationery"; /* Plain paper */
4397 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4398 left
= /* Left/right margins are standard */
4399 right
= media_lr_margin_supported
[1];
4400 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4403 col
= create_media_col(media
[i
], source
, NULL
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4404 ippSetCollection(attrs
, &attr
, i
, col
);
4409 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media
[0]);
4411 /* media-left/right-margin-supported */
4414 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
);
4415 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
);
4419 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
);
4420 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
);
4424 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", num_ready
, NULL
, ready
);
4426 /* media-supported */
4427 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-supported", num_media
, NULL
, media
);
4429 /* media-size-supported */
4430 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", num_media
, NULL
);
4431 for (i
= 0; i
< num_media
; i
++)
4433 pwg
= pwgMediaForPWG(media
[i
]);
4434 col
= create_media_size(pwg
->width
, pwg
->length
);
4436 ippSetCollection(attrs
, &attr
, i
, col
);
4440 /* media-source-supported */
4442 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
);
4444 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
);
4446 /* media-top-margin-supported */
4448 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
);
4450 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
);
4452 /* media-type-supported */
4454 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
);
4456 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
);
4458 /* orientation-requested-supported */
4459 if (cupsArrayFind(docformats
, (void *)"application/pdf") || cupsArrayFind(docformats
, (void *)"image/jpeg"))
4460 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported
) / sizeof(orientation_requested_supported
[0])), orientation_requested_supported
);
4462 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", IPP_ORIENT_PORTRAIT
);
4464 /* output-bin-default */
4466 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-up");
4468 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
4470 /* output-bin-supported */
4472 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-up");
4474 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
4476 /* page-ranges-supported */
4477 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
);
4479 /* pages-per-minute */
4480 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute", ppm
);
4482 /* pages-per-minute-color */
4484 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute-color", ppm_color
);
4486 /* print-color-mode-default */
4487 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
4489 /* print-color-mode-supported */
4491 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
);
4493 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
);
4495 /* print-content-optimize-default */
4496 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
4498 /* print-content-optimize-supported */
4499 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
4501 /* print-quality-default */
4502 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
4504 /* print-quality-supported */
4505 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
4507 /* print-rendering-intent-default */
4508 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
4510 /* print-rendering-intent-supported */
4511 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
4513 /* printer-device-id */
4514 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
4515 ptr
= device_id
+ strlen(device_id
);
4517 for (format
= (const char *)cupsArrayFirst(docformats
); format
; format
= (const char *)cupsArrayNext(docformats
))
4519 if (!strcasecmp(format
, "application/pdf"))
4520 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
4521 else if (!strcasecmp(format
, "application/postscript"))
4522 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
4523 else if (!strcasecmp(format
, "application/vnd.hp-PCL"))
4524 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
4525 else if (!strcasecmp(format
, "image/jpeg"))
4526 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
4527 else if (!strcasecmp(format
, "image/png"))
4528 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
4529 else if (!strcasecmp(format
, "image/pwg-raster"))
4530 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPWG", prefix
);
4531 else if (!strcasecmp(format
, "image/urf"))
4532 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sURF", prefix
);
4539 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
4544 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
4546 /* printer-make-and-model */
4547 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4548 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, make_model
);
4550 /* printer-resolution-default */
4551 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
4553 /* printer-resolution-supported */
4554 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
4556 /* printer-supply and printer-supply-description */
4559 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply_color
[0], strlen(printer_supply_color
[0]));
4560 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
4561 ippSetOctetString(attrs
, &attr
, i
, printer_supply_color
[i
], strlen(printer_supply_color
[i
]));
4563 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
);
4567 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply
[0], strlen(printer_supply
[0]));
4568 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
4569 ippSetOctetString(attrs
, &attr
, i
, printer_supply
[i
], strlen(printer_supply
[i
]));
4571 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
);
4574 /* pwg-raster-document-xxx-supported */
4575 if (cupsArrayFind(docformats
, (void *)"image/pwg-raster"))
4577 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
);
4579 if (ppm_color
> 0 && duplex
)
4580 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "rotated");
4582 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "normal");
4585 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
);
4587 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
);
4591 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
4593 /* sides-supported */
4595 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", (int)(sizeof(sides_supported
) / sizeof(sides_supported
[0])), NULL
, sides_supported
);
4597 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", NULL
, "one-sided");
4600 if (cupsArrayFind(docformats
, (void *)"image/urf"))
4605 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
);
4607 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_color
) / sizeof(urf_supported_color
[0])), NULL
, urf_supported_color
);
4611 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_duplex
) / sizeof(urf_supported_duplex
[0])), NULL
, urf_supported_duplex
);
4615 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])), NULL
, urf_supported
);
4624 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4627 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4628 load_ppd_attributes(
4629 const char *ppdfile
, /* I - PPD filename */
4630 cups_array_t
*docformats
) /* I - document-format-supported values */
4636 static const char * const overrides
[] =
4637 { /* overrides-supported */
4642 /* document-password-supported */
4643 if (!ippFindAttribute(printer
->attrs
, "document-password-supported", IPP_TAG_ZERO
))
4644 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
4646 /* overrides-supported */
4647 if (!ippFindAttribute(printer
->attrs
, "overrides-supported", IPP_TAG_ZERO
))
4648 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
4650 /* page-ranges-supported */
4651 if (!ippFindAttribute(printer
->attrs
, "page-ranges-supported", IPP_TAG_ZERO
))
4652 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
4653 /* pages-per-minute */
4654 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
4655 "pages-per-minute", ppm
);
4657 /* pages-per-minute-color */
4659 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
4660 "pages-per-minute-color", ppm_color
);
4668 * 'parse_options()' - Parse URL options into CUPS options.
4670 * The client->options string is destroyed by this function.
4673 static int /* O - Number of options */
4674 parse_options(ippeve_client_t
*client
, /* I - Client */
4675 cups_option_t
**options
)/* O - Options */
4677 char *name
, /* Name */
4679 *next
; /* Next name=value pair */
4680 int num_options
= 0; /* Number of options */
4685 for (name
= client
->options
; name
&& *name
; name
= next
)
4687 if ((value
= strchr(name
, '=')) == NULL
)
4691 if ((next
= strchr(value
, '&')) != NULL
)
4694 num_options
= cupsAddOption(name
, value
, num_options
, options
);
4697 return (num_options
);
4702 * 'process_attr_message()' - Process an ATTR: message from a command.
4706 process_attr_message(
4707 ippeve_job_t
*job
, /* I - Job */
4708 char *message
) /* I - Message */
4716 * 'process_client()' - Process client requests on a thread.
4719 static void * /* O - Exit status */
4720 process_client(ippeve_client_t
*client
) /* I - Client */
4723 * Loop until we are out of requests or timeout (30 seconds)...
4727 int first_time
= 1; /* First time request? */
4728 #endif /* HAVE_SSL */
4730 while (httpWait(client
->http
, 30000))
4736 * See if we need to negotiate a TLS connection...
4739 char buf
[1]; /* First byte from client */
4741 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
4743 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
4745 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
4747 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4751 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4756 #endif /* HAVE_SSL */
4758 if (!process_http(client
))
4763 * Close the conection to the client and return...
4766 delete_client(client
);
4773 * 'process_http()' - Process a HTTP request.
4776 int /* O - 1 on success, 0 on failure */
4777 process_http(ippeve_client_t
*client
) /* I - Client connection */
4779 char uri
[1024]; /* URI */
4780 http_state_t http_state
; /* HTTP state */
4781 http_status_t http_status
; /* HTTP status */
4782 ipp_state_t ipp_state
; /* State of IPP transfer */
4783 char scheme
[32], /* Method/scheme */
4784 userpass
[128], /* Username:password */
4785 hostname
[HTTP_MAX_HOST
];
4787 int port
; /* Port number */
4788 const char *encoding
; /* Content-Encoding value */
4789 static const char * const http_states
[] =
4790 { /* Strings for logging HTTP method */
4811 * Clear state variables...
4814 ippDelete(client
->request
);
4815 ippDelete(client
->response
);
4817 client
->request
= NULL
;
4818 client
->response
= NULL
;
4819 client
->operation
= HTTP_STATE_WAITING
;
4822 * Read a request from the connection...
4825 while ((http_state
= httpReadRequest(client
->http
, uri
,
4826 sizeof(uri
))) == HTTP_STATE_WAITING
)
4830 * Parse the request line...
4833 if (http_state
== HTTP_STATE_ERROR
)
4835 if (httpError(client
->http
) == EPIPE
)
4836 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4838 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4839 strerror(httpError(client
->http
)));
4843 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4845 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4846 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4849 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4851 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4852 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4856 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4860 * Separate the URI into its components...
4863 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4864 userpass
, sizeof(userpass
),
4865 hostname
, sizeof(hostname
), &port
,
4866 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
4867 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
4869 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4870 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4874 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
4875 *(client
->options
)++ = '\0';
4878 * Process the request...
4881 client
->start
= time(NULL
);
4882 client
->operation
= httpGetState(client
->http
);
4885 * Parse incoming parameters until the status changes...
4888 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4890 if (http_status
!= HTTP_STATUS_OK
)
4892 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4896 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4897 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4900 * HTTP/1.1 and higher require the "Host:" field...
4903 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4908 * Handle HTTP Upgrade...
4911 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4915 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
4917 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
4920 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
4922 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
4924 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
4928 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
4931 #endif /* HAVE_SSL */
4933 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4938 * Handle HTTP Expect...
4941 if (httpGetExpect(client
->http
) &&
4942 (client
->operation
== HTTP_STATE_POST
||
4943 client
->operation
== HTTP_STATE_PUT
))
4945 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4948 * Send 100-continue header...
4951 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4957 * Send 417-expectation-failed header...
4960 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4966 * Handle new transfers...
4969 encoding
= httpGetContentEncoding(client
->http
);
4971 switch (client
->operation
)
4973 case HTTP_STATE_OPTIONS
:
4975 * Do OPTIONS command...
4978 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4980 case HTTP_STATE_HEAD
:
4981 if (!strcmp(client
->uri
, "/icon.png"))
4982 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4983 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
4984 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4986 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4988 case HTTP_STATE_GET
:
4989 if (!strcmp(client
->uri
, "/icon.png"))
4992 * Send PNG icon file.
4995 int fd
; /* Icon file */
4996 struct stat fileinfo
; /* Icon file information */
4997 char buffer
[4096]; /* Copy buffer */
4998 ssize_t bytes
; /* Bytes */
5000 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5002 if (!stat(client
->printer
->icon
, &fileinfo
) &&
5003 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5005 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
5006 (size_t)fileinfo
.st_size
))
5012 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5013 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5015 httpFlushWrite(client
->http
);
5020 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5022 else if (!strcmp(client
->uri
, "/"))
5025 * Show web status page...
5028 ippeve_job_t
*job
; /* Current job */
5029 int i
; /* Looping var */
5030 ippeve_preason_t reason
; /* Current reason */
5031 static const char * const reasons
[] =
5032 { /* Reason strings */
5035 "Input Tray Missing",
5036 "Marker Supply Empty",
5037 "Marker Supply Low",
5038 "Marker Waste Almost Full",
5039 "Marker Waste Full",
5051 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5054 html_header(client
, client
->printer
->name
);
5056 "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION
")</b></p>\n"
5057 "<p>%s, %d job(s).", client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" : client
->printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(client
->printer
->jobs
));
5058 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
5059 if (client
->printer
->state_reasons
& reason
)
5060 html_printf(client
, "\n<br> %s", reasons
[i
]);
5061 html_printf(client
, "</p>\n");
5063 if (cupsArrayCount(client
->printer
->jobs
) > 0)
5065 _cupsRWLockRead(&(client
->printer
->rwlock
));
5067 html_printf(client
, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>When</th></tr></thead><tbody>\n");
5068 for (job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
); job
; job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
5070 char when
[256], /* When job queued/started/finished */
5071 hhmmss
[64]; /* Time HH:MM:SS */
5075 case IPP_JSTATE_PENDING
:
5076 case IPP_JSTATE_HELD
:
5077 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
5079 case IPP_JSTATE_PROCESSING
:
5080 case IPP_JSTATE_STOPPED
:
5081 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
5083 case IPP_JSTATE_ABORTED
:
5084 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5086 case IPP_JSTATE_CANCELED
:
5087 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5089 case IPP_JSTATE_COMPLETED
:
5090 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
5094 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
);
5096 html_printf(client
, "</tbody></table>\n");
5098 _cupsRWUnlock(&(client
->printer
->rwlock
));
5100 html_footer(client
);
5104 else if (!strcmp(client
->uri
, "/media"))
5107 * Show web media page...
5110 int num_options
; /* Number of form options */
5111 cups_option_t
*options
; /* Form options */
5113 /* TODO: Update me */
5114 int i
, /* Looping var */
5115 static const char * const sizes
[] =
5116 { /* Size strings */
5129 static const char * const types
[] =
5146 static const int sheets
[] = /* Number of sheets */
5156 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5159 html_header(client
, client
->printer
->name
);
5161 if ((num_options
= parse_options(client
, &options
)) > 0)
5164 * WARNING: A real printer/server implementation MUST NOT implement
5165 * media updates via a GET request - GET requests are supposed to be
5166 * idempotent (without side-effects) and we obviously are not
5167 * authenticating access here. This form is provided solely to
5168 * enable testing and development!
5172 /* TODO: UPDATE ME */
5173 const char *val
; /* Form value */
5175 if ((val
= cupsGetOption("main_size", num_options
, options
)) != NULL
)
5176 client
->printer
->main_size
= atoi(val
);
5177 if ((val
= cupsGetOption("main_type", num_options
, options
)) != NULL
)
5178 client
->printer
->main_type
= atoi(val
);
5179 if ((val
= cupsGetOption("main_level", num_options
, options
)) != NULL
)
5180 client
->printer
->main_level
= atoi(val
);
5182 if ((val
= cupsGetOption("envelope_size", num_options
, options
)) != NULL
)
5183 client
->printer
->envelope_size
= atoi(val
);
5184 if ((val
= cupsGetOption("envelope_level", num_options
, options
)) != NULL
)
5185 client
->printer
->envelope_level
= atoi(val
);
5187 if ((val
= cupsGetOption("photo_size", num_options
, options
)) != NULL
)
5188 client
->printer
->photo_size
= atoi(val
);
5189 if ((val
= cupsGetOption("photo_type", num_options
, options
)) != NULL
)
5190 client
->printer
->photo_type
= atoi(val
);
5191 if ((val
= cupsGetOption("photo_level", num_options
, options
)) != NULL
)
5192 client
->printer
->photo_level
= atoi(val
);
5194 if ((client
->printer
->main_level
< 100 && client
->printer
->main_level
> 0) || (client
->printer
->envelope_level
< 25 && client
->printer
->envelope_level
> 0) || (client
->printer
->photo_level
< 25 && client
->printer
->photo_level
> 0))
5195 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_LOW
;
5197 client
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_LOW
;
5199 if ((client
->printer
->main_level
== 0 && client
->printer
->main_size
> IPPEVE_MEDIA_SIZE_NONE
) || (client
->printer
->envelope_level
== 0 && client
->printer
->envelope_size
> IPPEVE_MEDIA_SIZE_NONE
) || (client
->printer
->photo_level
== 0 && client
->printer
->photo_size
> IPPEVE_MEDIA_SIZE_NONE
))
5201 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_EMPTY
;
5202 if (client
->printer
->active_job
)
5203 client
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
5206 client
->printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MEDIA_EMPTY
| IPPEVE_PREASON_MEDIA_NEEDED
);
5209 html_printf(client
, "<blockquote>Media updated.</blockquote>\n");
5212 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
5215 /* TODO: UPDATE ME */
5216 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
5217 html_printf(client
, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
5218 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5219 if (!strstr(sizes
[i
], "Envelope") && !strstr(sizes
[i
], "Photo"))
5220 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_size
? " selected" : "", sizes
[i
]);
5221 html_printf(client
, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
5222 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5223 if (!strstr(types
[i
], "Photo"))
5224 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->main_type
? " selected" : "", types
[i
]);
5225 html_printf(client
, "</select> <select name=\"main_level\">");
5226 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5227 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->main_level
? " selected" : "", sheets
[i
]);
5228 html_printf(client
, "</select></td></tr>\n");
5231 "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
5232 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5233 if (strstr(sizes
[i
], "Envelope"))
5234 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->envelope_size
? " selected" : "", sizes
[i
]);
5235 html_printf(client
, "</select> <select name=\"envelope_level\">");
5236 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5237 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->envelope_level
? " selected" : "", sheets
[i
]);
5238 html_printf(client
, "</select></td></tr>\n");
5241 "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
5242 for (i
= 0; i
< (int)(sizeof(sizes
) / sizeof(sizes
[0])); i
++)
5243 if (strstr(sizes
[i
], "Photo"))
5244 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_size
? " selected" : "", sizes
[i
]);
5245 html_printf(client
, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
5246 for (i
= 0; i
< (int)(sizeof(types
) / sizeof(types
[0])); i
++)
5247 if (strstr(types
[i
], "Photo"))
5248 html_printf(client
, "<option value=\"%d\"%s>%s</option>", i
, i
== client
->printer
->photo_type
? " selected" : "", types
[i
]);
5249 html_printf(client
, "</select> <select name=\"photo_level\">");
5250 for (i
= 0; i
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); i
++)
5251 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[i
], sheets
[i
] == client
->printer
->photo_level
? " selected" : "", sheets
[i
]);
5252 html_printf(client
, "</select></td></tr>\n");
5255 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
5256 html_footer(client
);
5260 else if (!strcmp(client
->uri
, "/supplies"))
5263 * Show web supplies page...
5266 int num_options
; /* Number of form options */
5267 cups_option_t
*options
; /* Form options */
5269 /* TODO: UPDATE ME */
5270 int i
, j
; /* Looping vars */
5271 static const int levels
[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
5274 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
5277 html_header(client
, client
->printer
->name
);
5279 if ((num_options
= parse_options(client
, &options
)) > 0)
5282 * WARNING: A real printer/server implementation MUST NOT implement
5283 * supply updates via a GET request - GET requests are supposed to be
5284 * idempotent (without side-effects) and we obviously are not
5285 * authenticating access here. This form is provided solely to
5286 * enable testing and development!
5289 // char name[64]; /* Form field */
5290 // const char *val; /* Form value */
5292 client
->printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
| IPPEVE_PREASON_MARKER_SUPPLY_LOW
| IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
| IPPEVE_PREASON_MARKER_WASTE_FULL
| IPPEVE_PREASON_TONER_EMPTY
| IPPEVE_PREASON_TONER_LOW
);
5295 /* TODO: UPDATE ME */
5296 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5298 snprintf(name
, sizeof(name
), "supply_%d", i
);
5299 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
5301 int level
= client
->printer
->supplies
[i
] = atoi(val
);
5307 client
->printer
->state_reasons
|= IPPEVE_PREASON_TONER_EMPTY
;
5308 else if (level
< 10)
5309 client
->printer
->state_reasons
|= IPPEVE_PREASON_TONER_LOW
;
5314 client
->printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_FULL
;
5315 else if (level
> 90)
5316 client
->printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
;
5322 html_printf(client
, "<blockquote>Supplies updated.</blockquote>\n");
5325 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
5327 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
5329 /* TODO: UPDATE ME */
5330 for (i
= 0; i
< (int)(sizeof(printer_supplies
) / sizeof(printer_supplies
[0])); i
++)
5332 html_printf(client
, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies
[i
], i
);
5333 for (j
= 0; j
< (int)(sizeof(levels
) / sizeof(levels
[0])); j
++)
5334 html_printf(client
, "<option value=\"%d\"%s>%d%%</option>", levels
[j
], levels
[j
] == client
->printer
->supplies
[i
] ? " selected" : "", levels
[j
]);
5335 html_printf(client
, "</select></td></tr>\n");
5338 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
5339 html_footer(client
);
5344 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5347 case HTTP_STATE_POST
:
5348 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5352 * Not an IPP request...
5355 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5359 * Read the IPP request...
5362 client
->request
= ippNew();
5364 while ((ipp_state
= ippRead(client
->http
,
5365 client
->request
)) != IPP_STATE_DATA
)
5367 if (ipp_state
== IPP_STATE_ERROR
)
5369 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
5370 cupsLastErrorString());
5371 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5377 * Now that we have the IPP request, process the request...
5380 return (process_ipp(client
));
5383 break; /* Anti-compiler-warning-code */
5391 * 'process_ipp()' - Process an IPP request.
5394 static int /* O - 1 on success, 0 on error */
5395 process_ipp(ippeve_client_t
*client
) /* I - Client */
5397 ipp_tag_t group
; /* Current group tag */
5398 ipp_attribute_t
*attr
; /* Current attribute */
5399 ipp_attribute_t
*charset
; /* Character set attribute */
5400 ipp_attribute_t
*language
; /* Language attribute */
5401 ipp_attribute_t
*uri
; /* Printer URI attribute */
5402 int major
, minor
; /* Version number */
5403 const char *name
; /* Name of attribute */
5406 debug_attributes("Request", client
->request
, 1);
5409 * First build an empty response message for this request...
5412 client
->operation_id
= ippGetOperation(client
->request
);
5413 client
->response
= ippNewResponse(client
->request
);
5416 * Then validate the request header and required attributes...
5419 major
= ippGetVersion(client
->request
, &minor
);
5421 if (major
< 1 || major
> 2)
5424 * Return an error, since we only support IPP 1.x and 2.x.
5427 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
5429 else if ((major
* 10 + minor
) > MaxVersion
)
5431 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5432 httpFlush(client
->http
); /* Flush trailing (junk) data */
5434 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5437 else if (ippGetRequestId(client
->request
) <= 0)
5439 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
5441 else if (!ippFirstAttribute(client
->request
))
5443 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
5448 * Make sure that the attributes are provided in the correct order and
5449 * don't repeat groups...
5452 for (attr
= ippFirstAttribute(client
->request
),
5453 group
= ippGetGroupTag(attr
);
5455 attr
= ippNextAttribute(client
->request
))
5457 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5460 * Out of order; return an error...
5463 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5464 "Attribute groups are out of order (%x < %x).",
5465 ippGetGroupTag(attr
), group
);
5469 group
= ippGetGroupTag(attr
);
5475 * Then make sure that the first three attributes are:
5477 * attributes-charset
5478 * attributes-natural-language
5479 * printer-uri/job-uri
5482 attr
= ippFirstAttribute(client
->request
);
5483 name
= ippGetName(attr
);
5484 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
5485 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
5490 attr
= ippNextAttribute(client
->request
);
5491 name
= ippGetName(attr
);
5493 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
5494 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
5499 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
5500 IPP_TAG_URI
)) != NULL
)
5502 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
5503 IPP_TAG_URI
)) != NULL
)
5509 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
5510 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
5513 * Bad character set...
5516 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5517 "Unsupported character set \"%s\".",
5518 ippGetString(charset
, 0, NULL
));
5520 else if (!charset
|| !language
|| !uri
)
5523 * Return an error, since attributes-charset,
5524 * attributes-natural-language, and printer-uri/job-uri are required
5525 * for all operations.
5528 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5529 "Missing required attributes.");
5533 char scheme
[32], /* URI scheme */
5534 userpass
[32], /* Username/password in URI */
5535 host
[256], /* Host name in URI */
5536 resource
[256]; /* Resource path in URI */
5537 int port
; /* Port number in URI */
5539 name
= ippGetName(uri
);
5541 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
5542 scheme
, sizeof(scheme
),
5543 userpass
, sizeof(userpass
),
5544 host
, sizeof(host
), &port
,
5545 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
5546 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
5547 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
5548 else if ((!strcmp(name
, "job-uri") &&
5549 strncmp(resource
, "/ipp/print/", 11)) ||
5550 (!strcmp(name
, "printer-uri") &&
5551 strcmp(resource
, "/ipp/print")))
5552 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
5553 name
, ippGetString(uri
, 0, NULL
));
5557 * Try processing the operation...
5560 switch (ippGetOperation(client
->request
))
5562 case IPP_OP_PRINT_JOB
:
5563 ipp_print_job(client
);
5566 case IPP_OP_PRINT_URI
:
5567 ipp_print_uri(client
);
5570 case IPP_OP_VALIDATE_JOB
:
5571 ipp_validate_job(client
);
5574 case IPP_OP_CREATE_JOB
:
5575 ipp_create_job(client
);
5578 case IPP_OP_SEND_DOCUMENT
:
5579 ipp_send_document(client
);
5582 case IPP_OP_SEND_URI
:
5583 ipp_send_uri(client
);
5586 case IPP_OP_CANCEL_JOB
:
5587 ipp_cancel_job(client
);
5590 case IPP_OP_GET_JOB_ATTRIBUTES
:
5591 ipp_get_job_attributes(client
);
5594 case IPP_OP_GET_JOBS
:
5595 ipp_get_jobs(client
);
5598 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
5599 ipp_get_printer_attributes(client
);
5602 case IPP_OP_CLOSE_JOB
:
5603 ipp_close_job(client
);
5606 case IPP_OP_IDENTIFY_PRINTER
:
5607 ipp_identify_printer(client
);
5611 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
5612 "Operation not supported.");
5621 * Send the HTTP header and return...
5624 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5625 httpFlush(client
->http
); /* Flush trailing (junk) data */
5627 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
5628 ippLength(client
->response
)));
5633 * 'process_job()' - Process a print job.
5636 static void * /* O - Thread exit status */
5637 process_job(ippeve_job_t
*job
) /* I - Job */
5639 job
->state
= IPP_JSTATE_PROCESSING
;
5640 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
5641 job
->processing
= time(NULL
);
5643 while (job
->printer
->state_reasons
& IPPEVE_PREASON_MEDIA_EMPTY
)
5645 job
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
5650 job
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_NEEDED
;
5652 if (job
->printer
->command
)
5655 * Execute a command with the job spool file and wait for it to complete...
5658 int pid
, /* Process ID */
5659 status
; /* Exit status */
5660 time_t start
, /* Start time */
5662 char *myargv
[3], /* Command-line arguments */
5663 *myenvp
[400]; /* Environment variables */
5664 int myenvc
; /* Number of environment variables */
5665 ipp_attribute_t
*attr
; /* Job attribute */
5666 char val
[1280], /* IPP_NAME=value */
5667 *valptr
; /* Pointer into string */
5669 int mypipe
[2]; /* Pipe for stderr */
5670 char line
[2048], /* Line from stderr */
5671 *ptr
, /* Pointer into line */
5672 *endptr
; /* End of line */
5673 ssize_t bytes
; /* Bytes read */
5674 #endif /* !_WIN32 */
5676 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
, job
->filename
);
5680 * Setup the command-line arguments...
5683 myargv
[0] = job
->printer
->command
;
5684 myargv
[1] = job
->filename
;
5688 * Copy the current environment, then add environment variables for every
5689 * Job attribute and Printer -default attributes...
5692 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
5693 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
5695 if (myenvc
> (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 32))
5697 fprintf(stderr
, "Too many environment variables to process job #%d.\n", job
->id
);
5698 job
->state
= IPP_JSTATE_ABORTED
;
5702 if (asprintf(myenvp
+ myenvc
, "CONTENT_TYPE=%s", job
->format
) > 0)
5705 if (job
->printer
->device_uri
&& asprintf(myenvp
+ myenvc
, "DEVICE_URI=%s", job
->printer
->device_uri
) > 0)
5708 if (job
->printer
->ppdfile
&& asprintf(myenvp
+ myenvc
, "PPD=%s", job
->printer
->ppdfile
) > 0)
5711 if ((attr
= ippFindAttribute(job
->attrs
, "document-name", IPP_TAG_NAME
)) != NULL
&& asprintf(myenvp
+ myenvc
, "DOCUMENT_NAME=%s", ippGetString(attr
, 0, NULL
)) > 0)
5714 for (attr
= ippFirstAttribute(job
->printer
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->printer
->attrs
))
5717 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
5718 * then add the value(s) from the attribute.
5721 const char *name
= ippGetName(attr
),
5722 /* Attribute name */
5723 *suffix
= strstr(name
, "-default");
5724 /* Suffix on attribute name */
5726 if (!suffix
|| suffix
[8])
5734 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5739 *valptr
++ = (char)toupper(*name
& 255);
5744 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5746 myenvp
[myenvc
++] = strdup(val
);
5749 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
5752 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
5753 * value(s) from the attribute.
5756 const char *name
= ippGetName(attr
);
5757 /* Attribute name */
5767 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
5772 *valptr
++ = (char)toupper(*name
& 255);
5777 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
5779 myenvp
[myenvc
++] = strdup(val
);
5784 fprintf(stderr
, "Too many environment variables to process job #%d.\n", job
->id
);
5785 job
->state
= IPP_JSTATE_ABORTED
;
5789 myenvp
[myenvc
] = NULL
;
5792 * Now run the program...
5796 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
5801 perror("Unable to create pipe for stderr");
5802 mypipe
[0] = mypipe
[1] = -1;
5805 if ((pid
= fork()) == 0)
5808 * Child comes here...
5816 execve(job
->printer
->command
, myargv
, myenvp
);
5822 * Unable to fork process...
5825 perror("Unable to start job processing command");
5832 * Free memory used for environment...
5836 free(myenvp
[-- myenvc
]);
5841 * Free memory used for environment...
5845 free(myenvp
[-- myenvc
]);
5848 * If the pipe exists, read from it until EOF...
5856 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
5861 while ((ptr
= strchr(line
, '\n')) != NULL
)
5865 if (!strncmp(line
, "STATE:", 6))
5868 * Process printer-state-reasons keywords.
5871 process_state_message(job
, line
);
5873 else if (!strncmp(line
, "ATTR:", 5))
5876 * Process printer attribute update.
5879 process_attr_message(job
, line
);
5881 else if (Verbosity
> 1)
5882 fprintf(stderr
, "%s: %s\n", job
->printer
->command
, line
);
5886 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
5896 * Wait for child to complete...
5899 # ifdef HAVE_WAITPID
5900 while (waitpid(pid
, &status
, 0) < 0);
5902 while (wait(&status
) < 0);
5903 # endif /* HAVE_WAITPID */
5910 if (WIFEXITED(status
))
5911 #endif /* !_WIN32 */
5912 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
5913 job
->printer
->command
, WEXITSTATUS(status
));
5916 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
5917 job
->printer
->command
, WTERMSIG(status
));
5918 #endif /* !_WIN32 */
5919 job
->state
= IPP_JSTATE_ABORTED
;
5921 else if (status
< 0)
5922 job
->state
= IPP_JSTATE_ABORTED
;
5924 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
5925 job
->printer
->command
);
5928 * Make sure processing takes at least 5 seconds...
5932 if ((end
- start
) < 5)
5938 * Sleep for a random amount of time to simulate job processing.
5941 sleep((unsigned)(5 + (rand() % 11)));
5945 job
->state
= IPP_JSTATE_CANCELED
;
5946 else if (job
->state
== IPP_JSTATE_PROCESSING
)
5947 job
->state
= IPP_JSTATE_COMPLETED
;
5951 job
->completed
= time(NULL
);
5952 job
->printer
->state
= IPP_PSTATE_IDLE
;
5953 job
->printer
->active_job
= NULL
;
5960 * 'process_state_message()' - Process a STATE: message from a command.
5964 process_state_message(
5965 ippeve_job_t
*job
, /* I - Job */
5966 char *message
) /* I - Message */
5968 int i
; /* Looping var */
5969 ippeve_preason_t state_reasons
, /* printer-state-reasons values */
5970 bit
; /* Current reason bit */
5971 char *ptr
, /* Pointer into message */
5972 *next
; /* Next keyword in message */
5973 int remove
; /* Non-zero if we are removing keywords */
5977 * Skip leading "STATE:" and any whitespace...
5980 for (message
+= 6; *message
; message
++)
5981 if (*message
!= ' ' && *message
!= '\t')
5985 * Support the following forms of message:
5987 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
5989 * "-keyword[,keyword,...]" to remove keywords.
5991 * "+keyword[,keyword,...]" to add keywords.
5993 * Keywords may or may not have a suffix (-report, -warning, -error) per
5997 if (*message
== '-')
6000 state_reasons
= job
->printer
->state_reasons
;
6003 else if (*message
== '+')
6006 state_reasons
= job
->printer
->state_reasons
;
6012 state_reasons
= IPPEVE_PREASON_NONE
;
6017 if ((next
= strchr(message
, ',')) != NULL
)
6020 if ((ptr
= strstr(message
, "-error")) != NULL
)
6022 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6024 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6027 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
6029 if (!strcmp(message
, ippeve_preason_strings
[i
]))
6032 state_reasons
&= ~bit
;
6034 state_reasons
|= bit
;
6044 job
->printer
->state_reasons
= state_reasons
;
6049 * 'register_printer()' - Register a printer object via Bonjour.
6052 static int /* O - 1 on success, 0 on error */
6054 ippeve_printer_t
*printer
, /* I - Printer */
6055 const char *subtypes
) /* I - Service subtype(s) */
6057 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6058 ippeve_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6059 int i
, /* Looping var */
6060 count
; /* Number of values */
6061 ipp_attribute_t
*color_supported
,
6062 *document_format_supported
,
6064 *printer_make_and_model
,
6068 *urf_supported
; /* Printer attributes */
6069 const char *value
; /* Value string */
6070 char formats
[252], /* List of supported formats */
6071 urf
[252], /* List of supported URF values */
6072 *ptr
; /* Pointer into string */
6074 color_supported
= ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_BOOLEAN
);
6075 document_format_supported
= ippFindAttribute(printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
);
6076 printer_location
= ippFindAttribute(printer
->attrs
, "printer-location", IPP_TAG_TEXT
);
6077 printer_make_and_model
= ippFindAttribute(printer
->attrs
, "printer-make-and-model", IPP_TAG_TEXT
);
6078 printer_more_info
= ippFindAttribute(printer
->attrs
, "printer-more-info", IPP_TAG_URI
);
6079 printer_uuid
= ippFindAttribute(printer
->attrs
, "printer-uuid", IPP_TAG_URI
);
6080 sides_supported
= ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
);
6081 urf_supported
= ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_KEYWORD
);
6083 for (i
= 0, count
= ippGetCount(document_format_supported
), ptr
= formats
; i
< count
; i
++)
6085 value
= ippGetString(document_format_supported
, i
, NULL
);
6087 if (!strcasecmp(value
, "application/octet-stream"))
6090 if (ptr
> formats
&& ptr
< (formats
+ sizeof(formats
) - 1))
6093 strlcpy(ptr
, value
, sizeof(formats
) - (size_t)(ptr
- formats
));
6096 if (ptr
>= (formats
+ sizeof(formats
) - 1))
6101 for (i
= 0, count
= ippGetCount(urf_supported
), ptr
= urf
; i
< count
; i
++)
6103 value
= ippGetString(urf_supported
, i
, NULL
);
6105 if (ptr
> urf
&& ptr
< (urf
+ sizeof(urf
) - 1))
6108 strlcpy(ptr
, value
, sizeof(urf
) - (size_t)(ptr
- urf
));
6111 if (ptr
>= (urf
+ sizeof(urf
) - 1))
6115 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6117 DNSServiceErrorType error
; /* Error from Bonjour */
6118 char regtype
[256]; /* Bonjour service type */
6122 * Build the TXT record for IPP...
6125 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6126 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6127 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
6128 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(value
), value
);
6129 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
6130 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(value
), value
);
6131 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
6132 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(value
), value
);
6133 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
), formats
);
6134 TXTRecordSetValue(&ipp_txt
, "Color", 1, ippGetBoolean(color_supported
, 0) ? "T" : "F");
6135 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, ippGetCount(sides_supported
) > 1 ? "T" : "F");
6136 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
6137 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(value
) - 9, value
+ 9);
6139 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6140 # endif /* HAVE_SSL */
6142 TXTRecordSetValue(&ipp_txt
, "URF", strlen(urf
), urf
);
6143 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6144 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6147 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6148 * defend our service name but not actually support LPD...
6151 printer
->printer_ref
= DNSSDMaster
;
6153 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
)
6155 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_printer._tcp", error
);
6160 * Then register the _ipp._tcp (IPP) service type with the real port number to
6161 * advertise our IPP printer...
6164 printer
->ipp_ref
= DNSSDMaster
;
6166 if (subtypes
&& *subtypes
)
6167 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtypes
);
6169 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6171 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
)
6173 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
6179 * Then register the _ipps._tcp (IPP) service type with the real port number to
6180 * advertise our IPPS printer...
6183 printer
->ipps_ref
= DNSSDMaster
;
6185 if (subtypes
&& *subtypes
)
6186 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtypes
);
6188 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6190 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
)
6192 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
6195 # endif /* HAVE_SSL */
6198 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6199 * real port number to advertise our IPP printer...
6202 printer
->http_ref
= DNSSDMaster
;
6204 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
)
6206 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_http._tcp,_printer", error
);
6210 TXTRecordDeallocate(&ipp_txt
);
6212 #elif defined(HAVE_AVAHI)
6213 char temp
[256]; /* Subtype service string */
6216 * Create the TXT record...
6220 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6221 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
6222 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s", value
);
6223 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
6224 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", value
);
6225 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
6226 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", value
);
6227 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6228 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", ippGetBoolean(color_supported
, 0) ? "T" : "F");
6229 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", ippGetCount(sides_supported
) > 1 ? "T" : "F");
6230 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
6231 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", value
+ 9);
6233 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6234 # endif /* HAVE_SSL */
6236 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "URF=%s", urf
);
6237 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "txtvers=1");
6238 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "qtotal=1");
6241 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6244 avahi_threaded_poll_lock(DNSSDMaster
);
6246 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6248 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
);
6251 * Then register the ippeve._tcp (IPP)...
6254 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
);
6255 if (subtypes
&& *subtypes
)
6257 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", subtype
);
6258 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6263 * ippeves._tcp (IPPS) for secure printing...
6266 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
);
6267 if (subtypes
&& *subtypes
)
6269 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", subtype
);
6270 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
6272 #endif /* HAVE_SSL */
6275 * Finally _http.tcp (HTTP) for the web interface...
6278 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
);
6279 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");
6285 avahi_entry_group_commit(printer
->ipp_ref
);
6286 avahi_threaded_poll_unlock(DNSSDMaster
);
6288 avahi_string_list_free(ipp_txt
);
6289 #endif /* HAVE_DNSSD */
6296 * 'respond_http()' - Send a HTTP response.
6299 int /* O - 1 on success, 0 on failure */
6301 ippeve_client_t
*client
, /* I - Client */
6302 http_status_t code
, /* I - HTTP status of response */
6303 const char *content_encoding
, /* I - Content-Encoding of response */
6304 const char *type
, /* I - MIME media type of response */
6305 size_t length
) /* I - Length of response */
6307 char message
[1024]; /* Text message */
6310 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
6312 if (code
== HTTP_STATUS_CONTINUE
)
6315 * 100-continue doesn't send any headers...
6318 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
6322 * Format an error message...
6325 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
6327 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
6329 type
= "text/plain";
6330 length
= strlen(message
);
6336 * Send the HTTP response header...
6339 httpClearFields(client
->http
);
6341 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
6342 client
->operation
== HTTP_STATE_OPTIONS
)
6343 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
6347 if (!strcmp(type
, "text/html"))
6348 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
6349 "text/html; charset=utf-8");
6351 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
6353 if (content_encoding
)
6354 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
6357 httpSetLength(client
->http
, length
);
6359 if (httpWriteResponse(client
->http
, code
) < 0)
6363 * Send the response data...
6369 * Send a plain text message.
6372 if (httpPrintf(client
->http
, "%s", message
) < 0)
6375 if (httpWrite2(client
->http
, "", 0) < 0)
6378 else if (client
->response
)
6381 * Send an IPP response...
6384 debug_attributes("Response", client
->response
, 2);
6386 ippSetState(client
->response
, IPP_STATE_IDLE
);
6388 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
6397 * 'respond_ipp()' - Send an IPP response.
6401 respond_ipp(ippeve_client_t
*client
, /* I - Client */
6402 ipp_status_t status
, /* I - status-code */
6403 const char *message
, /* I - printf-style status-message */
6404 ...) /* I - Additional args as needed */
6406 const char *formatted
= NULL
; /* Formatted message */
6409 ippSetStatusCode(client
->response
, status
);
6413 va_list ap
; /* Pointer to additional args */
6414 ipp_attribute_t
*attr
; /* New status-message attribute */
6416 va_start(ap
, message
);
6417 if ((attr
= ippFindAttribute(client
->response
, "status-message",
6418 IPP_TAG_TEXT
)) != NULL
)
6419 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
6421 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
6422 "status-message", NULL
, message
, ap
);
6425 formatted
= ippGetString(attr
, 0, NULL
);
6429 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
6430 ippOpString(client
->operation_id
), ippErrorString(status
),
6433 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
6434 ippOpString(client
->operation_id
), ippErrorString(status
));
6439 * 'respond_unsupported()' - Respond with an unsupported attribute.
6443 respond_unsupported(
6444 ippeve_client_t
*client
, /* I - Client */
6445 ipp_attribute_t
*attr
) /* I - Atribute */
6447 ipp_attribute_t
*temp
; /* Copy of attribute */
6450 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6451 "Unsupported %s %s%s value.", ippGetName(attr
),
6452 ippGetCount(attr
) > 1 ? "1setOf " : "",
6453 ippTagString(ippGetValueTag(attr
)));
6455 temp
= ippCopyAttribute(client
->response
, attr
, 0);
6456 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
6461 * 'run_printer()' - Run the printer service.
6465 run_printer(ippeve_printer_t
*printer
) /* I - Printer */
6467 int num_fds
; /* Number of file descriptors */
6468 struct pollfd polldata
[3]; /* poll() data */
6469 int timeout
; /* Timeout for poll() */
6470 ippeve_client_t
*client
; /* New client */
6474 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
6477 polldata
[0].fd
= printer
->ipv4
;
6478 polldata
[0].events
= POLLIN
;
6480 polldata
[1].fd
= printer
->ipv6
;
6481 polldata
[1].events
= POLLIN
;
6486 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
6487 polldata
[num_fds
++].events
= POLLIN
;
6488 #endif /* HAVE_DNSSD */
6491 * Loop until we are killed or have a hard error...
6496 if (cupsArrayCount(printer
->jobs
))
6501 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
6503 perror("poll() failed");
6507 if (polldata
[0].revents
& POLLIN
)
6509 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
6511 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6515 _cupsThreadDetach(t
);
6519 perror("Unable to create client thread");
6520 delete_client(client
);
6525 if (polldata
[1].revents
& POLLIN
)
6527 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
6529 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
6533 _cupsThreadDetach(t
);
6537 perror("Unable to create client thread");
6538 delete_client(client
);
6544 if (polldata
[2].revents
& POLLIN
)
6545 DNSServiceProcessResult(DNSSDMaster
);
6546 #endif /* HAVE_DNSSD */
6549 * Clean out old jobs...
6552 clean_jobs(printer
);
6558 * 'time_string()' - Return the local time in hours, minutes, and seconds.
6562 time_string(time_t tv
, /* I - Time value */
6563 char *buffer
, /* I - Buffer */
6564 size_t bufsize
) /* I - Size of buffer */
6566 struct tm
*curtime
= localtime(&tv
);
6569 strftime(buffer
, bufsize
, "%X", curtime
);
6575 * 'usage()' - Show program usage.
6579 usage(int status
) /* O - Exit status */
6583 puts(CUPS_SVERSION
" - Copyright (c) 2010-2018 by Apple Inc. All rights reserved.");
6587 puts("Usage: ippserver [options] \"name\"");
6590 puts("-2 Supports 2-sided printing (default=1-sided)");
6591 puts("-M manufacturer Manufacturer name (default=Test)");
6592 puts("-P PIN printing mode");
6593 puts("-V max-version Set maximum supported IPP version");
6594 puts("-a attributes-file Load printer attributes from file");
6595 puts("-c command Run command for every print job");
6596 printf("-d spool-directory Spool directory "
6597 "(default=/tmp/ippserver.%d)\n", (int)getpid());
6598 puts("-f type/subtype[,...] List of supported types "
6599 "(default=application/pdf,image/jpeg)");
6600 puts("-h Show program help");
6601 puts("-i iconfile.png PNG icon file (default=printer.png)");
6602 puts("-k Keep job spool files");
6603 puts("-l location Location of printer (default=empty string)");
6604 puts("-m model Model name (default=Printer)");
6605 puts("-n hostname Hostname for printer");
6606 puts("-p port Port number (default=auto)");
6607 puts("-r subtype Bonjour service subtype (default=_print)");
6608 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
6609 puts("-v[vvv] Be (very) verbose");
6616 * 'valid_doc_attributes()' - Determine whether the document attributes are
6619 * When one or more document attributes are invalid, this function adds a
6620 * suitable response and attributes to the unsupported group.
6623 static int /* O - 1 if valid, 0 if not */
6624 valid_doc_attributes(
6625 ippeve_client_t
*client
) /* I - Client */
6627 int valid
= 1; /* Valid attributes? */
6628 ipp_op_t op
= ippGetOperation(client
->request
);
6630 const char *op_name
= ippOpString(op
);
6631 /* IPP operation name */
6632 ipp_attribute_t
*attr
, /* Current attribute */
6633 *supported
; /* xxx-supported attribute */
6634 const char *compression
= NULL
,
6635 /* compression value */
6636 *format
= NULL
; /* document-format value */
6640 * Check operation attributes...
6643 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
6646 * If compression is specified, only accept a supported value in a Print-Job
6647 * or Send-Document request...
6650 compression
= ippGetString(attr
, 0, NULL
);
6651 supported
= ippFindAttribute(client
->printer
->attrs
,
6652 "compression-supported", IPP_TAG_KEYWORD
);
6654 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6655 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
6656 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
6657 op
!= IPP_OP_VALIDATE_JOB
) ||
6658 !ippContainsString(supported
, compression
))
6660 respond_unsupported(client
, attr
);
6665 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
6667 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
6669 if (strcmp(compression
, "none"))
6672 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
6673 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
6679 * Is it a format we support?
6682 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
6684 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
6685 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
6687 respond_unsupported(client
, attr
);
6692 format
= ippGetString(attr
, 0, NULL
);
6694 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
6695 client
->hostname
, op_name
, format
);
6697 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
6702 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
6704 format
= "application/octet-stream"; /* Should never happen */
6706 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
6709 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
6712 * Auto-type the file using the first 8 bytes of the file...
6715 unsigned char header
[8]; /* First 8 bytes of file */
6717 memset(header
, 0, sizeof(header
));
6718 httpPeek(client
->http
, (char *)header
, sizeof(header
));
6720 if (!memcmp(header
, "%PDF", 4))
6721 format
= "application/pdf";
6722 else if (!memcmp(header
, "%!", 2))
6723 format
= "application/postscript";
6724 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
6725 format
= "image/jpeg";
6726 else if (!memcmp(header
, "\211PNG", 4))
6727 format
= "image/png";
6728 else if (!memcmp(header
, "RAS2", 4))
6729 format
= "image/pwg-raster";
6730 else if (!memcmp(header
, "UNIRAST", 8))
6731 format
= "image/urf";
6737 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
6738 client
->hostname
, op_name
, format
);
6740 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
6744 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
6746 respond_unsupported(client
, attr
);
6754 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
6755 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
6762 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
6764 * When one or more job attributes are invalid, this function adds a suitable
6765 * response and attributes to the unsupported group.
6768 static int /* O - 1 if valid, 0 if not */
6769 valid_job_attributes(
6770 ippeve_client_t
*client
) /* I - Client */
6772 int i
, /* Looping var */
6773 count
, /* Number of values */
6774 valid
= 1; /* Valid attributes? */
6775 ipp_attribute_t
*attr
, /* Current attribute */
6776 *supported
; /* xxx-supported attribute */
6780 * Check operation attributes...
6783 valid
= valid_doc_attributes(client
);
6786 * Check the various job template attributes...
6789 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
6791 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6792 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
6794 respond_unsupported(client
, attr
);
6799 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
6801 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
6803 respond_unsupported(client
, attr
);
6808 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
6810 if (ippGetCount(attr
) != 1 ||
6811 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6812 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6813 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6814 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
6816 respond_unsupported(client
, attr
);
6821 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
6823 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
6825 respond_unsupported(client
, attr
);
6830 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
6832 if (ippGetCount(attr
) != 1 ||
6833 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6834 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
6836 respond_unsupported(client
, attr
);
6840 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
6843 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
6845 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
6847 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
6848 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
6850 respond_unsupported(client
, attr
);
6855 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
6857 if (ippGetCount(attr
) != 1 ||
6858 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6859 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6860 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
6861 strcmp(ippGetString(attr
, 0, NULL
), "none"))
6863 respond_unsupported(client
, attr
);
6868 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
6870 if (ippGetCount(attr
) != 1 ||
6871 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
6872 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
6873 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
6875 respond_unsupported(client
, attr
);
6880 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
6882 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
6884 respond_unsupported(client
, attr
);
6890 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
6892 ipp_t
*col
, /* media-col collection */
6893 *size
; /* media-size collection */
6894 ipp_attribute_t
*member
, /* Member attribute */
6895 *x_dim
, /* x-dimension */
6896 *y_dim
; /* y-dimension */
6897 int x_value
, /* y-dimension value */
6898 y_value
; /* x-dimension value */
6900 if (ippGetCount(attr
) != 1 ||
6901 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
6903 respond_unsupported(client
, attr
);
6907 col
= ippGetCollection(attr
, 0);
6909 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
6911 if (ippGetCount(member
) != 1 ||
6912 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
6913 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
6914 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
6916 respond_unsupported(client
, attr
);
6921 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
6923 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
6925 respond_unsupported(client
, attr
);
6930 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
6932 if (ippGetCount(member
) != 1)
6934 respond_unsupported(client
, attr
);
6939 size
= ippGetCollection(member
, 0);
6941 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
6942 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
6944 respond_unsupported(client
, attr
);
6949 x_value
= ippGetInteger(x_dim
, 0);
6950 y_value
= ippGetInteger(y_dim
, 0);
6951 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
6952 count
= ippGetCount(supported
);
6954 for (i
= 0; i
< count
; i
++)
6956 size
= ippGetCollection(supported
, i
);
6957 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
6958 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
6960 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
6966 respond_unsupported(client
, attr
);
6974 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
6976 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
6977 (strcmp(ippGetString(attr
, 0, NULL
),
6978 "separate-documents-uncollated-copies") &&
6979 strcmp(ippGetString(attr
, 0, NULL
),
6980 "separate-documents-collated-copies")))
6982 respond_unsupported(client
, attr
);
6987 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
6989 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
6990 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
6991 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
6993 respond_unsupported(client
, attr
);
6998 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
7000 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
7002 respond_unsupported(client
, attr
);
7007 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
7009 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
7010 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
7011 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
7013 respond_unsupported(client
, attr
);
7018 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
7020 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
7022 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
7025 respond_unsupported(client
, attr
);
7030 int xdpi
, /* Horizontal resolution for job template attribute */
7031 ydpi
, /* Vertical resolution for job template attribute */
7032 sydpi
; /* Vertical resolution for supported value */
7033 ipp_res_t units
, /* Units for job template attribute */
7034 sunits
; /* Units for supported value */
7036 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
7037 count
= ippGetCount(supported
);
7039 for (i
= 0; i
< count
; i
++)
7041 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
7047 respond_unsupported(client
, attr
);
7053 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
7055 const char *sides
= ippGetString(attr
, 0, NULL
);
7056 /* "sides" value... */
7058 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
7060 respond_unsupported(client
, attr
);
7063 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
7065 if (!ippContainsString(supported
, sides
))
7067 respond_unsupported(client
, attr
);
7071 else if (strcmp(sides
, "one-sided"))
7073 respond_unsupported(client
, attr
);