2 * IPP Everywhere printer application for CUPS.
4 * Copyright © 2010-2019 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * Note: This program began life as the "ippserver" sample code that first
10 * appeared in CUPS 1.4. The name has been changed in order to distinguish it
11 * from the PWG's much more ambitious "ippserver" program, which supports
12 * different kinds of IPP services and multiple services per instance - the
13 * "ippeveprinter" program exposes a single print service conforming to the
14 * current IPP Everywhere specification, thus the new name.
18 * Include necessary headers...
21 #include <cups/cups-private.h>
22 #include <cups/debug-private.h>
24 # include <cups/ppd-private.h>
25 #endif /* !CUPS_LITE */
34 # define WEXITSTATUS(s) (s)
35 # include <winsock2.h>
39 extern char **environ
;
41 # include <sys/fcntl.h>
42 # include <sys/wait.h>
48 #elif defined(HAVE_AVAHI)
49 # include <avahi-client/client.h>
50 # include <avahi-client/publish.h>
51 # include <avahi-common/error.h>
52 # include <avahi-common/thread-watch.h>
53 #endif /* HAVE_DNSSD */
55 #ifdef HAVE_SYS_MOUNT_H
56 # include <sys/mount.h>
57 #endif /* HAVE_SYS_MOUNT_H */
58 #ifdef HAVE_SYS_STATFS_H
59 # include <sys/statfs.h>
60 #endif /* HAVE_SYS_STATFS_H */
61 #ifdef HAVE_SYS_STATVFS_H
62 # include <sys/statvfs.h>
63 #endif /* HAVE_SYS_STATVFS_H */
66 #endif /* HAVE_SYS_VFS_H */
69 # ifdef HAVE_PAM_PAM_APPL_H
70 # include <pam/pam_appl.h>
72 # include <security/pam_appl.h>
73 # endif /* HAVE_PAM_PAM_APPL_H */
74 #endif /* HAVE_LIBPAM */
76 #include "printer-png.h"
83 enum ippeve_preason_e
/* printer-state-reasons bit values */
85 IPPEVE_PREASON_NONE
= 0x0000, /* none */
86 IPPEVE_PREASON_OTHER
= 0x0001, /* other */
87 IPPEVE_PREASON_COVER_OPEN
= 0x0002, /* cover-open */
88 IPPEVE_PREASON_INPUT_TRAY_MISSING
= 0x0004,
89 /* input-tray-missing */
90 IPPEVE_PREASON_MARKER_SUPPLY_EMPTY
= 0x0008,
91 /* marker-supply-empty */
92 IPPEVE_PREASON_MARKER_SUPPLY_LOW
= 0x0010,
93 /* marker-supply-low */
94 IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
= 0x0020,
95 /* marker-waste-almost-full */
96 IPPEVE_PREASON_MARKER_WASTE_FULL
= 0x0040,
97 /* marker-waste-full */
98 IPPEVE_PREASON_MEDIA_EMPTY
= 0x0080, /* media-empty */
99 IPPEVE_PREASON_MEDIA_JAM
= 0x0100, /* media-jam */
100 IPPEVE_PREASON_MEDIA_LOW
= 0x0200, /* media-low */
101 IPPEVE_PREASON_MEDIA_NEEDED
= 0x0400, /* media-needed */
102 IPPEVE_PREASON_MOVING_TO_PAUSED
= 0x0800,
103 /* moving-to-paused */
104 IPPEVE_PREASON_PAUSED
= 0x1000, /* paused */
105 IPPEVE_PREASON_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
106 IPPEVE_PREASON_TONER_EMPTY
= 0x4000, /* toner-empty */
107 IPPEVE_PREASON_TONER_LOW
= 0x8000 /* toner-low */
109 typedef unsigned int ippeve_preason_t
; /* Bitfield for printer-state-reasons */
110 static const char * const ippeve_preason_strings
[] =
111 { /* Strings for each bit */
112 /* "none" is implied for no bits set */
115 "input-tray-missing",
116 "marker-supply-empty",
118 "marker-waste-almost-full",
133 * URL scheme for web resources...
137 # define WEB_SCHEME "https"
139 # define WEB_SCHEME "http"
140 #endif /* HAVE_SSL */
148 typedef DNSServiceRef ippeve_srv_t
; /* Service reference */
149 typedef TXTRecordRef ippeve_txt_t
; /* TXT record */
151 #elif defined(HAVE_AVAHI)
152 typedef AvahiEntryGroup
*ippeve_srv_t
; /* Service reference */
153 typedef AvahiStringList
*ippeve_txt_t
; /* TXT record */
156 typedef void *ippeve_srv_t
; /* Service reference */
157 typedef void *ippeve_txt_t
; /* TXT record */
158 #endif /* HAVE_DNSSD */
161 typedef struct ippeve_authdata_s
/* Authentication data */
163 char username
[HTTP_MAX_VALUE
], /* Username string */
164 *password
; /* Password string */
166 #endif /* HAVE_LIBPAM */
168 typedef struct ippeve_filter_s
/**** Attribute filter ****/
170 cups_array_t
*ra
; /* Requested attributes */
171 ipp_tag_t group_tag
; /* Group to copy */
174 typedef struct ippeve_job_s ippeve_job_t
;
176 typedef struct ippeve_printer_s
/**** Printer data ****/
178 /* TODO: One IPv4 and one IPv6 listener are really not sufficient */
179 int ipv4
, /* IPv4 listener */
180 ipv6
; /* IPv6 listener */
181 ippeve_srv_t ipp_ref
, /* Bonjour IPP service */
182 ipps_ref
, /* Bonjour IPPS service */
183 http_ref
, /* Bonjour HTTP service */
184 printer_ref
; /* Bonjour LPD service */
185 char *dnssd_name
, /* printer-dnssd-name */
186 *name
, /* printer-name */
187 *icon
, /* Icon filename */
188 *directory
, /* Spool directory */
189 *hostname
, /* Hostname */
190 *uri
, /* printer-uri-supported */
191 *device_uri
, /* Device URI (if any) */
192 *output_format
, /* Output format */
194 *ppdfile
, /* PPD file (if any) */
195 #endif /* !CUPS_LITE */
196 *command
; /* Command to run with job file */
198 int web_forms
; /* Enable web interface forms? */
199 size_t urilen
; /* Length of printer URI */
200 ipp_t
*attrs
; /* Static attributes */
201 time_t start_time
; /* Startup time */
202 time_t config_time
; /* printer-config-change-time */
203 ipp_pstate_t state
; /* printer-state value */
204 ippeve_preason_t state_reasons
; /* printer-state-reasons values */
205 time_t state_time
; /* printer-state-change-time */
206 cups_array_t
*jobs
; /* Jobs */
207 ippeve_job_t
*active_job
; /* Current active/pending job */
208 int next_job_id
; /* Next job-id value */
209 _cups_rwlock_t rwlock
; /* Printer lock */
212 struct ippeve_job_s
/**** Job data ****/
215 const char *name
, /* job-name */
216 *username
, /* job-originating-user-name */
217 *format
; /* document-format */
218 ipp_jstate_t state
; /* job-state value */
219 char *message
; /* job-state-message value */
220 int msglevel
; /* job-state-message log level (0=error, 1=info) */
221 time_t created
, /* time-at-creation value */
222 processing
, /* time-at-processing value */
223 completed
; /* time-at-completed value */
224 int impressions
, /* job-impressions value */
225 impcompleted
; /* job-impressions-completed value */
226 ipp_t
*attrs
; /* Static attributes */
227 int cancel
; /* Non-zero when job canceled */
228 char *filename
; /* Print file name */
229 int fd
; /* Print file descriptor */
230 ippeve_printer_t
*printer
; /* Printer */
233 typedef struct ippeve_client_s
/**** Client data ****/
235 http_t
*http
; /* HTTP connection */
236 ipp_t
*request
, /* IPP request */
237 *response
; /* IPP response */
238 time_t start
; /* Request start time */
239 http_state_t operation
; /* Request operation */
240 ipp_op_t operation_id
; /* IPP operation-id */
241 char uri
[1024], /* Request URI */
242 *options
; /* URI options */
243 http_addr_t addr
; /* Client address */
244 char hostname
[256], /* Client hostname */
245 username
[HTTP_MAX_VALUE
];
246 /* Authenticated username, if any */
247 ippeve_printer_t
*printer
; /* Printer */
248 ippeve_job_t
*job
; /* Current job, if any */
256 static http_status_t
authenticate_request(ippeve_client_t
*client
);
257 static void clean_jobs(ippeve_printer_t
*printer
);
258 static int compare_jobs(ippeve_job_t
*a
, ippeve_job_t
*b
);
259 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
, ipp_tag_t group_tag
, int quickcopy
);
260 static void copy_job_attributes(ippeve_client_t
*client
, ippeve_job_t
*job
, cups_array_t
*ra
);
261 static ippeve_client_t
*create_client(ippeve_printer_t
*printer
, int sock
);
262 static ippeve_job_t
*create_job(ippeve_client_t
*client
);
263 static int create_job_file(ippeve_job_t
*job
, char *fname
, size_t fnamesize
, const char *dir
, const char *ext
);
264 static int create_listener(const char *name
, int port
, int family
);
265 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
);
266 static ipp_t
*create_media_size(int width
, int length
);
267 static ippeve_printer_t
*create_printer(const char *servername
, int serverport
, const char *name
, const char *location
, const char *icon
, cups_array_t
*docformats
, const char *subtypes
, const char *directory
, const char *command
, const char *device_uri
, const char *output_format
, ipp_t
*attrs
);
268 static void debug_attributes(const char *title
, ipp_t
*ipp
, int response
);
269 static void delete_client(ippeve_client_t
*client
);
270 static void delete_job(ippeve_job_t
*job
);
271 static void delete_printer(ippeve_printer_t
*printer
);
273 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
);
274 #elif defined(HAVE_AVAHI)
275 static void dnssd_callback(AvahiEntryGroup
*p
, AvahiEntryGroupState state
, void *context
);
276 static void dnssd_client_cb(AvahiClient
*c
, AvahiClientState state
, void *userdata
);
277 #endif /* HAVE_DNSSD */
278 static void dnssd_init(void);
279 static int filter_cb(ippeve_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
280 static ippeve_job_t
*find_job(ippeve_client_t
*client
);
281 static void finish_document_data(ippeve_client_t
*client
, ippeve_job_t
*job
);
282 static void finish_document_uri(ippeve_client_t
*client
, ippeve_job_t
*job
);
283 static void html_escape(ippeve_client_t
*client
, const char *s
, size_t slen
);
284 static void html_footer(ippeve_client_t
*client
);
285 static void html_header(ippeve_client_t
*client
, const char *title
, int refresh
);
286 static void html_printf(ippeve_client_t
*client
, const char *format
, ...) _CUPS_FORMAT(2, 3);
287 static void ipp_cancel_job(ippeve_client_t
*client
);
288 static void ipp_close_job(ippeve_client_t
*client
);
289 static void ipp_create_job(ippeve_client_t
*client
);
290 static void ipp_get_job_attributes(ippeve_client_t
*client
);
291 static void ipp_get_jobs(ippeve_client_t
*client
);
292 static void ipp_get_printer_attributes(ippeve_client_t
*client
);
293 static void ipp_identify_printer(ippeve_client_t
*client
);
294 static void ipp_print_job(ippeve_client_t
*client
);
295 static void ipp_print_uri(ippeve_client_t
*client
);
296 static void ipp_send_document(ippeve_client_t
*client
);
297 static void ipp_send_uri(ippeve_client_t
*client
);
298 static void ipp_validate_job(ippeve_client_t
*client
);
299 static ipp_t
*load_ippserver_attributes(const char *servername
, int serverport
, const char *filename
, cups_array_t
*docformats
);
300 static ipp_t
*load_legacy_attributes(const char *make
, const char *model
, int ppm
, int ppm_color
, int duplex
, cups_array_t
*docformats
);
302 static ipp_t
*load_ppd_attributes(const char *ppdfile
, cups_array_t
*docformats
);
303 #endif /* !CUPS_LITE */
305 static int pam_func(int, const struct pam_message
**, struct pam_response
**, void *);
306 #endif /* HAVE_LIBPAM */
307 static int parse_options(ippeve_client_t
*client
, cups_option_t
**options
);
308 static void process_attr_message(ippeve_job_t
*job
, char *message
);
309 static void *process_client(ippeve_client_t
*client
);
310 static int process_http(ippeve_client_t
*client
);
311 static int process_ipp(ippeve_client_t
*client
);
312 static void *process_job(ippeve_job_t
*job
);
313 static void process_state_message(ippeve_job_t
*job
, char *message
);
314 static int register_printer(ippeve_printer_t
*printer
, const char *subtypes
);
315 static int respond_http(ippeve_client_t
*client
, http_status_t code
, const char *content_coding
, const char *type
, size_t length
);
316 static void respond_ipp(ippeve_client_t
*client
, ipp_status_t status
, const char *message
, ...) _CUPS_FORMAT(3, 4);
317 static void respond_unsupported(ippeve_client_t
*client
, ipp_attribute_t
*attr
);
318 static void run_printer(ippeve_printer_t
*printer
);
319 static int show_media(ippeve_client_t
*client
);
320 static int show_status(ippeve_client_t
*client
);
321 static int show_supplies(ippeve_client_t
*client
);
322 static char *time_string(time_t tv
, char *buffer
, size_t bufsize
);
323 static void usage(int status
) _CUPS_NORETURN
;
324 static int valid_doc_attributes(ippeve_client_t
*client
);
325 static int valid_job_attributes(ippeve_client_t
*client
);
333 static DNSServiceRef DNSSDMaster
= NULL
;
334 #elif defined(HAVE_AVAHI)
335 static AvahiThreadedPoll
*DNSSDMaster
= NULL
;
336 static AvahiClient
*DNSSDClient
= NULL
;
337 #endif /* HAVE_DNSSD */
339 static int KeepFiles
= 0, /* Keep spooled job files? */
340 MaxVersion
= 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
341 Verbosity
= 0; /* Verbosity level */
342 static const char *PAMService
= NULL
;
347 * 'main()' - Main entry to the sample server.
350 int /* O - Exit status */
351 main(int argc
, /* I - Number of command-line args */
352 char *argv
[]) /* I - Command-line arguments */
354 int i
; /* Looping var */
355 const char *opt
, /* Current option character */
356 *attrfile
= NULL
, /* ippserver attributes file */
357 *command
= NULL
, /* Command to run with job files */
358 *device_uri
= NULL
, /* Device URI */
359 *output_format
= NULL
, /* Output format */
360 *icon
= NULL
, /* Icon file */
362 *keypath
= NULL
, /* Keychain path */
363 #endif /* HAVE_SSL */
364 *location
= "", /* Location of printer */
365 *make
= "Example", /* Manufacturer */
366 *model
= "Printer", /* Model */
367 *name
= NULL
, /* Printer name */
369 *ppdfile
= NULL
, /* PPD file */
370 #endif /* !CUPS_LITE */
371 *subtypes
= "_print"; /* DNS-SD service subtype */
372 int legacy
= 0, /* Legacy mode? */
373 duplex
= 0, /* Duplex mode */
374 ppm
= 10, /* Pages per minute for mono */
375 ppm_color
= 0, /* Pages per minute for color */
376 web_forms
= 1; /* Enable web site forms? */
377 ipp_t
*attrs
= NULL
; /* Printer attributes */
378 char directory
[1024] = ""; /* Spool directory */
379 cups_array_t
*docformats
= NULL
; /* Supported formats */
380 const char *servername
= NULL
; /* Server host name */
381 int serverport
= 0; /* Server port number (0 = auto) */
382 ippeve_printer_t
*printer
; /* Printer object */
386 * Parse command-line arguments...
389 for (i
= 1; i
< argc
; i
++)
391 if (!strcmp(argv
[i
], "--help"))
395 else if (!strcmp(argv
[i
], "--no-web-forms"))
399 else if (!strcmp(argv
[i
], "--pam-service"))
405 PAMService
= argv
[i
];
407 else if (!strcmp(argv
[i
], "--version"))
412 else if (!strncmp(argv
[i
], "--", 2))
414 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
417 else if (argv
[i
][0] == '-')
419 for (opt
= argv
[i
] + 1; *opt
; opt
++)
423 case '2' : /* -2 (enable 2-sided printing) */
428 case 'A' : /* -A (enable authentication) */
433 case 'D' : /* -D device-uri */
438 device_uri
= argv
[i
];
441 case 'F' : /* -F output/format */
446 output_format
= argv
[i
];
450 case 'K' : /* -K keypath */
457 #endif /* HAVE_SSL */
459 case 'M' : /* -M manufacturer */
469 case 'P' : /* -P filename.ppd */
476 #endif /* !CUPS_LITE */
478 case 'V' : /* -V max-version */
483 if (!strcmp(argv
[i
], "2.0"))
485 else if (!strcmp(argv
[i
], "1.1"))
491 case 'a' : /* -a attributes-file */
499 case 'c' : /* -c command */
507 case 'd' : /* -d spool-directory */
512 strlcpy(directory
, argv
[i
], sizeof(directory
));
515 case 'f' : /* -f type/subtype[,...] */
520 docformats
= _cupsArrayNewStrings(argv
[i
], ',');
524 case 'i' : /* -i icon.png */
532 case 'k' : /* -k (keep files) */
536 case 'l' : /* -l location */
544 case 'm' : /* -m model */
553 case 'n' : /* -n hostname */
558 servername
= argv
[i
];
561 case 'p' : /* -p port */
563 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
566 serverport
= atoi(argv
[i
]);
569 case 'r' : /* -r subtype */
577 case 's' : /* -s speed[,color-speed] */
582 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
588 case 'v' : /* -v (be verbose) */
592 default : /* Unknown */
593 _cupsLangPrintf(stderr
, _("%s: Unknown option \"-%c\"."), argv
[0], *opt
);
604 _cupsLangPrintf(stderr
, _("%s: Unknown option \"%s\"."), argv
[0], argv
[i
]);
613 if (attrfile
!= NULL
&& legacy
)
616 if (((ppdfile
!= NULL
) + (attrfile
!= NULL
) + legacy
) > 1)
618 #endif /* CUPS_LITE */
621 * Apply defaults as needed...
628 * Windows is almost always used as a single user system, so use a default
629 * port number of 8631.
636 * Use 8000 + UID mod 1000 for the default port number...
639 serverport
= 8000 + ((int)getuid() % 1000);
642 _cupsLangPrintf(stderr
, _("Listening on port %d."), serverport
);
647 const char *tmpdir
; /* Temporary directory */
650 if ((tmpdir
= getenv("TEMP")) == NULL
)
652 #elif defined(__APPLE__) && TARGET_OS_OSX
653 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
654 tmpdir
= "/private/tmp";
656 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
660 snprintf(directory
, sizeof(directory
), "%s/ippeveprinter.%d", tmpdir
, (int)getpid());
662 if (mkdir(directory
, 0755) && errno
!= EEXIST
)
664 _cupsLangPrintf(stderr
, _("Unable to create spool directory \"%s\": %s"), directory
, strerror(errno
));
669 _cupsLangPrintf(stderr
, _("Using spool directory \"%s\"."), directory
);
673 * Initialize DNS-SD...
679 * Create the printer...
683 docformats
= _cupsArrayNewStrings(ppm_color
> 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ',');
686 attrs
= load_ippserver_attributes(servername
, serverport
, attrfile
, docformats
);
690 attrs
= load_ppd_attributes(ppdfile
, docformats
);
693 command
= "ippeveps";
696 output_format
= "application/postscript";
698 #endif /* !CUPS_LITE */
700 attrs
= load_legacy_attributes(make
, model
, ppm
, ppm_color
, duplex
, docformats
);
702 if ((printer
= create_printer(servername
, serverport
, name
, location
, icon
, docformats
, subtypes
, directory
, command
, device_uri
, output_format
, attrs
)) == NULL
)
705 printer
->web_forms
= web_forms
;
709 printer
->ppdfile
= strdup(ppdfile
);
710 #endif /* !CUPS_LITE */
713 cupsSetServerCredentials(keypath
, printer
->hostname
, 1);
714 #endif /* HAVE_SSL */
717 * Run the print service...
720 run_printer(printer
);
723 * Destroy the printer and exit...
726 delete_printer(printer
);
733 * 'authenticate_request()' - Try to authenticate the request.
736 static http_status_t
/* O - HTTP_STATUS_CONTINUE to keep going, otherwise status to return */
737 authenticate_request(
738 ippeve_client_t
*client
) /* I - Client */
742 * If PAM isn't enabled, return 'continue' now...
745 const char *authorization
; /* Pointer into Authorization string */
746 int userlen
; /* Username:password length */
747 pam_handle_t
*pamh
; /* PAM authentication handle */
748 int pamerr
; /* PAM error code */
749 struct pam_conv pamdata
; /* PAM conversation data */
750 ippeve_authdata_t data
; /* Authentication data */
754 return (HTTP_STATUS_CONTINUE
);
757 * Try authenticating using PAM...
760 authorization
= httpGetField(client
->http
, HTTP_FIELD_AUTHORIZATION
);
763 return (HTTP_STATUS_UNAUTHORIZED
);
765 if (strncmp(authorization
, "Basic ", 6))
767 fputs("Unsupported scheme in Authorization header.\n", stderr
);
768 return (HTTP_STATUS_BAD_REQUEST
);
772 while (isspace(*authorization
& 255))
775 userlen
= sizeof(data
.username
);
776 httpDecode64_2(data
.username
, &userlen
, authorization
);
778 if ((data
.password
= strchr(data
.username
, ':')) == NULL
)
780 fputs("No password in Authorization header.\n", stderr
);
781 return (HTTP_STATUS_BAD_REQUEST
);
784 *(data
.password
)++ = '\0';
786 if (!data
.username
[0])
788 fputs("No username in Authorization header.\n", stderr
);
789 return (HTTP_STATUS_BAD_REQUEST
);
792 pamdata
.conv
= pam_func
;
793 pamdata
.appdata_ptr
= &data
;
795 if ((pamerr
= pam_start(PAMService
, data
.username
, &pamdata
, &pamh
)) != PAM_SUCCESS
)
797 fprintf(stderr
, "pam_start() returned %d (%s)\n", pamerr
, pam_strerror(pamh
, pamerr
));
798 return (HTTP_STATUS_SERVER_ERROR
);
801 if ((pamerr
= pam_authenticate(pamh
, PAM_SILENT
)) != PAM_SUCCESS
)
803 fprintf(stderr
, "pam_authenticate() returned %d (%s)\n", pamerr
, pam_strerror(pamh
, pamerr
));
805 return (HTTP_STATUS_UNAUTHORIZED
);
808 if ((pamerr
= pam_acct_mgmt(pamh
, PAM_SILENT
)) != PAM_SUCCESS
)
810 fprintf(stderr
, "pam_acct_mgmt() returned %d (%s)\n", pamerr
, pam_strerror(pamh
, pamerr
));
812 return (HTTP_STATUS_SERVER_ERROR
);
815 strlcpy(client
->username
, data
.username
, sizeof(client
->username
));
817 pam_end(pamh
, PAM_SUCCESS
);
819 return (HTTP_STATUS_CONTINUE
);
823 * No authentication support built-in, return 'continue'...
826 return (HTTP_STATUS_CONTINUE
);
827 #endif /* HAVE_LIBPAM */
832 * 'clean_jobs()' - Clean out old (completed) jobs.
836 clean_jobs(ippeve_printer_t
*printer
) /* I - Printer */
838 ippeve_job_t
*job
; /* Current job */
839 time_t cleantime
; /* Clean time */
842 if (cupsArrayCount(printer
->jobs
) == 0)
845 cleantime
= time(NULL
) - 60;
847 _cupsRWLockWrite(&(printer
->rwlock
));
848 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
);
850 job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
851 if (job
->completed
&& job
->completed
< cleantime
)
853 cupsArrayRemove(printer
->jobs
, job
);
858 _cupsRWUnlock(&(printer
->rwlock
));
863 * 'compare_jobs()' - Compare two jobs.
866 static int /* O - Result of comparison */
867 compare_jobs(ippeve_job_t
*a
, /* I - First job */
868 ippeve_job_t
*b
) /* I - Second job */
870 return (b
->id
- a
->id
);
875 * 'copy_attributes()' - Copy attributes from one request to another.
879 copy_attributes(ipp_t
*to
, /* I - Destination request */
880 ipp_t
*from
, /* I - Source request */
881 cups_array_t
*ra
, /* I - Requested attributes */
882 ipp_tag_t group_tag
, /* I - Group to copy */
883 int quickcopy
) /* I - Do a quick copy? */
885 ippeve_filter_t filter
; /* Filter data */
889 filter
.group_tag
= group_tag
;
891 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
896 * 'copy_job_attrs()' - Copy job attributes to the response.
901 ippeve_client_t
*client
, /* I - Client */
902 ippeve_job_t
*job
, /* I - Job */
903 cups_array_t
*ra
) /* I - requested-attributes */
905 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
907 if (!ra
|| cupsArrayFind(ra
, "date-time-at-completed"))
910 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
912 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-completed");
915 if (!ra
|| cupsArrayFind(ra
, "date-time-at-processing"))
918 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
920 ippAddOutOfBand(client
->response
, IPP_TAG_JOB
, IPP_TAG_NOVALUE
, "date-time-at-processing");
923 if (!ra
|| cupsArrayFind(ra
, "job-impressions"))
924 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions", job
->impressions
);
926 if (!ra
|| cupsArrayFind(ra
, "job-impressions-completed"))
927 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->impcompleted
);
929 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
930 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-printer-up-time", (int)(time(NULL
) - client
->printer
->start_time
));
932 if (!ra
|| cupsArrayFind(ra
, "job-state"))
933 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state", (int)job
->state
);
935 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
939 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_TAG_TEXT
, "job-state-message", NULL
, job
->message
);
945 case IPP_JSTATE_PENDING
:
946 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
949 case IPP_JSTATE_HELD
:
951 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
952 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
953 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
955 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
958 case IPP_JSTATE_PROCESSING
:
960 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
962 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
965 case IPP_JSTATE_STOPPED
:
966 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
969 case IPP_JSTATE_CANCELED
:
970 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
973 case IPP_JSTATE_ABORTED
:
974 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
977 case IPP_JSTATE_COMPLETED
:
978 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
984 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
988 case IPP_JSTATE_PENDING
:
989 ippAddString(client
->response
, IPP_TAG_JOB
,
990 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
994 case IPP_JSTATE_HELD
:
996 ippAddString(client
->response
, IPP_TAG_JOB
,
997 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
998 "job-state-reasons", NULL
, "job-incoming");
999 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
1000 ippAddString(client
->response
, IPP_TAG_JOB
,
1001 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1002 "job-state-reasons", NULL
, "job-hold-until-specified");
1004 ippAddString(client
->response
, IPP_TAG_JOB
,
1005 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1006 "job-state-reasons", NULL
, "job-data-insufficient");
1009 case IPP_JSTATE_PROCESSING
:
1011 ippAddString(client
->response
, IPP_TAG_JOB
,
1012 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1013 "job-state-reasons", NULL
, "processing-to-stop-point");
1015 ippAddString(client
->response
, IPP_TAG_JOB
,
1016 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1017 "job-state-reasons", NULL
, "job-printing");
1020 case IPP_JSTATE_STOPPED
:
1021 ippAddString(client
->response
, IPP_TAG_JOB
,
1022 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
1023 NULL
, "job-stopped");
1026 case IPP_JSTATE_CANCELED
:
1027 ippAddString(client
->response
, IPP_TAG_JOB
,
1028 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
1029 NULL
, "job-canceled-by-user");
1032 case IPP_JSTATE_ABORTED
:
1033 ippAddString(client
->response
, IPP_TAG_JOB
,
1034 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
1035 NULL
, "aborted-by-system");
1038 case IPP_JSTATE_COMPLETED
:
1039 ippAddString(client
->response
, IPP_TAG_JOB
,
1040 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
1041 NULL
, "job-completed-successfully");
1046 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
1047 ippAddInteger(client
->response
, IPP_TAG_JOB
,
1048 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
1049 "time-at-completed", (int)(job
->completed
- client
->printer
->start_time
));
1051 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
1052 ippAddInteger(client
->response
, IPP_TAG_JOB
,
1053 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
1054 "time-at-processing", (int)(job
->processing
- client
->printer
->start_time
));
1059 * 'create_client()' - Accept a new network connection and create a client
1063 static ippeve_client_t
* /* O - Client */
1064 create_client(ippeve_printer_t
*printer
, /* I - Printer */
1065 int sock
) /* I - Listen socket */
1067 ippeve_client_t
*client
; /* Client */
1070 if ((client
= calloc(1, sizeof(ippeve_client_t
))) == NULL
)
1072 perror("Unable to allocate memory for client");
1076 client
->printer
= printer
;
1079 * Accept the client and get the remote address...
1082 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
1084 perror("Unable to accept client connection");
1091 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
1094 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
1101 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
1105 static ippeve_job_t
* /* O - Job */
1106 create_job(ippeve_client_t
*client
) /* I - Client */
1108 ippeve_job_t
*job
; /* Job */
1109 ipp_attribute_t
*attr
; /* Job attribute */
1110 char uri
[1024], /* job-uri value */
1111 uuid
[64]; /* job-uuid value */
1114 _cupsRWLockWrite(&(client
->printer
->rwlock
));
1115 if (client
->printer
->active_job
&&
1116 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
1119 * Only accept a single job at a time...
1122 _cupsRWUnlock(&(client
->printer
->rwlock
));
1127 * Allocate and initialize the job object...
1130 if ((job
= calloc(1, sizeof(ippeve_job_t
))) == NULL
)
1132 perror("Unable to allocate memory for job");
1136 job
->printer
= client
->printer
;
1137 job
->attrs
= ippNew();
1138 job
->state
= IPP_JSTATE_HELD
;
1142 * Copy all of the job attributes...
1145 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
1148 * Get the requesting-user-name, document format, and priority...
1151 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1152 job
->username
= ippGetString(attr
, 0, NULL
);
1154 job
->username
= "anonymous";
1156 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name", NULL
, job
->username
);
1158 if (ippGetOperation(client
->request
) != IPP_OP_CREATE_JOB
)
1160 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
1161 job
->format
= ippGetString(attr
, 0, NULL
);
1162 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
1163 job
->format
= ippGetString(attr
, 0, NULL
);
1165 job
->format
= "application/octet-stream";
1168 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_INTEGER
)) != NULL
)
1169 job
->impressions
= ippGetInteger(attr
, 0);
1171 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1172 job
->name
= ippGetString(attr
, 0, NULL
);
1175 * Add job description attributes and add to the jobs array...
1178 job
->id
= client
->printer
->next_job_id
++;
1180 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1181 httpAssembleUUID(client
->printer
->hostname
, client
->printer
->port
, client
->printer
->name
, job
->id
, uuid
, sizeof(uuid
));
1183 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&job
->created
)));
1184 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1185 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1186 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uuid", NULL
, uuid
);
1187 if ((attr
= ippFindAttribute(client
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
1188 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, ippGetString(attr
, 0, NULL
));
1190 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
1191 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)(job
->created
- client
->printer
->start_time
));
1193 cupsArrayAdd(client
->printer
->jobs
, job
);
1194 client
->printer
->active_job
= job
;
1196 _cupsRWUnlock(&(client
->printer
->rwlock
));
1203 * 'create_job_file()' - Create a file for the document in a job.
1206 static int /* O - File descriptor or -1 on error */
1208 ippeve_job_t
*job
, /* I - Job */
1209 char *fname
, /* I - Filename buffer */
1210 size_t fnamesize
, /* I - Size of filename buffer */
1211 const char *directory
, /* I - Directory to store in */
1212 const char *ext
) /* I - Extension (`NULL` for default) */
1214 char name
[256], /* "Safe" filename */
1215 *nameptr
; /* Pointer into filename */
1216 const char *job_name
; /* job-name value */
1220 * Make a name from the job-name attribute...
1223 if ((job_name
= ippGetString(ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
), 0, NULL
)) == NULL
)
1224 job_name
= "untitled";
1226 for (nameptr
= name
; *job_name
&& nameptr
< (name
+ sizeof(name
) - 1); job_name
++)
1228 if (isalnum(*job_name
& 255) || *job_name
== '-')
1230 *nameptr
++ = (char)tolower(*job_name
& 255);
1236 while (job_name
[1] && !isalnum(job_name
[1] & 255) && job_name
[1] != '-')
1244 * Figure out the extension...
1249 if (!strcasecmp(job
->format
, "image/jpeg"))
1251 else if (!strcasecmp(job
->format
, "image/png"))
1253 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
1255 else if (!strcasecmp(job
->format
, "image/urf"))
1257 else if (!strcasecmp(job
->format
, "application/pdf"))
1259 else if (!strcasecmp(job
->format
, "application/postscript"))
1261 else if (!strcasecmp(job
->format
, "application/vnd.hp-pcl"))
1268 * Create a filename with the job-id, job-name, and document-format (extension)...
1271 snprintf(fname
, fnamesize
, "%s/%d-%s.%s", directory
, job
->id
, name
, ext
);
1273 return (open(fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666));
1278 * 'create_listener()' - Create a listener socket.
1281 static int /* O - Listener socket or -1 on error */
1282 create_listener(const char *name
, /* I - Host name (`NULL` for any address) */
1283 int port
, /* I - Port number */
1284 int family
) /* I - Address family */
1286 int sock
; /* Listener socket */
1287 http_addrlist_t
*addrlist
; /* Listen address */
1288 char service
[255]; /* Service port */
1291 snprintf(service
, sizeof(service
), "%d", port
);
1292 if ((addrlist
= httpAddrGetList(name
, family
, service
)) == NULL
)
1295 sock
= httpAddrListen(&(addrlist
->addr
), port
);
1297 httpAddrFreeList(addrlist
);
1304 * 'create_media_col()' - Create a media-col value.
1307 static ipp_t
* /* O - media-col collection */
1308 create_media_col(const char *media
, /* I - Media name */
1309 const char *source
, /* I - Media source, if any */
1310 const char *type
, /* I - Media type, if any */
1311 int width
, /* I - x-dimension in 2540ths */
1312 int length
, /* I - y-dimension in 2540ths */
1313 int bottom
, /* I - Bottom margin in 2540ths */
1314 int left
, /* I - Left margin in 2540ths */
1315 int right
, /* I - Right margin in 2540ths */
1316 int top
) /* I - Top margin in 2540ths */
1318 ipp_t
*media_col
= ippNew(), /* media-col value */
1319 *media_size
= create_media_size(width
, length
);
1320 /* media-size value */
1321 char media_key
[256]; /* media-key value */
1322 const char *media_key_suffix
= ""; /* media-key suffix */
1325 if (bottom
== 0 && left
== 0 && right
== 0 && top
== 0)
1326 media_key_suffix
= "_borderless";
1329 snprintf(media_key
, sizeof(media_key
), "%s_%s_%s%s", media
, source
, type
, media_key_suffix
);
1331 snprintf(media_key
, sizeof(media_key
), "%s__%s%s", media
, type
, media_key_suffix
);
1333 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, source
, media_key_suffix
);
1335 snprintf(media_key
, sizeof(media_key
), "%s%s", media
, media_key_suffix
);
1337 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
, media_key
);
1338 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1339 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-size-name", NULL
, media
);
1341 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin", bottom
);
1343 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin", left
);
1345 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin", right
);
1347 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin", top
);
1349 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source", NULL
, source
);
1351 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type", NULL
, type
);
1353 ippDelete(media_size
);
1360 * 'create_media_size()' - Create a media-size value.
1363 static ipp_t
* /* O - media-col collection */
1364 create_media_size(int width
, /* I - x-dimension in 2540ths */
1365 int length
) /* I - y-dimension in 2540ths */
1367 ipp_t
*media_size
= ippNew(); /* media-size value */
1370 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension", width
);
1371 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension", length
);
1373 return (media_size
);
1378 * 'create_printer()' - Create, register, and listen for connections to a
1382 static ippeve_printer_t
* /* O - Printer */
1384 const char *servername
, /* I - Server hostname (NULL for default) */
1385 int serverport
, /* I - Server port */
1386 const char *name
, /* I - printer-name */
1387 const char *location
, /* I - printer-location */
1388 const char *icon
, /* I - printer-icons */
1389 cups_array_t
*docformats
, /* I - document-format-supported */
1390 const char *subtypes
, /* I - Bonjour service subtype(s) */
1391 const char *directory
, /* I - Spool directory */
1392 const char *command
, /* I - Command to run on job files, if any */
1393 const char *device_uri
, /* I - Output device, if any */
1394 const char *output_format
, /* I - Output format, if any */
1395 ipp_t
*attrs
) /* I - Capability attributes */
1397 ippeve_printer_t
*printer
; /* Printer */
1398 int i
; /* Looping var */
1400 char path
[1024]; /* Full path to command */
1401 #endif /* !_WIN32 */
1402 char uri
[1024], /* Printer URI */
1404 securi
[1024], /* Secure printer URI */
1405 *uris
[2], /* All URIs */
1406 #endif /* HAVE_SSL */
1407 icons
[1024], /* printer-icons URI */
1408 adminurl
[1024], /* printer-more-info URI */
1409 supplyurl
[1024],/* printer-supply-info-uri URI */
1410 uuid
[128]; /* printer-uuid */
1411 int k_supported
; /* Maximum file size supported */
1412 int num_formats
; /* Number of supported document formats */
1413 const char *formats
[100], /* Supported document formats */
1414 *format
; /* Current format */
1415 int num_sup_attrs
; /* Number of supported attributes */
1416 const char *sup_attrs
[100];/* Supported attributes */
1417 char xxx_supported
[256];
1418 /* Name of -supported attribute */
1419 _cups_globals_t
*cg
= _cupsGlobals();
1420 /* Global path values */
1422 struct statvfs spoolinfo
; /* FS info for spool directory */
1423 double spoolsize
; /* FS size */
1424 #elif defined(HAVE_STATFS)
1425 struct statfs spoolinfo
; /* FS info for spool directory */
1426 double spoolsize
; /* FS size */
1427 #endif /* HAVE_STATVFS */
1428 static const char * const versions
[] =/* ipp-versions-supported values */
1433 static const char * const features
[] =/* ipp-features-supported values */
1437 static const int ops
[] = /* operations-supported values */
1441 IPP_OP_VALIDATE_JOB
,
1443 IPP_OP_SEND_DOCUMENT
,
1446 IPP_OP_GET_JOB_ATTRIBUTES
,
1448 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1449 IPP_OP_CANCEL_MY_JOBS
,
1451 IPP_OP_IDENTIFY_PRINTER
1453 static const char * const charsets
[] =/* charset-supported values */
1458 static const char * const compressions
[] =/* compression-supported values */
1463 #endif /* HAVE_LIBZ */
1466 static const char * const identify_actions
[] =
1471 static const char * const job_creation
[] =
1472 { /* job-creation-attributes-supported values */
1478 "document-metadata",
1480 "document-natural-language",
1481 "document-password",
1484 "ipp-attribute-fidelity",
1487 "job-accouunting-sheets",
1488 "job-accounting-user-id",
1489 "job-authorization-uri",
1493 "job-hold-until-time",
1494 "job-mandatory-attributes",
1495 "job-message-to-operator",
1497 "job-pages-per-set",
1499 "job-password-encryption",
1502 "job-recipient-name",
1504 "job-sheet-message",
1509 "multiple-document-handling",
1511 "orientation-requested",
1517 "presentation-direction-number-up",
1519 "print-content-optimize",
1521 "print-rendering-intent",
1523 "printer-resolution",
1529 "x-side1-image-shift",
1530 "x-side2-image-shift",
1533 "y-side1-image-shift",
1534 "y-side2-image-shift"
1536 static const char * const media_col_supported
[] =
1537 { /* media-col-supported values */
1538 "media-bottom-margin",
1539 "media-left-margin",
1540 "media-right-margin",
1547 static const char * const multiple_document_handling
[] =
1548 { /* multiple-document-handling-supported values */
1549 "separate-documents-uncollated-copies",
1550 "separate-documents-collated-copies"
1552 static const char * const reference_uri_schemes_supported
[] =
1553 { /* reference-uri-schemes-supported */
1559 #endif /* HAVE_SSL */
1562 static const char * const uri_authentication_supported
[] =
1563 { /* uri-authentication-supported values */
1567 static const char * const uri_authentication_basic
[] =
1568 { /* uri-authentication-supported values with authentication */
1572 static const char * const uri_security_supported
[] =
1573 { /* uri-security-supported values */
1577 #endif /* HAVE_SSL */
1578 static const char * const which_jobs
[] =
1579 { /* which-jobs-supported values */
1588 "processing-stopped"
1594 * If a command was specified, make sure it exists and is executable...
1599 if (*command
== '/' || !strncmp(command
, "./", 2))
1601 if (access(command
, X_OK
))
1603 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1609 snprintf(path
, sizeof(path
), "%s/command/%s", cg
->cups_serverbin
, command
);
1611 if (access(command
, X_OK
))
1613 _cupsLangPrintf(stderr
, _("Unable to execute command \"%s\": %s"), command
, strerror(errno
));
1620 #endif /* !_WIN32 */
1623 * Allocate memory for the printer...
1626 if ((printer
= calloc(1, sizeof(ippeve_printer_t
))) == NULL
)
1628 _cupsLangPrintError(NULL
, _("Unable to allocate memory for printer"));
1634 printer
->name
= strdup(name
);
1635 printer
->dnssd_name
= strdup(name
);
1636 printer
->command
= command
? strdup(command
) : NULL
;
1637 printer
->device_uri
= device_uri
? strdup(device_uri
) : NULL
;
1638 printer
->output_format
= output_format
? strdup(output_format
) : NULL
;
1639 printer
->directory
= strdup(directory
);
1640 printer
->icon
= icon
? strdup(icon
) : NULL
;
1641 printer
->port
= serverport
;
1642 printer
->start_time
= time(NULL
);
1643 printer
->config_time
= printer
->start_time
;
1644 printer
->state
= IPP_PSTATE_IDLE
;
1645 printer
->state_reasons
= IPPEVE_PREASON_NONE
;
1646 printer
->state_time
= printer
->start_time
;
1647 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1648 printer
->next_job_id
= 1;
1652 printer
->hostname
= strdup(servername
);
1656 char temp
[1024]; /* Temporary string */
1658 printer
->hostname
= strdup(httpGetHostname(NULL
, temp
, sizeof(temp
)));
1661 _cupsRWInit(&(printer
->rwlock
));
1664 * Create the listener sockets...
1667 if ((printer
->ipv4
= create_listener(servername
, printer
->port
, AF_INET
)) < 0)
1669 perror("Unable to create IPv4 listener");
1673 if ((printer
->ipv6
= create_listener(servername
, printer
->port
, AF_INET6
)) < 0)
1675 perror("Unable to create IPv6 listener");
1680 * Prepare URI values for the printer attributes...
1683 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1684 printer
->uri
= strdup(uri
);
1685 printer
->urilen
= strlen(uri
);
1688 httpAssembleURI(HTTP_URI_CODING_ALL
, securi
, sizeof(securi
), "ipps", NULL
, printer
->hostname
, printer
->port
, "/ipp/print");
1689 #endif /* HAVE_SSL */
1691 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/icon.png");
1692 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/");
1693 httpAssembleURI(HTTP_URI_CODING_ALL
, supplyurl
, sizeof(supplyurl
), WEB_SCHEME
, NULL
, printer
->hostname
, printer
->port
, "/supplies");
1694 httpAssembleUUID(printer
->hostname
, serverport
, name
, 0, uuid
, sizeof(uuid
));
1698 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1699 fprintf(stderr
, "printer-supply-info-uri=\"%s\"\n", supplyurl
);
1701 fprintf(stderr
, "printer-uri=\"%s\",\"%s\"\n", uri
, securi
);
1703 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1704 #endif /* HAVE_SSL */
1708 * Get the maximum spool size based on the size of the filesystem used for
1709 * the spool directory. If the host OS doesn't support the statfs call
1710 * or the filesystem is larger than 2TiB, always report INT_MAX.
1714 if (statvfs(printer
->directory
, &spoolinfo
))
1715 k_supported
= INT_MAX
;
1716 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1717 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1718 k_supported
= INT_MAX
;
1720 k_supported
= (int)spoolsize
;
1722 #elif defined(HAVE_STATFS)
1723 if (statfs(printer
->directory
, &spoolinfo
))
1724 k_supported
= INT_MAX
;
1725 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1726 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1727 k_supported
= INT_MAX
;
1729 k_supported
= (int)spoolsize
;
1732 k_supported
= INT_MAX
;
1733 #endif /* HAVE_STATVFS */
1736 * Assemble the final list of document formats...
1739 if (!cupsArrayFind(docformats
, (void *)"application/octet-stream"))
1740 cupsArrayAdd(docformats
, (void *)"application/octet-stream");
1742 for (num_formats
= 0, format
= (const char *)cupsArrayFirst(docformats
); format
&& num_formats
< (int)(sizeof(formats
) / sizeof(formats
[0])); format
= (const char *)cupsArrayNext(docformats
))
1743 formats
[num_formats
++] = format
;
1746 * Get the list of attributes that can be used when creating a job...
1750 sup_attrs
[num_sup_attrs
++] = "document-access";
1751 sup_attrs
[num_sup_attrs
++] = "document-charset";
1752 sup_attrs
[num_sup_attrs
++] = "document-format";
1753 sup_attrs
[num_sup_attrs
++] = "document-message";
1754 sup_attrs
[num_sup_attrs
++] = "document-metadata";
1755 sup_attrs
[num_sup_attrs
++] = "document-name";
1756 sup_attrs
[num_sup_attrs
++] = "document-natural-language";
1757 sup_attrs
[num_sup_attrs
++] = "ipp-attribute-fidelity";
1758 sup_attrs
[num_sup_attrs
++] = "job-name";
1759 sup_attrs
[num_sup_attrs
++] = "job-priority";
1761 for (i
= 0; i
< (int)(sizeof(job_creation
) / sizeof(job_creation
[0])) && num_sup_attrs
< (int)(sizeof(sup_attrs
) / sizeof(sup_attrs
[0])); i
++)
1763 snprintf(xxx_supported
, sizeof(xxx_supported
), "%s-supported", job_creation
[i
]);
1764 if (ippFindAttribute(attrs
, xxx_supported
, IPP_TAG_ZERO
))
1765 sup_attrs
[num_sup_attrs
++] = job_creation
[i
];
1769 * Fill out the rest of the printer attributes.
1772 printer
->attrs
= attrs
;
1774 /* charset-configured */
1775 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-configured", NULL
, "utf-8");
1777 /* charset-supported */
1778 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_CHARSET
), "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]), NULL
, charsets
);
1780 /* compression-supported */
1781 if (!ippFindAttribute(printer
->attrs
, "compression-supported", IPP_TAG_ZERO
))
1782 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "compression-supported", (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
, compressions
);
1784 /* document-format-default */
1785 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_MIMETYPE
), "document-format-default", NULL
, "application/octet-stream");
1787 /* document-format-supported */
1788 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
, "document-format-supported", num_formats
, NULL
, formats
);
1790 /* generated-natural-language-supported */
1791 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "generated-natural-language-supported", NULL
, "en");
1793 /* identify-actions-default */
1794 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1796 /* identify-actions-supported */
1797 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
);
1799 /* ipp-features-supported */
1800 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1802 /* ipp-versions-supported */
1803 if (MaxVersion
== 11)
1804 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", NULL
, "1.1");
1806 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", (int)(sizeof(versions
) / sizeof(versions
[0])), NULL
, versions
);
1808 /* job-creation-attributes-supported */
1809 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-creation-attributes-supported", num_sup_attrs
, NULL
, sup_attrs
);
1811 /* job-ids-supported */
1812 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1814 /* job-k-octets-supported */
1815 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0, k_supported
);
1817 /* job-priority-default */
1818 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-default", 50);
1820 /* job-priority-supported */
1821 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "job-priority-supported", 1);
1823 /* job-sheets-default */
1824 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-default", NULL
, "none");
1826 /* job-sheets-supported */
1827 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-sheets-supported", NULL
, "none");
1829 /* media-col-supported */
1830 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
);
1832 /* multiple-document-handling-supported */
1833 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
);
1835 /* multiple-document-jobs-supported */
1836 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1838 /* multiple-operation-time-out */
1839 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1841 /* multiple-operation-time-out-action */
1842 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1844 /* natural-language-configured */
1845 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_LANGUAGE
), "natural-language-configured", NULL
, "en");
1847 /* operations-supported */
1848 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1850 /* pdl-override-supported */
1851 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pdl-override-supported", NULL
, "attempted");
1853 /* preferred-attributes-supported */
1854 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1856 /* printer-get-attributes-supported */
1857 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1859 /* printer-geo-location */
1860 ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_UNKNOWN
, "printer-geo-location");
1863 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-icons", NULL
, icons
);
1865 /* printer-is-accepting-jobs */
1866 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1869 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info", NULL
, name
);
1871 /* printer-location */
1872 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location", NULL
, location
);
1874 /* printer-more-info */
1875 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-more-info", NULL
, adminurl
);
1878 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name", NULL
, name
);
1880 /* printer-organization */
1881 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "");
1883 /* printer-organizational-unit */
1884 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "");
1886 /* printer-supply-info-uri */
1887 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-supply-info-uri", NULL
, supplyurl
);
1889 /* printer-uri-supported */
1894 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", 2, NULL
, (const char **)uris
);
1897 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1898 #endif /* HAVE_SSL */
1901 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1903 /* reference-uri-scheme-supported */
1904 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
);
1906 /* uri-authentication-supported */
1909 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_basic
);
1911 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", 2, NULL
, uri_authentication_supported
);
1914 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "basic");
1916 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-authentication-supported", NULL
, "none");
1917 #endif /* HAVE_SSL */
1919 /* uri-security-supported */
1921 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", 2, NULL
, uri_security_supported
);
1923 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "uri-security-supported", NULL
, "none");
1924 #endif /* HAVE_SSL */
1926 /* which-jobs-supported */
1927 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
);
1929 debug_attributes("Printer", printer
->attrs
, 0);
1932 * Register the printer with Bonjour...
1935 if (!register_printer(printer
, subtypes
))
1946 * If we get here we were unable to create the printer...
1951 delete_printer(printer
);
1958 * 'debug_attributes()' - Print attributes in a request or response.
1962 debug_attributes(const char *title
, /* I - Title */
1963 ipp_t
*ipp
, /* I - Request/response */
1964 int type
) /* I - 0 = object, 1 = request, 2 = response */
1966 ipp_tag_t group_tag
; /* Current group */
1967 ipp_attribute_t
*attr
; /* Current attribute */
1968 char buffer
[2048]; /* String buffer for value */
1969 int major
, minor
; /* Version */
1975 fprintf(stderr
, "%s:\n", title
);
1976 major
= ippGetVersion(ipp
, &minor
);
1977 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1979 fprintf(stderr
, " operation-id=%s(%04x)\n",
1980 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1982 fprintf(stderr
, " status-code=%s(%04x)\n",
1983 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1984 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1986 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1988 attr
= ippNextAttribute(ipp
))
1990 if (ippGetGroupTag(attr
) != group_tag
)
1992 group_tag
= ippGetGroupTag(attr
);
1993 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1996 if (ippGetName(attr
))
1998 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1999 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
2000 ippGetCount(attr
) > 1 ? "1setOf " : "",
2001 ippTagString(ippGetValueTag(attr
)), buffer
);
2008 * 'delete_client()' - Close the socket and free all memory used by a client
2013 delete_client(ippeve_client_t
*client
) /* I - Client */
2016 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
2019 * Flush pending writes before closing...
2022 httpFlushWrite(client
->http
);
2028 httpClose(client
->http
);
2030 ippDelete(client
->request
);
2031 ippDelete(client
->response
);
2038 * 'delete_job()' - Remove from the printer and free all memory used by a job
2043 delete_job(ippeve_job_t
*job
) /* I - Job */
2046 fprintf(stderr
, "[Job %d] Removing job from history.\n", job
->id
);
2048 ippDelete(job
->attrs
);
2056 unlink(job
->filename
);
2058 free(job
->filename
);
2066 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2067 * used by a printer object.
2071 delete_printer(ippeve_printer_t
*printer
) /* I - Printer */
2073 if (printer
->ipv4
>= 0)
2074 close(printer
->ipv4
);
2076 if (printer
->ipv6
>= 0)
2077 close(printer
->ipv6
);
2080 if (printer
->printer_ref
)
2081 DNSServiceRefDeallocate(printer
->printer_ref
);
2082 if (printer
->ipp_ref
)
2083 DNSServiceRefDeallocate(printer
->ipp_ref
);
2084 if (printer
->ipps_ref
)
2085 DNSServiceRefDeallocate(printer
->ipps_ref
);
2086 if (printer
->http_ref
)
2087 DNSServiceRefDeallocate(printer
->http_ref
);
2088 #elif defined(HAVE_AVAHI)
2089 avahi_threaded_poll_lock(DNSSDMaster
);
2091 if (printer
->printer_ref
)
2092 avahi_entry_group_free(printer
->printer_ref
);
2093 if (printer
->ipp_ref
)
2094 avahi_entry_group_free(printer
->ipp_ref
);
2095 if (printer
->ipps_ref
)
2096 avahi_entry_group_free(printer
->ipps_ref
);
2097 if (printer
->http_ref
)
2098 avahi_entry_group_free(printer
->http_ref
);
2100 avahi_threaded_poll_unlock(DNSSDMaster
);
2101 #endif /* HAVE_DNSSD */
2103 if (printer
->dnssd_name
)
2104 free(printer
->dnssd_name
);
2106 free(printer
->name
);
2108 free(printer
->icon
);
2109 if (printer
->command
)
2110 free(printer
->command
);
2111 if (printer
->device_uri
)
2112 free(printer
->device_uri
);
2114 if (printer
->ppdfile
)
2115 free(printer
->ppdfile
);
2116 #endif /* !CUPS_LITE */
2117 if (printer
->directory
)
2118 free(printer
->directory
);
2119 if (printer
->hostname
)
2120 free(printer
->hostname
);
2124 ippDelete(printer
->attrs
);
2125 cupsArrayDelete(printer
->jobs
);
2133 * 'dnssd_callback()' - Handle Bonjour registration events.
2136 static void DNSSD_API
2138 DNSServiceRef sdRef
, /* I - Service reference */
2139 DNSServiceFlags flags
, /* I - Status flags */
2140 DNSServiceErrorType errorCode
, /* I - Error, if any */
2141 const char *name
, /* I - Service name */
2142 const char *regtype
, /* I - Service type */
2143 const char *domain
, /* I - Domain for service */
2144 ippeve_printer_t
*printer
) /* I - Printer */
2152 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n", regtype
, (int)errorCode
);
2155 else if (strcasecmp(name
, printer
->dnssd_name
))
2158 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2160 /* No lock needed since only the main thread accesses/changes this */
2161 free(printer
->dnssd_name
);
2162 printer
->dnssd_name
= strdup(name
);
2167 #elif defined(HAVE_AVAHI)
2169 * 'dnssd_callback()' - Handle Bonjour registration events.
2174 AvahiEntryGroup
*srv
, /* I - Service */
2175 AvahiEntryGroupState state
, /* I - Registration state */
2176 void *context
) /* I - Printer */
2185 * 'dnssd_client_cb()' - Client callback for Avahi.
2187 * Called whenever the client or server state changes...
2192 AvahiClient
*c
, /* I - Client */
2193 AvahiClientState state
, /* I - Current state */
2194 void *userdata
) /* I - User data (unused) */
2204 fprintf(stderr
, "Ignored Avahi state %d.\n", state
);
2207 case AVAHI_CLIENT_FAILURE
:
2208 if (avahi_client_errno(c
) == AVAHI_ERR_DISCONNECTED
)
2210 fputs("Avahi server crashed, exiting.\n", stderr
);
2216 #endif /* HAVE_DNSSD */
2220 * 'dnssd_init()' - Initialize the DNS-SD service connections...
2227 if (DNSServiceCreateConnection(&DNSSDMaster
) != kDNSServiceErr_NoError
)
2229 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2233 #elif defined(HAVE_AVAHI)
2234 int error
; /* Error code, if any */
2236 if ((DNSSDMaster
= avahi_threaded_poll_new()) == NULL
)
2238 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2242 if ((DNSSDClient
= avahi_client_new(avahi_threaded_poll_get(DNSSDMaster
), AVAHI_CLIENT_NO_FAIL
, dnssd_client_cb
, NULL
, &error
)) == NULL
)
2244 fputs("Error: Unable to initialize Bonjour.\n", stderr
);
2248 avahi_threaded_poll_start(DNSSDMaster
);
2249 #endif /* HAVE_DNSSD */
2254 * 'filter_cb()' - Filter printer attributes based on the requested array.
2257 static int /* O - 1 to copy, 0 to ignore */
2258 filter_cb(ippeve_filter_t
*filter
, /* I - Filter parameters */
2259 ipp_t
*dst
, /* I - Destination (unused) */
2260 ipp_attribute_t
*attr
) /* I - Source attribute */
2263 * Filter attributes as needed...
2266 #ifndef _WIN32 /* Avoid MS compiler bug */
2268 #endif /* !_WIN32 */
2270 ipp_tag_t group
= ippGetGroupTag(attr
);
2271 const char *name
= ippGetName(attr
);
2273 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
)))
2276 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2281 * 'find_job()' - Find a job specified in a request.
2284 static ippeve_job_t
* /* O - Job or NULL */
2285 find_job(ippeve_client_t
*client
) /* I - Client */
2287 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2288 ippeve_job_t key
, /* Job search key */
2289 *job
; /* Matching job, if any */
2292 if ((attr
= ippFindAttribute(client
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
2294 const char *uri
= ippGetString(attr
, 0, NULL
);
2296 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2297 uri
[client
->printer
->urilen
] == '/')
2298 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2302 else if ((attr
= ippFindAttribute(client
->request
, "job-id", IPP_TAG_INTEGER
)) != NULL
)
2303 key
.id
= ippGetInteger(attr
, 0);
2305 _cupsRWLockRead(&(client
->printer
->rwlock
));
2306 job
= (ippeve_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2307 _cupsRWUnlock(&(client
->printer
->rwlock
));
2314 * 'finish_document()' - Finish receiving a document file and start processing.
2318 finish_document_data(
2319 ippeve_client_t
*client
, /* I - Client */
2320 ippeve_job_t
*job
) /* I - Job */
2322 char filename
[1024], /* Filename buffer */
2323 buffer
[4096]; /* Copy buffer */
2324 ssize_t bytes
; /* Bytes read */
2325 cups_array_t
*ra
; /* Attributes to send in response */
2326 _cups_thread_t t
; /* Thread */
2330 * Create a file for the request data...
2332 * TODO: Update code to support piping large raster data to the print command.
2335 if ((job
->fd
= create_job_file(job
, filename
, sizeof(filename
), client
->printer
->directory
, NULL
)) < 0)
2337 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2343 fprintf(stderr
, "Created job file \"%s\", format \"%s\".\n", filename
, job
->format
);
2345 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2347 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2349 int error
= errno
; /* Write error */
2356 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2365 * Got an error while reading the print data, so abort this job.
2373 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to read print file.");
2380 int error
= errno
; /* Write error */
2386 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2392 job
->filename
= strdup(filename
);
2393 job
->state
= IPP_JSTATE_PENDING
;
2396 * Process the job...
2399 t
= _cupsThreadCreate((_cups_thread_func_t
)process_job
, job
);
2403 _cupsThreadDetach(t
);
2407 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2412 * Return the job info...
2415 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2417 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2418 cupsArrayAdd(ra
, "job-id");
2419 cupsArrayAdd(ra
, "job-state");
2420 cupsArrayAdd(ra
, "job-state-message");
2421 cupsArrayAdd(ra
, "job-state-reasons");
2422 cupsArrayAdd(ra
, "job-uri");
2424 copy_job_attributes(client
, job
, ra
);
2425 cupsArrayDelete(ra
);
2429 * If we get here we had to abort the job...
2434 job
->state
= IPP_JSTATE_ABORTED
;
2435 job
->completed
= time(NULL
);
2437 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2438 cupsArrayAdd(ra
, "job-id");
2439 cupsArrayAdd(ra
, "job-state");
2440 cupsArrayAdd(ra
, "job-state-reasons");
2441 cupsArrayAdd(ra
, "job-uri");
2443 copy_job_attributes(client
, job
, ra
);
2444 cupsArrayDelete(ra
);
2449 * 'finish_uri()' - Finish fetching a document URI and start processing.
2453 finish_document_uri(
2454 ippeve_client_t
*client
, /* I - Client */
2455 ippeve_job_t
*job
) /* I - Job */
2457 ipp_attribute_t
*uri
; /* document-uri */
2458 char scheme
[256], /* URI scheme */
2459 userpass
[256], /* Username and password info */
2460 hostname
[256], /* Hostname */
2461 resource
[1024]; /* Resource path */
2462 int port
; /* Port number */
2463 http_uri_status_t uri_status
; /* URI decode status */
2464 http_encryption_t encryption
; /* Encryption to use, if any */
2465 http_t
*http
; /* Connection for http/https URIs */
2466 http_status_t status
; /* Access status for http/https URIs */
2467 int infile
; /* Input file for local file URIs */
2468 char filename
[1024], /* Filename buffer */
2469 buffer
[4096]; /* Copy buffer */
2470 ssize_t bytes
; /* Bytes read */
2471 ipp_attribute_t
*attr
; /* Current attribute */
2472 cups_array_t
*ra
; /* Attributes to send in response */
2476 * Do we have a file to print?
2479 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2481 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Unexpected document data following request.");
2487 * Do we have a document URI?
2490 if ((uri
= ippFindAttribute(client
->request
, "document-uri", IPP_TAG_URI
)) == NULL
)
2492 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
2497 if (ippGetCount(uri
) != 1)
2499 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Too many document-uri values.");
2504 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
2505 scheme
, sizeof(scheme
), userpass
,
2506 sizeof(userpass
), hostname
, sizeof(hostname
),
2507 &port
, resource
, sizeof(resource
));
2508 if (uri_status
< HTTP_URI_STATUS_OK
)
2510 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s", httpURIStatusString(uri_status
));
2515 if (strcmp(scheme
, "file") &&
2517 strcmp(scheme
, "https") &&
2518 #endif /* HAVE_SSL */
2519 strcmp(scheme
, "http"))
2521 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
, "URI scheme \"%s\" not supported.", scheme
);
2526 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
2528 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
2534 * Get the document format for the job...
2537 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2539 if ((attr
= ippFindAttribute(job
->attrs
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
2540 job
->format
= ippGetString(attr
, 0, NULL
);
2542 job
->format
= "application/octet-stream";
2545 * Create a file for the request data...
2548 if ((job
->fd
= create_job_file(job
, filename
, sizeof(filename
), client
->printer
->directory
, NULL
)) < 0)
2550 _cupsRWUnlock(&(client
->printer
->rwlock
));
2552 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to create print file: %s", strerror(errno
));
2557 _cupsRWUnlock(&(client
->printer
->rwlock
));
2559 if (!strcmp(scheme
, "file"))
2561 if ((infile
= open(resource
, O_RDONLY
)) < 0)
2563 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to access URI: %s", strerror(errno
));
2570 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
2571 (errno
== EAGAIN
|| errno
== EINTR
))
2575 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2577 int error
= errno
; /* Write error */
2585 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2597 if (port
== 443 || !strcmp(scheme
, "https"))
2598 encryption
= HTTP_ENCRYPTION_ALWAYS
;
2600 #endif /* HAVE_SSL */
2601 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
2603 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
, 1, 30000, NULL
)) == NULL
)
2605 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to connect to %s: %s", hostname
, cupsLastErrorString());
2615 httpClearFields(http
);
2616 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
2617 if (httpGet(http
, resource
))
2619 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", strerror(errno
));
2630 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
2632 if (status
!= HTTP_STATUS_OK
)
2634 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
, "Unable to GET URI: %s", httpStatus(status
));
2645 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
2647 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2649 int error
= errno
; /* Write error */
2657 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2658 "Unable to write print file: %s", strerror(error
));
2669 int error
= errno
; /* Write error */
2675 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to write print file: %s", strerror(error
));
2680 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2683 job
->filename
= strdup(filename
);
2684 job
->state
= IPP_JSTATE_PENDING
;
2686 _cupsRWUnlock(&(client
->printer
->rwlock
));
2689 * Process the job...
2695 * Return the job info...
2698 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2700 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2701 cupsArrayAdd(ra
, "job-id");
2702 cupsArrayAdd(ra
, "job-state");
2703 cupsArrayAdd(ra
, "job-state-reasons");
2704 cupsArrayAdd(ra
, "job-uri");
2706 copy_job_attributes(client
, job
, ra
);
2707 cupsArrayDelete(ra
);
2711 * If we get here we had to abort the job...
2716 job
->state
= IPP_JSTATE_ABORTED
;
2717 job
->completed
= time(NULL
);
2719 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2720 cupsArrayAdd(ra
, "job-id");
2721 cupsArrayAdd(ra
, "job-state");
2722 cupsArrayAdd(ra
, "job-state-reasons");
2723 cupsArrayAdd(ra
, "job-uri");
2725 copy_job_attributes(client
, job
, ra
);
2726 cupsArrayDelete(ra
);
2731 * 'html_escape()' - Write a HTML-safe string.
2735 html_escape(ippeve_client_t
*client
, /* I - Client */
2736 const char *s
, /* I - String to write */
2737 size_t slen
) /* I - Number of characters to write */
2739 const char *start
, /* Start of segment */
2740 *end
; /* End of string */
2744 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2746 while (*s
&& s
< end
)
2748 if (*s
== '&' || *s
== '<')
2751 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2754 httpWrite2(client
->http
, "&", 5);
2756 httpWrite2(client
->http
, "<", 4);
2765 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2770 * 'html_footer()' - Show the web interface footer.
2772 * This function also writes the trailing 0-length chunk.
2776 html_footer(ippeve_client_t
*client
) /* I - Client */
2782 httpWrite2(client
->http
, "", 0);
2787 * 'html_header()' - Show the web interface header and title.
2791 html_header(ippeve_client_t
*client
, /* I - Client */
2792 const char *title
, /* I - Title */
2793 int refresh
) /* I - Refresh timer, if any */
2799 "<title>%s</title>\n"
2800 "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2801 "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2802 "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title
);
2804 html_printf(client
, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh
);
2806 "<meta name=\"viewport\" content=\"width=device-width\">\n"
2808 "body { font-family: sans-serif; margin: 0; }\n"
2809 "div.body { padding: 0px 10px 10px; }\n"
2810 "span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
2811 "span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
2812 "table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
2813 "table.form td, table.form th { padding: 5px 2px; }\n"
2814 "table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
2815 "table.form th { text-align: right; }\n"
2816 "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2817 "table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2818 "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2819 "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2820 "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2821 "table.nav { border-collapse: collapse; width: 100%%; }\n"
2822 "table.nav td { margin: 0; text-align: center; }\n"
2823 "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"
2824 "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2825 "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2826 "td.nav:hover { background: #666; color: #fff; }\n"
2827 "td.nav:active { background: #000; color: #ff0; }\n"
2831 "<table class=\"nav\"><tr>"
2832 "<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2833 "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2834 "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2836 "<div class=\"body\">\n", !strcmp(client
->uri
, "/") ? " sel" : "", !strcmp(client
->uri
, "/supplies") ? " sel" : "", !strcmp(client
->uri
, "/media") ? " sel" : "");
2841 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2845 html_printf(ippeve_client_t
*client
, /* I - Client */
2846 const char *format
, /* I - Printf-style format string */
2847 ...) /* I - Additional arguments as needed */
2849 va_list ap
; /* Pointer to arguments */
2850 const char *start
; /* Start of string */
2851 char size
, /* Size character (h, l, L) */
2852 type
; /* Format type character */
2853 int width
, /* Width of field */
2854 prec
; /* Number of characters of precision */
2855 char tformat
[100], /* Temporary format string for sprintf() */
2856 *tptr
, /* Pointer into temporary format */
2857 temp
[1024]; /* Buffer for formatted numbers */
2858 char *s
; /* Pointer to string */
2862 * Loop through the format string, formatting as needed...
2865 va_start(ap
, format
);
2873 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2876 *tptr
++ = *format
++;
2880 httpWrite2(client
->http
, "%", 1);
2885 else if (strchr(" -+#\'", *format
))
2886 *tptr
++ = *format
++;
2891 * Get width from argument...
2895 width
= va_arg(ap
, int);
2897 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2898 tptr
+= strlen(tptr
);
2904 while (isdigit(*format
& 255))
2906 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2909 width
= width
* 10 + *format
++ - '0';
2915 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2923 * Get precision from argument...
2927 prec
= va_arg(ap
, int);
2929 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2930 tptr
+= strlen(tptr
);
2936 while (isdigit(*format
& 255))
2938 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2941 prec
= prec
* 10 + *format
++ - '0';
2946 if (*format
== 'l' && format
[1] == 'l')
2950 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2958 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2960 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2975 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2984 case 'E' : /* Floating point formats */
2989 if ((size_t)(width
+ 2) > sizeof(temp
))
2992 sprintf(temp
, tformat
, va_arg(ap
, double));
2994 httpWrite2(client
->http
, temp
, strlen(temp
));
2997 case 'B' : /* Integer formats */
3005 if ((size_t)(width
+ 2) > sizeof(temp
))
3008 # ifdef HAVE_LONG_LONG
3010 sprintf(temp
, tformat
, va_arg(ap
, long long));
3012 # endif /* HAVE_LONG_LONG */
3014 sprintf(temp
, tformat
, va_arg(ap
, long));
3016 sprintf(temp
, tformat
, va_arg(ap
, int));
3018 httpWrite2(client
->http
, temp
, strlen(temp
));
3021 case 'p' : /* Pointer value */
3022 if ((size_t)(width
+ 2) > sizeof(temp
))
3025 sprintf(temp
, tformat
, va_arg(ap
, void *));
3027 httpWrite2(client
->http
, temp
, strlen(temp
));
3030 case 'c' : /* Character or character array */
3033 temp
[0] = (char)va_arg(ap
, int);
3035 html_escape(client
, temp
, 1);
3038 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
3041 case 's' : /* String */
3042 if ((s
= va_arg(ap
, char *)) == NULL
)
3045 html_escape(client
, s
, strlen(s
));
3054 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
3061 * 'ipp_cancel_job()' - Cancel a job.
3065 ipp_cancel_job(ippeve_client_t
*client
) /* I - Client */
3067 ippeve_job_t
*job
; /* Job information */
3074 if ((job
= find_job(client
)) == NULL
)
3076 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3081 * See if the job is already completed, canceled, or aborted; if so,
3082 * we can't cancel...
3087 case IPP_JSTATE_CANCELED
:
3088 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3089 "Job #%d is already canceled - can\'t cancel.", job
->id
);
3092 case IPP_JSTATE_ABORTED
:
3093 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3094 "Job #%d is already aborted - can\'t cancel.", job
->id
);
3097 case IPP_JSTATE_COMPLETED
:
3098 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3099 "Job #%d is already completed - can\'t cancel.", job
->id
);
3107 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3109 if (job
->state
== IPP_JSTATE_PROCESSING
||
3110 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
3114 job
->state
= IPP_JSTATE_CANCELED
;
3115 job
->completed
= time(NULL
);
3118 _cupsRWUnlock(&(client
->printer
->rwlock
));
3120 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3127 * 'ipp_close_job()' - Close an open job.
3131 ipp_close_job(ippeve_client_t
*client
) /* I - Client */
3133 ippeve_job_t
*job
; /* Job information */
3140 if ((job
= find_job(client
)) == NULL
)
3142 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3147 * See if the job is already completed, canceled, or aborted; if so,
3148 * we can't cancel...
3153 case IPP_JSTATE_CANCELED
:
3154 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3155 "Job #%d is canceled - can\'t close.", job
->id
);
3158 case IPP_JSTATE_ABORTED
:
3159 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3160 "Job #%d is aborted - can\'t close.", job
->id
);
3163 case IPP_JSTATE_COMPLETED
:
3164 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3165 "Job #%d is completed - can\'t close.", job
->id
);
3168 case IPP_JSTATE_PROCESSING
:
3169 case IPP_JSTATE_STOPPED
:
3170 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3171 "Job #%d is already closed.", job
->id
);
3175 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3182 * 'ipp_create_job()' - Create a job object.
3186 ipp_create_job(ippeve_client_t
*client
) /* I - Client */
3188 ippeve_job_t
*job
; /* New job */
3189 cups_array_t
*ra
; /* Attributes to send in response */
3193 * Validate print job attributes...
3196 if (!valid_job_attributes(client
))
3198 httpFlush(client
->http
);
3203 * Do we have a file to print?
3206 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3208 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3209 "Unexpected document data following request.");
3217 if ((job
= create_job(client
)) == NULL
)
3219 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3220 "Currently printing another job.");
3225 * Return the job info...
3228 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3230 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3231 cupsArrayAdd(ra
, "job-id");
3232 cupsArrayAdd(ra
, "job-state");
3233 cupsArrayAdd(ra
, "job-state-message");
3234 cupsArrayAdd(ra
, "job-state-reasons");
3235 cupsArrayAdd(ra
, "job-uri");
3237 copy_job_attributes(client
, job
, ra
);
3238 cupsArrayDelete(ra
);
3243 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
3247 ipp_get_job_attributes(
3248 ippeve_client_t
*client
) /* I - Client */
3250 ippeve_job_t
*job
; /* Job */
3251 cups_array_t
*ra
; /* requested-attributes */
3254 if ((job
= find_job(client
)) == NULL
)
3256 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
3260 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3262 ra
= ippCreateRequestedArray(client
->request
);
3263 copy_job_attributes(client
, job
, ra
);
3264 cupsArrayDelete(ra
);
3269 * 'ipp_get_jobs()' - Get a list of job objects.
3273 ipp_get_jobs(ippeve_client_t
*client
) /* I - Client */
3275 ipp_attribute_t
*attr
; /* Current attribute */
3276 const char *which_jobs
= NULL
;
3277 /* which-jobs values */
3278 int job_comparison
; /* Job comparison */
3279 ipp_jstate_t job_state
; /* job-state value */
3280 int first_job_id
, /* First job ID */
3281 limit
, /* Maximum number of jobs to return */
3282 count
; /* Number of jobs that match */
3283 const char *username
; /* Username */
3284 ippeve_job_t
*job
; /* Current job pointer */
3285 cups_array_t
*ra
; /* Requested attributes array */
3289 * See if the "which-jobs" attribute have been specified...
3292 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
3293 IPP_TAG_KEYWORD
)) != NULL
)
3295 which_jobs
= ippGetString(attr
, 0, NULL
);
3296 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
3299 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
3301 job_comparison
= -1;
3302 job_state
= IPP_JSTATE_STOPPED
;
3304 else if (!strcmp(which_jobs
, "completed"))
3307 job_state
= IPP_JSTATE_CANCELED
;
3309 else if (!strcmp(which_jobs
, "aborted"))
3312 job_state
= IPP_JSTATE_ABORTED
;
3314 else if (!strcmp(which_jobs
, "all"))
3317 job_state
= IPP_JSTATE_PENDING
;
3319 else if (!strcmp(which_jobs
, "canceled"))
3322 job_state
= IPP_JSTATE_CANCELED
;
3324 else if (!strcmp(which_jobs
, "pending"))
3327 job_state
= IPP_JSTATE_PENDING
;
3329 else if (!strcmp(which_jobs
, "pending-held"))
3332 job_state
= IPP_JSTATE_HELD
;
3334 else if (!strcmp(which_jobs
, "processing"))
3337 job_state
= IPP_JSTATE_PROCESSING
;
3339 else if (!strcmp(which_jobs
, "processing-stopped"))
3342 job_state
= IPP_JSTATE_STOPPED
;
3346 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
3347 "The which-jobs value \"%s\" is not supported.", which_jobs
);
3348 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3349 "which-jobs", NULL
, which_jobs
);
3354 * See if they want to limit the number of jobs reported...
3357 if ((attr
= ippFindAttribute(client
->request
, "limit",
3358 IPP_TAG_INTEGER
)) != NULL
)
3360 limit
= ippGetInteger(attr
, 0);
3362 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
3367 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
3368 IPP_TAG_INTEGER
)) != NULL
)
3370 first_job_id
= ippGetInteger(attr
, 0);
3372 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
, first_job_id
);
3378 * See if we only want to see jobs for a specific user...
3383 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
3384 IPP_TAG_BOOLEAN
)) != NULL
)
3386 int my_jobs
= ippGetBoolean(attr
, 0);
3388 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
, my_jobs
? "true" : "false");
3392 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
3393 IPP_TAG_NAME
)) == NULL
)
3395 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3396 "Need requesting-user-name with my-jobs.");
3400 username
= ippGetString(attr
, 0, NULL
);
3402 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n", client
->hostname
, username
);
3407 * OK, build a list of jobs for this printer...
3410 ra
= ippCreateRequestedArray(client
->request
);
3412 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3414 _cupsRWLockRead(&(client
->printer
->rwlock
));
3416 for (count
= 0, job
= (ippeve_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
3417 (limit
<= 0 || count
< limit
) && job
;
3418 job
= (ippeve_job_t
*)cupsArrayNext(client
->printer
->jobs
))
3421 * Filter out jobs that don't match...
3424 if ((job_comparison
< 0 && job
->state
> job_state
) ||
3425 (job_comparison
== 0 && job
->state
!= job_state
) ||
3426 (job_comparison
> 0 && job
->state
< job_state
) ||
3427 job
->id
< first_job_id
||
3428 (username
&& job
->username
&&
3429 strcasecmp(username
, job
->username
)))
3433 ippAddSeparator(client
->response
);
3436 copy_job_attributes(client
, job
, ra
);
3439 cupsArrayDelete(ra
);
3441 _cupsRWUnlock(&(client
->printer
->rwlock
));
3446 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3450 ipp_get_printer_attributes(
3451 ippeve_client_t
*client
) /* I - Client */
3453 cups_array_t
*ra
; /* Requested attributes array */
3454 ippeve_printer_t
*printer
; /* Printer */
3458 * Send the attributes...
3461 ra
= ippCreateRequestedArray(client
->request
);
3462 printer
= client
->printer
;
3464 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3466 _cupsRWLockRead(&(printer
->rwlock
));
3468 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
3469 IPP_TAG_CUPS_CONST
);
3471 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-date-time"))
3472 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-config-change-date-time", ippTimeToDate(printer
->config_time
));
3474 if (!ra
|| cupsArrayFind(ra
, "printer-config-change-time"))
3475 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-config-change-time", (int)(printer
->config_time
- printer
->start_time
));
3477 if (!ra
|| cupsArrayFind(ra
, "printer-current-time"))
3478 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-current-time", ippTimeToDate(time(NULL
)));
3481 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
3482 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state", (int)printer
->state
);
3484 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-date-time"))
3485 ippAddDate(client
->response
, IPP_TAG_PRINTER
, "printer-state-change-date-time", ippTimeToDate(printer
->state_time
));
3487 if (!ra
|| cupsArrayFind(ra
, "printer-state-change-time"))
3488 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-state-change-time", (int)(printer
->state_time
- printer
->start_time
));
3490 if (!ra
|| cupsArrayFind(ra
, "printer-state-message"))
3492 static const char * const messages
[] = { "Idle.", "Printing.", "Stopped." };
3494 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-state-message", NULL
, messages
[printer
->state
- IPP_PSTATE_IDLE
]);
3497 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
3499 if (printer
->state_reasons
== IPPEVE_PREASON_NONE
)
3501 ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-state-reasons", NULL
, "none");
3505 ipp_attribute_t
*attr
= NULL
; /* printer-state-reasons */
3506 ippeve_preason_t bit
; /* Reason bit */
3507 int i
; /* Looping var */
3508 char reason
[32]; /* Reason string */
3510 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
3512 if (printer
->state_reasons
& bit
)
3514 snprintf(reason
, sizeof(reason
), "%s-%s", ippeve_preason_strings
[i
], printer
->state
== IPP_PSTATE_IDLE
? "report" : printer
->state
== IPP_PSTATE_PROCESSING
? "warning" : "error");
3516 ippSetString(client
->response
, &attr
, ippGetCount(attr
), reason
);
3518 attr
= ippAddString(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, reason
);
3524 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
3525 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "printer-up-time", (int)(time(NULL
) - printer
->start_time
));
3527 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
3528 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "queued-job-count", printer
->active_job
&& printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
3530 _cupsRWUnlock(&(printer
->rwlock
));
3532 cupsArrayDelete(ra
);
3537 * 'ipp_identify_printer()' - Beep or display a message.
3541 ipp_identify_printer(
3542 ippeve_client_t
*client
) /* I - Client */
3544 ipp_attribute_t
*actions
, /* identify-actions */
3545 *message
; /* message */
3548 actions
= ippFindAttribute(client
->request
, "identify-actions", IPP_TAG_KEYWORD
);
3549 message
= ippFindAttribute(client
->request
, "message", IPP_TAG_TEXT
);
3551 if (!actions
|| ippContainsString(actions
, "sound"))
3557 if (ippContainsString(actions
, "display"))
3558 printf("IDENTIFY from %s: %s\n", client
->hostname
, message
? ippGetString(message
, 0, NULL
) : "No message supplied");
3560 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3565 * 'ipp_print_job()' - Create a job object with an attached document.
3569 ipp_print_job(ippeve_client_t
*client
) /* I - Client */
3571 ippeve_job_t
*job
; /* New job */
3575 * Validate print job attributes...
3578 if (!valid_job_attributes(client
))
3580 httpFlush(client
->http
);
3585 * Do we have a file to print?
3588 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
3590 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
3598 if ((job
= create_job(client
)) == NULL
)
3600 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3605 * Then finish getting the document data and process things...
3608 finish_document_data(client
, job
);
3613 * 'ipp_print_uri()' - Create a job object with a referenced document.
3617 ipp_print_uri(ippeve_client_t
*client
) /* I - Client */
3619 ippeve_job_t
*job
; /* New job */
3623 * Validate print job attributes...
3626 if (!valid_job_attributes(client
))
3628 httpFlush(client
->http
);
3636 if ((job
= create_job(client
)) == NULL
)
3638 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
, "Currently printing another job.");
3643 * Then finish getting the document data and process things...
3646 finish_document_uri(client
, job
);
3651 * 'ipp_send_document()' - Add an attached document to a job object created with
3657 ippeve_client_t
*client
) /* I - Client */
3659 ippeve_job_t
*job
; /* Job information */
3660 ipp_attribute_t
*attr
; /* Current attribute */
3667 if ((job
= find_job(client
)) == NULL
)
3669 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3670 httpFlush(client
->http
);
3675 * See if we already have a document for this job or the job has already
3676 * in a non-pending state...
3679 if (job
->state
> IPP_JSTATE_HELD
)
3681 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job is not in a pending state.");
3682 httpFlush(client
->http
);
3685 else if (job
->filename
|| job
->fd
>= 0)
3687 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
, "Multiple document jobs are not supported.");
3688 httpFlush(client
->http
);
3693 * Make sure we have the "last-document" operation attribute...
3696 if ((attr
= ippFindAttribute(client
->request
, "last-document", IPP_TAG_ZERO
)) == NULL
)
3698 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing required last-document attribute.");
3699 httpFlush(client
->http
);
3702 else if (ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
3704 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The last-document attribute is not in the operation group.");
3705 httpFlush(client
->http
);
3708 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 || !ippGetBoolean(attr
, 0))
3710 respond_unsupported(client
, attr
);
3711 httpFlush(client
->http
);
3716 * Validate document attributes...
3719 if (!valid_doc_attributes(client
))
3721 httpFlush(client
->http
);
3726 * Then finish getting the document data and process things...
3729 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3731 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3733 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3734 job
->format
= ippGetString(attr
, 0, NULL
);
3735 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3736 job
->format
= ippGetString(attr
, 0, NULL
);
3738 job
->format
= "application/octet-stream";
3740 _cupsRWUnlock(&(client
->printer
->rwlock
));
3742 finish_document_data(client
, job
);
3747 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3752 ipp_send_uri(ippeve_client_t
*client
) /* I - Client */
3754 ippeve_job_t
*job
; /* Job information */
3755 ipp_attribute_t
*attr
; /* Current attribute */
3762 if ((job
= find_job(client
)) == NULL
)
3764 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3765 httpFlush(client
->http
);
3770 * See if we already have a document for this job or the job has already
3771 * in a non-pending state...
3774 if (job
->state
> IPP_JSTATE_HELD
)
3776 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
, "Job is not in a pending state.");
3777 httpFlush(client
->http
);
3780 else if (job
->filename
|| job
->fd
>= 0)
3782 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
, "Multiple document jobs are not supported.");
3783 httpFlush(client
->http
);
3787 if ((attr
= ippFindAttribute(client
->request
, "last-document", IPP_TAG_ZERO
)) == NULL
)
3789 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing required last-document attribute.");
3790 httpFlush(client
->http
);
3793 else if (ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
3795 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "The last-document attribute is not in the operation group.");
3796 httpFlush(client
->http
);
3799 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 || !ippGetBoolean(attr
, 0))
3801 respond_unsupported(client
, attr
);
3802 httpFlush(client
->http
);
3807 * Validate document attributes...
3810 if (!valid_doc_attributes(client
))
3812 httpFlush(client
->http
);
3817 * Then finish getting the document data and process things...
3820 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3822 copy_attributes(job
->attrs
, client
->request
, NULL
, IPP_TAG_JOB
, 0);
3824 if ((attr
= ippFindAttribute(job
->attrs
, "document-format-detected", IPP_TAG_MIMETYPE
)) != NULL
)
3825 job
->format
= ippGetString(attr
, 0, NULL
);
3826 else if ((attr
= ippFindAttribute(job
->attrs
, "document-format-supplied", IPP_TAG_MIMETYPE
)) != NULL
)
3827 job
->format
= ippGetString(attr
, 0, NULL
);
3829 job
->format
= "application/octet-stream";
3831 _cupsRWUnlock(&(client
->printer
->rwlock
));
3833 finish_document_uri(client
, job
);
3838 * 'ipp_validate_job()' - Validate job creation attributes.
3842 ipp_validate_job(ippeve_client_t
*client
) /* I - Client */
3844 if (valid_job_attributes(client
))
3845 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3850 * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
3853 static int /* O - 1 to use, 0 to ignore */
3855 _ipp_file_t
*f
, /* I - IPP file */
3856 void *user_data
, /* I - User data pointer (unused) */
3857 const char *attr
) /* I - Attribute name */
3859 int i
, /* Current element */
3860 result
; /* Result of comparison */
3861 static const char * const ignored
[] =
3862 { /* Ignored attributes */
3863 "attributes-charset",
3864 "attributes-natural-language",
3865 "charset-configured",
3866 "charset-supported",
3867 "device-service-count",
3869 "document-format-varying-attributes",
3870 "generated-natural-language-supported",
3871 "identify-actions-default",
3872 "identify-actions-supported",
3873 "ipp-features-supported",
3874 "ipp-versions-supproted",
3875 "ippget-event-life",
3876 "job-hold-until-supported",
3877 "job-hold-until-time-supported",
3878 "job-ids-supported",
3879 "job-k-octets-supported",
3880 "job-settable-attributes-supported",
3881 "multiple-document-jobs-supported",
3882 "multiple-operation-time-out",
3883 "multiple-operation-time-out-action",
3884 "natural-language-configured",
3885 "notify-attributes-supported",
3886 "notify-events-default",
3887 "notify-events-supported",
3888 "notify-lease-duration-default",
3889 "notify-lease-duration-supported",
3890 "notify-max-events-supported",
3891 "notify-pull-method-supported",
3892 "operations-supported",
3894 "printer-alert-description",
3895 "printer-camera-image-uri",
3896 "printer-charge-info",
3897 "printer-charge-info-uri",
3898 "printer-config-change-date-time",
3899 "printer-config-change-time",
3900 "printer-current-time",
3901 "printer-detailed-status-messages",
3902 "printer-dns-sd-name",
3903 "printer-fax-log-uri",
3904 "printer-get-attributes-supported",
3908 "printer-is-accepting-jobs",
3909 "printer-message-date-time",
3910 "printer-message-from-operator",
3911 "printer-message-time",
3912 "printer-more-info",
3913 "printer-service-type",
3914 "printer-settable-attributes-supported",
3916 "printer-state-message",
3917 "printer-state-reasons",
3918 "printer-static-resource-directory-uri",
3919 "printer-static-resource-k-octets-free",
3920 "printer-static-resource-k-octets-supported",
3921 "printer-strings-languages-supported",
3922 "printer-strings-uri",
3923 "printer-supply-info-uri",
3925 "printer-uri-supported",
3926 "printer-xri-supported",
3928 "reference-uri-scheme-supported",
3929 "uri-authentication-supported",
3930 "uri-security-supported",
3931 "which-jobs-supported",
3932 "xri-authentication-supported",
3933 "xri-security-supported",
3934 "xri-uri-scheme-supported"
3941 for (i
= 0, result
= 1; i
< (int)(sizeof(ignored
) / sizeof(ignored
[0])); i
++)
3943 if ((result
= strcmp(attr
, ignored
[i
])) <= 0)
3947 return (result
!= 0);
3952 * 'ippserver_error_cb()' - Log an error message.
3955 static int /* O - 1 to continue, 0 to stop */
3957 _ipp_file_t
*f
, /* I - IPP file data */
3958 void *user_data
, /* I - User data pointer (unused) */
3959 const char *error
) /* I - Error message */
3964 _cupsLangPrintf(stderr
, "%s\n", error
);
3971 * 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
3974 static int /* O - 1 to continue, 0 to stop */
3976 _ipp_file_t
*f
, /* I - IPP file data */
3977 _ipp_vars_t
*vars
, /* I - IPP variables */
3978 void *user_data
, /* I - User data pointer (unused) */
3979 const char *token
) /* I - Current token */
3987 * NULL token means do the initial setup - create an empty IPP message and
3991 f
->attrs
= ippNew();
3992 f
->group_tag
= IPP_TAG_PRINTER
;
3996 _cupsLangPrintf(stderr
, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token
, f
->linenum
, f
->filename
);
4004 * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
4007 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4008 load_ippserver_attributes(
4009 const char *servername
, /* I - Server name or `NULL` for default */
4010 int serverport
, /* I - Server port number */
4011 const char *filename
, /* I - ippserver attribute filename */
4012 cups_array_t
*docformats
) /* I - document-format-supported values */
4014 ipp_t
*attrs
; /* IPP attributes */
4015 _ipp_vars_t vars
; /* IPP variables */
4016 char temp
[256]; /* Temporary string */
4019 (void)docformats
; /* for now */
4022 * Setup callbacks and variables for the printer configuration file...
4024 * The following additional variables are supported:
4026 * - SERVERNAME: The host name of the server.
4027 * - SERVERPORT: The default port of the server.
4030 _ippVarsInit(&vars
, (_ipp_fattr_cb_t
)ippserver_attr_cb
, (_ipp_ferror_cb_t
)ippserver_error_cb
, (_ipp_ftoken_cb_t
)ippserver_token_cb
);
4034 _ippVarsSet(&vars
, "SERVERNAME", servername
);
4038 httpGetHostname(NULL
, temp
, sizeof(temp
));
4039 _ippVarsSet(&vars
, "SERVERNAME", temp
);
4042 snprintf(temp
, sizeof(temp
), "%d", serverport
);
4043 _ippVarsSet(&vars
, "SERVERPORT", temp
);
4046 * Load attributes and values for the printer...
4049 attrs
= _ippFileParse(&vars
, filename
, NULL
);
4052 * Free memory and return...
4055 _ippVarsDeinit(&vars
);
4062 * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
4066 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4067 load_legacy_attributes(
4068 const char *make
, /* I - Manufacturer name */
4069 const char *model
, /* I - Model name */
4070 int ppm
, /* I - pages-per-minute */
4071 int ppm_color
, /* I - pages-per-minute-color */
4072 int duplex
, /* I - Duplex support? */
4073 cups_array_t
*docformats
) /* I - document-format-supported values */
4075 int i
; /* Looping var */
4076 ipp_t
*attrs
, /* IPP attributes */
4077 *col
; /* Collection value */
4078 ipp_attribute_t
*attr
; /* Current attribute */
4079 char device_id
[1024],/* printer-device-id */
4080 *ptr
, /* Pointer into device ID */
4081 make_model
[128];/* printer-make-and-model */
4082 const char *format
, /* Current document format */
4083 *prefix
; /* Prefix for device ID */
4084 int num_media
; /* Number of media */
4085 const char * const *media
; /* List of media */
4086 int num_ready
; /* Number of loaded media */
4087 const char * const *ready
; /* List of loaded media */
4088 pwg_media_t
*pwg
; /* PWG media size information */
4089 static const char * const media_supported
[] =
4090 { /* media-supported values */
4091 "na_letter_8.5x11in", /* Letter */
4092 "na_legal_8.5x14in", /* Legal */
4093 "iso_a4_210x297mm", /* A4 */
4094 "na_number-10_4.125x9.5in", /* #10 Envelope */
4095 "iso_dl_110x220mm" /* DL Envelope */
4097 static const char * const media_supported_color
[] =
4098 { /* media-supported values */
4099 "na_letter_8.5x11in", /* Letter */
4100 "na_legal_8.5x14in", /* Legal */
4101 "iso_a4_210x297mm", /* A4 */
4102 "na_number-10_4.125x9.5in", /* #10 Envelope */
4103 "iso_dl_110x220mm", /* DL Envelope */
4104 "na_index-3x5_3x5in", /* Photo 3x5 */
4105 "oe_photo-l_3.5x5in", /* Photo L */
4106 "na_index-4x6_4x6in", /* Photo 4x6 */
4107 "iso_a6_105x148mm", /* A6 */
4108 "na_5x7_5x7in" /* Photo 5x7 aka 2L */
4109 "iso_a5_148x210mm", /* A5 */
4111 static const char * const media_ready
[] =
4112 { /* media-ready values */
4113 "na_letter_8.5x11in", /* Letter */
4114 "na_number-10_4.125x9.5in" /* #10 */
4116 static const char * const media_ready_color
[] =
4117 { /* media-ready values */
4118 "na_letter_8.5x11in", /* Letter */
4119 "na_index-4x6_4x6in" /* Photo 4x6 */
4121 static const char * const media_source_supported
[] =
4122 { /* media-source-supported values */
4126 "by-pass-tray" /* AKA multi-purpose tray */
4128 static const char * const media_source_supported_color
[] =
4129 { /* media-source-supported values */
4134 static const char * const media_type_supported
[] =
4135 { /* media-type-supported values */
4142 "stationery-letterhead",
4145 static const char * const media_type_supported_color
[] =
4146 { /* media-type-supported values */
4153 "stationery-letterhead",
4155 "photographic-glossy",
4156 "photographic-high-gloss",
4157 "photographic-matte",
4158 "photographic-satin",
4159 "photographic-semi-gloss"
4161 static const int media_bottom_margin_supported
[] =
4162 { /* media-bottom-margin-supported values */
4165 static const int media_bottom_margin_supported_color
[] =
4166 { /* media-bottom/top-margin-supported values */
4168 1168 /* 0.46" (common HP inkjet bottom margin) */
4170 static const int media_lr_margin_supported
[] =
4171 { /* media-left/right-margin-supported values */
4172 340, /* 3.4mm (historical HP PCL A4 margin) */
4175 static const int media_lr_margin_supported_color
[] =
4176 { /* media-left/right-margin-supported values */
4178 340, /* 3.4mm (historical HP PCL A4 margin) */
4181 static const int media_top_margin_supported
[] =
4182 { /* media-top-margin-supported values */
4185 static const int media_top_margin_supported_color
[] =
4186 { /* media-top/top-margin-supported values */
4188 102 /* 0.04" (common HP inkjet top margin */
4190 static const int orientation_requested_supported
[4] =
4191 { /* orientation-requested-supported values */
4192 IPP_ORIENT_PORTRAIT
,
4193 IPP_ORIENT_LANDSCAPE
,
4194 IPP_ORIENT_REVERSE_LANDSCAPE
,
4195 IPP_ORIENT_REVERSE_PORTRAIT
4197 static const char * const overrides_supported
[] =
4198 { /* overrides-supported values */
4202 "orientation-requested",
4205 static const char * const print_color_mode_supported
[] =
4206 { /* print-color-mode-supported values */
4209 static const char * const print_color_mode_supported_color
[] =
4210 { /* print-color-mode-supported values */
4215 static const int print_quality_supported
[] =
4216 { /* print-quality-supported values */
4221 static const char * const printer_input_tray
[] =
4222 { /* printer-input-tray values */
4223 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4224 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
4225 "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
4226 "type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
4228 static const char * const printer_input_tray_color
[] =
4229 { /* printer-input-tray values */
4230 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4231 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
4232 "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
4234 static const char * const printer_supply
[] =
4235 { /* printer-supply values */
4236 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4237 "maxcapacity=100;level=25;colorantname=unknown;",
4238 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4239 "maxcapacity=100;level=75;colorantname=black;"
4241 static const char * const printer_supply_color
[] =
4242 { /* printer-supply values */
4243 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4244 "maxcapacity=100;level=25;colorantname=unknown;",
4245 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4246 "maxcapacity=100;level=75;colorantname=black;",
4247 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4248 "maxcapacity=100;level=50;colorantname=cyan;",
4249 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4250 "maxcapacity=100;level=33;colorantname=magenta;",
4251 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4252 "maxcapacity=100;level=67;colorantname=yellow;"
4254 static const char * const printer_supply_description
[] =
4255 { /* printer-supply-description values */
4259 static const char * const printer_supply_description_color
[] =
4260 { /* printer-supply-description values */
4267 static const int pwg_raster_document_resolution_supported
[] =
4272 static const char * const pwg_raster_document_type_supported
[] =
4277 static const char * const pwg_raster_document_type_supported_color
[] =
4284 static const char * const sides_supported
[] =
4285 { /* sides-supported values */
4287 "two-sided-long-edge",
4288 "two-sided-short-edge"
4290 static const char * const urf_supported
[] =
4291 { /* urf-supported values */
4299 static const char * const urf_supported_color
[] =
4300 { /* urf-supported values */
4303 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4309 static const char * const urf_supported_color_duplex
[] =
4310 { /* urf-supported values */
4313 "MT1-2-3-4-5-6-8-9-10-11-12-13",
4320 static const char * const urf_supported_duplex
[] =
4321 { /* urf-supported values */
4336 num_media
= (int)(sizeof(media_supported_color
) / sizeof(media_supported_color
[0]));
4337 media
= media_supported_color
;
4338 num_ready
= (int)(sizeof(media_ready_color
) / sizeof(media_ready_color
[0]));
4339 ready
= media_ready_color
;
4343 num_media
= (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
4344 media
= media_supported
;
4345 num_ready
= (int)(sizeof(media_ready
) / sizeof(media_ready
[0]));
4346 ready
= media_ready
;
4349 /* color-supported */
4350 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", ppm_color
> 0);
4352 /* copies-default */
4353 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
4355 /* copies-supported */
4356 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, (cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
|| cupsArrayFind(docformats
, (void *)"image/jpeg") != NULL
) ? 999 : 1);
4358 /* document-password-supported */
4359 if (cupsArrayFind(docformats
, (void *)"application/pdf"))
4360 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 1023);
4362 /* finishings-default */
4363 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
4365 /* finishings-supported */
4366 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", IPP_FINISHINGS_NONE
);
4368 /* media-bottom-margin-supported */
4370 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
);
4372 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
);
4374 /* media-col-database and media-col-default */
4375 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", num_media
, NULL
);
4376 for (i
= 0; i
< num_media
; i
++)
4378 int bottom
, left
, /* media-xxx-margins */
4380 const char *source
; /* media-source, if any */
4382 pwg
= pwgMediaForPWG(media
[i
]);
4384 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4386 source
= "photo"; /* Photo size media from photo tray */
4387 bottom
= /* Borderless margins */
4392 else if (pwg
->width
< 21000)
4394 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4395 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4396 left
= /* Left/right margins are standard */
4397 right
= media_lr_margin_supported
[1];
4398 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4400 else if (pwg
->width
== 21000)
4402 source
= NULL
; /* A4 from any tray */
4403 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4404 left
= /* Left/right margins are reduced */
4405 right
= media_lr_margin_supported
[0];
4406 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4410 source
= NULL
; /* Other size media from any tray */
4411 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4412 left
= /* Left/right margins are standard */
4413 right
= media_lr_margin_supported
[1];
4414 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4417 col
= create_media_col(media
[i
], source
, NULL
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4418 ippSetCollection(attrs
, &attr
, i
, col
);
4423 /* media-col-default */
4424 pwg
= pwgMediaForPWG(ready
[0]);
4426 if (pwg
->width
== 21000)
4427 col
= create_media_col(ready
[0], "main", "stationery", pwg
->width
, pwg
->length
, ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0], media_lr_margin_supported
[0], media_lr_margin_supported
[0], ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0]);
4429 col
= create_media_col(ready
[0], "main", "stationery", pwg
->width
, pwg
->length
, ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0], media_lr_margin_supported
[1], media_lr_margin_supported
[1], ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0]);
4431 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default", col
);
4435 /* media-col-ready */
4436 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-ready", num_ready
, NULL
);
4437 for (i
= 0; i
< num_ready
; i
++)
4439 int bottom
, left
, /* media-xxx-margins */
4441 const char *source
, /* media-source */
4442 *type
; /* media-type */
4444 pwg
= pwgMediaForPWG(ready
[i
]);
4446 if (pwg
->width
< 21000 && pwg
->length
< 21000)
4448 source
= "photo"; /* Photo size media from photo tray */
4449 type
= "photographic-glossy"; /* Glossy photo paper */
4450 bottom
= /* Borderless margins */
4455 else if (pwg
->width
< 21000)
4457 source
= "by-pass-tray"; /* Envelopes from multi-purpose tray */
4458 type
= "envelope"; /* Envelope */
4459 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4460 left
= /* Left/right margins are standard */
4461 right
= media_lr_margin_supported
[1];
4462 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4464 else if (pwg
->width
== 21000)
4466 source
= "main"; /* A4 from main tray */
4467 type
= "stationery"; /* Plain paper */
4468 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4469 left
= /* Left/right margins are reduced */
4470 right
= media_lr_margin_supported
[0];
4471 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4475 source
= "main"; /* A4 from main tray */
4476 type
= "stationery"; /* Plain paper */
4477 bottom
= ppm_color
> 0 ? media_bottom_margin_supported_color
[1] : media_bottom_margin_supported
[0];
4478 left
= /* Left/right margins are standard */
4479 right
= media_lr_margin_supported
[1];
4480 top
= ppm_color
> 0 ? media_top_margin_supported_color
[1] : media_top_margin_supported
[0];
4483 col
= create_media_col(ready
[i
], source
, type
, pwg
->width
, pwg
->length
, bottom
, left
, right
, top
);
4484 ippSetCollection(attrs
, &attr
, i
, col
);
4489 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-default", NULL
, media
[0]);
4491 /* media-left/right-margin-supported */
4494 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
);
4495 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
);
4499 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
);
4500 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
);
4504 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", num_ready
, NULL
, ready
);
4506 /* media-supported */
4507 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-supported", num_media
, NULL
, media
);
4509 /* media-size-supported */
4510 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", num_media
, NULL
);
4511 for (i
= 0; i
< num_media
; i
++)
4513 pwg
= pwgMediaForPWG(media
[i
]);
4514 col
= create_media_size(pwg
->width
, pwg
->length
);
4516 ippSetCollection(attrs
, &attr
, i
, col
);
4520 /* media-source-supported */
4522 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
);
4524 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
);
4526 /* media-top-margin-supported */
4528 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
);
4530 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
);
4532 /* media-type-supported */
4534 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
);
4536 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
);
4538 /* orientation-requested-default */
4539 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-default", IPP_ORIENT_PORTRAIT
);
4541 /* orientation-requested-supported */
4542 if (cupsArrayFind(docformats
, (void *)"application/pdf") || cupsArrayFind(docformats
, (void *)"image/jpeg"))
4543 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported
) / sizeof(orientation_requested_supported
[0])), orientation_requested_supported
);
4545 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", IPP_ORIENT_PORTRAIT
);
4547 /* output-bin-default */
4549 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-up");
4551 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
4553 /* output-bin-supported */
4555 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-up");
4557 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
4559 /* overrides-supported */
4560 if (cupsArrayFind(docformats
, (void *)"application/pdf"))
4561 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "overrides-supported", (int)(sizeof(overrides_supported
) / sizeof(overrides_supported
[0])), NULL
, overrides_supported
);
4563 /* page-ranges-supported */
4564 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", cupsArrayFind(docformats
, (void *)"application/pdf") != NULL
);
4566 /* pages-per-minute */
4567 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute", ppm
);
4569 /* pages-per-minute-color */
4571 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute-color", ppm_color
);
4573 /* print-color-mode-default */
4574 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, ppm_color
> 0 ? "auto" : "monochrome");
4576 /* print-color-mode-supported */
4578 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
);
4580 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
);
4582 /* print-content-optimize-default */
4583 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
4585 /* print-content-optimize-supported */
4586 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
4588 /* print-quality-default */
4589 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
4591 /* print-quality-supported */
4592 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
4594 /* print-rendering-intent-default */
4595 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
4597 /* print-rendering-intent-supported */
4598 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
4600 /* printer-device-id */
4601 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
4602 ptr
= device_id
+ strlen(device_id
);
4604 for (format
= (const char *)cupsArrayFirst(docformats
); format
; format
= (const char *)cupsArrayNext(docformats
))
4606 if (!strcasecmp(format
, "application/pdf"))
4607 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
4608 else if (!strcasecmp(format
, "application/postscript"))
4609 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
4610 else if (!strcasecmp(format
, "application/vnd.hp-PCL"))
4611 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
4612 else if (!strcasecmp(format
, "image/jpeg"))
4613 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
4614 else if (!strcasecmp(format
, "image/png"))
4615 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
4616 else if (!strcasecmp(format
, "image/pwg-raster"))
4617 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPWG", prefix
);
4618 else if (!strcasecmp(format
, "image/urf"))
4619 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sURF", prefix
);
4626 if (ptr
< (device_id
+ sizeof(device_id
) - 1))
4631 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
4633 /* printer-input-tray */
4636 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray_color
[0], (int)strlen(printer_input_tray_color
[0]));
4637 for (i
= 1; i
< (int)(sizeof(printer_input_tray_color
) / sizeof(printer_input_tray_color
[0])); i
++)
4638 ippSetOctetString(attrs
, &attr
, i
, printer_input_tray_color
[i
], (int)strlen(printer_input_tray_color
[i
]));
4642 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray
[0], (int)strlen(printer_input_tray
[0]));
4643 for (i
= 1; i
< (int)(sizeof(printer_input_tray
) / sizeof(printer_input_tray
[0])); i
++)
4644 ippSetOctetString(attrs
, &attr
, i
, printer_input_tray
[i
], (int)strlen(printer_input_tray
[i
]));
4647 /* printer-make-and-model */
4648 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4649 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, make_model
);
4651 /* printer-resolution-default */
4652 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
4654 /* printer-resolution-supported */
4655 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
4657 /* printer-supply and printer-supply-description */
4660 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply_color
[0], (int)strlen(printer_supply_color
[0]));
4661 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
4662 ippSetOctetString(attrs
, &attr
, i
, printer_supply_color
[i
], (int)strlen(printer_supply_color
[i
]));
4664 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
);
4668 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply
[0], (int)strlen(printer_supply
[0]));
4669 for (i
= 1; i
< (int)(sizeof(printer_supply
) / sizeof(printer_supply
[0])); i
++)
4670 ippSetOctetString(attrs
, &attr
, i
, printer_supply
[i
], (int)strlen(printer_supply
[i
]));
4672 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
);
4675 /* pwg-raster-document-xxx-supported */
4676 if (cupsArrayFind(docformats
, (void *)"image/pwg-raster"))
4678 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
);
4680 if (ppm_color
> 0 && duplex
)
4681 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "rotated");
4683 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "normal");
4686 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
);
4688 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
);
4692 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
4694 /* sides-supported */
4696 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", (int)(sizeof(sides_supported
) / sizeof(sides_supported
[0])), NULL
, sides_supported
);
4698 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", NULL
, "one-sided");
4701 if (cupsArrayFind(docformats
, (void *)"image/urf"))
4706 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
);
4708 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_color
) / sizeof(urf_supported_color
[0])), NULL
, urf_supported_color
);
4712 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported_duplex
) / sizeof(urf_supported_duplex
[0])), NULL
, urf_supported_duplex
);
4716 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", (int)(sizeof(urf_supported
) / sizeof(urf_supported
[0])), NULL
, urf_supported
);
4726 * 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4729 static ipp_t
* /* O - IPP attributes or `NULL` on error */
4730 load_ppd_attributes(
4731 const char *ppdfile
, /* I - PPD filename */
4732 cups_array_t
*docformats
) /* I - document-format-supported values */
4734 int i
, j
; /* Looping vars */
4735 ipp_t
*attrs
; /* Attributes */
4736 ipp_attribute_t
*attr
; /* Current attribute */
4737 ipp_t
*col
; /* Current collection value */
4738 ppd_file_t
*ppd
; /* PPD data */
4739 ppd_attr_t
*ppd_attr
; /* PPD attribute */
4740 ppd_choice_t
*ppd_choice
; /* PPD choice */
4741 ppd_size_t
*ppd_size
; /* Default PPD size */
4742 pwg_size_t
*pwg_size
, /* Current PWG size */
4743 *default_size
= NULL
; /* Default PWG size */
4744 const char *default_source
= NULL
, /* Default media source */
4745 *default_type
= NULL
; /* Default media type */
4746 pwg_map_t
*pwg_map
; /* Mapping from PWG to PPD keywords */
4747 _ppd_cache_t
*pc
; /* PPD cache */
4748 _pwg_finishings_t
*finishings
; /* Current finishings value */
4749 const char *template; /* Current finishings-template value */
4750 int num_margins
; /* Number of media-xxx-margin-supported values */
4751 int margins
[10]; /* media-xxx-margin-supported values */
4752 int xres
, /* Default horizontal resolution */
4753 yres
; /* Default vertical resolution */
4754 int num_urf
; /* Number of urf-supported values */
4755 const char *urf
[10]; /* urf-supported values */
4756 char urf_rs
[32]; /* RS value */
4757 static const int orientation_requested_supported
[4] =
4758 { /* orientation-requested-supported values */
4759 IPP_ORIENT_PORTRAIT
,
4760 IPP_ORIENT_LANDSCAPE
,
4761 IPP_ORIENT_REVERSE_LANDSCAPE
,
4762 IPP_ORIENT_REVERSE_PORTRAIT
4764 static const char * const overrides_supported
[] =
4765 { /* overrides-supported */
4769 "orientation-requested",
4772 static const char * const print_color_mode_supported
[] =
4773 { /* print-color-mode-supported values */
4776 static const char * const print_color_mode_supported_color
[] =
4777 { /* print-color-mode-supported values */
4782 static const int print_quality_supported
[] =
4783 { /* print-quality-supported values */
4788 static const char * const printer_supply
[] =
4789 { /* printer-supply values */
4790 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4791 "maxcapacity=100;level=25;colorantname=unknown;",
4792 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4793 "maxcapacity=100;level=75;colorantname=black;"
4795 static const char * const printer_supply_color
[] =
4796 { /* printer-supply values */
4797 "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4798 "maxcapacity=100;level=25;colorantname=unknown;",
4799 "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4800 "maxcapacity=100;level=75;colorantname=black;",
4801 "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4802 "maxcapacity=100;level=50;colorantname=cyan;",
4803 "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4804 "maxcapacity=100;level=33;colorantname=magenta;",
4805 "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4806 "maxcapacity=100;level=67;colorantname=yellow;"
4808 static const char * const printer_supply_description
[] =
4809 { /* printer-supply-description values */
4813 static const char * const printer_supply_description_color
[] =
4814 { /* printer-supply-description values */
4821 static const char * const pwg_raster_document_type_supported
[] =
4826 static const char * const pwg_raster_document_type_supported_color
[] =
4833 static const char * const sides_supported
[] =
4834 { /* sides-supported values */
4836 "two-sided-long-edge",
4837 "two-sided-short-edge"
4842 * Open the PPD file...
4845 if ((ppd
= ppdOpenFile(ppdfile
)) == NULL
)
4847 ppd_status_t status
; /* Load error */
4849 status
= ppdLastError(&i
);
4850 _cupsLangPrintf(stderr
, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile
, ppdErrorString(status
), i
);
4854 ppdMarkDefaults(ppd
);
4856 pc
= _ppdCacheCreateWithPPD(ppd
);
4858 if ((ppd_size
= ppdPageSize(ppd
, NULL
)) != NULL
)
4861 * Look up default size...
4864 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4866 if (!strcmp(pwg_size
->map
.ppd
, ppd_size
->name
))
4868 default_size
= pwg_size
;
4877 * Default to A4 or Letter...
4880 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
4882 if (!strcmp(pwg_size
->map
.ppd
, "Letter") || !strcmp(pwg_size
->map
.ppd
, "A4"))
4884 default_size
= pwg_size
;
4890 default_size
= pc
->sizes
; /* Last resort: first size */
4893 if ((ppd_choice
= ppdFindMarkedChoice(ppd
, "InputSlot")) != NULL
)
4894 default_source
= _ppdCacheGetSource(pc
, ppd_choice
->choice
);
4896 if ((ppd_choice
= ppdFindMarkedChoice(ppd
, "MediaType")) != NULL
)
4897 default_source
= _ppdCacheGetType(pc
, ppd_choice
->choice
);
4899 if ((ppd_attr
= ppdFindAttr(ppd
, "DefaultResolution", NULL
)) != NULL
)
4902 * Use the PPD-defined default resolution...
4905 if ((i
= sscanf(ppd_attr
->value
, "%dx%d", &xres
, &yres
)) == 1)
4913 * Use default of 300dpi...
4919 snprintf(urf_rs
, sizeof(urf_rs
), "RS%d", yres
< xres
? yres
: xres
);
4922 urf
[num_urf
++] = "V1.4";
4923 urf
[num_urf
++] = "CP1";
4924 urf
[num_urf
++] = urf_rs
;
4925 urf
[num_urf
++] = "W8";
4926 if (pc
->sides_2sided_long
)
4927 urf
[num_urf
++] = "DM1";
4928 if (ppd
->color_device
)
4929 urf
[num_urf
++] = "SRGB24";
4932 * PostScript printers accept PDF via one of the CUPS PDF to PostScript
4933 * filters, along with PostScript (of course) and JPEG...
4936 cupsArrayAdd(docformats
, "application/pdf");
4937 cupsArrayAdd(docformats
, "application/postscript");
4938 cupsArrayAdd(docformats
, "image/jpeg");
4941 * Create the attributes...
4946 /* color-supported */
4947 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "color-supported", (char)ppd
->color_device
);
4949 /* copies-default */
4950 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "copies-default", 1);
4952 /* copies-supported */
4953 ippAddRange(attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
4955 /* document-password-supported */
4956 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
4958 /* finishing-template-supported */
4959 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template-supported", cupsArrayCount(pc
->templates
) + 1, NULL
, NULL
);
4960 ippSetString(attrs
, &attr
, 0, "none");
4961 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4962 ippSetString(attrs
, &attr
, i
, template);
4964 /* finishings-col-database */
4965 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "finishings-col-database", cupsArrayCount(pc
->templates
) + 1, NULL
);
4968 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4969 ippSetCollection(attrs
, &attr
, 0, col
);
4972 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4975 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, template);
4976 ippSetCollection(attrs
, &attr
, i
, col
);
4980 /* finishings-col-default */
4982 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4983 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "finishings-col-default", col
);
4986 /* finishings-col-ready */
4987 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "finishings-col-ready", cupsArrayCount(pc
->templates
) + 1, NULL
);
4990 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, "none");
4991 ippSetCollection(attrs
, &attr
, 0, col
);
4994 for (i
= 1, template = (const char *)cupsArrayFirst(pc
->templates
); template; i
++, template = (const char *)cupsArrayNext(pc
->templates
))
4997 ippAddString(col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishing-template", NULL
, template);
4998 ippSetCollection(attrs
, &attr
, i
, col
);
5002 /* finishings-col-supported */
5003 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "finishings-col-supported", NULL
, "finishing-template");
5005 /* finishings-default */
5006 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-default", IPP_FINISHINGS_NONE
);
5008 /* finishings-ready */
5009 attr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-ready", cupsArrayCount(pc
->finishings
) + 1, NULL
);
5010 ippSetInteger(attrs
, &attr
, 0, IPP_FINISHINGS_NONE
);
5011 for (i
= 1, finishings
= (_pwg_finishings_t
*)cupsArrayFirst(pc
->finishings
); finishings
; i
++, finishings
= (_pwg_finishings_t
*)cupsArrayNext(pc
->finishings
))
5012 ippSetInteger(attrs
, &attr
, i
, (int)finishings
->value
);
5014 /* finishings-supported */
5015 attr
= ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "finishings-supported", cupsArrayCount(pc
->finishings
) + 1, NULL
);
5016 ippSetInteger(attrs
, &attr
, 0, IPP_FINISHINGS_NONE
);
5017 for (i
= 1, finishings
= (_pwg_finishings_t
*)cupsArrayFirst(pc
->finishings
); finishings
; i
++, finishings
= (_pwg_finishings_t
*)cupsArrayNext(pc
->finishings
))
5018 ippSetInteger(attrs
, &attr
, i
, (int)finishings
->value
);
5020 /* media-bottom-margin-supported */
5021 for (i
= 0, num_margins
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
&& num_margins
< (int)(sizeof(margins
) / sizeof(margins
[0])); i
++, pwg_size
++)
5023 for (j
= 0; j
< num_margins
; j
++)
5025 if (margins
[j
] == pwg_size
->bottom
)
5029 if (j
>= num_margins
)
5030 margins
[num_margins
++] = pwg_size
->bottom
;
5033 for (i
= 0; i
< (num_margins
- 1); i
++)
5035 for (j
= i
+ 1; j
< num_margins
; j
++)
5037 if (margins
[i
] > margins
[j
])
5039 int mtemp
= margins
[i
];
5041 margins
[i
] = margins
[j
];
5047 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-bottom-margin-supported", num_margins
, margins
);
5049 /* media-col-database */
5050 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-col-database", pc
->num_sizes
, NULL
);
5051 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
5053 col
= create_media_col(pwg_size
->map
.pwg
, NULL
, NULL
, pwg_size
->width
, pwg_size
->length
, pwg_size
->bottom
, pwg_size
->left
, pwg_size
->right
, pwg_size
->top
);
5054 ippSetCollection(attrs
, &attr
, i
, col
);
5058 /* media-col-default */
5059 col
= create_media_col(default_size
->map
.pwg
, default_source
, default_type
, default_size
->width
, default_size
->length
, default_size
->bottom
, default_size
->left
, default_size
->right
, default_size
->top
);
5060 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-default", col
);
5063 /* media-col-ready */
5064 col
= create_media_col(default_size
->map
.pwg
, default_source
, default_type
, default_size
->width
, default_size
->length
, default_size
->bottom
, default_size
->left
, default_size
->right
, default_size
->top
);
5065 ippAddCollection(attrs
, IPP_TAG_PRINTER
, "media-col-ready", col
);
5069 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-default", NULL
, default_size
->map
.pwg
);
5071 /* media-left-margin-supported */
5072 for (i
= 0, num_margins
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
&& num_margins
< (int)(sizeof(margins
) / sizeof(margins
[0])); i
++, pwg_size
++)
5074 for (j
= 0; j
< num_margins
; j
++)
5076 if (margins
[j
] == pwg_size
->left
)
5080 if (j
>= num_margins
)
5081 margins
[num_margins
++] = pwg_size
->left
;
5084 for (i
= 0; i
< (num_margins
- 1); i
++)
5086 for (j
= i
+ 1; j
< num_margins
; j
++)
5088 if (margins
[i
] > margins
[j
])
5090 int mtemp
= margins
[i
];
5092 margins
[i
] = margins
[j
];
5098 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-left-margin-supported", num_margins
, margins
);
5101 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", NULL
, default_size
->map
.pwg
);
5103 /* media-right-margin-supported */
5104 for (i
= 0, num_margins
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
&& num_margins
< (int)(sizeof(margins
) / sizeof(margins
[0])); i
++, pwg_size
++)
5106 for (j
= 0; j
< num_margins
; j
++)
5108 if (margins
[j
] == pwg_size
->right
)
5112 if (j
>= num_margins
)
5113 margins
[num_margins
++] = pwg_size
->right
;
5116 for (i
= 0; i
< (num_margins
- 1); i
++)
5118 for (j
= i
+ 1; j
< num_margins
; j
++)
5120 if (margins
[i
] > margins
[j
])
5122 int mtemp
= margins
[i
];
5124 margins
[i
] = margins
[j
];
5130 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-right-margin-supported", num_margins
, margins
);
5132 /* media-supported */
5133 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-supported", pc
->num_sizes
, NULL
, NULL
);
5134 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
5135 ippSetString(attrs
, &attr
, i
, pwg_size
->map
.pwg
);
5137 /* media-size-supported */
5138 attr
= ippAddCollections(attrs
, IPP_TAG_PRINTER
, "media-size-supported", pc
->num_sizes
, NULL
);
5139 for (i
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
; i
++, pwg_size
++)
5141 col
= create_media_size(pwg_size
->width
, pwg_size
->length
);
5142 ippSetCollection(attrs
, &attr
, i
, col
);
5146 /* media-source-supported */
5147 if (pc
->num_sources
> 0)
5149 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-source-supported", pc
->num_sources
, NULL
, NULL
);
5150 for (i
= 0, pwg_map
= pc
->sources
; i
< pc
->num_sources
; i
++, pwg_map
++)
5151 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
5155 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-source-supported", NULL
, "auto");
5158 /* media-top-margin-supported */
5159 for (i
= 0, num_margins
= 0, pwg_size
= pc
->sizes
; i
< pc
->num_sizes
&& num_margins
< (int)(sizeof(margins
) / sizeof(margins
[0])); i
++, pwg_size
++)
5161 for (j
= 0; j
< num_margins
; j
++)
5163 if (margins
[j
] == pwg_size
->top
)
5167 if (j
>= num_margins
)
5168 margins
[num_margins
++] = pwg_size
->top
;
5171 for (i
= 0; i
< (num_margins
- 1); i
++)
5173 for (j
= i
+ 1; j
< num_margins
; j
++)
5175 if (margins
[i
] > margins
[j
])
5177 int mtemp
= margins
[i
];
5179 margins
[i
] = margins
[j
];
5185 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "media-top-margin-supported", num_margins
, margins
);
5187 /* media-type-supported */
5188 if (pc
->num_types
> 0)
5190 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type-supported", pc
->num_types
, NULL
, NULL
);
5191 for (i
= 0, pwg_map
= pc
->types
; i
< pc
->num_types
; i
++, pwg_map
++)
5192 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
5196 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "media-type-supported", NULL
, "auto");
5199 /* orientation-requested-default */
5200 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-default", IPP_ORIENT_PORTRAIT
);
5202 /* orientation-requested-supported */
5203 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported
) / sizeof(orientation_requested_supported
[0])), orientation_requested_supported
);
5205 /* output-bin-default */
5206 if (pc
->num_bins
> 0)
5207 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "output-bin-default", NULL
, pc
->bins
->pwg
);
5209 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-default", NULL
, "face-down");
5211 /* output-bin-supported */
5212 if (pc
->num_bins
> 0)
5214 attr
= ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "output-bin-supported", pc
->num_bins
, NULL
, NULL
);
5215 for (i
= 0, pwg_map
= pc
->bins
; i
< pc
->num_bins
; i
++, pwg_map
++)
5216 ippSetString(attrs
, &attr
, i
, pwg_map
->pwg
);
5220 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "output-bin-supported", NULL
, "face-down");
5223 /* overrides-supported */
5224 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides_supported
) / sizeof(overrides_supported
[0])), NULL
, overrides_supported
);
5226 /* page-ranges-supported */
5227 ippAddBoolean(attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
5229 /* pages-per-minute */
5230 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute", ppd
->throughput
);
5232 /* pages-per-minute-color */
5233 if (ppd
->color_device
)
5234 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "pages-per-minute-color", ppd
->throughput
);
5236 /* print-color-mode-default */
5237 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, ppd
->color_device
? "auto" : "monochrome");
5239 /* print-color-mode-supported */
5240 if (ppd
->color_device
)
5241 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
);
5243 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
);
5245 /* print-content-optimize-default */
5246 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
5248 /* print-content-optimize-supported */
5249 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
5251 /* print-quality-default */
5252 ippAddInteger(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
5254 /* print-quality-supported */
5255 ippAddIntegers(attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
5257 /* print-rendering-intent-default */
5258 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
5260 /* print-rendering-intent-supported */
5261 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
5263 /* printer-device-id */
5264 if ((ppd_attr
= ppdFindAttr(ppd
, "1284DeviceId", NULL
)) != NULL
)
5267 * Use the device ID string from the PPD...
5270 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, ppd_attr
->value
);
5275 * Synthesize a device ID string...
5278 char device_id
[1024]; /* Device ID string */
5280 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;CMD:PS;", ppd
->manufacturer
, ppd
->modelname
);
5282 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-device-id", NULL
, device_id
);
5285 /* printer-input-tray */
5286 if (pc
->num_sources
> 0)
5288 for (i
= 0, attr
= NULL
; i
< pc
->num_sources
; i
++)
5290 char input_tray
[1024]; /* printer-input-tray value */
5292 if (!strcmp(pc
->sources
[i
].pwg
, "manual") || strstr(pc
->sources
[i
].pwg
, "-man") != NULL
)
5293 snprintf(input_tray
, sizeof(input_tray
), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc
->sources
[i
].pwg
);
5295 snprintf(input_tray
, sizeof(input_tray
), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc
->sources
[i
].pwg
);
5298 ippSetOctetString(attrs
, &attr
, i
, input_tray
, (int)strlen(input_tray
));
5300 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", input_tray
, (int)strlen(input_tray
));
5305 static const char *printer_input_tray
= "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto";
5307 ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-input-tray", printer_input_tray
, (int)strlen(printer_input_tray
));
5310 /* printer-make-and-model */
5311 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-make-and-model", NULL
, ppd
->nickname
);
5313 /* printer-resolution-default */
5314 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-default", IPP_RES_PER_INCH
, xres
, yres
);
5316 /* printer-resolution-supported */
5317 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "printer-resolution-supported", IPP_RES_PER_INCH
, xres
, yres
);
5319 /* printer-supply and printer-supply-description */
5320 if (ppd
->color_device
)
5322 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply_color
[0], (int)strlen(printer_supply_color
[0]));
5323 for (i
= 1; i
< (int)(sizeof(printer_supply_color
) / sizeof(printer_supply_color
[0])); i
++)
5324 ippSetOctetString(attrs
, &attr
, i
, printer_supply_color
[i
], (int)strlen(printer_supply_color
[i
]));
5326 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
);
5330 attr
= ippAddOctetString(attrs
, IPP_TAG_PRINTER
, "printer-supply", printer_supply
[0], (int)strlen(printer_supply
[0]));
5331 for (i
= 1; i
< (int)(sizeof(printer_supply
) / sizeof(printer_supply
[0])); i
++)
5332 ippSetOctetString(attrs
, &attr
, i
, printer_supply
[i
], (int)strlen(printer_supply
[i
]));
5334 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
);
5337 /* pwg-raster-document-xxx-supported */
5338 if (cupsArrayFind(docformats
, (void *)"image/pwg-raster"))
5340 ippAddResolution(attrs
, IPP_TAG_PRINTER
, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH
, xres
, yres
);
5342 if (pc
->sides_2sided_long
)
5343 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "pwg-raster-document-sheet-back", NULL
, "normal");
5345 if (ppd
->color_device
)
5346 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
);
5348 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
);
5352 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-default", NULL
, "one-sided");
5354 /* sides-supported */
5355 if (pc
->sides_2sided_long
)
5356 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", (int)(sizeof(sides_supported
) / sizeof(sides_supported
[0])), NULL
, sides_supported
);
5358 ippAddString(attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "sides-supported", NULL
, "one-sided");
5361 if (cupsArrayFind(docformats
, (void *)"image/urf"))
5362 ippAddStrings(attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "urf-supported", num_urf
, NULL
, urf
);
5365 * Free the PPD file and return the attributes...
5368 _ppdCacheDestroy(pc
);
5374 #endif /* !CUPS_LITE */
5379 * 'pam_func()' - PAM conversation function.
5382 static int /* O - Success or failure */
5384 int num_msg
, /* I - Number of messages */
5385 const struct pam_message
**msg
, /* I - Messages */
5386 struct pam_response
**resp
, /* O - Responses */
5388 /* I - Pointer to connection */
5390 int i
; /* Looping var */
5391 struct pam_response
*replies
; /* Replies */
5392 ippeve_authdata_t
*data
; /* Pointer to auth data */
5396 * Allocate memory for the responses...
5399 if ((replies
= malloc(sizeof(struct pam_response
) * (size_t)num_msg
)) == NULL
)
5400 return (PAM_CONV_ERR
);
5403 * Answer all of the messages...
5406 data
= (ippeve_authdata_t
*)appdata_ptr
;
5408 for (i
= 0; i
< num_msg
; i
++)
5410 switch (msg
[i
]->msg_style
)
5412 case PAM_PROMPT_ECHO_ON
:
5413 replies
[i
].resp_retcode
= PAM_SUCCESS
;
5414 replies
[i
].resp
= strdup(data
->username
);
5417 case PAM_PROMPT_ECHO_OFF
:
5418 replies
[i
].resp_retcode
= PAM_SUCCESS
;
5419 replies
[i
].resp
= strdup(data
->password
);
5423 replies
[i
].resp_retcode
= PAM_SUCCESS
;
5424 replies
[i
].resp
= NULL
;
5428 replies
[i
].resp_retcode
= PAM_SUCCESS
;
5429 replies
[i
].resp
= NULL
;
5434 return (PAM_CONV_ERR
);
5439 * Return the responses back to PAM...
5444 return (PAM_SUCCESS
);
5446 #endif /* HAVE_LIBPAM */
5450 * 'parse_options()' - Parse URL options into CUPS options.
5452 * The client->options string is destroyed by this function.
5455 static int /* O - Number of options */
5456 parse_options(ippeve_client_t
*client
, /* I - Client */
5457 cups_option_t
**options
)/* O - Options */
5459 char *name
, /* Name */
5461 *next
; /* Next name=value pair */
5462 int num_options
= 0; /* Number of options */
5467 for (name
= client
->options
; name
&& *name
; name
= next
)
5469 if ((value
= strchr(name
, '=')) == NULL
)
5473 if ((next
= strchr(value
, '&')) != NULL
)
5476 num_options
= cupsAddOption(name
, value
, num_options
, options
);
5479 return (num_options
);
5484 * 'process_attr_message()' - Process an ATTR: message from a command.
5488 process_attr_message(
5489 ippeve_job_t
*job
, /* I - Job */
5490 char *message
) /* I - Message */
5492 int i
, /* Looping var */
5493 num_options
= 0; /* Number of name=value pairs */
5494 cups_option_t
*options
= NULL
, /* name=value pairs from message */
5495 *option
; /* Current option */
5496 ipp_attribute_t
*attr
; /* Current attribute */
5500 * Grab attributes from the message line...
5503 num_options
= cupsParseOptions(message
+ 5, num_options
, &options
);
5506 * Loop through the options and record them in the printer or job objects...
5509 for (i
= num_options
, option
= options
; i
> 0; i
--, option
++)
5511 if (!strcmp(option
->name
, "job-impressions"))
5514 * Update job-impressions attribute...
5517 job
->impressions
= atoi(option
->value
);
5519 else if (!strcmp(option
->name
, "job-impressions-completed"))
5522 * Update job-impressions-completed attribute...
5525 job
->impcompleted
= atoi(option
->value
);
5527 else if (!strncmp(option
->name
, "marker-", 7) || !strcmp(option
->name
, "printer-alert") || !strcmp(option
->name
, "printer-alert-description") || !strcmp(option
->name
, "printer-supply") || !strcmp(option
->name
, "printer-supply-description"))
5530 * Update Printer Status attribute...
5533 _cupsRWLockWrite(&job
->printer
->rwlock
);
5535 if ((attr
= ippFindAttribute(job
->printer
->attrs
, option
->name
, IPP_TAG_ZERO
)) != NULL
)
5536 ippDeleteAttribute(job
->printer
->attrs
, attr
);
5538 cupsEncodeOption(job
->printer
->attrs
, IPP_TAG_PRINTER
, option
->name
, option
->value
);
5540 _cupsRWUnlock(&job
->printer
->rwlock
);
5545 * Something else that isn't currently supported...
5548 fprintf(stderr
, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job
->id
, option
->name
, option
->value
);
5552 cupsFreeOptions(num_options
, options
);
5557 * 'process_client()' - Process client requests on a thread.
5560 static void * /* O - Exit status */
5561 process_client(ippeve_client_t
*client
) /* I - Client */
5564 * Loop until we are out of requests or timeout (30 seconds)...
5568 int first_time
= 1; /* First time request? */
5569 #endif /* HAVE_SSL */
5571 while (httpWait(client
->http
, 30000))
5577 * See if we need to negotiate a TLS connection...
5580 char buf
[1]; /* First byte from client */
5582 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
5584 fprintf(stderr
, "%s Starting HTTPS session.\n", client
->hostname
);
5586 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
5588 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5592 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5597 #endif /* HAVE_SSL */
5599 if (!process_http(client
))
5604 * Close the conection to the client and return...
5607 delete_client(client
);
5614 * 'process_http()' - Process a HTTP request.
5617 int /* O - 1 on success, 0 on failure */
5618 process_http(ippeve_client_t
*client
) /* I - Client connection */
5620 char uri
[1024]; /* URI */
5621 http_state_t http_state
; /* HTTP state */
5622 http_status_t http_status
; /* HTTP status */
5623 ipp_state_t ipp_state
; /* State of IPP transfer */
5624 char scheme
[32], /* Method/scheme */
5625 userpass
[128], /* Username:password */
5626 hostname
[HTTP_MAX_HOST
];
5628 int port
; /* Port number */
5629 static const char * const http_states
[] =
5630 { /* Strings for logging HTTP method */
5651 * Clear state variables...
5654 client
->username
[0] = '\0';
5656 ippDelete(client
->request
);
5657 ippDelete(client
->response
);
5659 client
->request
= NULL
;
5660 client
->response
= NULL
;
5661 client
->operation
= HTTP_STATE_WAITING
;
5664 * Read a request from the connection...
5667 while ((http_state
= httpReadRequest(client
->http
, uri
,
5668 sizeof(uri
))) == HTTP_STATE_WAITING
)
5672 * Parse the request line...
5675 if (http_state
== HTTP_STATE_ERROR
)
5677 if (httpError(client
->http
) == EPIPE
)
5678 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
5680 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
, strerror(httpError(client
->http
)));
5684 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
5686 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
5687 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5690 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
5692 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
5693 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5697 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
], uri
);
5700 * Separate the URI into its components...
5703 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
5704 userpass
, sizeof(userpass
),
5705 hostname
, sizeof(hostname
), &port
,
5706 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
&&
5707 (http_state
!= HTTP_STATE_OPTIONS
|| strcmp(uri
, "*")))
5709 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
5710 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5714 if ((client
->options
= strchr(client
->uri
, '?')) != NULL
)
5715 *(client
->options
)++ = '\0';
5718 * Process the request...
5721 client
->start
= time(NULL
);
5722 client
->operation
= httpGetState(client
->http
);
5725 * Parse incoming parameters until the status changes...
5728 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
5730 if (http_status
!= HTTP_STATUS_OK
)
5732 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5736 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
5737 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
5740 * HTTP/1.1 and higher require the "Host:" field...
5743 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5748 * Handle HTTP Upgrade...
5751 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
), "Upgrade"))
5754 if (strstr(httpGetField(client
->http
, HTTP_FIELD_UPGRADE
), "TLS/") != NULL
&& !httpIsEncrypted(client
->http
))
5756 if (!respond_http(client
, HTTP_STATUS_SWITCHING_PROTOCOLS
, NULL
, NULL
, 0))
5759 fprintf(stderr
, "%s Upgrading to encrypted connection.\n", client
->hostname
);
5761 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_REQUIRED
))
5763 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
5767 fprintf(stderr
, "%s Connection now encrypted.\n", client
->hostname
);
5770 #endif /* HAVE_SSL */
5772 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
5777 * Handle new transfers...
5780 switch (client
->operation
)
5782 case HTTP_STATE_OPTIONS
:
5784 * Do OPTIONS command...
5787 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
5789 case HTTP_STATE_HEAD
:
5790 if (!strcmp(client
->uri
, "/icon.png"))
5791 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
5792 else if (!strcmp(client
->uri
, "/") || !strcmp(client
->uri
, "/media") || !strcmp(client
->uri
, "/supplies"))
5793 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
5795 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5797 case HTTP_STATE_GET
:
5798 if (!strcmp(client
->uri
, "/icon.png"))
5801 * Send PNG icon file.
5804 if (client
->printer
->icon
)
5806 int fd
; /* Icon file */
5807 struct stat fileinfo
; /* Icon file information */
5808 char buffer
[4096]; /* Copy buffer */
5809 ssize_t bytes
; /* Bytes */
5811 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
5813 if (!stat(client
->printer
->icon
, &fileinfo
) && (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
5815 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", (size_t)fileinfo
.st_size
))
5821 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
5822 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
5824 httpFlushWrite(client
->http
);
5829 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5833 fputs("Icon file is internal printer.png.\n", stderr
);
5835 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", sizeof(printer_png
)))
5838 httpWrite2(client
->http
, (const char *)printer_png
, sizeof(printer_png
));
5839 httpFlushWrite(client
->http
);
5845 * Authenticate if needed...
5848 if ((http_status
= authenticate_request(client
)) != HTTP_STATUS_CONTINUE
)
5850 return (respond_http(client
, http_status
, NULL
, NULL
, 0));
5853 if (!strcmp(client
->uri
, "/"))
5856 * Show web status page...
5859 return (show_status(client
));
5861 else if (!strcmp(client
->uri
, "/media"))
5864 * Show web media page...
5867 return (show_media(client
));
5869 else if (!strcmp(client
->uri
, "/supplies"))
5872 * Show web supplies page...
5875 return (show_supplies(client
));
5878 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
5882 case HTTP_STATE_POST
:
5883 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
5887 * Not an IPP request...
5890 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
5894 * Read the IPP request...
5897 client
->request
= ippNew();
5899 while ((ipp_state
= ippRead(client
->http
,
5900 client
->request
)) != IPP_STATE_DATA
)
5902 if (ipp_state
== IPP_STATE_ERROR
)
5904 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
, cupsLastErrorString());
5905 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5911 * Now that we have the IPP request, process the request...
5914 return (process_ipp(client
));
5917 break; /* Anti-compiler-warning-code */
5925 * 'process_ipp()' - Process an IPP request.
5928 static int /* O - 1 on success, 0 on error */
5929 process_ipp(ippeve_client_t
*client
) /* I - Client */
5931 ipp_tag_t group
; /* Current group tag */
5932 ipp_attribute_t
*attr
; /* Current attribute */
5933 ipp_attribute_t
*charset
; /* Character set attribute */
5934 ipp_attribute_t
*language
; /* Language attribute */
5935 ipp_attribute_t
*uri
; /* Printer URI attribute */
5936 int major
, minor
; /* Version number */
5937 const char *name
; /* Name of attribute */
5938 http_status_t status
; /* Authentication status */
5941 debug_attributes("Request", client
->request
, 1);
5944 * First build an empty response message for this request...
5947 client
->operation_id
= ippGetOperation(client
->request
);
5948 client
->response
= ippNewResponse(client
->request
);
5951 * Then validate the request header and required attributes...
5954 major
= ippGetVersion(client
->request
, &minor
);
5956 if (major
< 1 || major
> 2)
5959 * Return an error, since we only support IPP 1.x and 2.x.
5962 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
, "Bad request version number %d.%d.", major
, minor
);
5964 else if ((major
* 10 + minor
) > MaxVersion
)
5966 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
5967 httpFlush(client
->http
); /* Flush trailing (junk) data */
5969 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
5972 else if (ippGetRequestId(client
->request
) <= 0)
5974 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.", ippGetRequestId(client
->request
));
5976 else if (!ippFirstAttribute(client
->request
))
5978 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No attributes in request.");
5983 * Make sure that the attributes are provided in the correct order and
5984 * don't repeat groups...
5987 for (attr
= ippFirstAttribute(client
->request
),
5988 group
= ippGetGroupTag(attr
);
5990 attr
= ippNextAttribute(client
->request
))
5992 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
5995 * Out of order; return an error...
5998 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
5999 "Attribute groups are out of order (%x < %x).",
6000 ippGetGroupTag(attr
), group
);
6004 group
= ippGetGroupTag(attr
);
6010 * Then make sure that the first three attributes are:
6012 * attributes-charset
6013 * attributes-natural-language
6014 * printer-uri/job-uri
6017 attr
= ippFirstAttribute(client
->request
);
6018 name
= ippGetName(attr
);
6019 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
6020 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
6025 attr
= ippNextAttribute(client
->request
);
6026 name
= ippGetName(attr
);
6028 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
6029 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
6034 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
6035 IPP_TAG_URI
)) != NULL
)
6037 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
6038 IPP_TAG_URI
)) != NULL
)
6044 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
6045 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
6048 * Bad character set...
6051 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6052 "Unsupported character set \"%s\".",
6053 ippGetString(charset
, 0, NULL
));
6055 else if (!charset
|| !language
|| !uri
)
6058 * Return an error, since attributes-charset,
6059 * attributes-natural-language, and printer-uri/job-uri are required
6060 * for all operations.
6063 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
6064 "Missing required attributes.");
6068 char scheme
[32], /* URI scheme */
6069 userpass
[32], /* Username/password in URI */
6070 host
[256], /* Host name in URI */
6071 resource
[256]; /* Resource path in URI */
6072 int port
; /* Port number in URI */
6074 name
= ippGetName(uri
);
6076 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
6077 scheme
, sizeof(scheme
),
6078 userpass
, sizeof(userpass
),
6079 host
, sizeof(host
), &port
,
6080 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
6081 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
6082 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
6083 else if ((!strcmp(name
, "job-uri") &&
6084 strncmp(resource
, "/ipp/print/", 11)) ||
6085 (!strcmp(name
, "printer-uri") &&
6086 strcmp(resource
, "/ipp/print")))
6087 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
6088 name
, ippGetString(uri
, 0, NULL
));
6089 else if (client
->operation_id
!= IPP_OP_GET_PRINTER_ATTRIBUTES
&& (status
= authenticate_request(client
)) != HTTP_STATUS_CONTINUE
)
6091 httpFlush(client
->http
);
6093 return (respond_http(client
, status
, NULL
, NULL
, 0));
6098 * Handle HTTP Expect...
6101 if (httpGetExpect(client
->http
))
6103 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
6106 * Send 100-continue header...
6109 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
6115 * Send 417-expectation-failed header...
6118 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
6121 httpFlush(client
->http
);
6127 * Try processing the operation...
6130 switch (client
->operation_id
)
6132 case IPP_OP_PRINT_JOB
:
6133 ipp_print_job(client
);
6136 case IPP_OP_PRINT_URI
:
6137 ipp_print_uri(client
);
6140 case IPP_OP_VALIDATE_JOB
:
6141 ipp_validate_job(client
);
6144 case IPP_OP_CREATE_JOB
:
6145 ipp_create_job(client
);
6148 case IPP_OP_SEND_DOCUMENT
:
6149 ipp_send_document(client
);
6152 case IPP_OP_SEND_URI
:
6153 ipp_send_uri(client
);
6156 case IPP_OP_CANCEL_JOB
:
6157 ipp_cancel_job(client
);
6160 case IPP_OP_GET_JOB_ATTRIBUTES
:
6161 ipp_get_job_attributes(client
);
6164 case IPP_OP_GET_JOBS
:
6165 ipp_get_jobs(client
);
6168 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
6169 ipp_get_printer_attributes(client
);
6172 case IPP_OP_CLOSE_JOB
:
6173 ipp_close_job(client
);
6176 case IPP_OP_IDENTIFY_PRINTER
:
6177 ipp_identify_printer(client
);
6181 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
6182 "Operation not supported.");
6191 * Send the HTTP header and return...
6194 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
6195 httpFlush(client
->http
); /* Flush trailing (junk) data */
6197 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
6198 ippLength(client
->response
)));
6203 * 'process_job()' - Process a print job.
6206 static void * /* O - Thread exit status */
6207 process_job(ippeve_job_t
*job
) /* I - Job */
6209 job
->state
= IPP_JSTATE_PROCESSING
;
6210 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
6211 job
->processing
= time(NULL
);
6213 while (job
->printer
->state_reasons
& IPPEVE_PREASON_MEDIA_EMPTY
)
6215 job
->printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
6220 job
->printer
->state_reasons
&= (ippeve_preason_t
)~IPPEVE_PREASON_MEDIA_NEEDED
;
6222 if (job
->printer
->command
)
6225 * Execute a command with the job spool file and wait for it to complete...
6228 int pid
, /* Process ID */
6229 status
; /* Exit status */
6230 struct timeval start
, /* Start time */
6232 char *myargv
[3], /* Command-line arguments */
6233 *myenvp
[400]; /* Environment variables */
6234 int myenvc
; /* Number of environment variables */
6235 ipp_attribute_t
*attr
; /* Job attribute */
6236 char val
[1280], /* IPP_NAME=value */
6237 *valptr
; /* Pointer into string */
6239 int mystdout
= -1; /* File for stdout */
6240 int mypipe
[2]; /* Pipe for stderr */
6241 char line
[2048], /* Line from stderr */
6242 *ptr
, /* Pointer into line */
6243 *endptr
; /* End of line */
6244 ssize_t bytes
; /* Bytes read */
6245 #endif /* !_WIN32 */
6247 fprintf(stderr
, "[Job %d] Running command \"%s %s\".\n", job
->id
, job
->printer
->command
, job
->filename
);
6248 gettimeofday(&start
, NULL
);
6251 * Setup the command-line arguments...
6254 myargv
[0] = job
->printer
->command
;
6255 myargv
[1] = job
->filename
;
6259 * Copy the current environment, then add environment variables for every
6260 * Job attribute and Printer -default attributes...
6263 for (myenvc
= 0; environ
[myenvc
] && myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); myenvc
++)
6264 myenvp
[myenvc
] = strdup(environ
[myenvc
]);
6266 if (myenvc
> (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 32))
6268 fprintf(stderr
, "[Job %d] Too many environment variables to process job.\n", job
->id
);
6269 job
->state
= IPP_JSTATE_ABORTED
;
6273 snprintf(val
, sizeof(val
), "CONTENT_TYPE=%s", job
->format
);
6274 myenvp
[myenvc
++] = strdup(val
);
6276 if (job
->printer
->device_uri
)
6278 snprintf(val
, sizeof(val
), "DEVICE_URI=%s", job
->printer
->device_uri
);
6279 myenvp
[myenvc
++] = strdup(val
);
6282 if (job
->printer
->output_format
)
6284 snprintf(val
, sizeof(val
), "OUTPUT_TYPE=%s", job
->printer
->output_format
);
6285 myenvp
[myenvc
++] = strdup(val
);
6289 if (job
->printer
->ppdfile
)
6291 snprintf(val
, sizeof(val
), "PPD=%s", job
->printer
->ppdfile
);
6292 myenvp
[myenvc
++] = strdup(val
);
6294 #endif /* !CUPS_LITE */
6296 for (attr
= ippFirstAttribute(job
->printer
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->printer
->attrs
))
6299 * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
6300 * "pwg-xxx" to "IPP_PWG_XXX", then add the value(s) from the attribute.
6303 const char *name
= ippGetName(attr
),
6304 /* Attribute name */
6305 *suffix
= strstr(name
, "-default");
6306 /* Suffix on attribute name */
6308 if (strncmp(name
, "pwg-", 4) && (!suffix
|| suffix
[8]))
6316 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6321 *valptr
++ = (char)toupper(*name
& 255);
6326 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6328 myenvp
[myenvc
++] = strdup(val
);
6331 for (attr
= ippFirstAttribute(job
->attrs
); attr
&& myenvc
< (int)(sizeof(myenvp
) / sizeof(myenvp
[0]) - 1); attr
= ippNextAttribute(job
->attrs
))
6334 * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6335 * value(s) from the attribute.
6338 const char *name
= ippGetName(attr
);
6339 /* Attribute name */
6349 while (*name
&& valptr
< (val
+ sizeof(val
) - 2))
6354 *valptr
++ = (char)toupper(*name
& 255);
6359 ippAttributeString(attr
, valptr
, sizeof(val
) - (size_t)(valptr
- val
));
6361 myenvp
[myenvc
++] = strdup(val
);
6366 fprintf(stderr
, "[Job %d] Too many environment variables to process job.\n", job
->id
);
6367 job
->state
= IPP_JSTATE_ABORTED
;
6371 myenvp
[myenvc
] = NULL
;
6374 * Now run the program...
6378 status
= _spawnvpe(_P_WAIT
, job
->printer
->command
, myargv
, myenvp
);
6381 if (job
->printer
->device_uri
)
6383 char scheme
[32], /* URI scheme */
6384 userpass
[256], /* username:password (unused) */
6385 host
[256], /* Hostname or IP address */
6386 resource
[256]; /* Resource path */
6387 int port
; /* Port number */
6390 if (httpSeparateURI(HTTP_URI_CODING_ALL
, job
->printer
->device_uri
, scheme
, sizeof(scheme
), userpass
, sizeof(userpass
), host
, sizeof(host
), &port
, resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
6392 fprintf(stderr
, "[Job %d] Bad device URI \"%s\".\n", job
->id
, job
->printer
->device_uri
);
6394 else if (!strcmp(scheme
, "file"))
6396 struct stat fileinfo
; /* See if this is a file or directory... */
6398 if (stat(resource
, &fileinfo
))
6400 if (errno
== ENOENT
)
6402 if ((mystdout
= open(resource
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) >= 0)
6403 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6405 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6408 fprintf(stderr
, "[Job %d] Unable to access \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6410 else if (S_ISDIR(fileinfo
.st_mode
))
6412 if ((mystdout
= create_job_file(job
, line
, sizeof(line
), resource
, "prn")) >= 0)
6413 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, line
);
6415 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, line
, strerror(errno
));
6417 else if (!S_ISREG(fileinfo
.st_mode
))
6419 if ((mystdout
= open(resource
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) >= 0)
6420 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6422 fprintf(stderr
, "[Job %d] Unable to create \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6424 else if ((mystdout
= open(resource
, O_WRONLY
)) >= 0)
6425 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, resource
);
6427 fprintf(stderr
, "[Job %d] Unable to open \"%s\": %s\n", job
->id
, resource
, strerror(errno
));
6429 else if (!strcmp(scheme
, "socket"))
6431 http_addrlist_t
*addrlist
; /* List of addresses */
6432 char service
[32]; /* Service number */
6434 snprintf(service
, sizeof(service
), "%d", port
);
6436 if ((addrlist
= httpAddrGetList(host
, AF_UNSPEC
, service
)) == NULL
)
6437 fprintf(stderr
, "[Job %d] Unable to find \"%s\": %s\n", job
->id
, host
, cupsLastErrorString());
6438 else if (!httpAddrConnect2(addrlist
, &mystdout
, 30000, &(job
->cancel
)))
6439 fprintf(stderr
, "[Job %d] Unable to connect to \"%s\": %s\n", job
->id
, host
, cupsLastErrorString());
6441 httpAddrFreeList(addrlist
);
6445 fprintf(stderr
, "[Job %d] Unsupported device URI scheme \"%s\".\n", job
->id
, scheme
);
6448 else if ((mystdout
= create_job_file(job
, line
, sizeof(line
), job
->printer
->directory
, "prn")) >= 0)
6450 fprintf(stderr
, "[Job %d] Saving print command output to \"%s\".\n", job
->id
, line
);
6454 mystdout
= open("/dev/null", O_WRONLY
);
6458 fprintf(stderr
, "[Job %d] Unable to create pipe for stderr: %s\n", job
->id
, strerror(errno
));
6459 mypipe
[0] = mypipe
[1] = -1;
6462 if ((pid
= fork()) == 0)
6465 * Child comes here...
6477 execve(job
->printer
->command
, myargv
, myenvp
);
6483 * Unable to fork process...
6486 fprintf(stderr
, "[Job %d] Unable to start job processing command: %s\n", job
->id
, strerror(errno
));
6494 * Free memory used for environment...
6498 free(myenvp
[-- myenvc
]);
6503 * Free memory used for environment...
6507 free(myenvp
[-- myenvc
]);
6510 * Close the output file in the parent process...
6516 * If the pipe exists, read from it until EOF...
6524 while ((bytes
= read(mypipe
[0], endptr
, sizeof(line
) - (size_t)(endptr
- line
) - 1)) > 0)
6529 while ((ptr
= strchr(line
, '\n')) != NULL
)
6531 int level
= 3; /* Message log level */
6535 if (!strncmp(line
, "ATTR:", 5))
6538 * Process job/printer attribute updates.
6541 process_attr_message(job
, line
);
6543 else if (!strncmp(line
, "DEBUG:", 6))
6551 else if (!strncmp(line
, "ERROR:", 6))
6558 job
->message
= strdup(line
+ 6);
6561 else if (!strncmp(line
, "INFO:", 5))
6564 * Informational/progress message...
6570 job
->message
= strdup(line
+ 5);
6574 else if (!strncmp(line
, "STATE:", 6))
6577 * Process printer-state-reasons keywords.
6580 process_state_message(job
, line
);
6583 if (Verbosity
>= level
)
6584 fprintf(stderr
, "[Job %d] Command - %s\n", job
->id
, line
);
6588 memmove(line
, ptr
, (size_t)(endptr
- ptr
));
6598 * Wait for child to complete...
6601 # ifdef HAVE_WAITPID
6602 while (waitpid(pid
, &status
, 0) < 0);
6604 while (wait(&status
) < 0);
6605 # endif /* HAVE_WAITPID */
6612 if (WIFEXITED(status
))
6613 #endif /* !_WIN32 */
6614 fprintf(stderr
, "[Job %d] Command \"%s\" exited with status %d.\n", job
->id
, job
->printer
->command
, WEXITSTATUS(status
));
6617 fprintf(stderr
, "[Job %d] Command \"%s\" terminated with signal %d.\n", job
->id
, job
->printer
->command
, WTERMSIG(status
));
6618 #endif /* !_WIN32 */
6619 job
->state
= IPP_JSTATE_ABORTED
;
6621 else if (status
< 0)
6622 job
->state
= IPP_JSTATE_ABORTED
;
6624 fprintf(stderr
, "[Job %d] Command \"%s\" completed successfully.\n", job
->id
, job
->printer
->command
);
6627 * Report the total processing time...
6630 gettimeofday(&end
, NULL
);
6632 fprintf(stderr
, "[Job %d] Processing time was %.3f seconds.\n", job
->id
, end
.tv_sec
- start
.tv_sec
+ 0.000001 * (end
.tv_usec
- start
.tv_usec
));
6637 * Sleep for a random amount of time to simulate job processing.
6640 sleep((unsigned)(5 + (CUPS_RAND() % 11)));
6644 job
->state
= IPP_JSTATE_CANCELED
;
6645 else if (job
->state
== IPP_JSTATE_PROCESSING
)
6646 job
->state
= IPP_JSTATE_COMPLETED
;
6650 job
->completed
= time(NULL
);
6651 job
->printer
->state
= IPP_PSTATE_IDLE
;
6652 job
->printer
->active_job
= NULL
;
6659 * 'process_state_message()' - Process a STATE: message from a command.
6663 process_state_message(
6664 ippeve_job_t
*job
, /* I - Job */
6665 char *message
) /* I - Message */
6667 int i
; /* Looping var */
6668 ippeve_preason_t state_reasons
, /* printer-state-reasons values */
6669 bit
; /* Current reason bit */
6670 char *ptr
, /* Pointer into message */
6671 *next
; /* Next keyword in message */
6672 int remove
; /* Non-zero if we are removing keywords */
6676 * Skip leading "STATE:" and any whitespace...
6679 for (message
+= 6; *message
; message
++)
6680 if (*message
!= ' ' && *message
!= '\t')
6684 * Support the following forms of message:
6686 * "keyword[,keyword,...]" to set the printer-state-reasons value(s).
6688 * "-keyword[,keyword,...]" to remove keywords.
6690 * "+keyword[,keyword,...]" to add keywords.
6692 * Keywords may or may not have a suffix (-report, -warning, -error) per
6696 if (*message
== '-')
6699 state_reasons
= job
->printer
->state_reasons
;
6702 else if (*message
== '+')
6705 state_reasons
= job
->printer
->state_reasons
;
6711 state_reasons
= IPPEVE_PREASON_NONE
;
6716 if ((next
= strchr(message
, ',')) != NULL
)
6719 if ((ptr
= strstr(message
, "-error")) != NULL
)
6721 else if ((ptr
= strstr(message
, "-report")) != NULL
)
6723 else if ((ptr
= strstr(message
, "-warning")) != NULL
)
6726 for (i
= 0, bit
= 1; i
< (int)(sizeof(ippeve_preason_strings
) / sizeof(ippeve_preason_strings
[0])); i
++, bit
*= 2)
6728 if (!strcmp(message
, ippeve_preason_strings
[i
]))
6731 state_reasons
&= ~bit
;
6733 state_reasons
|= bit
;
6743 job
->printer
->state_reasons
= state_reasons
;
6748 * 'register_printer()' - Register a printer object via Bonjour.
6751 static int /* O - 1 on success, 0 on error */
6753 ippeve_printer_t
*printer
, /* I - Printer */
6754 const char *subtypes
) /* I - Service subtype(s) */
6756 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
6757 ippeve_txt_t ipp_txt
; /* Bonjour IPP TXT record */
6758 int i
, /* Looping var */
6759 count
; /* Number of values */
6760 ipp_attribute_t
*color_supported
,
6761 *document_format_supported
,
6763 *printer_make_and_model
,
6767 *urf_supported
; /* Printer attributes */
6768 const char *value
; /* Value string */
6769 char formats
[252], /* List of supported formats */
6770 urf
[252], /* List of supported URF values */
6771 *ptr
; /* Pointer into string */
6774 if (!strcmp(subtypes
, "off"))
6777 color_supported
= ippFindAttribute(printer
->attrs
, "color-supported", IPP_TAG_BOOLEAN
);
6778 document_format_supported
= ippFindAttribute(printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
);
6779 printer_location
= ippFindAttribute(printer
->attrs
, "printer-location", IPP_TAG_TEXT
);
6780 printer_make_and_model
= ippFindAttribute(printer
->attrs
, "printer-make-and-model", IPP_TAG_TEXT
);
6781 printer_more_info
= ippFindAttribute(printer
->attrs
, "printer-more-info", IPP_TAG_URI
);
6782 printer_uuid
= ippFindAttribute(printer
->attrs
, "printer-uuid", IPP_TAG_URI
);
6783 sides_supported
= ippFindAttribute(printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
);
6784 urf_supported
= ippFindAttribute(printer
->attrs
, "urf-supported", IPP_TAG_KEYWORD
);
6786 for (i
= 0, count
= ippGetCount(document_format_supported
), ptr
= formats
; i
< count
; i
++)
6788 value
= ippGetString(document_format_supported
, i
, NULL
);
6790 if (!strcasecmp(value
, "application/octet-stream"))
6793 if (ptr
> formats
&& ptr
< (formats
+ sizeof(formats
) - 1))
6796 strlcpy(ptr
, value
, sizeof(formats
) - (size_t)(ptr
- formats
));
6799 if (ptr
>= (formats
+ sizeof(formats
) - 1))
6804 for (i
= 0, count
= ippGetCount(urf_supported
), ptr
= urf
; i
< count
; i
++)
6806 value
= ippGetString(urf_supported
, i
, NULL
);
6808 if (ptr
> urf
&& ptr
< (urf
+ sizeof(urf
) - 1))
6811 strlcpy(ptr
, value
, sizeof(urf
) - (size_t)(ptr
- urf
));
6814 if (ptr
>= (urf
+ sizeof(urf
) - 1))
6818 #endif /* HAVE_DNSSD || HAVE_AVAHI */
6820 DNSServiceErrorType error
; /* Error from Bonjour */
6821 char regtype
[256]; /* Bonjour service type */
6822 uint32_t interface
; /* Interface index */
6826 * Build the TXT record for IPP...
6829 TXTRecordCreate(&ipp_txt
, 1024, NULL
);
6830 TXTRecordSetValue(&ipp_txt
, "rp", 9, "ipp/print");
6831 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
6832 TXTRecordSetValue(&ipp_txt
, "ty", (uint8_t)strlen(value
), value
);
6833 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
6834 TXTRecordSetValue(&ipp_txt
, "adminurl", (uint8_t)strlen(value
), value
);
6835 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
6836 TXTRecordSetValue(&ipp_txt
, "note", (uint8_t)strlen(value
), value
);
6837 TXTRecordSetValue(&ipp_txt
, "pdl", (uint8_t)strlen(formats
), formats
);
6838 TXTRecordSetValue(&ipp_txt
, "Color", 1, ippGetBoolean(color_supported
, 0) ? "T" : "F");
6839 TXTRecordSetValue(&ipp_txt
, "Duplex", 1, ippGetCount(sides_supported
) > 1 ? "T" : "F");
6840 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
6841 TXTRecordSetValue(&ipp_txt
, "UUID", (uint8_t)strlen(value
) - 9, value
+ 9);
6843 TXTRecordSetValue(&ipp_txt
, "TLS", 3, "1.2");
6844 # endif /* HAVE_SSL */
6846 TXTRecordSetValue(&ipp_txt
, "URF", (uint8_t)strlen(urf
), urf
);
6847 TXTRecordSetValue(&ipp_txt
, "txtvers", 1, "1");
6848 TXTRecordSetValue(&ipp_txt
, "qtotal", 1, "1");
6851 * Register the _printer._tcp (LPD) service type with a port number of 0 to
6852 * defend our service name but not actually support LPD...
6855 interface
= !strcmp(printer
->hostname
, "localhost") ? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
;
6857 printer
->printer_ref
= DNSSDMaster
;
6859 if ((error
= DNSServiceRegister(&(printer
->printer_ref
), kDNSServiceFlagsShareConnection
, interface
, printer
->dnssd_name
, "_printer._tcp", NULL
/* domain */, NULL
/* host */, 0 /* port */, 0 /* txtLen */, NULL
/* txtRecord */, (DNSServiceRegisterReply
)dnssd_callback
, printer
)) != kDNSServiceErr_NoError
)
6861 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_printer._tcp", error
);
6866 * Then register the _ipp._tcp (IPP) service type with the real port number to
6867 * advertise our IPP printer...
6870 printer
->ipp_ref
= DNSSDMaster
;
6872 if (subtypes
&& *subtypes
)
6873 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtypes
);
6875 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
6877 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
), kDNSServiceFlagsShareConnection
, interface
, printer
->dnssd_name
, regtype
, NULL
/* domain */, NULL
/* host */, htons(printer
->port
), TXTRecordGetLength(&ipp_txt
), TXTRecordGetBytesPtr(&ipp_txt
), (DNSServiceRegisterReply
)dnssd_callback
, printer
)) != kDNSServiceErr_NoError
)
6879 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
6885 * Then register the _ipps._tcp (IPP) service type with the real port number to
6886 * advertise our IPPS printer...
6889 printer
->ipps_ref
= DNSSDMaster
;
6891 if (subtypes
&& *subtypes
)
6892 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtypes
);
6894 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
6896 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
), kDNSServiceFlagsShareConnection
, interface
, printer
->dnssd_name
, regtype
, NULL
/* domain */, NULL
/* host */, htons(printer
->port
), TXTRecordGetLength(&ipp_txt
), TXTRecordGetBytesPtr(&ipp_txt
), (DNSServiceRegisterReply
)dnssd_callback
, printer
)) != kDNSServiceErr_NoError
)
6898 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, regtype
, error
);
6901 # endif /* HAVE_SSL */
6904 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
6905 * real port number to advertise our IPP printer...
6908 printer
->http_ref
= DNSSDMaster
;
6910 if ((error
= DNSServiceRegister(&(printer
->http_ref
), kDNSServiceFlagsShareConnection
, interface
, printer
->dnssd_name
, "_http._tcp,_printer", NULL
/* domain */, NULL
/* host */, htons(printer
->port
), 0 /* txtLen */, NULL
/* txtRecord */, (DNSServiceRegisterReply
)dnssd_callback
, printer
)) != kDNSServiceErr_NoError
)
6912 _cupsLangPrintf(stderr
, _("Unable to register \"%s.%s\": %d"), printer
->dnssd_name
, "_http._tcp,_printer", error
);
6916 TXTRecordDeallocate(&ipp_txt
);
6918 #elif defined(HAVE_AVAHI)
6919 char temp
[256]; /* Subtype service string */
6922 * Create the TXT record...
6926 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "rp=ipp/print");
6927 if ((value
= ippGetString(printer_make_and_model
, 0, NULL
)) != NULL
)
6928 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "ty=%s", value
);
6929 if ((value
= ippGetString(printer_more_info
, 0, NULL
)) != NULL
)
6930 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "adminurl=%s", value
);
6931 if ((value
= ippGetString(printer_location
, 0, NULL
)) != NULL
)
6932 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "note=%s", value
);
6933 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "pdl=%s", formats
);
6934 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Color=%s", ippGetBoolean(color_supported
, 0) ? "T" : "F");
6935 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "Duplex=%s", ippGetCount(sides_supported
) > 1 ? "T" : "F");
6936 if ((value
= ippGetString(printer_uuid
, 0, NULL
)) != NULL
)
6937 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "UUID=%s", value
+ 9);
6939 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "TLS=1.2");
6940 # endif /* HAVE_SSL */
6942 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "URF=%s", urf
);
6943 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "txtvers=1");
6944 ipp_txt
= avahi_string_list_add_printf(ipp_txt
, "qtotal=1");
6947 * Register _printer._tcp (LPD) with port 0 to reserve the service name...
6950 avahi_threaded_poll_lock(DNSSDMaster
);
6952 printer
->ipp_ref
= avahi_entry_group_new(DNSSDClient
, dnssd_callback
, NULL
);
6954 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
);
6957 * Then register the _ipp._tcp (IPP)...
6960 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
);
6961 if (subtypes
&& *subtypes
)
6963 char *temptypes
= strdup(subtypes
), *start
, *end
;
6965 for (start
= temptypes
; *start
; start
= end
)
6967 if ((end
= strchr(start
, ',')) != NULL
)
6970 end
= start
+ strlen(start
);
6972 snprintf(temp
, sizeof(temp
), "%s._sub._ipp._tcp", start
);
6973 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipp._tcp", NULL
, temp
);
6981 * _ipps._tcp (IPPS) for secure printing...
6984 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
);
6985 if (subtypes
&& *subtypes
)
6987 char *temptypes
= strdup(subtypes
), *start
, *end
;
6989 for (start
= temptypes
; *start
; start
= end
)
6991 if ((end
= strchr(start
, ',')) != NULL
)
6994 end
= start
+ strlen(start
);
6996 snprintf(temp
, sizeof(temp
), "%s._sub._ipps._tcp", start
);
6997 avahi_entry_group_add_service_subtype(printer
->ipp_ref
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0, printer
->dnssd_name
, "_ipps._tcp", NULL
, temp
);
7002 #endif /* HAVE_SSL */
7005 * Finally _http.tcp (HTTP) for the web interface...
7008 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
);
7009 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");
7015 avahi_entry_group_commit(printer
->ipp_ref
);
7016 avahi_threaded_poll_unlock(DNSSDMaster
);
7018 avahi_string_list_free(ipp_txt
);
7019 #endif /* HAVE_DNSSD */
7026 * 'respond_http()' - Send a HTTP response.
7029 int /* O - 1 on success, 0 on failure */
7031 ippeve_client_t
*client
, /* I - Client */
7032 http_status_t code
, /* I - HTTP status of response */
7033 const char *content_encoding
, /* I - Content-Encoding of response */
7034 const char *type
, /* I - MIME media type of response */
7035 size_t length
) /* I - Length of response */
7037 char message
[1024]; /* Text message */
7040 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
7042 if (code
== HTTP_STATUS_CONTINUE
)
7045 * 100-continue doesn't send any headers...
7048 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
7052 * Format an error message...
7055 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
&& code
!= HTTP_STATUS_SWITCHING_PROTOCOLS
)
7057 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
7059 type
= "text/plain";
7060 length
= strlen(message
);
7066 * Send the HTTP response header...
7069 httpClearFields(client
->http
);
7071 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
7072 client
->operation
== HTTP_STATE_OPTIONS
)
7073 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
7075 if (code
== HTTP_STATUS_UNAUTHORIZED
)
7077 char value
[256]; /* WWW-Authenticate value */
7079 snprintf(value
, sizeof(value
), "Basic realm=\"%s\"", PAMService
);
7080 httpSetField(client
->http
, HTTP_FIELD_WWW_AUTHENTICATE
, value
);
7085 if (!strcmp(type
, "text/html"))
7086 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
7087 "text/html; charset=utf-8");
7089 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
7091 if (content_encoding
)
7092 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
7095 httpSetLength(client
->http
, length
);
7097 if (httpWriteResponse(client
->http
, code
) < 0)
7101 * Send the response data...
7107 * Send a plain text message.
7110 if (httpPrintf(client
->http
, "%s", message
) < 0)
7113 if (httpWrite2(client
->http
, "", 0) < 0)
7116 else if (client
->response
)
7119 * Send an IPP response...
7122 debug_attributes("Response", client
->response
, 2);
7124 ippSetState(client
->response
, IPP_STATE_IDLE
);
7126 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
7135 * 'respond_ipp()' - Send an IPP response.
7139 respond_ipp(ippeve_client_t
*client
, /* I - Client */
7140 ipp_status_t status
, /* I - status-code */
7141 const char *message
, /* I - printf-style status-message */
7142 ...) /* I - Additional args as needed */
7144 const char *formatted
= NULL
; /* Formatted message */
7147 ippSetStatusCode(client
->response
, status
);
7151 va_list ap
; /* Pointer to additional args */
7152 ipp_attribute_t
*attr
; /* New status-message attribute */
7154 va_start(ap
, message
);
7155 if ((attr
= ippFindAttribute(client
->response
, "status-message", IPP_TAG_TEXT
)) != NULL
)
7156 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
7158 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
, "status-message", NULL
, message
, ap
);
7161 formatted
= ippGetString(attr
, 0, NULL
);
7165 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
, ippOpString(client
->operation_id
), ippErrorString(status
), formatted
);
7167 fprintf(stderr
, "%s %s %s\n", client
->hostname
, ippOpString(client
->operation_id
), ippErrorString(status
));
7172 * 'respond_unsupported()' - Respond with an unsupported attribute.
7176 respond_unsupported(
7177 ippeve_client_t
*client
, /* I - Client */
7178 ipp_attribute_t
*attr
) /* I - Atribute */
7180 ipp_attribute_t
*temp
; /* Copy of attribute */
7183 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
, "Unsupported %s %s%s value.", ippGetName(attr
), ippGetCount(attr
) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr
)));
7185 temp
= ippCopyAttribute(client
->response
, attr
, 0);
7186 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
7191 * 'run_printer()' - Run the printer service.
7195 run_printer(ippeve_printer_t
*printer
) /* I - Printer */
7197 int num_fds
; /* Number of file descriptors */
7198 struct pollfd polldata
[3]; /* poll() data */
7199 int timeout
; /* Timeout for poll() */
7200 ippeve_client_t
*client
; /* New client */
7204 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
7207 polldata
[0].fd
= printer
->ipv4
;
7208 polldata
[0].events
= POLLIN
;
7210 polldata
[1].fd
= printer
->ipv6
;
7211 polldata
[1].events
= POLLIN
;
7216 polldata
[num_fds
].fd
= DNSServiceRefSockFD(DNSSDMaster
);
7217 polldata
[num_fds
++].events
= POLLIN
;
7218 #endif /* HAVE_DNSSD */
7221 * Loop until we are killed or have a hard error...
7226 if (cupsArrayCount(printer
->jobs
))
7231 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
7233 perror("poll() failed");
7237 if (polldata
[0].revents
& POLLIN
)
7239 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
7241 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
7245 _cupsThreadDetach(t
);
7249 perror("Unable to create client thread");
7250 delete_client(client
);
7255 if (polldata
[1].revents
& POLLIN
)
7257 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
7259 _cups_thread_t t
= _cupsThreadCreate((_cups_thread_func_t
)process_client
, client
);
7263 _cupsThreadDetach(t
);
7267 perror("Unable to create client thread");
7268 delete_client(client
);
7274 if (polldata
[2].revents
& POLLIN
)
7275 DNSServiceProcessResult(DNSSDMaster
);
7276 #endif /* HAVE_DNSSD */
7279 * Clean out old jobs...
7282 clean_jobs(printer
);
7288 * 'show_media()' - Show media load state.
7291 static int /* O - 1 on success, 0 on failure */
7292 show_media(ippeve_client_t
*client
) /* I - Client connection */
7294 ippeve_printer_t
*printer
= client
->printer
;
7296 int i
, j
, /* Looping vars */
7297 num_ready
, /* Number of ready media */
7298 num_sizes
, /* Number of media sizes */
7299 num_sources
, /* Number of media sources */
7300 num_types
; /* Number of media types */
7301 ipp_attribute_t
*media_col_ready
,/* media-col-ready attribute */
7302 *media_ready
, /* media-ready attribute */
7303 *media_sizes
, /* media-supported attribute */
7304 *media_sources
, /* media-source-supported attribute */
7305 *media_types
, /* media-type-supported attribute */
7306 *input_tray
; /* printer-input-tray attribute */
7307 ipp_t
*media_col
; /* media-col value */
7308 const char *media_size
, /* media value */
7309 *media_source
, /* media-source value */
7310 *media_type
, /* media-type value */
7311 *ready_size
, /* media-col-ready media-size[-name] value */
7312 *ready_source
, /* media-col-ready media-source value */
7313 *ready_tray
, /* printer-input-tray value */
7314 *ready_type
; /* media-col-ready media-type value */
7315 char tray_str
[1024], /* printer-input-tray string value */
7316 *tray_ptr
; /* Pointer into value */
7317 int tray_len
; /* Length of printer-input-tray value */
7318 int ready_sheets
; /* printer-input-tray sheets value */
7319 int num_options
= 0;/* Number of form options */
7320 cups_option_t
*options
= NULL
;/* Form options */
7321 static const int sheets
[] = /* Number of sheets */
7333 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7336 html_header(client
, printer
->name
, 0);
7338 if ((media_col_ready
= ippFindAttribute(printer
->attrs
, "media-col-ready", IPP_TAG_BEGIN_COLLECTION
)) == NULL
)
7340 html_printf(client
, "<p>Error: No media-col-ready defined for printer.</p>\n");
7341 html_footer(client
);
7345 media_ready
= ippFindAttribute(printer
->attrs
, "media-ready", IPP_TAG_ZERO
);
7347 if ((media_sizes
= ippFindAttribute(printer
->attrs
, "media-supported", IPP_TAG_ZERO
)) == NULL
)
7349 html_printf(client
, "<p>Error: No media-supported defined for printer.</p>\n");
7350 html_footer(client
);
7354 if ((media_sources
= ippFindAttribute(printer
->attrs
, "media-source-supported", IPP_TAG_ZERO
)) == NULL
)
7356 html_printf(client
, "<p>Error: No media-source-supported defined for printer.</p>\n");
7357 html_footer(client
);
7361 if ((media_types
= ippFindAttribute(printer
->attrs
, "media-type-supported", IPP_TAG_ZERO
)) == NULL
)
7363 html_printf(client
, "<p>Error: No media-type-supported defined for printer.</p>\n");
7364 html_footer(client
);
7368 if ((input_tray
= ippFindAttribute(printer
->attrs
, "printer-input-tray", IPP_TAG_STRING
)) == NULL
)
7370 html_printf(client
, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7371 html_footer(client
);
7375 num_ready
= ippGetCount(media_col_ready
);
7376 num_sizes
= ippGetCount(media_sizes
);
7377 num_sources
= ippGetCount(media_sources
);
7378 num_types
= ippGetCount(media_types
);
7380 if (num_sources
!= ippGetCount(input_tray
))
7382 html_printf(client
, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7383 html_footer(client
);
7388 * Process form data if present...
7391 if (printer
->web_forms
)
7392 num_options
= parse_options(client
, &options
);
7394 if (num_options
> 0)
7397 * WARNING: A real printer/server implementation MUST NOT implement
7398 * media updates via a GET request - GET requests are supposed to be
7399 * idempotent (without side-effects) and we obviously are not
7400 * authenticating access here. This form is provided solely to
7401 * enable testing and development!
7404 char name
[255]; /* Form name */
7405 const char *val
; /* Form value */
7406 pwg_media_t
*media
; /* Media info */
7408 _cupsRWLockWrite(&printer
->rwlock
);
7410 ippDeleteAttribute(printer
->attrs
, media_col_ready
);
7411 media_col_ready
= NULL
;
7415 ippDeleteAttribute(printer
->attrs
, media_ready
);
7419 printer
->state_reasons
&= (ippeve_preason_t
)~(IPPEVE_PREASON_MEDIA_LOW
| IPPEVE_PREASON_MEDIA_EMPTY
| IPPEVE_PREASON_MEDIA_NEEDED
);
7421 for (i
= 0; i
< num_sources
; i
++)
7423 media_source
= ippGetString(media_sources
, i
, NULL
);
7425 if (!strcmp(media_source
, "auto") || !strcmp(media_source
, "manual") || strstr(media_source
, "-man") != NULL
)
7428 snprintf(name
, sizeof(name
), "size%d", i
);
7429 if ((media_size
= cupsGetOption(name
, num_options
, options
)) != NULL
&& (media
= pwgMediaForPWG(media_size
)) != NULL
)
7431 snprintf(name
, sizeof(name
), "type%d", i
);
7432 if ((media_type
= cupsGetOption(name
, num_options
, options
)) != NULL
&& !*media_type
)
7436 ippSetString(printer
->attrs
, &media_ready
, ippGetCount(media_ready
), media_size
);
7438 media_ready
= ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-ready", NULL
, media_size
);
7440 media_col
= create_media_col(media_size
, media_source
, media_type
, media
->width
, media
->length
, -1, -1, -1, -1);
7442 if (media_col_ready
)
7443 ippSetCollection(printer
->attrs
, &media_col_ready
, ippGetCount(media_col_ready
), media_col
);
7445 media_col_ready
= ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-ready", media_col
);
7446 ippDelete(media_col
);
7451 snprintf(name
, sizeof(name
), "level%d", i
);
7452 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
7453 ready_sheets
= atoi(val
);
7457 snprintf(tray_str
, sizeof(tray_str
), "type=sheetFeedAuto%sRemovableTray;mediafeed=%d;mediaxfeed=%d;maxcapacity=%d;level=%d;status=0;name=%s;", !strcmp(media_source
, "by-pass-tray") ? "Non" : "", media
? media
->length
: 0, media
? media
->width
: 0, strcmp(media_source
, "by-pass-tray") ? 250 : 25, ready_sheets
, media_source
);
7459 ippSetOctetString(printer
->attrs
, &input_tray
, i
, tray_str
, (int)strlen(tray_str
));
7461 if (ready_sheets
== 0)
7463 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_EMPTY
;
7464 if (printer
->active_job
)
7465 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_NEEDED
;
7467 else if (ready_sheets
< 25 && ready_sheets
> 0)
7468 printer
->state_reasons
|= IPPEVE_PREASON_MEDIA_LOW
;
7471 if (!media_col_ready
)
7472 media_col_ready
= ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-col-ready");
7475 media_ready
= ippAddOutOfBand(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "media-ready");
7477 _cupsRWUnlock(&printer
->rwlock
);
7480 if (printer
->web_forms
)
7481 html_printf(client
, "<form method=\"GET\" action=\"/media\">\n");
7483 html_printf(client
, "<table class=\"form\" summary=\"Media\">\n");
7484 for (i
= 0; i
< num_sources
; i
++)
7486 media_source
= ippGetString(media_sources
, i
, NULL
);
7488 if (!strcmp(media_source
, "auto") || !strcmp(media_source
, "manual") || strstr(media_source
, "-man") != NULL
)
7491 for (j
= 0, ready_size
= NULL
, ready_type
= NULL
; j
< num_ready
; j
++)
7493 media_col
= ippGetCollection(media_col_ready
, j
);
7494 ready_size
= ippGetString(ippFindAttribute(media_col
, "media-size-name", IPP_TAG_ZERO
), 0, NULL
);
7495 ready_source
= ippGetString(ippFindAttribute(media_col
, "media-source", IPP_TAG_ZERO
), 0, NULL
);
7496 ready_type
= ippGetString(ippFindAttribute(media_col
, "media-type", IPP_TAG_ZERO
), 0, NULL
);
7498 if (ready_source
&& !strcmp(ready_source
, media_source
))
7501 ready_source
= NULL
;
7506 html_printf(client
, "<tr><th>%s:</th>", media_source
);
7512 if (printer
->web_forms
)
7514 html_printf(client
, "<td><select name=\"size%d\"><option value=\"\">None</option>", i
);
7515 for (j
= 0; j
< num_sizes
; j
++)
7517 media_size
= ippGetString(media_sizes
, j
, NULL
);
7519 html_printf(client
, "<option%s>%s</option>", (ready_size
&& !strcmp(ready_size
, media_size
)) ? " selected" : "", media_size
);
7521 html_printf(client
, "</select>");
7524 html_printf(client
, "<td>%s", ready_size
);
7530 if (printer
->web_forms
)
7532 html_printf(client
, " <select name=\"type%d\"><option value=\"\">None</option>", i
);
7533 for (j
= 0; j
< num_types
; j
++)
7535 media_type
= ippGetString(media_types
, j
, NULL
);
7537 html_printf(client
, "<option%s>%s</option>", (ready_type
&& !strcmp(ready_type
, media_type
)) ? " selected" : "", media_type
);
7539 html_printf(client
, "</select>");
7541 else if (ready_type
)
7542 html_printf(client
, ", %s", ready_type
);
7545 * Level/sheets loaded...
7548 if ((ready_tray
= ippGetOctetString(input_tray
, i
, &tray_len
)) != NULL
)
7550 if (tray_len
> (int)(sizeof(tray_str
) - 1))
7551 tray_len
= (int)sizeof(tray_str
) - 1;
7552 memcpy(tray_str
, ready_tray
, (size_t)tray_len
);
7553 tray_str
[tray_len
] = '\0';
7555 if ((tray_ptr
= strstr(tray_str
, "level=")) != NULL
)
7556 ready_sheets
= atoi(tray_ptr
+ 6);
7563 if (printer
->web_forms
)
7565 html_printf(client
, " <select name=\"level%d\">", i
);
7566 for (j
= 0; j
< (int)(sizeof(sheets
) / sizeof(sheets
[0])); j
++)
7568 if (!strcmp(media_source
, "by-pass-tray") && sheets
[j
] > 25)
7572 html_printf(client
, "<option value=\"%d\"%s>Unknown</option>", sheets
[j
], sheets
[j
] == ready_sheets
? " selected" : "");
7574 html_printf(client
, "<option value=\"%d\"%s>%d sheets</option>", sheets
[j
], sheets
[j
] == ready_sheets
? " selected" : "", sheets
[j
]);
7576 html_printf(client
, "</select></td></tr>\n");
7578 else if (ready_sheets
== 1)
7579 html_printf(client
, ", 1 sheet</td></tr>\n");
7580 else if (ready_sheets
> 0)
7581 html_printf(client
, ", %d sheets</td></tr>\n", ready_sheets
);
7583 html_printf(client
, "</td></tr>\n");
7586 if (printer
->web_forms
)
7588 html_printf(client
, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
7589 if (num_options
> 0)
7590 html_printf(client
, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
7591 html_printf(client
, "</td></tr></table></form>\n");
7593 if (num_options
> 0)
7594 html_printf(client
, "<script>\n"
7595 "setTimeout(hide_status, 3000);\n"
7596 "function hide_status() {\n"
7597 " var status = document.getElementById('status');\n"
7598 " status.style.display = 'none';\n"
7603 html_printf(client
, "</table>\n");
7605 html_footer(client
);
7612 * 'show_status()' - Show printer/system state.
7615 static int /* O - 1 on success, 0 on failure */
7616 show_status(ippeve_client_t
*client
) /* I - Client connection */
7618 ippeve_printer_t
*printer
= client
->printer
;
7620 ippeve_job_t
*job
; /* Current job */
7621 int i
; /* Looping var */
7622 ippeve_preason_t reason
; /* Current reason */
7623 static const char * const reasons
[] = /* Reason strings */
7627 "Input Tray Missing",
7628 "Marker Supply Empty",
7629 "Marker Supply Low",
7630 "Marker Waste Almost Full",
7631 "Marker Waste Full",
7642 static const char * const state_colors
[] =
7643 { /* State colors */
7645 "#EE0", /* Processing */
7646 "#C00" /* Stopped */
7650 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7653 html_header(client
, printer
->name
, printer
->state
== IPP_PSTATE_PROCESSING
? 5 : 15);
7654 html_printf(client
, "<h1><img style=\"background: %s; border-radius: 10px; float: left; margin-right: 10px; padding: 10px;\" src=\"/icon.png\" width=\"64\" height=\"64\">%s Jobs</h1>\n", state_colors
[printer
->state
- IPP_PSTATE_IDLE
], printer
->name
);
7655 html_printf(client
, "<p>%s, %d job(s).", printer
->state
== IPP_PSTATE_IDLE
? "Idle" : printer
->state
== IPP_PSTATE_PROCESSING
? "Printing" : "Stopped", cupsArrayCount(printer
->jobs
));
7656 for (i
= 0, reason
= 1; i
< (int)(sizeof(reasons
) / sizeof(reasons
[0])); i
++, reason
<<= 1)
7657 if (printer
->state_reasons
& reason
)
7658 html_printf(client
, "\n<br> %s", reasons
[i
]);
7659 html_printf(client
, "</p>\n");
7661 if (cupsArrayCount(printer
->jobs
) > 0)
7663 _cupsRWLockRead(&(printer
->rwlock
));
7665 html_printf(client
, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>Status</th></tr></thead><tbody>\n");
7666 for (job
= (ippeve_job_t
*)cupsArrayFirst(printer
->jobs
); job
; job
= (ippeve_job_t
*)cupsArrayNext(printer
->jobs
))
7668 char when
[256], /* When job queued/started/finished */
7669 hhmmss
[64]; /* Time HH:MM:SS */
7673 case IPP_JSTATE_PENDING
:
7674 case IPP_JSTATE_HELD
:
7675 snprintf(when
, sizeof(when
), "Queued at %s", time_string(job
->created
, hhmmss
, sizeof(hhmmss
)));
7677 case IPP_JSTATE_PROCESSING
:
7678 case IPP_JSTATE_STOPPED
:
7679 snprintf(when
, sizeof(when
), "Started at %s", time_string(job
->processing
, hhmmss
, sizeof(hhmmss
)));
7681 case IPP_JSTATE_ABORTED
:
7682 snprintf(when
, sizeof(when
), "Aborted at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7684 case IPP_JSTATE_CANCELED
:
7685 snprintf(when
, sizeof(when
), "Canceled at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7687 case IPP_JSTATE_COMPLETED
:
7688 snprintf(when
, sizeof(when
), "Completed at %s", time_string(job
->completed
, hhmmss
, sizeof(hhmmss
)));
7692 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
);
7694 html_printf(client
, "</tbody></table>\n");
7696 _cupsRWUnlock(&(printer
->rwlock
));
7699 html_footer(client
);
7706 * 'show_supplies()' - Show printer supplies.
7709 static int /* O - 1 on success, 0 on failure */
7711 ippeve_client_t
*client
) /* I - Client connection */
7713 ippeve_printer_t
*printer
= client
->printer
;
7715 int i
, /* Looping var */
7716 num_supply
; /* Number of supplies */
7717 ipp_attribute_t
*supply
, /* printer-supply attribute */
7718 *supply_desc
; /* printer-supply-description attribute */
7719 int num_options
= 0; /* Number of form options */
7720 cups_option_t
*options
= NULL
; /* Form options */
7721 int supply_len
, /* Length of supply value */
7722 level
; /* Supply level */
7723 const char *supply_value
; /* Supply value */
7724 char supply_text
[1024], /* Supply string */
7725 *supply_ptr
; /* Pointer into supply string */
7726 static const char * const printer_supply
[] =
7727 { /* printer-supply values */
7728 "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
7729 "maxcapacity=100;level=%d;colorantname=unknown;",
7730 "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
7731 "maxcapacity=100;level=%d;colorantname=black;",
7732 "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
7733 "maxcapacity=100;level=%d;colorantname=cyan;",
7734 "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
7735 "maxcapacity=100;level=%d;colorantname=magenta;",
7736 "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
7737 "maxcapacity=100;level=%d;colorantname=yellow;"
7739 static const char * const backgrounds
[] =
7740 { /* Background colors for the supply-level bars */
7741 "#777 linear-gradient(#333,#777)",
7742 "#000 linear-gradient(#666,#000)",
7743 "#0FF linear-gradient(#6FF,#0FF)",
7744 "#F0F linear-gradient(#F6F,#F0F)",
7745 "#CC0 linear-gradient(#EE6,#EE0)"
7747 static const char * const colors
[] = /* Text colors for the supply-level bars */
7757 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0))
7760 html_header(client
, printer
->name
, 0);
7762 if ((supply
= ippFindAttribute(printer
->attrs
, "printer-supply", IPP_TAG_STRING
)) == NULL
)
7764 html_printf(client
, "<p>Error: No printer-supply defined for printer.</p>\n");
7765 html_footer(client
);
7769 num_supply
= ippGetCount(supply
);
7771 if ((supply_desc
= ippFindAttribute(printer
->attrs
, "printer-supply-description", IPP_TAG_TEXT
)) == NULL
)
7773 html_printf(client
, "<p>Error: No printer-supply-description defined for printer.</p>\n");
7774 html_footer(client
);
7778 if (num_supply
!= ippGetCount(supply_desc
))
7780 html_printf(client
, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
7781 html_footer(client
);
7785 if (printer
->web_forms
)
7786 num_options
= parse_options(client
, &options
);
7788 if (num_options
> 0)
7791 * WARNING: A real printer/server implementation MUST NOT implement
7792 * supply updates via a GET request - GET requests are supposed to be
7793 * idempotent (without side-effects) and we obviously are not
7794 * authenticating access here. This form is provided solely to
7795 * enable testing and development!
7798 char name
[64]; /* Form field */
7799 const char *val
; /* Form value */
7801 _cupsRWLockWrite(&printer
->rwlock
);
7803 ippDeleteAttribute(printer
->attrs
, supply
);
7806 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
);
7808 for (i
= 0; i
< num_supply
; i
++)
7810 snprintf(name
, sizeof(name
), "supply%d", i
);
7811 if ((val
= cupsGetOption(name
, num_options
, options
)) != NULL
)
7813 level
= atoi(val
); /* New level */
7815 snprintf(supply_text
, sizeof(supply_text
), printer_supply
[i
], level
);
7817 ippSetOctetString(printer
->attrs
, &supply
, ippGetCount(supply
), supply_text
, (int)strlen(supply_text
));
7819 supply
= ippAddOctetString(printer
->attrs
, IPP_TAG_PRINTER
, "printer-supply", supply_text
, (int)strlen(supply_text
));
7824 printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_FULL
;
7825 else if (level
> 90)
7826 printer
->state_reasons
|= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL
;
7831 printer
->state_reasons
|= IPPEVE_PREASON_TONER_EMPTY
;
7832 else if (level
< 10)
7833 printer
->state_reasons
|= IPPEVE_PREASON_TONER_LOW
;
7838 _cupsRWUnlock(&printer
->rwlock
);
7841 if (printer
->web_forms
)
7842 html_printf(client
, "<form method=\"GET\" action=\"/supplies\">\n");
7844 html_printf(client
, "<table class=\"form\" summary=\"Supplies\">\n");
7845 for (i
= 0; i
< num_supply
; i
++)
7847 supply_value
= ippGetOctetString(supply
, i
, &supply_len
);
7848 if (supply_len
> (int)(sizeof(supply_text
) - 1))
7849 supply_len
= (int)sizeof(supply_text
) - 1;
7851 memcpy(supply_text
, supply_value
, (size_t)supply_len
);
7852 supply_text
[supply_len
] = '\0';
7854 if ((supply_ptr
= strstr(supply_text
, "level=")) != NULL
)
7855 level
= atoi(supply_ptr
+ 6);
7859 if (printer
->web_forms
)
7860 html_printf(client
, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc
, i
, NULL
), i
, level
);
7862 html_printf(client
, "<tr><th>%s:</th>", ippGetString(supply_desc
, i
, NULL
));
7865 html_printf(client
, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; padding: 5px %dpx;\"></span> %d%%</td></tr>\n", backgrounds
[i
], level
* 2, level
);
7867 html_printf(client
, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; color: %s; padding: 5px %dpx;\">%d%%</span></td></tr>\n", backgrounds
[i
], colors
[i
], level
* 2, level
);
7870 if (printer
->web_forms
)
7872 html_printf(client
, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
7873 if (num_options
> 0)
7874 html_printf(client
, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
7875 html_printf(client
, "</td></tr>\n</table>\n</form>\n");
7877 if (num_options
> 0)
7878 html_printf(client
, "<script>\n"
7879 "setTimeout(hide_status, 3000);\n"
7880 "function hide_status() {\n"
7881 " var status = document.getElementById('status');\n"
7882 " status.style.display = 'none';\n"
7887 html_printf(client
, "</table>\n");
7889 html_footer(client
);
7896 * 'time_string()' - Return the local time in hours, minutes, and seconds.
7900 time_string(time_t tv
, /* I - Time value */
7901 char *buffer
, /* I - Buffer */
7902 size_t bufsize
) /* I - Size of buffer */
7904 struct tm date
; /* Local time and date */
7906 localtime_r(&tv
, &date
);
7908 strftime(buffer
, bufsize
, "%X", &date
);
7915 * 'usage()' - Show program usage.
7919 usage(int status
) /* O - Exit status */
7921 _cupsLangPuts(stdout
, _("Usage: ippeveprinter [options] \"name\""));
7922 _cupsLangPuts(stdout
, _("Options:"));
7923 _cupsLangPuts(stdout
, _("--help Show program help"));
7924 _cupsLangPuts(stdout
, _("--no-web-forms Disable web forms for media and supplies"));
7925 _cupsLangPuts(stdout
, _("--pam-service service Use the named PAM service"));
7926 _cupsLangPuts(stdout
, _("--version Show program version"));
7927 _cupsLangPuts(stdout
, _("-2 Set 2-sided printing support (default=1-sided)"));
7928 _cupsLangPuts(stdout
, _("-A Enable authentication"));
7929 _cupsLangPuts(stdout
, _("-D device-uri Set the device URI for the printer"));
7930 _cupsLangPuts(stdout
, _("-F output-type/subtype Set the output format for the printer"));
7932 _cupsLangPuts(stdout
, _("-K keypath Set location of server X.509 certificates and keys."));
7933 #endif /* HAVE_SSL */
7934 _cupsLangPuts(stdout
, _("-M manufacturer Set manufacturer name (default=Test)"));
7935 _cupsLangPuts(stdout
, _("-P filename.ppd Load printer attributes from PPD file"));
7936 _cupsLangPuts(stdout
, _("-V version Set default IPP version"));
7937 _cupsLangPuts(stdout
, _("-a filename.conf Load printer attributes from conf file"));
7938 _cupsLangPuts(stdout
, _("-c command Set print command"));
7939 _cupsLangPuts(stdout
, _("-d spool-directory Set spool directory"));
7940 _cupsLangPuts(stdout
, _("-f type/subtype[,...] Set supported file types"));
7941 _cupsLangPuts(stdout
, _("-i iconfile.png Set icon file"));
7942 _cupsLangPuts(stdout
, _("-k Keep job spool files"));
7943 _cupsLangPuts(stdout
, _("-l location Set location of printer"));
7944 _cupsLangPuts(stdout
, _("-m model Set model name (default=Printer)"));
7945 _cupsLangPuts(stdout
, _("-n hostname Set hostname for printer"));
7946 _cupsLangPuts(stdout
, _("-p port Set port number for printer"));
7947 _cupsLangPuts(stdout
, _("-r subtype,[subtype] Set DNS-SD service subtype"));
7948 _cupsLangPuts(stdout
, _("-s speed[,color-speed] Set speed in pages per minute"));
7949 _cupsLangPuts(stdout
, _("-v Be verbose"));
7956 * 'valid_doc_attributes()' - Determine whether the document attributes are
7959 * When one or more document attributes are invalid, this function adds a
7960 * suitable response and attributes to the unsupported group.
7963 static int /* O - 1 if valid, 0 if not */
7964 valid_doc_attributes(
7965 ippeve_client_t
*client
) /* I - Client */
7967 int valid
= 1; /* Valid attributes? */
7968 ipp_op_t op
= ippGetOperation(client
->request
);
7970 const char *op_name
= ippOpString(op
);
7971 /* IPP operation name */
7972 ipp_attribute_t
*attr
, /* Current attribute */
7973 *supported
; /* xxx-supported attribute */
7974 const char *compression
= NULL
,
7975 /* compression value */
7976 *format
= NULL
; /* document-format value */
7980 * Check operation attributes...
7983 if ((attr
= ippFindAttribute(client
->request
, "compression", IPP_TAG_ZERO
)) != NULL
)
7986 * If compression is specified, only accept a supported value in a Print-Job
7987 * or Send-Document request...
7990 compression
= ippGetString(attr
, 0, NULL
);
7991 supported
= ippFindAttribute(client
->printer
->attrs
,
7992 "compression-supported", IPP_TAG_KEYWORD
);
7994 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
7995 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
7996 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
7997 op
!= IPP_OP_VALIDATE_JOB
) ||
7998 !ippContainsString(supported
, compression
))
8000 respond_unsupported(client
, attr
);
8005 fprintf(stderr
, "%s %s compression=\"%s\"\n", client
->hostname
, op_name
, compression
);
8007 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "compression-supplied", NULL
, compression
);
8009 if (strcmp(compression
, "none"))
8012 fprintf(stderr
, "Receiving job file with \"%s\" compression.\n", compression
);
8013 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
8019 * Is it a format we support?
8022 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_ZERO
)) != NULL
)
8024 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
8025 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
8027 respond_unsupported(client
, attr
);
8032 format
= ippGetString(attr
, 0, NULL
);
8034 fprintf(stderr
, "%s %s document-format=\"%s\"\n", client
->hostname
, op_name
, format
);
8036 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-supplied", NULL
, format
);
8041 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
, "document-format-default", IPP_TAG_MIMETYPE
), 0, NULL
);
8043 format
= "application/octet-stream"; /* Should never happen */
8045 attr
= ippAddString(client
->request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
8048 if (format
&& !strcmp(format
, "application/octet-stream") && (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
|| ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
8051 * Auto-type the file using the first 8 bytes of the file...
8054 unsigned char header
[8]; /* First 8 bytes of file */
8056 memset(header
, 0, sizeof(header
));
8057 httpPeek(client
->http
, (char *)header
, sizeof(header
));
8059 if (!memcmp(header
, "%PDF", 4))
8060 format
= "application/pdf";
8061 else if (!memcmp(header
, "%!", 2))
8062 format
= "application/postscript";
8063 else if (!memcmp(header
, "\377\330\377", 3) && header
[3] >= 0xe0 && header
[3] <= 0xef)
8064 format
= "image/jpeg";
8065 else if (!memcmp(header
, "\211PNG", 4))
8066 format
= "image/png";
8067 else if (!memcmp(header
, "RAS2", 4))
8068 format
= "image/pwg-raster";
8069 else if (!memcmp(header
, "UNIRAST", 8))
8070 format
= "image/urf";
8076 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n", client
->hostname
, op_name
, format
);
8078 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
, "document-format-detected", NULL
, format
);
8082 if (op
!= IPP_OP_CREATE_JOB
&& (supported
= ippFindAttribute(client
->printer
->attrs
, "document-format-supported", IPP_TAG_MIMETYPE
)) != NULL
&& !ippContainsString(supported
, format
))
8084 respond_unsupported(client
, attr
);
8092 if ((attr
= ippFindAttribute(client
->request
, "document-name", IPP_TAG_NAME
)) != NULL
)
8093 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "document-name-supplied", NULL
, ippGetString(attr
, 0, NULL
));
8100 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
8102 * When one or more job attributes are invalid, this function adds a suitable
8103 * response and attributes to the unsupported group.
8106 static int /* O - 1 if valid, 0 if not */
8107 valid_job_attributes(
8108 ippeve_client_t
*client
) /* I - Client */
8110 int i
, /* Looping var */
8111 count
, /* Number of values */
8112 valid
= 1; /* Valid attributes? */
8113 ipp_attribute_t
*attr
, /* Current attribute */
8114 *supported
; /* xxx-supported attribute */
8118 * Check operation attributes...
8121 valid
= valid_doc_attributes(client
);
8124 * Check the various job template attributes...
8127 if ((attr
= ippFindAttribute(client
->request
, "copies", IPP_TAG_ZERO
)) != NULL
)
8129 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
8130 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
8132 respond_unsupported(client
, attr
);
8137 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity", IPP_TAG_ZERO
)) != NULL
)
8139 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
8141 respond_unsupported(client
, attr
);
8146 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until", IPP_TAG_ZERO
)) != NULL
)
8148 if (ippGetCount(attr
) != 1 ||
8149 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
8150 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
8151 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
8152 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
8154 respond_unsupported(client
, attr
);
8159 if ((attr
= ippFindAttribute(client
->request
, "job-impressions", IPP_TAG_ZERO
)) != NULL
)
8161 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
|| ippGetInteger(attr
, 0) < 0)
8163 respond_unsupported(client
, attr
);
8168 if ((attr
= ippFindAttribute(client
->request
, "job-name", IPP_TAG_ZERO
)) != NULL
)
8170 if (ippGetCount(attr
) != 1 ||
8171 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
8172 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
8174 respond_unsupported(client
, attr
);
8178 ippSetGroupTag(client
->request
, &attr
, IPP_TAG_JOB
);
8181 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
, "Untitled");
8183 if ((attr
= ippFindAttribute(client
->request
, "job-priority", IPP_TAG_ZERO
)) != NULL
)
8185 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
8186 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
8188 respond_unsupported(client
, attr
);
8193 if ((attr
= ippFindAttribute(client
->request
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
8195 if (ippGetCount(attr
) != 1 ||
8196 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
8197 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
8198 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
8199 strcmp(ippGetString(attr
, 0, NULL
), "none"))
8201 respond_unsupported(client
, attr
);
8206 if ((attr
= ippFindAttribute(client
->request
, "media", IPP_TAG_ZERO
)) != NULL
)
8208 if (ippGetCount(attr
) != 1 ||
8209 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
8210 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
8211 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
8213 respond_unsupported(client
, attr
);
8218 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
8220 if (!ippContainsString(supported
, ippGetString(attr
, 0, NULL
)))
8222 respond_unsupported(client
, attr
);
8228 if ((attr
= ippFindAttribute(client
->request
, "media-col", IPP_TAG_ZERO
)) != NULL
)
8230 ipp_t
*col
, /* media-col collection */
8231 *size
; /* media-size collection */
8232 ipp_attribute_t
*member
, /* Member attribute */
8233 *x_dim
, /* x-dimension */
8234 *y_dim
; /* y-dimension */
8235 int x_value
, /* y-dimension value */
8236 y_value
; /* x-dimension value */
8238 if (ippGetCount(attr
) != 1 ||
8239 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
8241 respond_unsupported(client
, attr
);
8245 col
= ippGetCollection(attr
, 0);
8247 if ((member
= ippFindAttribute(col
, "media-size-name", IPP_TAG_ZERO
)) != NULL
)
8249 if (ippGetCount(member
) != 1 ||
8250 (ippGetValueTag(member
) != IPP_TAG_NAME
&&
8251 ippGetValueTag(member
) != IPP_TAG_NAMELANG
&&
8252 ippGetValueTag(member
) != IPP_TAG_KEYWORD
))
8254 respond_unsupported(client
, attr
);
8259 supported
= ippFindAttribute(client
->printer
->attrs
, "media-supported", IPP_TAG_KEYWORD
);
8261 if (!ippContainsString(supported
, ippGetString(member
, 0, NULL
)))
8263 respond_unsupported(client
, attr
);
8268 else if ((member
= ippFindAttribute(col
, "media-size", IPP_TAG_BEGIN_COLLECTION
)) != NULL
)
8270 if (ippGetCount(member
) != 1)
8272 respond_unsupported(client
, attr
);
8277 size
= ippGetCollection(member
, 0);
8279 if ((x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(x_dim
) != 1 ||
8280 (y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_INTEGER
)) == NULL
|| ippGetCount(y_dim
) != 1)
8282 respond_unsupported(client
, attr
);
8287 x_value
= ippGetInteger(x_dim
, 0);
8288 y_value
= ippGetInteger(y_dim
, 0);
8289 supported
= ippFindAttribute(client
->printer
->attrs
, "media-size-supported", IPP_TAG_BEGIN_COLLECTION
);
8290 count
= ippGetCount(supported
);
8292 for (i
= 0; i
< count
; i
++)
8294 size
= ippGetCollection(supported
, i
);
8295 x_dim
= ippFindAttribute(size
, "x-dimension", IPP_TAG_ZERO
);
8296 y_dim
= ippFindAttribute(size
, "y-dimension", IPP_TAG_ZERO
);
8298 if (ippContainsInteger(x_dim
, x_value
) && ippContainsInteger(y_dim
, y_value
))
8304 respond_unsupported(client
, attr
);
8312 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling", IPP_TAG_ZERO
)) != NULL
)
8314 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
8315 (strcmp(ippGetString(attr
, 0, NULL
),
8316 "separate-documents-uncollated-copies") &&
8317 strcmp(ippGetString(attr
, 0, NULL
),
8318 "separate-documents-collated-copies")))
8320 respond_unsupported(client
, attr
);
8325 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested", IPP_TAG_ZERO
)) != NULL
)
8327 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
8328 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
8329 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
8331 respond_unsupported(client
, attr
);
8336 if ((attr
= ippFindAttribute(client
->request
, "page-ranges", IPP_TAG_ZERO
)) != NULL
)
8338 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
8340 respond_unsupported(client
, attr
);
8345 if ((attr
= ippFindAttribute(client
->request
, "print-quality", IPP_TAG_ZERO
)) != NULL
)
8347 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
8348 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
8349 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
8351 respond_unsupported(client
, attr
);
8356 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution", IPP_TAG_ZERO
)) != NULL
)
8358 supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
8360 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
8363 respond_unsupported(client
, attr
);
8368 int xdpi
, /* Horizontal resolution for job template attribute */
8369 ydpi
, /* Vertical resolution for job template attribute */
8370 sydpi
; /* Vertical resolution for supported value */
8371 ipp_res_t units
, /* Units for job template attribute */
8372 sunits
; /* Units for supported value */
8374 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
8375 count
= ippGetCount(supported
);
8377 for (i
= 0; i
< count
; i
++)
8379 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
8385 respond_unsupported(client
, attr
);
8391 if ((attr
= ippFindAttribute(client
->request
, "sides", IPP_TAG_ZERO
)) != NULL
)
8393 const char *sides
= ippGetString(attr
, 0, NULL
);
8394 /* "sides" value... */
8396 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
8398 respond_unsupported(client
, attr
);
8401 else if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported", IPP_TAG_KEYWORD
)) != NULL
)
8403 if (!ippContainsString(supported
, sides
))
8405 respond_unsupported(client
, attr
);
8409 else if (strcmp(sides
, "one-sided"))
8411 respond_unsupported(client
, attr
);