diff options
Diffstat (limited to 'test/ippeveprinter.c')
-rw-r--r-- | test/ippeveprinter.c | 8080 |
1 files changed, 0 insertions, 8080 deletions
diff --git a/test/ippeveprinter.c b/test/ippeveprinter.c deleted file mode 100644 index 177e9a61a..000000000 --- a/test/ippeveprinter.c +++ /dev/null @@ -1,8080 +0,0 @@ -/* - * IPP Everywhere printer application for CUPS. - * - * Copyright © 2010-2019 by Apple Inc. - * - * Licensed under Apache License v2.0. See the file "LICENSE" for more - * information.º - * - * Note: This program began life as the "ippserver" sample code that first - * appeared in CUPS 1.4. The name has been changed in order to distinguish it - * from the PWG's much more ambitious "ippserver" program, which supports - * different kinds of IPP services and multiple services per instance - the - * "ippeveprinter" program exposes a single print service conforming to the - * current IPP Everywhere specification, thus the new name. - */ - -/* - * Include necessary headers... - */ - -#include <cups/cups-private.h> -#if !CUPS_LITE -# include <cups/ppd-private.h> -#endif /* !CUPS_LITE */ - -#include <limits.h> -#include <sys/stat.h> - -#ifdef _WIN32 -# include <fcntl.h> -# include <io.h> -# include <process.h> -# define WEXITSTATUS(s) (s) -# include <winsock2.h> -typedef ULONG nfds_t; -# define poll WSAPoll -#else -extern char **environ; - -# include <sys/fcntl.h> -# include <sys/wait.h> -# include <poll.h> -#endif /* _WIN32 */ - -#ifdef HAVE_DNSSD -# include <dns_sd.h> -#elif defined(HAVE_AVAHI) -# include <avahi-client/client.h> -# include <avahi-client/publish.h> -# include <avahi-common/error.h> -# include <avahi-common/thread-watch.h> -#endif /* HAVE_DNSSD */ -#ifdef HAVE_SYS_MOUNT_H -# include <sys/mount.h> -#endif /* HAVE_SYS_MOUNT_H */ -#ifdef HAVE_SYS_STATFS_H -# include <sys/statfs.h> -#endif /* HAVE_SYS_STATFS_H */ -#ifdef HAVE_SYS_STATVFS_H -# include <sys/statvfs.h> -#endif /* HAVE_SYS_STATVFS_H */ -#ifdef HAVE_SYS_VFS_H -# include <sys/vfs.h> -#endif /* HAVE_SYS_VFS_H */ - -#include "printer-png.h" - - -/* - * Constants... - */ - -enum ippeve_preason_e /* printer-state-reasons bit values */ -{ - IPPEVE_PREASON_NONE = 0x0000, /* none */ - IPPEVE_PREASON_OTHER = 0x0001, /* other */ - IPPEVE_PREASON_COVER_OPEN = 0x0002, /* cover-open */ - IPPEVE_PREASON_INPUT_TRAY_MISSING = 0x0004, - /* input-tray-missing */ - IPPEVE_PREASON_MARKER_SUPPLY_EMPTY = 0x0008, - /* marker-supply-empty */ - IPPEVE_PREASON_MARKER_SUPPLY_LOW = 0x0010, - /* marker-supply-low */ - IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020, - /* marker-waste-almost-full */ - IPPEVE_PREASON_MARKER_WASTE_FULL = 0x0040, - /* marker-waste-full */ - IPPEVE_PREASON_MEDIA_EMPTY = 0x0080, /* media-empty */ - IPPEVE_PREASON_MEDIA_JAM = 0x0100, /* media-jam */ - IPPEVE_PREASON_MEDIA_LOW = 0x0200, /* media-low */ - IPPEVE_PREASON_MEDIA_NEEDED = 0x0400, /* media-needed */ - IPPEVE_PREASON_MOVING_TO_PAUSED = 0x0800, - /* moving-to-paused */ - IPPEVE_PREASON_PAUSED = 0x1000, /* paused */ - IPPEVE_PREASON_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */ - IPPEVE_PREASON_TONER_EMPTY = 0x4000, /* toner-empty */ - IPPEVE_PREASON_TONER_LOW = 0x8000 /* toner-low */ -}; -typedef unsigned int ippeve_preason_t; /* Bitfield for printer-state-reasons */ -static const char * const ippeve_preason_strings[] = -{ /* Strings for each bit */ - /* "none" is implied for no bits set */ - "other", - "cover-open", - "input-tray-missing", - "marker-supply-empty", - "marker-supply-low", - "marker-waste-almost-full", - "marker-waste-full", - "media-empty", - "media-jam", - "media-low", - "media-needed", - "moving-to-paused", - "paused", - "spool-area-full", - "toner-empty", - "toner-low" -}; - - -/* - * URL scheme for web resources... - */ - -#ifdef HAVE_SSL -# define WEB_SCHEME "https" -#else -# define WEB_SCHEME "http" -#endif /* HAVE_SSL */ - - -/* - * Structures... - */ - -#ifdef HAVE_DNSSD -typedef DNSServiceRef ippeve_srv_t; /* Service reference */ -typedef TXTRecordRef ippeve_txt_t; /* TXT record */ - -#elif defined(HAVE_AVAHI) -typedef AvahiEntryGroup *ippeve_srv_t; /* Service reference */ -typedef AvahiStringList *ippeve_txt_t; /* TXT record */ - -#else -typedef void *ippeve_srv_t; /* Service reference */ -typedef void *ippeve_txt_t; /* TXT record */ -#endif /* HAVE_DNSSD */ - -typedef struct ippeve_filter_s /**** Attribute filter ****/ -{ - cups_array_t *ra; /* Requested attributes */ - ipp_tag_t group_tag; /* Group to copy */ -} ippeve_filter_t; - -typedef struct ippeve_job_s ippeve_job_t; - -typedef struct ippeve_printer_s /**** Printer data ****/ -{ - /* TODO: One IPv4 and one IPv6 listener are really not sufficient */ - int ipv4, /* IPv4 listener */ - ipv6; /* IPv6 listener */ - ippeve_srv_t ipp_ref, /* Bonjour IPP service */ - ipps_ref, /* Bonjour IPPS service */ - http_ref, /* Bonjour HTTP service */ - printer_ref; /* Bonjour LPD service */ - char *dnssd_name, /* printer-dnssd-name */ - *name, /* printer-name */ - *icon, /* Icon filename */ - *directory, /* Spool directory */ - *hostname, /* Hostname */ - *uri, /* printer-uri-supported */ - *device_uri, /* Device URI (if any) */ -#if !CUPS_LITE - *ppdfile, /* PPD file (if any) */ -#endif /* !CUPS_LITE */ - *command; /* Command to run with job file */ - int port; /* Port */ - int web_forms; /* Enable web interface forms? */ - size_t urilen; /* Length of printer URI */ - ipp_t *attrs; /* Static attributes */ - time_t start_time; /* Startup time */ - time_t config_time; /* printer-config-change-time */ - ipp_pstate_t state; /* printer-state value */ - ippeve_preason_t state_reasons; /* printer-state-reasons values */ - time_t state_time; /* printer-state-change-time */ - cups_array_t *jobs; /* Jobs */ - ippeve_job_t *active_job; /* Current active/pending job */ - int next_job_id; /* Next job-id value */ - _cups_rwlock_t rwlock; /* Printer lock */ -} ippeve_printer_t; - -struct ippeve_job_s /**** Job data ****/ -{ - int id; /* Job ID */ - const char *name, /* job-name */ - *username, /* job-originating-user-name */ - *format; /* document-format */ - ipp_jstate_t state; /* job-state value */ - char *message; /* job-state-message value */ - int msglevel; /* job-state-message log level (0=error, 1=info) */ - time_t created, /* time-at-creation value */ - processing, /* time-at-processing value */ - completed; /* time-at-completed value */ - int impressions, /* job-impressions value */ - impcompleted; /* job-impressions-completed value */ - ipp_t *attrs; /* Static attributes */ - int cancel; /* Non-zero when job canceled */ - char *filename; /* Print file name */ - int fd; /* Print file descriptor */ - ippeve_printer_t *printer; /* Printer */ -}; - -typedef struct ippeve_client_s /**** Client data ****/ -{ - http_t *http; /* HTTP connection */ - ipp_t *request, /* IPP request */ - *response; /* IPP response */ - time_t start; /* Request start time */ - http_state_t operation; /* Request operation */ - ipp_op_t operation_id; /* IPP operation-id */ - char uri[1024], /* Request URI */ - *options; /* URI options */ - http_addr_t addr; /* Client address */ - char hostname[256]; /* Client hostname */ - ippeve_printer_t *printer; /* Printer */ - ippeve_job_t *job; /* Current job, if any */ -} ippeve_client_t; - - -/* - * Local functions... - */ - -static void clean_jobs(ippeve_printer_t *printer); -static int compare_jobs(ippeve_job_t *a, ippeve_job_t *b); -static void copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra, ipp_tag_t group_tag, int quickcopy); -static void copy_job_attributes(ippeve_client_t *client, ippeve_job_t *job, cups_array_t *ra); -static ippeve_client_t *create_client(ippeve_printer_t *printer, int sock); -static ippeve_job_t *create_job(ippeve_client_t *client); -static int create_job_file(ippeve_job_t *job, char *fname, size_t fnamesize, const char *dir, const char *ext); -static int create_listener(const char *name, int port, int family); -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); -static ipp_t *create_media_size(int width, int length); -static ippeve_printer_t *create_printer(const char *servername, int serverport, const char *name, const char *location, const char *icon, cups_array_t *docformats, const char *subtypes, const char *directory, const char *command, const char *device_uri, ipp_t *attrs); -static void debug_attributes(const char *title, ipp_t *ipp, int response); -static void delete_client(ippeve_client_t *client); -static void delete_job(ippeve_job_t *job); -static void delete_printer(ippeve_printer_t *printer); -#ifdef HAVE_DNSSD -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); -#elif defined(HAVE_AVAHI) -static void dnssd_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, void *context); -static void dnssd_client_cb(AvahiClient *c, AvahiClientState state, void *userdata); -#endif /* HAVE_DNSSD */ -static void dnssd_init(void); -static int filter_cb(ippeve_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr); -static ippeve_job_t *find_job(ippeve_client_t *client); -static void finish_document_data(ippeve_client_t *client, ippeve_job_t *job); -static void finish_document_uri(ippeve_client_t *client, ippeve_job_t *job); -static void html_escape(ippeve_client_t *client, const char *s, size_t slen); -static void html_footer(ippeve_client_t *client); -static void html_header(ippeve_client_t *client, const char *title, int refresh); -static void html_printf(ippeve_client_t *client, const char *format, ...) _CUPS_FORMAT(2, 3); -static void ipp_cancel_job(ippeve_client_t *client); -static void ipp_close_job(ippeve_client_t *client); -static void ipp_create_job(ippeve_client_t *client); -static void ipp_get_job_attributes(ippeve_client_t *client); -static void ipp_get_jobs(ippeve_client_t *client); -static void ipp_get_printer_attributes(ippeve_client_t *client); -static void ipp_identify_printer(ippeve_client_t *client); -static void ipp_print_job(ippeve_client_t *client); -static void ipp_print_uri(ippeve_client_t *client); -static void ipp_send_document(ippeve_client_t *client); -static void ipp_send_uri(ippeve_client_t *client); -static void ipp_validate_job(ippeve_client_t *client); -static ipp_t *load_ippserver_attributes(const char *servername, int serverport, const char *filename, cups_array_t *docformats); -static ipp_t *load_legacy_attributes(const char *make, const char *model, int ppm, int ppm_color, int duplex, cups_array_t *docformats); -#if !CUPS_LITE -static ipp_t *load_ppd_attributes(const char *ppdfile, cups_array_t *docformats); -#endif /* !CUPS_LITE */ -static int parse_options(ippeve_client_t *client, cups_option_t **options); -static void process_attr_message(ippeve_job_t *job, char *message); -static void *process_client(ippeve_client_t *client); -static int process_http(ippeve_client_t *client); -static int process_ipp(ippeve_client_t *client); -static void *process_job(ippeve_job_t *job); -static void process_state_message(ippeve_job_t *job, char *message); -static int register_printer(ippeve_printer_t *printer, const char *subtypes); -static int respond_http(ippeve_client_t *client, http_status_t code, const char *content_coding, const char *type, size_t length); -static void respond_ipp(ippeve_client_t *client, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4); -static void respond_unsupported(ippeve_client_t *client, ipp_attribute_t *attr); -static void run_printer(ippeve_printer_t *printer); -static int show_media(ippeve_client_t *client); -static int show_status(ippeve_client_t *client); -static int show_supplies(ippeve_client_t *client); -static char *time_string(time_t tv, char *buffer, size_t bufsize); -static void usage(int status) _CUPS_NORETURN; -static int valid_doc_attributes(ippeve_client_t *client); -static int valid_job_attributes(ippeve_client_t *client); - - -/* - * Globals... - */ - -#ifdef HAVE_DNSSD -static DNSServiceRef DNSSDMaster = NULL; -#elif defined(HAVE_AVAHI) -static AvahiThreadedPoll *DNSSDMaster = NULL; -static AvahiClient *DNSSDClient = NULL; -#endif /* HAVE_DNSSD */ - -static int KeepFiles = 0, /* Keep spooled job files? */ - MaxVersion = 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */ - Verbosity = 0; /* Verbosity level */ - - -/* - * 'main()' - Main entry to the sample server. - */ - -int /* O - Exit status */ -main(int argc, /* I - Number of command-line args */ - char *argv[]) /* I - Command-line arguments */ -{ - int i; /* Looping var */ - const char *opt, /* Current option character */ - *attrfile = NULL, /* ippserver attributes file */ - *command = NULL, /* Command to run with job files */ - *device_uri = NULL, /* Device URI */ - *icon = NULL, /* Icon file */ -#ifdef HAVE_SSL - *keypath = NULL, /* Keychain path */ -#endif /* HAVE_SSL */ - *location = "", /* Location of printer */ - *make = "Example", /* Manufacturer */ - *model = "Printer", /* Model */ - *name = NULL, /* Printer name */ -#if !CUPS_LITE - *ppdfile = NULL, /* PPD file */ -#endif /* !CUPS_LITE */ - *subtypes = "_print"; /* DNS-SD service subtype */ - int legacy = 0, /* Legacy mode? */ - duplex = 0, /* Duplex mode */ - ppm = 10, /* Pages per minute for mono */ - ppm_color = 0, /* Pages per minute for color */ - web_forms = 1; /* Enable web site forms? */ - ipp_t *attrs = NULL; /* Printer attributes */ - char directory[1024] = ""; /* Spool directory */ - cups_array_t *docformats = NULL; /* Supported formats */ - const char *servername = NULL; /* Server host name */ - int serverport = 0; /* Server port number (0 = auto) */ - ippeve_printer_t *printer; /* Printer object */ - - - /* - * Parse command-line arguments... - */ - - for (i = 1; i < argc; i ++) - { - if (!strcmp(argv[i], "--help")) - { - usage(0); - } - else if (!strcmp(argv[i], "--no-web-forms")) - { - web_forms = 0; - } - else if (!strcmp(argv[i], "--version")) - { - puts(CUPS_SVERSION); - return (0); - } - else if (!strncmp(argv[i], "--", 2)) - { - _cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]); - usage(1); - } - else if (argv[i][0] == '-') - { - for (opt = argv[i] + 1; *opt; opt ++) - { - switch (*opt) - { - case '2' : /* -2 (enable 2-sided printing) */ - duplex = 1; - legacy = 1; - break; - - case 'D' : /* -D device-uri */ - i ++; - if (i >= argc) - usage(1); - - device_uri = argv[i]; - break; - -#ifdef HAVE_SSL - case 'K' : /* -K keypath */ - i ++; - if (i >= argc) - usage(1); - - keypath = argv[i]; - break; -#endif /* HAVE_SSL */ - - case 'M' : /* -M manufacturer */ - i ++; - if (i >= argc) - usage(1); - - make = argv[i]; - legacy = 1; - break; - -#if !CUPS_LITE - case 'P' : /* -P filename.ppd */ - i ++; - if (i >= argc) - usage(1); - - ppdfile = argv[i]; - break; -#endif /* !CUPS_LITE */ - - case 'V' : /* -V max-version */ - i ++; - if (i >= argc) - usage(1); - - if (!strcmp(argv[i], "2.0")) - MaxVersion = 20; - else if (!strcmp(argv[i], "1.1")) - MaxVersion = 11; - else - usage(1); - break; - - case 'a' : /* -a attributes-file */ - i ++; - if (i >= argc) - usage(1); - - attrfile = argv[i]; - break; - - case 'c' : /* -c command */ - i ++; - if (i >= argc) - usage(1); - - command = argv[i]; - break; - - case 'd' : /* -d spool-directory */ - i ++; - if (i >= argc) - usage(1); - - strlcpy(directory, argv[i], sizeof(directory)); - break; - - case 'f' : /* -f type/subtype[,...] */ - i ++; - if (i >= argc) - usage(1); - - docformats = _cupsArrayNewStrings(argv[i], ','); - legacy = 1; - break; - - case 'i' : /* -i icon.png */ - i ++; - if (i >= argc) - usage(1); - - icon = argv[i]; - break; - - case 'k' : /* -k (keep files) */ - KeepFiles = 1; - break; - - case 'l' : /* -l location */ - i ++; - if (i >= argc) - usage(1); - - location = argv[i]; - break; - - case 'm' : /* -m model */ - i ++; - if (i >= argc) - usage(1); - - model = argv[i]; - legacy = 1; - break; - - case 'n' : /* -n hostname */ - i ++; - if (i >= argc) - usage(1); - - servername = argv[i]; - break; - - case 'p' : /* -p port */ - i ++; - if (i >= argc || !isdigit(argv[i][0] & 255)) - usage(1); - - serverport = atoi(argv[i]); - break; - - case 'r' : /* -r subtype */ - i ++; - if (i >= argc) - usage(1); - - subtypes = argv[i]; - break; - - case 's' : /* -s speed[,color-speed] */ - i ++; - if (i >= argc) - usage(1); - - if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1) - usage(1); - - legacy = 1; - break; - - case 'v' : /* -v (be verbose) */ - Verbosity ++; - break; - - default : /* Unknown */ - _cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."), argv[0], *opt); - usage(1); - } - } - } - else if (!name) - { - name = argv[i]; - } - else - { - _cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]); - usage(1); - } - } - - if (!name) - usage(1); - -#if CUPS_LITE - if (attrfile != NULL && legacy) - usage(1); -#else - if (((ppdfile != NULL) + (attrfile != NULL) + legacy) > 1) - usage(1); -#endif /* CUPS_LITE */ - - /* - * Apply defaults as needed... - */ - - if (!serverport) - { -#ifdef _WIN32 - /* - * Windows is almost always used as a single user system, so use a default - * port number of 8631. - */ - - serverport = 8631; - -#else - /* - * Use 8000 + UID mod 1000 for the default port number... - */ - - serverport = 8000 + ((int)getuid() % 1000); -#endif /* _WIN32 */ - - _cupsLangPrintf(stderr, _("Listening on port %d."), serverport); - } - - if (!directory[0]) - { - const char *tmpdir; /* Temporary directory */ - -#ifdef _WIN32 - if ((tmpdir = getenv("TEMP")) == NULL) - tmpdir = "C:/TEMP"; -#elif defined(__APPLE__) && TARGET_OS_OSX - if ((tmpdir = getenv("TMPDIR")) == NULL) - tmpdir = "/private/tmp"; -#else - if ((tmpdir = getenv("TMPDIR")) == NULL) - tmpdir = "/tmp"; -#endif /* _WIN32 */ - - snprintf(directory, sizeof(directory), "%s/ippeveprinter.%d", tmpdir, (int)getpid()); - - if (mkdir(directory, 0755) && errno != EEXIST) - { - _cupsLangPrintf(stderr, _("Unable to create spool directory \"%s\": %s"), directory, strerror(errno)); - usage(1); - } - - if (Verbosity) - _cupsLangPrintf(stderr, _("Using spool directory \"%s\"."), directory); - } - -#ifdef HAVE_SSL - cupsSetServerCredentials(keypath, servername, 1); -#endif /* HAVE_SSL */ - - /* - * Initialize DNS-SD... - */ - - dnssd_init(); - - /* - * Create the printer... - */ - - if (!docformats) - docformats = _cupsArrayNewStrings(ppm_color > 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ','); - - if (attrfile) - attrs = load_ippserver_attributes(servername, serverport, attrfile, docformats); -#if !CUPS_LITE - else if (ppdfile) - { - attrs = load_ppd_attributes(ppdfile, docformats); - - if (!command) - command = "ippeveps"; - } -#endif /* !CUPS_LITE */ - else - attrs = load_legacy_attributes(make, model, ppm, ppm_color, duplex, docformats); - - if ((printer = create_printer(servername, serverport, name, location, icon, docformats, subtypes, directory, command, device_uri, attrs)) == NULL) - return (1); - - printer->web_forms = web_forms; - -#if !CUPS_LITE - if (ppdfile) - printer->ppdfile = strdup(ppdfile); -#endif /* !CUPS_LITE */ - - /* - * Run the print service... - */ - - run_printer(printer); - - /* - * Destroy the printer and exit... - */ - - delete_printer(printer); - - return (0); -} - - -/* - * 'clean_jobs()' - Clean out old (completed) jobs. - */ - -static void -clean_jobs(ippeve_printer_t *printer) /* I - Printer */ -{ - ippeve_job_t *job; /* Current job */ - time_t cleantime; /* Clean time */ - - - if (cupsArrayCount(printer->jobs) == 0) - return; - - cleantime = time(NULL) - 60; - - _cupsRWLockWrite(&(printer->rwlock)); - for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); - job; - job = (ippeve_job_t *)cupsArrayNext(printer->jobs)) - if (job->completed && job->completed < cleantime) - { - cupsArrayRemove(printer->jobs, job); - delete_job(job); - } - else - break; - _cupsRWUnlock(&(printer->rwlock)); -} - - -/* - * 'compare_jobs()' - Compare two jobs. - */ - -static int /* O - Result of comparison */ -compare_jobs(ippeve_job_t *a, /* I - First job */ - ippeve_job_t *b) /* I - Second job */ -{ - return (b->id - a->id); -} - - -/* - * 'copy_attributes()' - Copy attributes from one request to another. - */ - -static void -copy_attributes(ipp_t *to, /* I - Destination request */ - ipp_t *from, /* I - Source request */ - cups_array_t *ra, /* I - Requested attributes */ - ipp_tag_t group_tag, /* I - Group to copy */ - int quickcopy) /* I - Do a quick copy? */ -{ - ippeve_filter_t filter; /* Filter data */ - - - filter.ra = ra; - filter.group_tag = group_tag; - - ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter); -} - - -/* - * 'copy_job_attrs()' - Copy job attributes to the response. - */ - -static void -copy_job_attributes( - ippeve_client_t *client, /* I - Client */ - ippeve_job_t *job, /* I - Job */ - cups_array_t *ra) /* I - requested-attributes */ -{ - copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0); - - if (!ra || cupsArrayFind(ra, "date-time-at-completed")) - { - if (job->completed) - ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed)); - else - ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed"); - } - - if (!ra || cupsArrayFind(ra, "date-time-at-processing")) - { - if (job->processing) - ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-processing", ippTimeToDate(job->processing)); - else - ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing"); - } - - if (!ra || cupsArrayFind(ra, "job-impressions")) - ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions", job->impressions); - - if (!ra || cupsArrayFind(ra, "job-impressions-completed")) - ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", job->impcompleted); - - if (!ra || cupsArrayFind(ra, "job-printer-up-time")) - ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-printer-up-time", (int)(time(NULL) - client->printer->start_time)); - - if (!ra || cupsArrayFind(ra, "job-state")) - ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state); - - if (!ra || cupsArrayFind(ra, "job-state-message")) - { - if (job->message) - { - ippAddString(client->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, job->message); - } - else - { - switch (job->state) - { - case IPP_JSTATE_PENDING : - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job pending."); - break; - - case IPP_JSTATE_HELD : - if (job->fd >= 0) - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job incoming."); - else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO)) - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job held."); - else - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job created."); - break; - - case IPP_JSTATE_PROCESSING : - if (job->cancel) - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceling."); - else - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job printing."); - break; - - case IPP_JSTATE_STOPPED : - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job stopped."); - break; - - case IPP_JSTATE_CANCELED : - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceled."); - break; - - case IPP_JSTATE_ABORTED : - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job aborted."); - break; - - case IPP_JSTATE_COMPLETED : - ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job completed."); - break; - } - } - } - - if (!ra || cupsArrayFind(ra, "job-state-reasons")) - { - switch (job->state) - { - case IPP_JSTATE_PENDING : - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", - NULL, "none"); - break; - - case IPP_JSTATE_HELD : - if (job->fd >= 0) - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), - "job-state-reasons", NULL, "job-incoming"); - else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO)) - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), - "job-state-reasons", NULL, "job-hold-until-specified"); - else - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), - "job-state-reasons", NULL, "job-data-insufficient"); - break; - - case IPP_JSTATE_PROCESSING : - if (job->cancel) - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), - "job-state-reasons", NULL, "processing-to-stop-point"); - else - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), - "job-state-reasons", NULL, "job-printing"); - break; - - case IPP_JSTATE_STOPPED : - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", - NULL, "job-stopped"); - break; - - case IPP_JSTATE_CANCELED : - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", - NULL, "job-canceled-by-user"); - break; - - case IPP_JSTATE_ABORTED : - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", - NULL, "aborted-by-system"); - break; - - case IPP_JSTATE_COMPLETED : - ippAddString(client->response, IPP_TAG_JOB, - IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons", - NULL, "job-completed-successfully"); - break; - } - } - - if (!ra || cupsArrayFind(ra, "time-at-completed")) - ippAddInteger(client->response, IPP_TAG_JOB, - job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE, - "time-at-completed", (int)(job->completed - client->printer->start_time)); - - if (!ra || cupsArrayFind(ra, "time-at-processing")) - ippAddInteger(client->response, IPP_TAG_JOB, - job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE, - "time-at-processing", (int)(job->processing - client->printer->start_time)); -} - - -/* - * 'create_client()' - Accept a new network connection and create a client - * object. - */ - -static ippeve_client_t * /* O - Client */ -create_client(ippeve_printer_t *printer, /* I - Printer */ - int sock) /* I - Listen socket */ -{ - ippeve_client_t *client; /* Client */ - - - if ((client = calloc(1, sizeof(ippeve_client_t))) == NULL) - { - perror("Unable to allocate memory for client"); - return (NULL); - } - - client->printer = printer; - - /* - * Accept the client and get the remote address... - */ - - if ((client->http = httpAcceptConnection(sock, 1)) == NULL) - { - perror("Unable to accept client connection"); - - free(client); - - return (NULL); - } - - httpGetHostname(client->http, client->hostname, sizeof(client->hostname)); - - if (Verbosity) - fprintf(stderr, "Accepted connection from %s\n", client->hostname); - - return (client); -} - - -/* - * 'create_job()' - Create a new job object from a Print-Job or Create-Job - * request. - */ - -static ippeve_job_t * /* O - Job */ -create_job(ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* Job */ - ipp_attribute_t *attr; /* Job attribute */ - char uri[1024], /* job-uri value */ - uuid[64]; /* job-uuid value */ - - - _cupsRWLockWrite(&(client->printer->rwlock)); - if (client->printer->active_job && - client->printer->active_job->state < IPP_JSTATE_CANCELED) - { - /* - * Only accept a single job at a time... - */ - - _cupsRWUnlock(&(client->printer->rwlock)); - return (NULL); - } - - /* - * Allocate and initialize the job object... - */ - - if ((job = calloc(1, sizeof(ippeve_job_t))) == NULL) - { - perror("Unable to allocate memory for job"); - return (NULL); - } - - job->printer = client->printer; - job->attrs = ippNew(); - job->state = IPP_JSTATE_HELD; - job->fd = -1; - - /* - * Copy all of the job attributes... - */ - - copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0); - - /* - * Get the requesting-user-name, document format, and priority... - */ - - if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL) - job->username = ippGetString(attr, 0, NULL); - else - job->username = "anonymous"; - - ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username); - - if (ippGetOperation(client->request) != IPP_OP_CREATE_JOB) - { - if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL) - job->format = ippGetString(attr, 0, NULL); - else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL) - job->format = ippGetString(attr, 0, NULL); - else - job->format = "application/octet-stream"; - } - - if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_INTEGER)) != NULL) - job->impressions = ippGetInteger(attr, 0); - - if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_NAME)) != NULL) - job->name = ippGetString(attr, 0, NULL); - - /* - * Add job description attributes and add to the jobs array... - */ - - job->id = client->printer->next_job_id ++; - - snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id); - httpAssembleUUID(client->printer->hostname, client->printer->port, client->printer->name, job->id, uuid, sizeof(uuid)); - - ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(&job->created))); - ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id); - ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri); - ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid); - if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL) - ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, ippGetString(attr, 0, NULL)); - else - ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, client->printer->uri); - ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time)); - - cupsArrayAdd(client->printer->jobs, job); - client->printer->active_job = job; - - _cupsRWUnlock(&(client->printer->rwlock)); - - return (job); -} - - -/* - * 'create_job_file()' - Create a file for the document in a job. - */ - -static int /* O - File descriptor or -1 on error */ -create_job_file( - ippeve_job_t *job, /* I - Job */ - char *fname, /* I - Filename buffer */ - size_t fnamesize, /* I - Size of filename buffer */ - const char *directory, /* I - Directory to store in */ - const char *ext) /* I - Extension (`NULL` for default) */ -{ - char name[256], /* "Safe" filename */ - *nameptr; /* Pointer into filename */ - const char *job_name; /* job-name value */ - - - /* - * Make a name from the job-name attribute... - */ - - if ((job_name = ippGetString(ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME), 0, NULL)) == NULL) - job_name = "untitled"; - - for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++) - { - if (isalnum(*job_name & 255) || *job_name == '-') - { - *nameptr++ = (char)tolower(*job_name & 255); - } - else - { - *nameptr++ = '_'; - - while (job_name[1] && !isalnum(job_name[1] & 255) && job_name[1] != '-') - job_name ++; - } - } - - *nameptr = '\0'; - - /* - * Figure out the extension... - */ - - if (!ext) - { - if (!strcasecmp(job->format, "image/jpeg")) - ext = "jpg"; - else if (!strcasecmp(job->format, "image/png")) - ext = "png"; - else if (!strcasecmp(job->format, "image/pwg-raster")) - ext = "pwg"; - else if (!strcasecmp(job->format, "image/urf")) - ext = "urf"; - else if (!strcasecmp(job->format, "application/pdf")) - ext = "pdf"; - else if (!strcasecmp(job->format, "application/postscript")) - ext = "ps"; - else if (!strcasecmp(job->format, "application/vnd.hp-pcl")) - ext = "pcl"; - else - ext = "dat"; - } - - /* - * Create a filename with the job-id, job-name, and document-format (extension)... - */ - - snprintf(fname, fnamesize, "%s/%d-%s.%s", directory, job->id, name, ext); - - return (open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0666)); -} - - -/* - * 'create_listener()' - Create a listener socket. - */ - -static int /* O - Listener socket or -1 on error */ -create_listener(const char *name, /* I - Host name (`NULL` for any address) */ - int port, /* I - Port number */ - int family) /* I - Address family */ -{ - int sock; /* Listener socket */ - http_addrlist_t *addrlist; /* Listen address */ - char service[255]; /* Service port */ - - - snprintf(service, sizeof(service), "%d", port); - if ((addrlist = httpAddrGetList(name, family, service)) == NULL) - return (-1); - - sock = httpAddrListen(&(addrlist->addr), port); - - httpAddrFreeList(addrlist); - - return (sock); -} - - -/* - * 'create_media_col()' - Create a media-col value. - */ - -static ipp_t * /* O - media-col collection */ -create_media_col(const char *media, /* I - Media name */ - const char *source, /* I - Media source, if any */ - const char *type, /* I - Media type, if any */ - int width, /* I - x-dimension in 2540ths */ - int length, /* I - y-dimension in 2540ths */ - int bottom, /* I - Bottom margin in 2540ths */ - int left, /* I - Left margin in 2540ths */ - int right, /* I - Right margin in 2540ths */ - int top) /* I - Top margin in 2540ths */ -{ - ipp_t *media_col = ippNew(), /* media-col value */ - *media_size = create_media_size(width, length); - /* media-size value */ - char media_key[256]; /* media-key value */ - const char *media_key_suffix = ""; /* media-key suffix */ - - - if (bottom == 0 && left == 0 && right == 0 && top == 0) - media_key_suffix = "_borderless"; - - if (type && source) - snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, type, media_key_suffix); - else if (type) - snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, media_key_suffix); - else if (source) - snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, media_key_suffix); - else - snprintf(media_key, sizeof(media_key), "%s%s", media, media_key_suffix); - - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL, media_key); - ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size); - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-size-name", NULL, media); - if (bottom >= 0) - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin", bottom); - if (left >= 0) - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin", left); - if (right >= 0) - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin", right); - if (top >= 0) - ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin", top); - if (source) - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source", NULL, source); - if (type) - ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type", NULL, type); - - ippDelete(media_size); - - return (media_col); -} - - -/* - * 'create_media_size()' - Create a media-size value. - */ - -static ipp_t * /* O - media-col collection */ -create_media_size(int width, /* I - x-dimension in 2540ths */ - int length) /* I - y-dimension in 2540ths */ -{ - ipp_t *media_size = ippNew(); /* media-size value */ - - - ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", width); - ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", length); - - return (media_size); -} - - -/* - * 'create_printer()' - Create, register, and listen for connections to a - * printer object. - */ - -static ippeve_printer_t * /* O - Printer */ -create_printer( - const char *servername, /* I - Server hostname (NULL for default) */ - int serverport, /* I - Server port */ - const char *name, /* I - printer-name */ - const char *location, /* I - printer-location */ - const char *icon, /* I - printer-icons */ - cups_array_t *docformats, /* I - document-format-supported */ - const char *subtypes, /* I - Bonjour service subtype(s) */ - const char *directory, /* I - Spool directory */ - const char *command, /* I - Command to run on job files, if any */ - const char *device_uri, /* I - Output device, if any */ - ipp_t *attrs) /* I - Capability attributes */ -{ - ippeve_printer_t *printer; /* Printer */ - int i; /* Looping var */ -#ifndef _WIN32 - char path[1024]; /* Full path to command */ -#endif /* !_WIN32 */ - char uri[1024], /* Printer URI */ -#ifdef HAVE_SSL - securi[1024], /* Secure printer URI */ - *uris[2], /* All URIs */ -#endif /* HAVE_SSL */ - icons[1024], /* printer-icons URI */ - adminurl[1024], /* printer-more-info URI */ - supplyurl[1024],/* printer-supply-info-uri URI */ - uuid[128]; /* printer-uuid */ - int k_supported; /* Maximum file size supported */ - int num_formats; /* Number of supported document formats */ - const char *formats[100], /* Supported document formats */ - *format; /* Current format */ - int num_job_attrs; /* Number of supported job attributes */ - const char *job_attrs[100];/* Job attributes */ - char xxx_supported[256]; - /* Name of -supported attribute */ - _cups_globals_t *cg = _cupsGlobals(); - /* Global path values */ -#ifdef HAVE_STATVFS - struct statvfs spoolinfo; /* FS info for spool directory */ - double spoolsize; /* FS size */ -#elif defined(HAVE_STATFS) - struct statfs spoolinfo; /* FS info for spool directory */ - double spoolsize; /* FS size */ -#endif /* HAVE_STATVFS */ - static const char * const versions[] =/* ipp-versions-supported values */ - { - "1.1", - "2.0" - }; - static const char * const features[] =/* ipp-features-supported values */ - { - "ipp-everywhere" - }; - static const int ops[] = /* operations-supported values */ - { - IPP_OP_PRINT_JOB, - IPP_OP_PRINT_URI, - IPP_OP_VALIDATE_JOB, - IPP_OP_CREATE_JOB, - IPP_OP_SEND_DOCUMENT, - IPP_OP_SEND_URI, - IPP_OP_CANCEL_JOB, - IPP_OP_GET_JOB_ATTRIBUTES, - IPP_OP_GET_JOBS, - IPP_OP_GET_PRINTER_ATTRIBUTES, - IPP_OP_CANCEL_MY_JOBS, - IPP_OP_CLOSE_JOB, - IPP_OP_IDENTIFY_PRINTER - }; - static const char * const charsets[] =/* charset-supported values */ - { - "us-ascii", - "utf-8" - }; - static const char * const compressions[] =/* compression-supported values */ - { -#ifdef HAVE_LIBZ - "deflate", - "gzip", -#endif /* HAVE_LIBZ */ - "none" - }; - static const char * const identify_actions[] = - { - "display", - "sound" - }; - static const char * const job_creation[] = - { /* job-creation-attributes-supported values */ - "copies", - "document-password", - "finishings", - "finishings-col", - "job-password", - "job-password-encryption", - "orientation-requested", - "output-bin", - "overrides", - "page-ranges", - "print-color-mode", - "print-content-optimize", - "print-rendering-intent", - "print-quality", - "printer-resolution", - "sides" - }; - static const char * const media_col_supported[] = - { /* media-col-supported values */ - "media-bottom-margin", - "media-left-margin", - "media-right-margin", - "media-size", - "media-size-name", - "media-source", - "media-top-margin", - "media-type" - }; - static const char * const multiple_document_handling[] = - { /* multiple-document-handling-supported values */ - "separate-documents-uncollated-copies", - "separate-documents-collated-copies" - }; - static const char * const reference_uri_schemes_supported[] = - { /* reference-uri-schemes-supported */ - "file", - "ftp", - "http" -#ifdef HAVE_SSL - , "https" -#endif /* HAVE_SSL */ - }; -#ifdef HAVE_SSL - static const char * const uri_authentication_supported[] = - { /* uri-authentication-supported values */ - "none", - "none" - }; - static const char * const uri_security_supported[] = - { /* uri-security-supported values */ - "none", - "tls" - }; -#endif /* HAVE_SSL */ - static const char * const which_jobs[] = - { /* which-jobs-supported values */ - "completed", - "not-completed", - "aborted", - "all", - "canceled", - "pending", - "pending-held", - "processing", - "processing-stopped" - }; - - -#ifndef _WIN32 - /* - * If a command was specified, make sure it exists and is executable... - */ - - if (command) - { - if (*command == '/' || !strncmp(command, "./", 2)) - { - if (access(command, X_OK)) - { - _cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno)); - return (NULL); - } - } - else - { - snprintf(path, sizeof(path), "%s/ippeveprinter/%s", cg->cups_serverbin, command); - - if (access(command, X_OK)) - { - _cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno)); - return (NULL); - } - - command = path; - } - } -#endif /* !_WIN32 */ - - /* - * Allocate memory for the printer... - */ - - if ((printer = calloc(1, sizeof(ippeve_printer_t))) == NULL) - { - _cupsLangPrintError(NULL, _("Unable to allocate memory for printer")); - return (NULL); - } - - printer->ipv4 = -1; - printer->ipv6 = -1; - printer->name = strdup(name); - printer->dnssd_name = strdup(name); - printer->command = command ? strdup(command) : NULL; - printer->device_uri = device_uri ? strdup(device_uri) : NULL; - printer->directory = strdup(directory); - printer->icon = icon ? strdup(icon) : NULL; - printer->port = serverport; - printer->start_time = time(NULL); - printer->config_time = printer->start_time; - printer->state = IPP_PSTATE_IDLE; - printer->state_reasons = IPPEVE_PREASON_NONE; - printer->state_time = printer->start_time; - printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL); - printer->next_job_id = 1; - - if (servername) - { - printer->hostname = strdup(servername); - } - else - { - char temp[1024]; /* Temporary string */ - - printer->hostname = strdup(httpGetHostname(NULL, temp, sizeof(temp))); - } - - _cupsRWInit(&(printer->rwlock)); - - /* - * Create the listener sockets... - */ - - if ((printer->ipv4 = create_listener(servername, printer->port, AF_INET)) < 0) - { - perror("Unable to create IPv4 listener"); - goto bad_printer; - } - - if ((printer->ipv6 = create_listener(servername, printer->port, AF_INET6)) < 0) - { - perror("Unable to create IPv6 listener"); - goto bad_printer; - } - - /* - * Prepare URI values for the printer attributes... - */ - - httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, printer->hostname, printer->port, "/ipp/print"); - printer->uri = strdup(uri); - printer->urilen = strlen(uri); - -#ifdef HAVE_SSL - httpAssembleURI(HTTP_URI_CODING_ALL, securi, sizeof(securi), "ipps", NULL, printer->hostname, printer->port, "/ipp/print"); -#endif /* HAVE_SSL */ - - httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), WEB_SCHEME, NULL, printer->hostname, printer->port, "/icon.png"); - httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/"); - httpAssembleURI(HTTP_URI_CODING_ALL, supplyurl, sizeof(supplyurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/supplies"); - httpAssembleUUID(printer->hostname, serverport, name, 0, uuid, sizeof(uuid)); - - if (Verbosity) - { - fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl); - fprintf(stderr, "printer-supply-info-uri=\"%s\"\n", supplyurl); -#ifdef HAVE_SSL - fprintf(stderr, "printer-uri=\"%s\"\n", uri); -#else - fprintf(stderr, "printer-uri=\"%s\",\"%s\"\n", uri, securi); -#endif /* HAVE_SSL */ - } - - /* - * Get the maximum spool size based on the size of the filesystem used for - * the spool directory. If the host OS doesn't support the statfs call - * or the filesystem is larger than 2TiB, always report INT_MAX. - */ - -#ifdef HAVE_STATVFS - if (statvfs(printer->directory, &spoolinfo)) - k_supported = INT_MAX; - else if ((spoolsize = (double)spoolinfo.f_frsize * - spoolinfo.f_blocks / 1024) > INT_MAX) - k_supported = INT_MAX; - else - k_supported = (int)spoolsize; - -#elif defined(HAVE_STATFS) - if (statfs(printer->directory, &spoolinfo)) - k_supported = INT_MAX; - else if ((spoolsize = (double)spoolinfo.f_bsize * - spoolinfo.f_blocks / 1024) > INT_MAX) - k_supported = INT_MAX; - else - k_supported = (int)spoolsize; - -#else - k_supported = INT_MAX; -#endif /* HAVE_STATVFS */ - - /* - * Assemble the final list of document formats... - */ - - if (!cupsArrayFind(docformats, (void *)"application/octet-stream")) - cupsArrayAdd(docformats, (void *)"application/octet-stream"); - - for (num_formats = 0, format = (const char *)cupsArrayFirst(docformats); format && num_formats < (int)(sizeof(formats) / sizeof(formats[0])); format = (const char *)cupsArrayNext(docformats)) - formats[num_formats ++] = format; - - /* - * Get the list of attributes that can be used when creating a job... - */ - - num_job_attrs = 0; - job_attrs[num_job_attrs ++] = "ipp-attribute-fidelity"; - job_attrs[num_job_attrs ++] = "job-name"; - job_attrs[num_job_attrs ++] = "job-priority"; - job_attrs[num_job_attrs ++] = "media"; - job_attrs[num_job_attrs ++] = "media-col"; - job_attrs[num_job_attrs ++] = "multiple-document-handling"; - - for (i = 0; i < (int)(sizeof(job_creation) / sizeof(job_creation[0])) && num_job_attrs < (int)(sizeof(job_attrs) / sizeof(job_attrs[0])); i ++) - { - snprintf(xxx_supported, sizeof(xxx_supported), "%s-supported", job_creation[i]); - if (ippFindAttribute(attrs, xxx_supported, IPP_TAG_ZERO)) - job_attrs[num_job_attrs ++] = job_creation[i]; - } - - /* - * Fill out the rest of the printer attributes. - */ - - printer->attrs = attrs; - - /* charset-configured */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-configured", NULL, "utf-8"); - - /* charset-supported */ - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-supported", sizeof(charsets) / sizeof(charsets[0]), NULL, charsets); - - /* compression-supported */ - if (!ippFindAttribute(printer->attrs, "compression-supported", IPP_TAG_ZERO)) - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "compression-supported", (int)(sizeof(compressions) / sizeof(compressions[0])), NULL, compressions); - - /* document-format-default */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format-default", NULL, "application/octet-stream"); - - /* document-format-supported */ - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, "document-format-supported", num_formats, NULL, formats); - - /* generated-natural-language-supported */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "generated-natural-language-supported", NULL, "en"); - - /* identify-actions-default */ - ippAddString (printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", NULL, "sound"); - - /* identify-actions-supported */ - 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); - - /* ipp-features-supported */ - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", sizeof(features) / sizeof(features[0]), NULL, features); - - /* ipp-versions-supported */ - if (MaxVersion == 11) - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", NULL, "1.1"); - else - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", (int)(sizeof(versions) / sizeof(versions[0])), NULL, versions); - - /* job-creation-attributes-supported */ - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", num_job_attrs, NULL, job_attrs); - - /* job-ids-supported */ - ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1); - - /* job-k-octets-supported */ - ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0, k_supported); - - /* job-priority-default */ - ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-default", 50); - - /* job-priority-supported */ - ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 1); - - /* job-sheets-default */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-default", NULL, "none"); - - /* job-sheets-supported */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none"); - - /* media-col-supported */ - 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); - - /* multiple-document-handling-supported */ - 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); - - /* multiple-document-jobs-supported */ - ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 0); - - /* multiple-operation-time-out */ - ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "multiple-operation-time-out", 60); - - /* multiple-operation-time-out-action */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-operation-time-out-action", NULL, "abort-job"); - - /* natural-language-configured */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "natural-language-configured", NULL, "en"); - - /* operations-supported */ - ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", sizeof(ops) / sizeof(ops[0]), ops); - - /* pdl-override-supported */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdl-override-supported", NULL, "attempted"); - - /* preferred-attributes-supported */ - ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "preferred-attributes-supported", 0); - - /* printer-get-attributes-supported */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format"); - - /* printer-geo-location */ - ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location"); - - /* printer-icons */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons", NULL, icons); - - /* printer-is-accepting-jobs */ - ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1); - - /* printer-info */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, name); - - /* printer-location */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, location); - - /* printer-more-info */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, adminurl); - - /* printer-name */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name); - - /* printer-organization */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organization", NULL, ""); - - /* printer-organizational-unit */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organizational-unit", NULL, ""); - - /* printer-supply-info-uri */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, supplyurl); - - /* printer-uri-supported */ -#ifdef HAVE_SSL - uris[0] = uri; - uris[1] = securi; - - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", 2, NULL, (const char **)uris); - -#else - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri); -#endif /* HAVE_SSL */ - - /* printer-uuid */ - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid); - - /* reference-uri-scheme-supported */ - 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); - - /* uri-authentication-supported */ -#ifdef HAVE_SSL - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_supported); -#else - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "none"); -#endif /* HAVE_SSL */ - - /* uri-security-supported */ -#ifdef HAVE_SSL - ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", 2, NULL, uri_security_supported); -#else - ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", NULL, "none"); -#endif /* HAVE_SSL */ - - /* which-jobs-supported */ - 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); - - debug_attributes("Printer", printer->attrs, 0); - - /* - * Register the printer with Bonjour... - */ - - if (!register_printer(printer, subtypes)) - goto bad_printer; - - /* - * Return it! - */ - - return (printer); - - - /* - * If we get here we were unable to create the printer... - */ - - bad_printer: - - delete_printer(printer); - - return (NULL); -} - - -/* - * 'debug_attributes()' - Print attributes in a request or response. - */ - -static void -debug_attributes(const char *title, /* I - Title */ - ipp_t *ipp, /* I - Request/response */ - int type) /* I - 0 = object, 1 = request, 2 = response */ -{ - ipp_tag_t group_tag; /* Current group */ - ipp_attribute_t *attr; /* Current attribute */ - char buffer[2048]; /* String buffer for value */ - int major, minor; /* Version */ - - - if (Verbosity <= 1) - return; - - fprintf(stderr, "%s:\n", title); - major = ippGetVersion(ipp, &minor); - fprintf(stderr, " version=%d.%d\n", major, minor); - if (type == 1) - fprintf(stderr, " operation-id=%s(%04x)\n", - ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp)); - else if (type == 2) - fprintf(stderr, " status-code=%s(%04x)\n", - ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp)); - fprintf(stderr, " request-id=%d\n\n", ippGetRequestId(ipp)); - - for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO; - attr; - attr = ippNextAttribute(ipp)) - { - if (ippGetGroupTag(attr) != group_tag) - { - group_tag = ippGetGroupTag(attr); - fprintf(stderr, " %s\n", ippTagString(group_tag)); - } - - if (ippGetName(attr)) - { - ippAttributeString(attr, buffer, sizeof(buffer)); - fprintf(stderr, " %s (%s%s) %s\n", ippGetName(attr), - ippGetCount(attr) > 1 ? "1setOf " : "", - ippTagString(ippGetValueTag(attr)), buffer); - } - } -} - - -/* - * 'delete_client()' - Close the socket and free all memory used by a client - * object. - */ - -static void -delete_client(ippeve_client_t *client) /* I - Client */ -{ - if (Verbosity) - fprintf(stderr, "Closing connection from %s\n", client->hostname); - - /* - * Flush pending writes before closing... - */ - - httpFlushWrite(client->http); - - /* - * Free memory... - */ - - httpClose(client->http); - - ippDelete(client->request); - ippDelete(client->response); - - free(client); -} - - -/* - * 'delete_job()' - Remove from the printer and free all memory used by a job - * object. - */ - -static void -delete_job(ippeve_job_t *job) /* I - Job */ -{ - if (Verbosity) - fprintf(stderr, "[Job %d] Removing job from history.\n", job->id); - - ippDelete(job->attrs); - - if (job->message) - free(job->message); - - if (job->filename) - { - if (!KeepFiles) - unlink(job->filename); - - free(job->filename); - } - - free(job); -} - - -/* - * 'delete_printer()' - Unregister, close listen sockets, and free all memory - * used by a printer object. - */ - -static void -delete_printer(ippeve_printer_t *printer) /* I - Printer */ -{ - if (printer->ipv4 >= 0) - close(printer->ipv4); - - if (printer->ipv6 >= 0) - close(printer->ipv6); - -#if HAVE_DNSSD - if (printer->printer_ref) - DNSServiceRefDeallocate(printer->printer_ref); - if (printer->ipp_ref) - DNSServiceRefDeallocate(printer->ipp_ref); - if (printer->ipps_ref) - DNSServiceRefDeallocate(printer->ipps_ref); - if (printer->http_ref) - DNSServiceRefDeallocate(printer->http_ref); -#elif defined(HAVE_AVAHI) - avahi_threaded_poll_lock(DNSSDMaster); - - if (printer->printer_ref) - avahi_entry_group_free(printer->printer_ref); - if (printer->ipp_ref) - avahi_entry_group_free(printer->ipp_ref); - if (printer->ipps_ref) - avahi_entry_group_free(printer->ipps_ref); - if (printer->http_ref) - avahi_entry_group_free(printer->http_ref); - - avahi_threaded_poll_unlock(DNSSDMaster); -#endif /* HAVE_DNSSD */ - - if (printer->dnssd_name) - free(printer->dnssd_name); - if (printer->name) - free(printer->name); - if (printer->icon) - free(printer->icon); - if (printer->command) - free(printer->command); - if (printer->device_uri) - free(printer->device_uri); -#if !CUPS_LITE - if (printer->ppdfile) - free(printer->ppdfile); -#endif /* !CUPS_LITE */ - if (printer->directory) - free(printer->directory); - if (printer->hostname) - free(printer->hostname); - if (printer->uri) - free(printer->uri); - - ippDelete(printer->attrs); - cupsArrayDelete(printer->jobs); - - free(printer); -} - - -#ifdef HAVE_DNSSD -/* - * 'dnssd_callback()' - Handle Bonjour registration events. - */ - -static void DNSSD_API -dnssd_callback( - DNSServiceRef sdRef, /* I - Service reference */ - DNSServiceFlags flags, /* I - Status flags */ - DNSServiceErrorType errorCode, /* I - Error, if any */ - const char *name, /* I - Service name */ - const char *regtype, /* I - Service type */ - const char *domain, /* I - Domain for service */ - ippeve_printer_t *printer) /* I - Printer */ -{ - (void)sdRef; - (void)flags; - (void)domain; - - if (errorCode) - { - fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n", regtype, (int)errorCode); - return; - } - else if (strcasecmp(name, printer->dnssd_name)) - { - if (Verbosity) - fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name); - - /* No lock needed since only the main thread accesses/changes this */ - free(printer->dnssd_name); - printer->dnssd_name = strdup(name); - } -} - - -#elif defined(HAVE_AVAHI) -/* - * 'dnssd_callback()' - Handle Bonjour registration events. - */ - -static void -dnssd_callback( - AvahiEntryGroup *srv, /* I - Service */ - AvahiEntryGroupState state, /* I - Registration state */ - void *context) /* I - Printer */ -{ - (void)srv; - (void)state; - (void)context; -} - - -/* - * 'dnssd_client_cb()' - Client callback for Avahi. - * - * Called whenever the client or server state changes... - */ - -static void -dnssd_client_cb( - AvahiClient *c, /* I - Client */ - AvahiClientState state, /* I - Current state */ - void *userdata) /* I - User data (unused) */ -{ - (void)userdata; - - if (!c) - return; - - switch (state) - { - default : - fprintf(stderr, "Ignored Avahi state %d.\n", state); - break; - - case AVAHI_CLIENT_FAILURE: - if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) - { - fputs("Avahi server crashed, exiting.\n", stderr); - exit(1); - } - break; - } -} -#endif /* HAVE_DNSSD */ - - -/* - * 'dnssd_init()' - Initialize the DNS-SD service connections... - */ - -static void -dnssd_init(void) -{ -#ifdef HAVE_DNSSD - if (DNSServiceCreateConnection(&DNSSDMaster) != kDNSServiceErr_NoError) - { - fputs("Error: Unable to initialize Bonjour.\n", stderr); - exit(1); - } - -#elif defined(HAVE_AVAHI) - int error; /* Error code, if any */ - - if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL) - { - fputs("Error: Unable to initialize Bonjour.\n", stderr); - exit(1); - } - - if ((DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssd_client_cb, NULL, &error)) == NULL) - { - fputs("Error: Unable to initialize Bonjour.\n", stderr); - exit(1); - } - - avahi_threaded_poll_start(DNSSDMaster); -#endif /* HAVE_DNSSD */ -} - - -/* - * 'filter_cb()' - Filter printer attributes based on the requested array. - */ - -static int /* O - 1 to copy, 0 to ignore */ -filter_cb(ippeve_filter_t *filter, /* I - Filter parameters */ - ipp_t *dst, /* I - Destination (unused) */ - ipp_attribute_t *attr) /* I - Source attribute */ -{ - /* - * Filter attributes as needed... - */ - -#ifndef _WIN32 /* Avoid MS compiler bug */ - (void)dst; -#endif /* !_WIN32 */ - - ipp_tag_t group = ippGetGroupTag(attr); - const char *name = ippGetName(attr); - - 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))) - return (0); - - return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL); -} - - -/* - * 'find_job()' - Find a job specified in a request. - */ - -static ippeve_job_t * /* O - Job or NULL */ -find_job(ippeve_client_t *client) /* I - Client */ -{ - ipp_attribute_t *attr; /* job-id or job-uri attribute */ - ippeve_job_t key, /* Job search key */ - *job; /* Matching job, if any */ - - - if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL) - { - const char *uri = ippGetString(attr, 0, NULL); - - if (!strncmp(uri, client->printer->uri, client->printer->urilen) && - uri[client->printer->urilen] == '/') - key.id = atoi(uri + client->printer->urilen + 1); - else - return (NULL); - } - else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL) - key.id = ippGetInteger(attr, 0); - - _cupsRWLockRead(&(client->printer->rwlock)); - job = (ippeve_job_t *)cupsArrayFind(client->printer->jobs, &key); - _cupsRWUnlock(&(client->printer->rwlock)); - - return (job); -} - - -/* - * 'finish_document()' - Finish receiving a document file and start processing. - */ - -static void -finish_document_data( - ippeve_client_t *client, /* I - Client */ - ippeve_job_t *job) /* I - Job */ -{ - char filename[1024], /* Filename buffer */ - buffer[4096]; /* Copy buffer */ - ssize_t bytes; /* Bytes read */ - cups_array_t *ra; /* Attributes to send in response */ - _cups_thread_t t; /* Thread */ - - - /* - * Create a file for the request data... - * - * TODO: Update code to support piping large raster data to the print command. - */ - - if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0) - { - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno)); - - goto abort_job; - } - - if (Verbosity) - fprintf(stderr, "Created job file \"%s\", format \"%s\".\n", filename, job->format); - - while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0) - { - if (write(job->fd, buffer, (size_t)bytes) < bytes) - { - int error = errno; /* Write error */ - - close(job->fd); - job->fd = -1; - - unlink(filename); - - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error)); - - goto abort_job; - } - } - - if (bytes < 0) - { - /* - * Got an error while reading the print data, so abort this job. - */ - - close(job->fd); - job->fd = -1; - - unlink(filename); - - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to read print file."); - - goto abort_job; - } - - if (close(job->fd)) - { - int error = errno; /* Write error */ - - job->fd = -1; - - unlink(filename); - - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error)); - - goto abort_job; - } - - job->fd = -1; - job->filename = strdup(filename); - job->state = IPP_JSTATE_PENDING; - - /* - * Process the job... - */ - - t = _cupsThreadCreate((_cups_thread_func_t)process_job, job); - - if (t) - { - _cupsThreadDetach(t); - } - else - { - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job."); - goto abort_job; - } - - /* - * Return the job info... - */ - - respond_ipp(client, IPP_STATUS_OK, NULL); - - ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); - cupsArrayAdd(ra, "job-id"); - cupsArrayAdd(ra, "job-state"); - cupsArrayAdd(ra, "job-state-message"); - cupsArrayAdd(ra, "job-state-reasons"); - cupsArrayAdd(ra, "job-uri"); - - copy_job_attributes(client, job, ra); - cupsArrayDelete(ra); - return; - - /* - * If we get here we had to abort the job... - */ - - abort_job: - - job->state = IPP_JSTATE_ABORTED; - job->completed = time(NULL); - - ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); - cupsArrayAdd(ra, "job-id"); - cupsArrayAdd(ra, "job-state"); - cupsArrayAdd(ra, "job-state-reasons"); - cupsArrayAdd(ra, "job-uri"); - - copy_job_attributes(client, job, ra); - cupsArrayDelete(ra); -} - - -/* - * 'finish_uri()' - Finish fetching a document URI and start processing. - */ - -static void -finish_document_uri( - ippeve_client_t *client, /* I - Client */ - ippeve_job_t *job) /* I - Job */ -{ - ipp_attribute_t *uri; /* document-uri */ - char scheme[256], /* URI scheme */ - userpass[256], /* Username and password info */ - hostname[256], /* Hostname */ - resource[1024]; /* Resource path */ - int port; /* Port number */ - http_uri_status_t uri_status; /* URI decode status */ - http_encryption_t encryption; /* Encryption to use, if any */ - http_t *http; /* Connection for http/https URIs */ - http_status_t status; /* Access status for http/https URIs */ - int infile; /* Input file for local file URIs */ - char filename[1024], /* Filename buffer */ - buffer[4096]; /* Copy buffer */ - ssize_t bytes; /* Bytes read */ - ipp_attribute_t *attr; /* Current attribute */ - cups_array_t *ra; /* Attributes to send in response */ - - - /* - * Do we have a file to print? - */ - - if (httpGetState(client->http) == HTTP_STATE_POST_RECV) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unexpected document data following request."); - - goto abort_job; - } - - /* - * Do we have a document URI? - */ - - if ((uri = ippFindAttribute(client->request, "document-uri", IPP_TAG_URI)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri."); - - goto abort_job; - } - - if (ippGetCount(uri) != 1) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Too many document-uri values."); - - goto abort_job; - } - - uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL), - scheme, sizeof(scheme), userpass, - sizeof(userpass), hostname, sizeof(hostname), - &port, resource, sizeof(resource)); - if (uri_status < HTTP_URI_STATUS_OK) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", httpURIStatusString(uri_status)); - - goto abort_job; - } - - if (strcmp(scheme, "file") && -#ifdef HAVE_SSL - strcmp(scheme, "https") && -#endif /* HAVE_SSL */ - strcmp(scheme, "http")) - { - respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, "URI scheme \"%s\" not supported.", scheme); - - goto abort_job; - } - - if (!strcmp(scheme, "file") && access(resource, R_OK)) - { - respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno)); - - goto abort_job; - } - - /* - * Get the document format for the job... - */ - - _cupsRWLockWrite(&(client->printer->rwlock)); - - if ((attr = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE)) != NULL) - job->format = ippGetString(attr, 0, NULL); - else - job->format = "application/octet-stream"; - - /* - * Create a file for the request data... - */ - - if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0) - { - _cupsRWUnlock(&(client->printer->rwlock)); - - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno)); - - goto abort_job; - } - - _cupsRWUnlock(&(client->printer->rwlock)); - - if (!strcmp(scheme, "file")) - { - if ((infile = open(resource, O_RDONLY)) < 0) - { - respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno)); - - goto abort_job; - } - - do - { - if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 && - (errno == EAGAIN || errno == EINTR)) - { - bytes = 1; - } - else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes) - { - int error = errno; /* Write error */ - - close(job->fd); - job->fd = -1; - - unlink(filename); - close(infile); - - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error)); - - goto abort_job; - } - } - while (bytes > 0); - - close(infile); - } - else - { -#ifdef HAVE_SSL - if (port == 443 || !strcmp(scheme, "https")) - encryption = HTTP_ENCRYPTION_ALWAYS; - else -#endif /* HAVE_SSL */ - encryption = HTTP_ENCRYPTION_IF_REQUESTED; - - if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to connect to %s: %s", hostname, cupsLastErrorString()); - - close(job->fd); - job->fd = -1; - - unlink(filename); - - goto abort_job; - } - - httpClearFields(http); - httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en"); - if (httpGet(http, resource)) - { - respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", strerror(errno)); - - close(job->fd); - job->fd = -1; - - unlink(filename); - httpClose(http); - - goto abort_job; - } - - while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); - - if (status != HTTP_STATUS_OK) - { - respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", httpStatus(status)); - - close(job->fd); - job->fd = -1; - - unlink(filename); - httpClose(http); - - goto abort_job; - } - - while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) - { - if (write(job->fd, buffer, (size_t)bytes) < bytes) - { - int error = errno; /* Write error */ - - close(job->fd); - job->fd = -1; - - unlink(filename); - httpClose(http); - - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, - "Unable to write print file: %s", strerror(error)); - - goto abort_job; - } - } - - httpClose(http); - } - - if (close(job->fd)) - { - int error = errno; /* Write error */ - - job->fd = -1; - - unlink(filename); - - respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error)); - - goto abort_job; - } - - _cupsRWLockWrite(&(client->printer->rwlock)); - - job->fd = -1; - job->filename = strdup(filename); - job->state = IPP_JSTATE_PENDING; - - _cupsRWUnlock(&(client->printer->rwlock)); - - /* - * Process the job... - */ - - process_job(job); - - /* - * Return the job info... - */ - - respond_ipp(client, IPP_STATUS_OK, NULL); - - ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); - cupsArrayAdd(ra, "job-id"); - cupsArrayAdd(ra, "job-state"); - cupsArrayAdd(ra, "job-state-reasons"); - cupsArrayAdd(ra, "job-uri"); - - copy_job_attributes(client, job, ra); - cupsArrayDelete(ra); - return; - - /* - * If we get here we had to abort the job... - */ - - abort_job: - - job->state = IPP_JSTATE_ABORTED; - job->completed = time(NULL); - - ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); - cupsArrayAdd(ra, "job-id"); - cupsArrayAdd(ra, "job-state"); - cupsArrayAdd(ra, "job-state-reasons"); - cupsArrayAdd(ra, "job-uri"); - - copy_job_attributes(client, job, ra); - cupsArrayDelete(ra); -} - - -/* - * 'html_escape()' - Write a HTML-safe string. - */ - -static void -html_escape(ippeve_client_t *client, /* I - Client */ - const char *s, /* I - String to write */ - size_t slen) /* I - Number of characters to write */ -{ - const char *start, /* Start of segment */ - *end; /* End of string */ - - - start = s; - end = s + (slen > 0 ? slen : strlen(s)); - - while (*s && s < end) - { - if (*s == '&' || *s == '<') - { - if (s > start) - httpWrite2(client->http, start, (size_t)(s - start)); - - if (*s == '&') - httpWrite2(client->http, "&", 5); - else - httpWrite2(client->http, "<", 4); - - start = s + 1; - } - - s ++; - } - - if (s > start) - httpWrite2(client->http, start, (size_t)(s - start)); -} - - -/* - * 'html_footer()' - Show the web interface footer. - * - * This function also writes the trailing 0-length chunk. - */ - -static void -html_footer(ippeve_client_t *client) /* I - Client */ -{ - html_printf(client, - "</div>\n" - "</body>\n" - "</html>\n"); - httpWrite2(client->http, "", 0); -} - - -/* - * 'html_header()' - Show the web interface header and title. - */ - -static void -html_header(ippeve_client_t *client, /* I - Client */ - const char *title, /* I - Title */ - int refresh) /* I - Refresh timer, if any */ -{ - html_printf(client, - "<!doctype html>\n" - "<html>\n" - "<head>\n" - "<title>%s</title>\n" - "<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n" - "<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n" - "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title); - if (refresh > 0) - html_printf(client, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh); - html_printf(client, - "<meta name=\"viewport\" content=\"width=device-width\">\n" - "<style>\n" - "body { font-family: sans-serif; margin: 0; }\n" - "div.body { padding: 0px 10px 10px; }\n" - "span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n" - "span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n" - "table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n" - "table.form td, table.form th { padding: 5px 2px; }\n" - "table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n" - "table.form th { text-align: right; }\n" - "table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n" - "table.striped tr:nth-child(even) { background: #fcfcfc; }\n" - "table.striped tr:nth-child(odd) { background: #f0f0f0; }\n" - "table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n" - "table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n" - "table.nav { border-collapse: collapse; width: 100%%; }\n" - "table.nav td { margin: 0; text-align: center; }\n" - "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" - "td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n" - "td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n" - "td.nav:hover { background: #666; color: #fff; }\n" - "td.nav:active { background: #000; color: #ff0; }\n" - "</style>\n" - "</head>\n" - "<body>\n" - "<table class=\"nav\"><tr>" - "<td class=\"nav%s\"><a href=\"/\">Status</a></td>" - "<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>" - "<td class=\"nav%s\"><a href=\"/media\">Media</a></td>" - "</tr></table>\n" - "<div class=\"body\">\n", !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : ""); -} - - -/* - * 'html_printf()' - Send formatted text to the client, quoting as needed. - */ - -static void -html_printf(ippeve_client_t *client, /* I - Client */ - const char *format, /* I - Printf-style format string */ - ...) /* I - Additional arguments as needed */ -{ - va_list ap; /* Pointer to arguments */ - const char *start; /* Start of string */ - char size, /* Size character (h, l, L) */ - type; /* Format type character */ - int width, /* Width of field */ - prec; /* Number of characters of precision */ - char tformat[100], /* Temporary format string for sprintf() */ - *tptr, /* Pointer into temporary format */ - temp[1024]; /* Buffer for formatted numbers */ - char *s; /* Pointer to string */ - - - /* - * Loop through the format string, formatting as needed... - */ - - va_start(ap, format); - start = format; - - while (*format) - { - if (*format == '%') - { - if (format > start) - httpWrite2(client->http, start, (size_t)(format - start)); - - tptr = tformat; - *tptr++ = *format++; - - if (*format == '%') - { - httpWrite2(client->http, "%", 1); - format ++; - start = format; - continue; - } - else if (strchr(" -+#\'", *format)) - *tptr++ = *format++; - - if (*format == '*') - { - /* - * Get width from argument... - */ - - format ++; - width = va_arg(ap, int); - - snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width); - tptr += strlen(tptr); - } - else - { - width = 0; - - while (isdigit(*format & 255)) - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - width = width * 10 + *format++ - '0'; - } - } - - if (*format == '.') - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - format ++; - - if (*format == '*') - { - /* - * Get precision from argument... - */ - - format ++; - prec = va_arg(ap, int); - - snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec); - tptr += strlen(tptr); - } - else - { - prec = 0; - - while (isdigit(*format & 255)) - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - prec = prec * 10 + *format++ - '0'; - } - } - } - - if (*format == 'l' && format[1] == 'l') - { - size = 'L'; - - if (tptr < (tformat + sizeof(tformat) - 2)) - { - *tptr++ = 'l'; - *tptr++ = 'l'; - } - - format += 2; - } - else if (*format == 'h' || *format == 'l' || *format == 'L') - { - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - size = *format++; - } - else - size = 0; - - - if (!*format) - { - start = format; - break; - } - - if (tptr < (tformat + sizeof(tformat) - 1)) - *tptr++ = *format; - - type = *format++; - *tptr = '\0'; - start = format; - - switch (type) - { - case 'E' : /* Floating point formats */ - case 'G' : - case 'e' : - case 'f' : - case 'g' : - if ((size_t)(width + 2) > sizeof(temp)) - break; - - sprintf(temp, tformat, va_arg(ap, double)); - - httpWrite2(client->http, temp, strlen(temp)); - break; - - case 'B' : /* Integer formats */ - case 'X' : - case 'b' : - case 'd' : - case 'i' : - case 'o' : - case 'u' : - case 'x' : - if ((size_t)(width + 2) > sizeof(temp)) - break; - -# ifdef HAVE_LONG_LONG - if (size == 'L') - sprintf(temp, tformat, va_arg(ap, long long)); - else -# endif /* HAVE_LONG_LONG */ - if (size == 'l') - sprintf(temp, tformat, va_arg(ap, long)); - else - sprintf(temp, tformat, va_arg(ap, int)); - - httpWrite2(client->http, temp, strlen(temp)); - break; - - case 'p' : /* Pointer value */ - if ((size_t)(width + 2) > sizeof(temp)) - break; - - sprintf(temp, tformat, va_arg(ap, void *)); - - httpWrite2(client->http, temp, strlen(temp)); - break; - - case 'c' : /* Character or character array */ - if (width <= 1) - { - temp[0] = (char)va_arg(ap, int); - temp[1] = '\0'; - html_escape(client, temp, 1); - } - else - html_escape(client, va_arg(ap, char *), (size_t)width); - break; - - case 's' : /* String */ - if ((s = va_arg(ap, char *)) == NULL) - s = "(null)"; - - html_escape(client, s, strlen(s)); - break; - } - } - else - format ++; - } - - if (format > start) - httpWrite2(client->http, start, (size_t)(format - start)); - - va_end(ap); -} - - -/* - * 'ipp_cancel_job()' - Cancel a job. - */ - -static void -ipp_cancel_job(ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* Job information */ - - - /* - * Get the job... - */ - - if ((job = find_job(client)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); - return; - } - - /* - * See if the job is already completed, canceled, or aborted; if so, - * we can't cancel... - */ - - switch (job->state) - { - case IPP_JSTATE_CANCELED : - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, - "Job #%d is already canceled - can\'t cancel.", job->id); - break; - - case IPP_JSTATE_ABORTED : - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, - "Job #%d is already aborted - can\'t cancel.", job->id); - break; - - case IPP_JSTATE_COMPLETED : - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, - "Job #%d is already completed - can\'t cancel.", job->id); - break; - - default : - /* - * Cancel the job... - */ - - _cupsRWLockWrite(&(client->printer->rwlock)); - - if (job->state == IPP_JSTATE_PROCESSING || - (job->state == IPP_JSTATE_HELD && job->fd >= 0)) - job->cancel = 1; - else - { - job->state = IPP_JSTATE_CANCELED; - job->completed = time(NULL); - } - - _cupsRWUnlock(&(client->printer->rwlock)); - - respond_ipp(client, IPP_STATUS_OK, NULL); - break; - } -} - - -/* - * 'ipp_close_job()' - Close an open job. - */ - -static void -ipp_close_job(ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* Job information */ - - - /* - * Get the job... - */ - - if ((job = find_job(client)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); - return; - } - - /* - * See if the job is already completed, canceled, or aborted; if so, - * we can't cancel... - */ - - switch (job->state) - { - case IPP_JSTATE_CANCELED : - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, - "Job #%d is canceled - can\'t close.", job->id); - break; - - case IPP_JSTATE_ABORTED : - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, - "Job #%d is aborted - can\'t close.", job->id); - break; - - case IPP_JSTATE_COMPLETED : - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, - "Job #%d is completed - can\'t close.", job->id); - break; - - case IPP_JSTATE_PROCESSING : - case IPP_JSTATE_STOPPED : - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, - "Job #%d is already closed.", job->id); - break; - - default : - respond_ipp(client, IPP_STATUS_OK, NULL); - break; - } -} - - -/* - * 'ipp_create_job()' - Create a job object. - */ - -static void -ipp_create_job(ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* New job */ - cups_array_t *ra; /* Attributes to send in response */ - - - /* - * Validate print job attributes... - */ - - if (!valid_job_attributes(client)) - { - httpFlush(client->http); - return; - } - - /* - * Do we have a file to print? - */ - - if (httpGetState(client->http) == HTTP_STATE_POST_RECV) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, - "Unexpected document data following request."); - return; - } - - /* - * Create the job... - */ - - if ((job = create_job(client)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_BUSY, - "Currently printing another job."); - return; - } - - /* - * Return the job info... - */ - - respond_ipp(client, IPP_STATUS_OK, NULL); - - ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); - cupsArrayAdd(ra, "job-id"); - cupsArrayAdd(ra, "job-state"); - cupsArrayAdd(ra, "job-state-message"); - cupsArrayAdd(ra, "job-state-reasons"); - cupsArrayAdd(ra, "job-uri"); - - copy_job_attributes(client, job, ra); - cupsArrayDelete(ra); -} - - -/* - * 'ipp_get_job_attributes()' - Get the attributes for a job object. - */ - -static void -ipp_get_job_attributes( - ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* Job */ - cups_array_t *ra; /* requested-attributes */ - - - if ((job = find_job(client)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found."); - return; - } - - respond_ipp(client, IPP_STATUS_OK, NULL); - - ra = ippCreateRequestedArray(client->request); - copy_job_attributes(client, job, ra); - cupsArrayDelete(ra); -} - - -/* - * 'ipp_get_jobs()' - Get a list of job objects. - */ - -static void -ipp_get_jobs(ippeve_client_t *client) /* I - Client */ -{ - ipp_attribute_t *attr; /* Current attribute */ - const char *which_jobs = NULL; - /* which-jobs values */ - int job_comparison; /* Job comparison */ - ipp_jstate_t job_state; /* job-state value */ - int first_job_id, /* First job ID */ - limit, /* Maximum number of jobs to return */ - count; /* Number of jobs that match */ - const char *username; /* Username */ - ippeve_job_t *job; /* Current job pointer */ - cups_array_t *ra; /* Requested attributes array */ - - - /* - * See if the "which-jobs" attribute have been specified... - */ - - if ((attr = ippFindAttribute(client->request, "which-jobs", - IPP_TAG_KEYWORD)) != NULL) - { - which_jobs = ippGetString(attr, 0, NULL); - fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs); - } - - if (!which_jobs || !strcmp(which_jobs, "not-completed")) - { - job_comparison = -1; - job_state = IPP_JSTATE_STOPPED; - } - else if (!strcmp(which_jobs, "completed")) - { - job_comparison = 1; - job_state = IPP_JSTATE_CANCELED; - } - else if (!strcmp(which_jobs, "aborted")) - { - job_comparison = 0; - job_state = IPP_JSTATE_ABORTED; - } - else if (!strcmp(which_jobs, "all")) - { - job_comparison = 1; - job_state = IPP_JSTATE_PENDING; - } - else if (!strcmp(which_jobs, "canceled")) - { - job_comparison = 0; - job_state = IPP_JSTATE_CANCELED; - } - else if (!strcmp(which_jobs, "pending")) - { - job_comparison = 0; - job_state = IPP_JSTATE_PENDING; - } - else if (!strcmp(which_jobs, "pending-held")) - { - job_comparison = 0; - job_state = IPP_JSTATE_HELD; - } - else if (!strcmp(which_jobs, "processing")) - { - job_comparison = 0; - job_state = IPP_JSTATE_PROCESSING; - } - else if (!strcmp(which_jobs, "processing-stopped")) - { - job_comparison = 0; - job_state = IPP_JSTATE_STOPPED; - } - else - { - respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, - "The which-jobs value \"%s\" is not supported.", which_jobs); - ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD, - "which-jobs", NULL, which_jobs); - return; - } - - /* - * See if they want to limit the number of jobs reported... - */ - - if ((attr = ippFindAttribute(client->request, "limit", - IPP_TAG_INTEGER)) != NULL) - { - limit = ippGetInteger(attr, 0); - - fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit); - } - else - limit = 0; - - if ((attr = ippFindAttribute(client->request, "first-job-id", - IPP_TAG_INTEGER)) != NULL) - { - first_job_id = ippGetInteger(attr, 0); - - fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, first_job_id); - } - else - first_job_id = 1; - - /* - * See if we only want to see jobs for a specific user... - */ - - username = NULL; - - if ((attr = ippFindAttribute(client->request, "my-jobs", - IPP_TAG_BOOLEAN)) != NULL) - { - int my_jobs = ippGetBoolean(attr, 0); - - fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, my_jobs ? "true" : "false"); - - if (my_jobs) - { - if ((attr = ippFindAttribute(client->request, "requesting-user-name", - IPP_TAG_NAME)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, - "Need requesting-user-name with my-jobs."); - return; - } - - username = ippGetString(attr, 0, NULL); - - fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", client->hostname, username); - } - } - - /* - * OK, build a list of jobs for this printer... - */ - - ra = ippCreateRequestedArray(client->request); - - respond_ipp(client, IPP_STATUS_OK, NULL); - - _cupsRWLockRead(&(client->printer->rwlock)); - - for (count = 0, job = (ippeve_job_t *)cupsArrayFirst(client->printer->jobs); - (limit <= 0 || count < limit) && job; - job = (ippeve_job_t *)cupsArrayNext(client->printer->jobs)) - { - /* - * Filter out jobs that don't match... - */ - - if ((job_comparison < 0 && job->state > job_state) || - (job_comparison == 0 && job->state != job_state) || - (job_comparison > 0 && job->state < job_state) || - job->id < first_job_id || - (username && job->username && - strcasecmp(username, job->username))) - continue; - - if (count > 0) - ippAddSeparator(client->response); - - count ++; - copy_job_attributes(client, job, ra); - } - - cupsArrayDelete(ra); - - _cupsRWUnlock(&(client->printer->rwlock)); -} - - -/* - * 'ipp_get_printer_attributes()' - Get the attributes for a printer object. - */ - -static void -ipp_get_printer_attributes( - ippeve_client_t *client) /* I - Client */ -{ - cups_array_t *ra; /* Requested attributes array */ - ippeve_printer_t *printer; /* Printer */ - - - /* - * Send the attributes... - */ - - ra = ippCreateRequestedArray(client->request); - printer = client->printer; - - respond_ipp(client, IPP_STATUS_OK, NULL); - - _cupsRWLockRead(&(printer->rwlock)); - - copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO, - IPP_TAG_CUPS_CONST); - - if (!ra || cupsArrayFind(ra, "printer-config-change-date-time")) - ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time)); - - if (!ra || cupsArrayFind(ra, "printer-config-change-time")) - ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time)); - - if (!ra || cupsArrayFind(ra, "printer-current-time")) - ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL))); - - - if (!ra || cupsArrayFind(ra, "printer-state")) - ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state); - - if (!ra || cupsArrayFind(ra, "printer-state-change-date-time")) - ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time)); - - if (!ra || cupsArrayFind(ra, "printer-state-change-time")) - ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time)); - - if (!ra || cupsArrayFind(ra, "printer-state-message")) - { - static const char * const messages[] = { "Idle.", "Printing.", "Stopped." }; - - ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]); - } - - if (!ra || cupsArrayFind(ra, "printer-state-reasons")) - { - if (printer->state_reasons == IPPEVE_PREASON_NONE) - { - ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none"); - } - else - { - ipp_attribute_t *attr = NULL; /* printer-state-reasons */ - ippeve_preason_t bit; /* Reason bit */ - int i; /* Looping var */ - char reason[32]; /* Reason string */ - - for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2) - { - if (printer->state_reasons & bit) - { - snprintf(reason, sizeof(reason), "%s-%s", ippeve_preason_strings[i], printer->state == IPP_PSTATE_IDLE ? "report" : printer->state == IPP_PSTATE_PROCESSING ? "warning" : "error"); - if (attr) - ippSetString(client->response, &attr, ippGetCount(attr), reason); - else - attr = ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, reason); - } - } - } - } - - if (!ra || cupsArrayFind(ra, "printer-up-time")) - ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time)); - - if (!ra || cupsArrayFind(ra, "queued-job-count")) - ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED); - - _cupsRWUnlock(&(printer->rwlock)); - - cupsArrayDelete(ra); -} - - -/* - * 'ipp_identify_printer()' - Beep or display a message. - */ - -static void -ipp_identify_printer( - ippeve_client_t *client) /* I - Client */ -{ - ipp_attribute_t *actions, /* identify-actions */ - *message; /* message */ - - - actions = ippFindAttribute(client->request, "identify-actions", IPP_TAG_KEYWORD); - message = ippFindAttribute(client->request, "message", IPP_TAG_TEXT); - - if (!actions || ippContainsString(actions, "sound")) - { - putchar(0x07); - fflush(stdout); - } - - if (ippContainsString(actions, "display")) - printf("IDENTIFY from %s: %s\n", client->hostname, message ? ippGetString(message, 0, NULL) : "No message supplied"); - - respond_ipp(client, IPP_STATUS_OK, NULL); -} - - -/* - * 'ipp_print_job()' - Create a job object with an attached document. - */ - -static void -ipp_print_job(ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* New job */ - - - /* - * Validate print job attributes... - */ - - if (!valid_job_attributes(client)) - { - httpFlush(client->http); - return; - } - - /* - * Do we have a file to print? - */ - - if (httpGetState(client->http) == HTTP_STATE_POST_SEND) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request."); - return; - } - - /* - * Create the job... - */ - - if ((job = create_job(client)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job."); - return; - } - - /* - * Then finish getting the document data and process things... - */ - - finish_document_data(client, job); -} - - -/* - * 'ipp_print_uri()' - Create a job object with a referenced document. - */ - -static void -ipp_print_uri(ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* New job */ - - - /* - * Validate print job attributes... - */ - - if (!valid_job_attributes(client)) - { - httpFlush(client->http); - return; - } - - /* - * Create the job... - */ - - if ((job = create_job(client)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job."); - return; - } - - /* - * Then finish getting the document data and process things... - */ - - finish_document_uri(client, job); -} - - -/* - * 'ipp_send_document()' - Add an attached document to a job object created with - * Create-Job. - */ - -static void -ipp_send_document( - ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* Job information */ - ipp_attribute_t *attr; /* Current attribute */ - - - /* - * Get the job... - */ - - if ((job = find_job(client)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); - httpFlush(client->http); - return; - } - - /* - * See if we already have a document for this job or the job has already - * in a non-pending state... - */ - - if (job->state > IPP_JSTATE_HELD) - { - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state."); - httpFlush(client->http); - return; - } - else if (job->filename || job->fd >= 0) - { - respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported."); - httpFlush(client->http); - return; - } - - /* - * Make sure we have the "last-document" operation attribute... - */ - - if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute."); - httpFlush(client->http); - return; - } - else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group."); - httpFlush(client->http); - return; - } - else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || !ippGetBoolean(attr, 0)) - { - respond_unsupported(client, attr); - httpFlush(client->http); - return; - } - - /* - * Validate document attributes... - */ - - if (!valid_doc_attributes(client)) - { - httpFlush(client->http); - return; - } - - /* - * Then finish getting the document data and process things... - */ - - _cupsRWLockWrite(&(client->printer->rwlock)); - - copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0); - - if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL) - job->format = ippGetString(attr, 0, NULL); - else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL) - job->format = ippGetString(attr, 0, NULL); - else - job->format = "application/octet-stream"; - - _cupsRWUnlock(&(client->printer->rwlock)); - - finish_document_data(client, job); -} - - -/* - * 'ipp_send_uri()' - Add a referenced document to a job object created with - * Create-Job. - */ - -static void -ipp_send_uri(ippeve_client_t *client) /* I - Client */ -{ - ippeve_job_t *job; /* Job information */ - ipp_attribute_t *attr; /* Current attribute */ - - - /* - * Get the job... - */ - - if ((job = find_job(client)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); - httpFlush(client->http); - return; - } - - /* - * See if we already have a document for this job or the job has already - * in a non-pending state... - */ - - if (job->state > IPP_JSTATE_HELD) - { - respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state."); - httpFlush(client->http); - return; - } - else if (job->filename || job->fd >= 0) - { - respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported."); - httpFlush(client->http); - return; - } - - if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute."); - httpFlush(client->http); - return; - } - else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group."); - httpFlush(client->http); - return; - } - else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || !ippGetBoolean(attr, 0)) - { - respond_unsupported(client, attr); - httpFlush(client->http); - return; - } - - /* - * Validate document attributes... - */ - - if (!valid_doc_attributes(client)) - { - httpFlush(client->http); - return; - } - - /* - * Then finish getting the document data and process things... - */ - - _cupsRWLockWrite(&(client->printer->rwlock)); - - copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0); - - if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL) - job->format = ippGetString(attr, 0, NULL); - else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL) - job->format = ippGetString(attr, 0, NULL); - else - job->format = "application/octet-stream"; - - _cupsRWUnlock(&(client->printer->rwlock)); - - finish_document_uri(client, job); -} - - -/* - * 'ipp_validate_job()' - Validate job creation attributes. - */ - -static void -ipp_validate_job(ippeve_client_t *client) /* I - Client */ -{ - if (valid_job_attributes(client)) - respond_ipp(client, IPP_STATUS_OK, NULL); -} - - -/* - * 'ippserver_attr_cb()' - Determine whether an attribute should be loaded. - */ - -static int /* O - 1 to use, 0 to ignore */ -ippserver_attr_cb( - _ipp_file_t *f, /* I - IPP file */ - void *user_data, /* I - User data pointer (unused) */ - const char *attr) /* I - Attribute name */ -{ - int i, /* Current element */ - result; /* Result of comparison */ - static const char * const ignored[] = - { /* Ignored attributes */ - "attributes-charset", - "attributes-natural-language", - "charset-configured", - "charset-supported", - "device-service-count", - "device-uuid", - "document-format-varying-attributes", - "generated-natural-language-supported", - "identify-actions-default", - "identify-actions-supported", - "ipp-features-supported", - "ipp-versions-supproted", - "ippget-event-life", - "job-hold-until-supported", - "job-hold-until-time-supported", - "job-ids-supported", - "job-k-octets-supported", - "job-settable-attributes-supported", - "multiple-document-jobs-supported", - "multiple-operation-time-out", - "multiple-operation-time-out-action", - "natural-language-configured", - "notify-attributes-supported", - "notify-events-default", - "notify-events-supported", - "notify-lease-duration-default", - "notify-lease-duration-supported", - "notify-max-events-supported", - "notify-pull-method-supported", - "operations-supported", - "printer-alert", - "printer-alert-description", - "printer-camera-image-uri", - "printer-charge-info", - "printer-charge-info-uri", - "printer-config-change-date-time", - "printer-config-change-time", - "printer-current-time", - "printer-detailed-status-messages", - "printer-dns-sd-name", - "printer-fax-log-uri", - "printer-get-attributes-supported", - "printer-icons", - "printer-id", - "printer-info", - "printer-is-accepting-jobs", - "printer-message-date-time", - "printer-message-from-operator", - "printer-message-time", - "printer-more-info", - "printer-service-type", - "printer-settable-attributes-supported", - "printer-state", - "printer-state-message", - "printer-state-reasons", - "printer-static-resource-directory-uri", - "printer-static-resource-k-octets-free", - "printer-static-resource-k-octets-supported", - "printer-strings-languages-supported", - "printer-strings-uri", - "printer-supply-info-uri", - "printer-up-time", - "printer-uri-supported", - "printer-xri-supported", - "queued-job-count", - "reference-uri-scheme-supported", - "uri-authentication-supported", - "uri-security-supported", - "which-jobs-supported", - "xri-authentication-supported", - "xri-security-supported", - "xri-uri-scheme-supported" - }; - - - (void)f; - (void)user_data; - - for (i = 0, result = 1; i < (int)(sizeof(ignored) / sizeof(ignored[0])); i ++) - { - if ((result = strcmp(attr, ignored[i])) <= 0) - break; - } - - return (result != 0); -} - - -/* - * 'ippserver_error_cb()' - Log an error message. - */ - -static int /* O - 1 to continue, 0 to stop */ -ippserver_error_cb( - _ipp_file_t *f, /* I - IPP file data */ - void *user_data, /* I - User data pointer (unused) */ - const char *error) /* I - Error message */ -{ - (void)f; - (void)user_data; - - _cupsLangPrintf(stderr, "%s\n", error); - - return (1); -} - - -/* - * 'ippserver_token_cb()' - Process ippserver-specific config file tokens. - */ - -static int /* O - 1 to continue, 0 to stop */ -ippserver_token_cb( - _ipp_file_t *f, /* I - IPP file data */ - _ipp_vars_t *vars, /* I - IPP variables */ - void *user_data, /* I - User data pointer (unused) */ - const char *token) /* I - Current token */ -{ - (void)vars; - (void)user_data; - - if (!token) - { - /* - * NULL token means do the initial setup - create an empty IPP message and - * return... - */ - - f->attrs = ippNew(); - f->group_tag = IPP_TAG_PRINTER; - } - else - { - _cupsLangPrintf(stderr, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token, f->linenum, f->filename); - } - - return (1); -} - - -/* - * 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file. - */ - -static ipp_t * /* O - IPP attributes or `NULL` on error */ -load_ippserver_attributes( - const char *servername, /* I - Server name or `NULL` for default */ - int serverport, /* I - Server port number */ - const char *filename, /* I - ippserver attribute filename */ - cups_array_t *docformats) /* I - document-format-supported values */ -{ - ipp_t *attrs; /* IPP attributes */ - _ipp_vars_t vars; /* IPP variables */ - char temp[256]; /* Temporary string */ - - - (void)docformats; /* for now */ - - /* - * Setup callbacks and variables for the printer configuration file... - * - * The following additional variables are supported: - * - * - SERVERNAME: The host name of the server. - * - SERVERPORT: The default port of the server. - */ - - _ippVarsInit(&vars, (_ipp_fattr_cb_t)ippserver_attr_cb, (_ipp_ferror_cb_t)ippserver_error_cb, (_ipp_ftoken_cb_t)ippserver_token_cb); - - if (servername) - { - _ippVarsSet(&vars, "SERVERNAME", servername); - } - else - { - httpGetHostname(NULL, temp, sizeof(temp)); - _ippVarsSet(&vars, "SERVERNAME", temp); - } - - snprintf(temp, sizeof(temp), "%d", serverport); - _ippVarsSet(&vars, "SERVERPORT", temp); - - /* - * Load attributes and values for the printer... - */ - - attrs = _ippFileParse(&vars, filename, NULL); - - /* - * Free memory and return... - */ - - _ippVarsDeinit(&vars); - - return (attrs); -} - - -/* - * 'load_legacy_attributes()' - Load IPP attributes using the old ippserver - * options. - */ - -static ipp_t * /* O - IPP attributes or `NULL` on error */ -load_legacy_attributes( - const char *make, /* I - Manufacturer name */ - const char *model, /* I - Model name */ - int ppm, /* I - pages-per-minute */ - int ppm_color, /* I - pages-per-minute-color */ - int duplex, /* I - Duplex support? */ - cups_array_t *docformats) /* I - document-format-supported values */ -{ - int i; /* Looping var */ - ipp_t *attrs, /* IPP attributes */ - *col; /* Collection value */ - ipp_attribute_t *attr; /* Current attribute */ - char device_id[1024],/* printer-device-id */ - *ptr, /* Pointer into device ID */ - make_model[128];/* printer-make-and-model */ - const char *format, /* Current document format */ - *prefix; /* Prefix for device ID */ - int num_media; /* Number of media */ - const char * const *media; /* List of media */ - int num_ready; /* Number of loaded media */ - const char * const *ready; /* List of loaded media */ - pwg_media_t *pwg; /* PWG media size information */ - static const char * const media_supported[] = - { /* media-supported values */ - "na_letter_8.5x11in", /* Letter */ - "na_legal_8.5x14in", /* Legal */ - "iso_a4_210x297mm", /* A4 */ - "na_number-10_4.125x9.5in", /* #10 Envelope */ - "iso_dl_110x220mm" /* DL Envelope */ - }; - static const char * const media_supported_color[] = - { /* media-supported values */ - "na_letter_8.5x11in", /* Letter */ - "na_legal_8.5x14in", /* Legal */ - "iso_a4_210x297mm", /* A4 */ - "na_number-10_4.125x9.5in", /* #10 Envelope */ - "iso_dl_110x220mm", /* DL Envelope */ - "na_index-3x5_3x5in", /* Photo 3x5 */ - "oe_photo-l_3.5x5in", /* Photo L */ - "na_index-4x6_4x6in", /* Photo 4x6 */ - "iso_a6_105x148mm", /* A6 */ - "na_5x7_5x7in" /* Photo 5x7 aka 2L */ - "iso_a5_148x210mm", /* A5 */ - }; - static const char * const media_ready[] = - { /* media-ready values */ - "na_letter_8.5x11in", /* Letter */ - "na_number-10_4.125x9.5in" /* #10 */ - }; - static const char * const media_ready_color[] = - { /* media-ready values */ - "na_letter_8.5x11in", /* Letter */ - "na_index-4x6_4x6in" /* Photo 4x6 */ - }; - static const char * const media_source_supported[] = - { /* media-source-supported values */ - "auto", - "main", - "manual", - "by-pass-tray" /* AKA multi-purpose tray */ - }; - static const char * const media_source_supported_color[] = - { /* media-source-supported values */ - "auto", - "main", - "photo" - }; - static const char * const media_type_supported[] = - { /* media-type-supported values */ - "auto", - "cardstock", - "envelope", - "labels", - "other", - "stationery", - "stationery-letterhead", - "transparency" - }; - static const char * const media_type_supported_color[] = - { /* media-type-supported values */ - "auto", - "cardstock", - "envelope", - "labels", - "other", - "stationery", - "stationery-letterhead", - "transparency", - "photographic-glossy", - "photographic-high-gloss", - "photographic-matte", - "photographic-satin", - "photographic-semi-gloss" - }; - static const int media_bottom_margin_supported[] = - { /* media-bottom-margin-supported values */ - 635 /* 1/4" */ - }; - static const int media_bottom_margin_supported_color[] = - { /* media-bottom/top-margin-supported values */ - 0, /* Borderless */ - 1168 /* 0.46" (common HP inkjet bottom margin) */ - }; - static const int media_lr_margin_supported[] = - { /* media-left/right-margin-supported values */ - 340, /* 3.4mm (historical HP PCL A4 margin) */ - 635 /* 1/4" */ - }; - static const int media_lr_margin_supported_color[] = - { /* media-left/right-margin-supported values */ - 0, /* Borderless */ - 340, /* 3.4mm (historical HP PCL A4 margin) */ - 635 /* 1/4" */ - }; - static const int media_top_margin_supported[] = - { /* media-top-margin-supported values */ - 635 /* 1/4" */ - }; - static const int media_top_margin_supported_color[] = - { /* media-top/top-margin-supported values */ - 0, /* Borderless */ - 102 /* 0.04" (common HP inkjet top margin */ - }; - static const int orientation_requested_supported[4] = - { /* orientation-requested-supported values */ - IPP_ORIENT_PORTRAIT, - IPP_ORIENT_LANDSCAPE, - IPP_ORIENT_REVERSE_LANDSCAPE, - IPP_ORIENT_REVERSE_PORTRAIT - }; - static const char * const overrides_supported[] = - { /* overrides-supported values */ - "document-numbers", - "media", - "media-col", - "orientation-requested", - "pages" - }; - static const char * const print_color_mode_supported[] = - { /* print-color-mode-supported values */ - "monochrome" - }; - static const char * const print_color_mode_supported_color[] = - { /* print-color-mode-supported values */ - "auto", - "color", - "monochrome" - }; - static const int print_quality_supported[] = - { /* print-quality-supported values */ - IPP_QUALITY_DRAFT, - IPP_QUALITY_NORMAL, - IPP_QUALITY_HIGH - }; - static const char * const printer_input_tray[] = - { /* printer-input-tray values */ - "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto", - "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main", - "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual", - "type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray" - }; - static const char * const printer_input_tray_color[] = - { /* printer-input-tray values */ - "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto", - "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main", - "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo" - }; - static const char * const printer_supply[] = - { /* printer-supply values */ - "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;" - "maxcapacity=100;level=25;colorantname=unknown;", - "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;" - "maxcapacity=100;level=75;colorantname=black;" - }; - static const char * const printer_supply_color[] = - { /* printer-supply values */ - "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;" - "maxcapacity=100;level=25;colorantname=unknown;", - "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;" - "maxcapacity=100;level=75;colorantname=black;", - "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;" - "maxcapacity=100;level=50;colorantname=cyan;", - "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;" - "maxcapacity=100;level=33;colorantname=magenta;", - "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;" - "maxcapacity=100;level=67;colorantname=yellow;" - }; - static const char * const printer_supply_description[] = - { /* printer-supply-description values */ - "Toner Waste Tank", - "Black Toner" - }; - static const char * const printer_supply_description_color[] = - { /* printer-supply-description values */ - "Ink Waste Tank", - "Black Ink", - "Cyan Ink", - "Magenta Ink", - "Yellow Ink" - }; - static const int pwg_raster_document_resolution_supported[] = - { - 300, - 600 - }; - static const char * const pwg_raster_document_type_supported[] = - { - "black_1", - "sgray_8" - }; - static const char * const pwg_raster_document_type_supported_color[] = - { - "black_1", - "sgray_8", - "srgb_8", - "srgb_16" - }; - static const char * const sides_supported[] = - { /* sides-supported values */ - "one-sided", - "two-sided-long-edge", - "two-sided-short-edge" - }; - static const char * const urf_supported[] = - { /* urf-supported values */ - "CP1", - "IS1-4-5-19", - "MT1-2-3-4-5-6", - "RS600", - "V1.4", - "W8" - }; - static const char * const urf_supported_color[] = - { /* urf-supported values */ - "CP1", - "IS1-4-5-7-19", - "MT1-2-3-4-5-6-8-9-10-11-12-13", - "RS600", - "SRGB24", - "V1.4", - "W8" - }; - static const char * const urf_supported_color_duplex[] = - { /* urf-supported values */ - "CP1", - "IS1-4-5-7-19", - "MT1-2-3-4-5-6-8-9-10-11-12-13", - "RS600", - "SRGB24", - "V1.4", - "W8", - "DM3" - }; - static const char * const urf_supported_duplex[] = - { /* urf-supported values */ - "CP1", - "IS1-4-5-19", - "MT1-2-3-4-5-6", - "RS600", - "V1.4", - "W8", - "DM1" - }; - - - attrs = ippNew(); - - if (ppm_color > 0) - { - num_media = (int)(sizeof(media_supported_color) / sizeof(media_supported_color[0])); - media = media_supported_color; - num_ready = (int)(sizeof(media_ready_color) / sizeof(media_ready_color[0])); - ready = media_ready_color; - } - else - { - num_media = (int)(sizeof(media_supported) / sizeof(media_supported[0])); - media = media_supported; - num_ready = (int)(sizeof(media_ready) / sizeof(media_ready[0])); - ready = media_ready; - } - - /* color-supported */ - ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0); - - /* copies-default */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1); - - /* copies-supported */ - ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, (cupsArrayFind(docformats, (void *)"application/pdf") != NULL || cupsArrayFind(docformats, (void *)"image/jpeg") != NULL) ? 999 : 1); - - /* document-password-supported */ - if (cupsArrayFind(docformats, (void *)"application/pdf")) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 1023); - - /* finishings-default */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE); - - /* finishings-supported */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE); - - /* media-bottom-margin-supported */ - if (ppm_color > 0) - 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); - else - 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); - - /* media-col-database and media-col-default */ - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_media, NULL); - for (i = 0; i < num_media; i ++) - { - int bottom, left, /* media-xxx-margins */ - right, top; - const char *source; /* media-source, if any */ - - pwg = pwgMediaForPWG(media[i]); - - if (pwg->width < 21000 && pwg->length < 21000) - { - source = "photo"; /* Photo size media from photo tray */ - bottom = /* Borderless margins */ - left = - right = - top = 0; - } - else if (pwg->width < 21000) - { - source = "by-pass-tray"; /* Envelopes from multi-purpose tray */ - bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0]; - left = /* Left/right margins are standard */ - right = media_lr_margin_supported[1]; - top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]; - } - else if (pwg->width == 21000) - { - source = NULL; /* A4 from any tray */ - bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0]; - left = /* Left/right margins are reduced */ - right = media_lr_margin_supported[0]; - top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]; - } - else - { - source = NULL; /* Other size media from any tray */ - bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0]; - left = /* Left/right margins are standard */ - right = media_lr_margin_supported[1]; - top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]; - } - - col = create_media_col(media[i], source, NULL, pwg->width, pwg->length, bottom, left, right, top); - ippSetCollection(attrs, &attr, i, col); - - ippDelete(col); - } - - /* media-col-default */ - pwg = pwgMediaForPWG(ready[0]); - - if (pwg->width == 21000) - 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]); - else - 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]); - - ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col); - - ippDelete(col); - - /* media-col-ready */ - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-ready", num_ready, NULL); - for (i = 0; i < num_ready; i ++) - { - int bottom, left, /* media-xxx-margins */ - right, top; - const char *source, /* media-source */ - *type; /* media-type */ - - pwg = pwgMediaForPWG(ready[i]); - - if (pwg->width < 21000 && pwg->length < 21000) - { - source = "photo"; /* Photo size media from photo tray */ - type = "photographic-glossy"; /* Glossy photo paper */ - bottom = /* Borderless margins */ - left = - right = - top = 0; - } - else if (pwg->width < 21000) - { - source = "by-pass-tray"; /* Envelopes from multi-purpose tray */ - type = "envelope"; /* Envelope */ - bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0]; - left = /* Left/right margins are standard */ - right = media_lr_margin_supported[1]; - top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]; - } - else if (pwg->width == 21000) - { - source = "main"; /* A4 from main tray */ - type = "stationery"; /* Plain paper */ - bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0]; - left = /* Left/right margins are reduced */ - right = media_lr_margin_supported[0]; - top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]; - } - else - { - source = "main"; /* A4 from main tray */ - type = "stationery"; /* Plain paper */ - bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0]; - left = /* Left/right margins are standard */ - right = media_lr_margin_supported[1]; - top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]; - } - - col = create_media_col(ready[i], source, type, pwg->width, pwg->length, bottom, left, right, top); - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - - /* media-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media[0]); - - /* media-left/right-margin-supported */ - if (ppm_color > 0) - { - 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); - 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); - } - else - { - 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); - 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); - } - - /* media-ready */ - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", num_ready, NULL, ready); - - /* media-supported */ - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", num_media, NULL, media); - - /* media-size-supported */ - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", num_media, NULL); - for (i = 0; i < num_media; i ++) - { - pwg = pwgMediaForPWG(media[i]); - col = create_media_size(pwg->width, pwg->length); - - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - - /* media-source-supported */ - if (ppm_color > 0) - 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); - else - 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); - - /* media-top-margin-supported */ - if (ppm_color > 0) - 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); - else - 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); - - /* media-type-supported */ - if (ppm_color > 0) - 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); - else - 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); - - /* orientation-requested-default */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT); - - /* orientation-requested-supported */ - if (cupsArrayFind(docformats, (void *)"application/pdf") || cupsArrayFind(docformats, (void *)"image/jpeg")) - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported); - else - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", IPP_ORIENT_PORTRAIT); - - /* output-bin-default */ - if (ppm_color > 0) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-up"); - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down"); - - /* output-bin-supported */ - if (ppm_color > 0) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-up"); - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down"); - - /* overrides-supported */ - if (cupsArrayFind(docformats, (void *)"application/pdf")) - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported); - - /* page-ranges-supported */ - ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", cupsArrayFind(docformats, (void *)"application/pdf") != NULL); - - /* pages-per-minute */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppm); - - /* pages-per-minute-color */ - if (ppm_color > 0) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppm_color); - - /* print-color-mode-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppm_color > 0 ? "auto" : "monochrome"); - - /* print-color-mode-supported */ - if (ppm_color > 0) - 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); - else - 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); - - /* print-content-optimize-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto"); - - /* print-content-optimize-supported */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto"); - - /* print-quality-default */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL); - - /* print-quality-supported */ - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported); - - /* print-rendering-intent-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto"); - - /* print-rendering-intent-supported */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto"); - - /* printer-device-id */ - snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model); - ptr = device_id + strlen(device_id); - prefix = "CMD:"; - for (format = (const char *)cupsArrayFirst(docformats); format; format = (const char *)cupsArrayNext(docformats)) - { - if (!strcasecmp(format, "application/pdf")) - snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix); - else if (!strcasecmp(format, "application/postscript")) - snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix); - else if (!strcasecmp(format, "application/vnd.hp-PCL")) - snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix); - else if (!strcasecmp(format, "image/jpeg")) - snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix); - else if (!strcasecmp(format, "image/png")) - snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix); - else if (!strcasecmp(format, "image/pwg-raster")) - snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPWG", prefix); - else if (!strcasecmp(format, "image/urf")) - snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sURF", prefix); - else - continue; - - ptr += strlen(ptr); - prefix = ","; - } - if (ptr < (device_id + sizeof(device_id) - 1)) - { - *ptr++ = ';'; - *ptr = '\0'; - } - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id); - - /* printer-input-tray */ - if (ppm_color > 0) - { - attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray_color[0], strlen(printer_input_tray_color[0])); - for (i = 1; i < (int)(sizeof(printer_input_tray_color) / sizeof(printer_input_tray_color[0])); i ++) - ippSetOctetString(attrs, &attr, i, printer_input_tray_color[i], strlen(printer_input_tray_color[i])); - } - else - { - attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray[0], strlen(printer_input_tray[0])); - for (i = 1; i < (int)(sizeof(printer_input_tray) / sizeof(printer_input_tray[0])); i ++) - ippSetOctetString(attrs, &attr, i, printer_input_tray[i], strlen(printer_input_tray[i])); - } - - /* printer-make-and-model */ - snprintf(make_model, sizeof(make_model), "%s %s", make, model); - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, make_model); - - /* printer-resolution-default */ - ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600); - - /* printer-resolution-supported */ - ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600); - - /* printer-supply and printer-supply-description */ - if (ppm_color > 0) - { - attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], strlen(printer_supply_color[0])); - for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++) - ippSetOctetString(attrs, &attr, i, printer_supply_color[i], strlen(printer_supply_color[i])); - - 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); - } - else - { - attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], strlen(printer_supply[0])); - for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++) - ippSetOctetString(attrs, &attr, i, printer_supply[i], strlen(printer_supply[i])); - - 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); - } - - /* pwg-raster-document-xxx-supported */ - if (cupsArrayFind(docformats, (void *)"image/pwg-raster")) - { - 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); - - if (ppm_color > 0 && duplex) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "rotated"); - else if (duplex) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal"); - - if (ppm_color > 0) - 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); - else - 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); - } - - /* sides-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided"); - - /* sides-supported */ - if (duplex) - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported); - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided"); - - /* urf-supported */ - if (cupsArrayFind(docformats, (void *)"image/urf")) - { - if (ppm_color > 0) - { - if (duplex) - 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); - else - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color) / sizeof(urf_supported_color[0])), NULL, urf_supported_color); - } - else if (duplex) - { - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_duplex) / sizeof(urf_supported_duplex[0])), NULL, urf_supported_duplex); - } - else - { - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])), NULL, urf_supported); - } - } - - return (attrs); -} - - -#if !CUPS_LITE -/* - * 'load_ppd_attributes()' - Load IPP attributes from a PPD file. - */ - -static ipp_t * /* O - IPP attributes or `NULL` on error */ -load_ppd_attributes( - const char *ppdfile, /* I - PPD filename */ - cups_array_t *docformats) /* I - document-format-supported values */ -{ - int i, j; /* Looping vars */ - ipp_t *attrs; /* Attributes */ - ipp_attribute_t *attr; /* Current attribute */ - ipp_t *col; /* Current collection value */ - ppd_file_t *ppd; /* PPD data */ - ppd_attr_t *ppd_attr; /* PPD attribute */ - ppd_choice_t *ppd_choice; /* PPD choice */ - ppd_size_t *ppd_size; /* Default PPD size */ - pwg_size_t *pwg_size, /* Current PWG size */ - *default_size = NULL; /* Default PWG size */ - const char *default_source = NULL, /* Default media source */ - *default_type = NULL; /* Default media type */ - pwg_map_t *pwg_map; /* Mapping from PWG to PPD keywords */ - _ppd_cache_t *pc; /* PPD cache */ - _pwg_finishings_t *finishings; /* Current finishings value */ - const char *template; /* Current finishings-template value */ - int num_margins; /* Number of media-xxx-margin-supported values */ - int margins[10]; /* media-xxx-margin-supported values */ - int xres, /* Default horizontal resolution */ - yres; /* Default vertical resolution */ - int num_urf; /* Number of urf-supported values */ - const char *urf[10]; /* urf-supported values */ - char urf_rs[32]; /* RS value */ - static const int orientation_requested_supported[4] = - { /* orientation-requested-supported values */ - IPP_ORIENT_PORTRAIT, - IPP_ORIENT_LANDSCAPE, - IPP_ORIENT_REVERSE_LANDSCAPE, - IPP_ORIENT_REVERSE_PORTRAIT - }; - static const char * const overrides_supported[] = - { /* overrides-supported */ - "document-numbers", - "media", - "media-col", - "orientation-requested", - "pages" - }; - static const char * const print_color_mode_supported[] = - { /* print-color-mode-supported values */ - "monochrome" - }; - static const char * const print_color_mode_supported_color[] = - { /* print-color-mode-supported values */ - "auto", - "color", - "monochrome" - }; - static const int print_quality_supported[] = - { /* print-quality-supported values */ - IPP_QUALITY_DRAFT, - IPP_QUALITY_NORMAL, - IPP_QUALITY_HIGH - }; - static const char * const printer_supply[] = - { /* printer-supply values */ - "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;" - "maxcapacity=100;level=25;colorantname=unknown;", - "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;" - "maxcapacity=100;level=75;colorantname=black;" - }; - static const char * const printer_supply_color[] = - { /* printer-supply values */ - "index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;" - "maxcapacity=100;level=25;colorantname=unknown;", - "index=2;class=supplyThatIsConsumed;type=ink;unit=percent;" - "maxcapacity=100;level=75;colorantname=black;", - "index=3;class=supplyThatIsConsumed;type=ink;unit=percent;" - "maxcapacity=100;level=50;colorantname=cyan;", - "index=4;class=supplyThatIsConsumed;type=ink;unit=percent;" - "maxcapacity=100;level=33;colorantname=magenta;", - "index=5;class=supplyThatIsConsumed;type=ink;unit=percent;" - "maxcapacity=100;level=67;colorantname=yellow;" - }; - static const char * const printer_supply_description[] = - { /* printer-supply-description values */ - "Toner Waste Tank", - "Black Toner" - }; - static const char * const printer_supply_description_color[] = - { /* printer-supply-description values */ - "Ink Waste Tank", - "Black Ink", - "Cyan Ink", - "Magenta Ink", - "Yellow Ink" - }; - static const char * const pwg_raster_document_type_supported[] = - { - "black_1", - "sgray_8" - }; - static const char * const pwg_raster_document_type_supported_color[] = - { - "black_1", - "sgray_8", - "srgb_8", - "srgb_16" - }; - static const char * const sides_supported[] = - { /* sides-supported values */ - "one-sided", - "two-sided-long-edge", - "two-sided-short-edge" - }; - - - /* - * Open the PPD file... - */ - - if ((ppd = ppdOpenFile(ppdfile)) == NULL) - { - ppd_status_t status; /* Load error */ - - status = ppdLastError(&i); - _cupsLangPrintf(stderr, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile, ppdErrorString(status), i); - return (NULL); - } - - ppdMarkDefaults(ppd); - - pc = _ppdCacheCreateWithPPD(ppd); - - if ((ppd_size = ppdPageSize(ppd, NULL)) != NULL) - { - /* - * Look up default size... - */ - - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - { - if (!strcmp(pwg_size->map.ppd, ppd_size->name)) - { - default_size = pwg_size; - break; - } - } - } - - if (!default_size) - { - /* - * Default to A4 or Letter... - */ - - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - { - if (!strcmp(pwg_size->map.ppd, "Letter") || !strcmp(pwg_size->map.ppd, "A4")) - { - default_size = pwg_size; - break; - } - } - - if (!default_size) - default_size = pc->sizes; /* Last resort: first size */ - } - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL) - default_source = _ppdCacheGetSource(pc, ppd_choice->choice); - - if ((ppd_choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL) - default_source = _ppdCacheGetType(pc, ppd_choice->choice); - - if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL) - { - /* - * Use the PPD-defined default resolution... - */ - - if ((i = sscanf(ppd_attr->value, "%dx%d", &xres, &yres)) == 1) - yres = xres; - else if (i < 0) - xres = yres = 300; - } - else - { - /* - * Use default of 300dpi... - */ - - xres = yres = 300; - } - - snprintf(urf_rs, sizeof(urf_rs), "RS%d", yres < xres ? yres : xres); - - num_urf = 0; - urf[num_urf ++] = "V1.4"; - urf[num_urf ++] = "CP1"; - urf[num_urf ++] = urf_rs; - urf[num_urf ++] = "W8"; - if (pc->sides_2sided_long) - urf[num_urf ++] = "DM1"; - if (ppd->color_device) - urf[num_urf ++] = "SRGB24"; - - /* - * PostScript printers accept PDF via one of the CUPS PDF to PostScript - * filters, along with PostScript (of course) and JPEG... - */ - - cupsArrayAdd(docformats, "application/pdf"); - cupsArrayAdd(docformats, "application/postscript"); - cupsArrayAdd(docformats, "image/jpeg"); - - /* - * Create the attributes... - */ - - attrs = ippNew(); - - /* color-supported */ - ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", (char)ppd->color_device); - - /* copies-default */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1); - - /* copies-supported */ - ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999); - - /* document-password-supported */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127); - - /* finishing-template-supported */ - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template-supported", cupsArrayCount(pc->templates) + 1, NULL, NULL); - ippSetString(attrs, &attr, 0, "none"); - for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates)) - ippSetString(attrs, &attr, i, template); - - /* finishings-col-database */ - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-database", cupsArrayCount(pc->templates) + 1, NULL); - - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none"); - ippSetCollection(attrs, &attr, 0, col); - ippDelete(col); - - for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates)) - { - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template); - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - - /* finishings-col-default */ - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none"); - ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col); - ippDelete(col); - - /* finishings-col-ready */ - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-ready", cupsArrayCount(pc->templates) + 1, NULL); - - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none"); - ippSetCollection(attrs, &attr, 0, col); - ippDelete(col); - - for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates)) - { - col = ippNew(); - ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template); - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - - /* finishings-col-supported */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishings-col-supported", NULL, "finishing-template"); - - /* finishings-default */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE); - - /* finishings-ready */ - attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", cupsArrayCount(pc->finishings) + 1, NULL); - ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE); - for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings)) - ippSetInteger(attrs, &attr, i, finishings->value); - - /* finishings-supported */ - attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", cupsArrayCount(pc->finishings) + 1, NULL); - ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE); - for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings)) - ippSetInteger(attrs, &attr, i, finishings->value); - - /* media-bottom-margin-supported */ - 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 ++) - { - for (j = 0; j < num_margins; j ++) - { - if (margins[j] == pwg_size->bottom) - break; - } - - if (j >= num_margins) - margins[num_margins ++] = pwg_size->bottom; - } - - for (i = 0; i < (num_margins - 1); i ++) - { - for (j = i + 1; j < num_margins; j ++) - { - if (margins[i] > margins[j]) - { - int mtemp = margins[i]; - - margins[i] = margins[j]; - margins[j] = mtemp; - } - } - } - - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", num_margins, margins); - - /* media-col-database */ - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", pc->num_sizes, NULL); - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - { - 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); - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - - /* media-col-default */ - 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); - ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col); - ippDelete(col); - - /* media-col-ready */ - 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); - ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col); - ippDelete(col); - - /* media-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, default_size->map.pwg); - - /* media-left-margin-supported */ - 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 ++) - { - for (j = 0; j < num_margins; j ++) - { - if (margins[j] == pwg_size->left) - break; - } - - if (j >= num_margins) - margins[num_margins ++] = pwg_size->left; - } - - for (i = 0; i < (num_margins - 1); i ++) - { - for (j = i + 1; j < num_margins; j ++) - { - if (margins[i] > margins[j]) - { - int mtemp = margins[i]; - - margins[i] = margins[j]; - margins[j] = mtemp; - } - } - } - - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", num_margins, margins); - - /* media-ready */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, default_size->map.pwg); - - /* media-right-margin-supported */ - 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 ++) - { - for (j = 0; j < num_margins; j ++) - { - if (margins[j] == pwg_size->right) - break; - } - - if (j >= num_margins) - margins[num_margins ++] = pwg_size->right; - } - - for (i = 0; i < (num_margins - 1); i ++) - { - for (j = i + 1; j < num_margins; j ++) - { - if (margins[i] > margins[j]) - { - int mtemp = margins[i]; - - margins[i] = margins[j]; - margins[j] = mtemp; - } - } - } - - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", num_margins, margins); - - /* media-supported */ - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-supported", pc->num_sizes, NULL, NULL); - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - ippSetString(attrs, &attr, i, pwg_size->map.pwg); - - /* media-size-supported */ - attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", pc->num_sizes, NULL); - for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++) - { - col = create_media_size(pwg_size->width, pwg_size->length); - ippSetCollection(attrs, &attr, i, col); - ippDelete(col); - } - - /* media-source-supported */ - if (pc->num_sources > 0) - { - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source-supported", pc->num_sources, NULL, NULL); - for (i = 0, pwg_map = pc->sources; i < pc->num_sources; i ++, pwg_map ++) - ippSetString(attrs, &attr, i, pwg_map->pwg); - } - else - { - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", NULL, "auto"); - } - - /* media-top-margin-supported */ - 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 ++) - { - for (j = 0; j < num_margins; j ++) - { - if (margins[j] == pwg_size->top) - break; - } - - if (j >= num_margins) - margins[num_margins ++] = pwg_size->top; - } - - for (i = 0; i < (num_margins - 1); i ++) - { - for (j = i + 1; j < num_margins; j ++) - { - if (margins[i] > margins[j]) - { - int mtemp = margins[i]; - - margins[i] = margins[j]; - margins[j] = mtemp; - } - } - } - - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", num_margins, margins); - - /* media-type-supported */ - if (pc->num_types > 0) - { - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type-supported", pc->num_types, NULL, NULL); - for (i = 0, pwg_map = pc->types; i < pc->num_types; i ++, pwg_map ++) - ippSetString(attrs, &attr, i, pwg_map->pwg); - } - else - { - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", NULL, "auto"); - } - - /* orientation-requested-default */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT); - - /* orientation-requested-supported */ - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported); - - /* output-bin-default */ - if (pc->num_bins > 0) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-default", NULL, pc->bins->pwg); - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down"); - - /* output-bin-supported */ - if (pc->num_bins > 0) - { - attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-supported", pc->num_bins, NULL, NULL); - for (i = 0, pwg_map = pc->bins; i < pc->num_bins; i ++, pwg_map ++) - ippSetString(attrs, &attr, i, pwg_map->pwg); - } - else - { - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down"); - } - - /* overrides-supported */ - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported); - - /* page-ranges-supported */ - ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1); - - /* pages-per-minute */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppd->throughput); - - /* pages-per-minute-color */ - if (ppd->color_device) - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppd->throughput); - - /* print-color-mode-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppd->color_device ? "auto" : "monochrome"); - - /* print-color-mode-supported */ - if (ppd->color_device) - 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); - else - 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); - - /* print-content-optimize-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto"); - - /* print-content-optimize-supported */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto"); - - /* print-quality-default */ - ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL); - - /* print-quality-supported */ - ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported); - - /* print-rendering-intent-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto"); - - /* print-rendering-intent-supported */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto"); - - /* printer-device-id */ - if ((ppd_attr = ppdFindAttr(ppd, "1284DeviceId", NULL)) != NULL) - { - /* - * Use the device ID string from the PPD... - */ - - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, ppd_attr->value); - } - else - { - /* - * Synthesize a device ID string... - */ - - char device_id[1024]; /* Device ID string */ - - snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;CMD:PS;", ppd->manufacturer, ppd->modelname); - - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id); - } - - /* printer-input-tray */ - if (pc->num_sources > 0) - { - for (i = 0, attr = NULL; i < pc->num_sources; i ++) - { - char input_tray[1024]; /* printer-input-tray value */ - - if (!strcmp(pc->sources[i].pwg, "manual") || strstr(pc->sources[i].pwg, "-man") != NULL) - snprintf(input_tray, sizeof(input_tray), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc->sources[i].pwg); - else - snprintf(input_tray, sizeof(input_tray), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc->sources[i].pwg); - - if (attr) - ippSetOctetString(attrs, &attr, i, input_tray, (int)strlen(input_tray)); - else - attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", input_tray, (int)strlen(input_tray)); - } - } - else - { - static const char *printer_input_tray = "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto"; - - ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray, (int)strlen(printer_input_tray)); - } - - /* printer-make-and-model */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, ppd->nickname); - - /* printer-resolution-default */ - ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, xres, yres); - - /* printer-resolution-supported */ - ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, xres, yres); - - /* printer-supply and printer-supply-description */ - if (ppd->color_device) - { - attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], strlen(printer_supply_color[0])); - for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++) - ippSetOctetString(attrs, &attr, i, printer_supply_color[i], strlen(printer_supply_color[i])); - - 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); - } - else - { - attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], strlen(printer_supply[0])); - for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++) - ippSetOctetString(attrs, &attr, i, printer_supply[i], strlen(printer_supply[i])); - - 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); - } - - /* pwg-raster-document-xxx-supported */ - if (cupsArrayFind(docformats, (void *)"image/pwg-raster")) - { - ippAddResolution(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH, xres, yres); - - if (pc->sides_2sided_long) - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal"); - - if (ppd->color_device) - 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); - else - 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); - } - - /* sides-default */ - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided"); - - /* sides-supported */ - if (pc->sides_2sided_long) - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported); - else - ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided"); - - /* urf-supported */ - if (cupsArrayFind(docformats, (void *)"image/urf")) - ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", num_urf, NULL, urf); - - /* - * Free the PPD file and return the attributes... - */ - - _ppdCacheDestroy(pc); - - ppdClose(ppd); - - return (attrs); -} -#endif /* !CUPS_LITE */ - - -/* - * 'parse_options()' - Parse URL options into CUPS options. - * - * The client->options string is destroyed by this function. - */ - -static int /* O - Number of options */ -parse_options(ippeve_client_t *client, /* I - Client */ - cups_option_t **options)/* O - Options */ -{ - char *name, /* Name */ - *value, /* Value */ - *next; /* Next name=value pair */ - int num_options = 0; /* Number of options */ - - - *options = NULL; - - for (name = client->options; name && *name; name = next) - { - if ((value = strchr(name, '=')) == NULL) - break; - - *value++ = '\0'; - if ((next = strchr(value, '&')) != NULL) - *next++ = '\0'; - - num_options = cupsAddOption(name, value, num_options, options); - } - - return (num_options); -} - - -/* - * 'process_attr_message()' - Process an ATTR: message from a command. - */ - -static void -process_attr_message( - ippeve_job_t *job, /* I - Job */ - char *message) /* I - Message */ -{ - int i, /* Looping var */ - num_options = 0; /* Number of name=value pairs */ - cups_option_t *options = NULL, /* name=value pairs from message */ - *option; /* Current option */ - ipp_attribute_t *attr; /* Current attribute */ - - - /* - * Grab attributes from the message line... - */ - - num_options = cupsParseOptions(message + 5, num_options, &options); - - /* - * Loop through the options and record them in the printer or job objects... - */ - - for (i = num_options, option = options; i > 0; i --, option ++) - { - if (!strcmp(option->name, "job-impressions")) - { - /* - * Update job-impressions attribute... - */ - - job->impressions = atoi(option->value); - } - else if (!strcmp(option->name, "job-impressions-completed")) - { - /* - * Update job-impressions-completed attribute... - */ - - job->impcompleted = atoi(option->value); - } - 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")) - { - /* - * Update Printer Status attribute... - */ - - _cupsRWLockWrite(&job->printer->rwlock); - - if ((attr = ippFindAttribute(job->printer->attrs, option->name, IPP_TAG_ZERO)) != NULL) - ippDeleteAttribute(job->printer->attrs, attr); - - cupsEncodeOption(job->printer->attrs, IPP_TAG_PRINTER, option->name, option->value); - - _cupsRWUnlock(&job->printer->rwlock); - } - else - { - /* - * Something else that isn't currently supported... - */ - - fprintf(stderr, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job->id, option->name, option->value); - } - } - - cupsFreeOptions(num_options, options); -} - - -/* - * 'process_client()' - Process client requests on a thread. - */ - -static void * /* O - Exit status */ -process_client(ippeve_client_t *client) /* I - Client */ -{ - /* - * Loop until we are out of requests or timeout (30 seconds)... - */ - -#ifdef HAVE_SSL - int first_time = 1; /* First time request? */ -#endif /* HAVE_SSL */ - - while (httpWait(client->http, 30000)) - { -#ifdef HAVE_SSL - if (first_time) - { - /* - * See if we need to negotiate a TLS connection... - */ - - char buf[1]; /* First byte from client */ - - if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0]))) - { - fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname); - - if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS)) - { - fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString()); - break; - } - - fprintf(stderr, "%s Connection now encrypted.\n", client->hostname); - } - - first_time = 0; - } -#endif /* HAVE_SSL */ - - if (!process_http(client)) - break; - } - - /* - * Close the conection to the client and return... - */ - - delete_client(client); - - return (NULL); -} - - -/* - * 'process_http()' - Process a HTTP request. - */ - -int /* O - 1 on success, 0 on failure */ -process_http(ippeve_client_t *client) /* I - Client connection */ -{ - char uri[1024]; /* URI */ - http_state_t http_state; /* HTTP state */ - http_status_t http_status; /* HTTP status */ - ipp_state_t ipp_state; /* State of IPP transfer */ - char scheme[32], /* Method/scheme */ - userpass[128], /* Username:password */ - hostname[HTTP_MAX_HOST]; - /* Hostname */ - int port; /* Port number */ - static const char * const http_states[] = - { /* Strings for logging HTTP method */ - "WAITING", - "OPTIONS", - "GET", - "GET_SEND", - "HEAD", - "POST", - "POST_RECV", - "POST_SEND", - "PUT", - "PUT_RECV", - "DELETE", - "TRACE", - "CONNECT", - "STATUS", - "UNKNOWN_METHOD", - "UNKNOWN_VERSION" - }; - - - /* - * Clear state variables... - */ - - ippDelete(client->request); - ippDelete(client->response); - - client->request = NULL; - client->response = NULL; - client->operation = HTTP_STATE_WAITING; - - /* - * Read a request from the connection... - */ - - while ((http_state = httpReadRequest(client->http, uri, - sizeof(uri))) == HTTP_STATE_WAITING) - usleep(1); - - /* - * Parse the request line... - */ - - if (http_state == HTTP_STATE_ERROR) - { - if (httpError(client->http) == EPIPE) - fprintf(stderr, "%s Client closed connection.\n", client->hostname); - else - fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, strerror(httpError(client->http))); - - return (0); - } - else if (http_state == HTTP_STATE_UNKNOWN_METHOD) - { - fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname); - respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); - return (0); - } - else if (http_state == HTTP_STATE_UNKNOWN_VERSION) - { - fprintf(stderr, "%s Bad HTTP version.\n", client->hostname); - respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); - return (0); - } - - fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], uri); - - /* - * Separate the URI into its components... - */ - - if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme), - userpass, sizeof(userpass), - hostname, sizeof(hostname), &port, - client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK && - (http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*"))) - { - fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri); - respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); - return (0); - } - - if ((client->options = strchr(client->uri, '?')) != NULL) - *(client->options)++ = '\0'; - - /* - * Process the request... - */ - - client->start = time(NULL); - client->operation = httpGetState(client->http); - - /* - * Parse incoming parameters until the status changes... - */ - - while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE); - - if (http_status != HTTP_STATUS_OK) - { - respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); - return (0); - } - - if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] && - httpGetVersion(client->http) >= HTTP_VERSION_1_1) - { - /* - * HTTP/1.1 and higher require the "Host:" field... - */ - - respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); - return (0); - } - - /* - * Handle HTTP Upgrade... - */ - - if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), - "Upgrade")) - { -#ifdef HAVE_SSL - if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http)) - { - if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0)) - return (0); - - fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname); - - if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED)) - { - fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString()); - return (0); - } - - fprintf(stderr, "%s Connection now encrypted.\n", client->hostname); - } - else -#endif /* HAVE_SSL */ - - if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0)) - return (0); - } - - /* - * Handle HTTP Expect... - */ - - if (httpGetExpect(client->http) && - (client->operation == HTTP_STATE_POST || - client->operation == HTTP_STATE_PUT)) - { - if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE) - { - /* - * Send 100-continue header... - */ - - if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0)) - return (0); - } - else - { - /* - * Send 417-expectation-failed header... - */ - - if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0)) - return (0); - } - } - - /* - * Handle new transfers... - */ - - switch (client->operation) - { - case HTTP_STATE_OPTIONS : - /* - * Do OPTIONS command... - */ - - return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0)); - - case HTTP_STATE_HEAD : - if (!strcmp(client->uri, "/icon.png")) - return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0)); - else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies")) - return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0)); - else - return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0)); - - case HTTP_STATE_GET : - if (!strcmp(client->uri, "/icon.png")) - { - /* - * Send PNG icon file. - */ - - if (client->printer->icon) - { - int fd; /* Icon file */ - struct stat fileinfo; /* Icon file information */ - char buffer[4096]; /* Copy buffer */ - ssize_t bytes; /* Bytes */ - - fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon); - - if (!stat(client->printer->icon, &fileinfo) && (fd = open(client->printer->icon, O_RDONLY)) >= 0) - { - if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size)) - { - close(fd); - return (0); - } - - while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) - httpWrite2(client->http, buffer, (size_t)bytes); - - httpFlushWrite(client->http); - - close(fd); - } - else - return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0)); - } - else - { - fputs("Icon file is internal printer.png.\n", stderr); - - if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png))) - return (0); - - httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png)); - httpFlushWrite(client->http); - } - } - else if (!strcmp(client->uri, "/")) - { - /* - * Show web status page... - */ - - return (show_status(client)); - } - else if (!strcmp(client->uri, "/media")) - { - /* - * Show web media page... - */ - - return (show_media(client)); - } - else if (!strcmp(client->uri, "/supplies")) - { - /* - * Show web supplies page... - */ - - return (show_supplies(client)); - } - else - return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0)); - break; - - case HTTP_STATE_POST : - if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE), - "application/ipp")) - { - /* - * Not an IPP request... - */ - - return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0)); - } - - /* - * Read the IPP request... - */ - - client->request = ippNew(); - - while ((ipp_state = ippRead(client->http, - client->request)) != IPP_STATE_DATA) - { - if (ipp_state == IPP_STATE_ERROR) - { - fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString()); - respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); - return (0); - } - } - - /* - * Now that we have the IPP request, process the request... - */ - - return (process_ipp(client)); - - default : - break; /* Anti-compiler-warning-code */ - } - - return (1); -} - - -/* - * 'process_ipp()' - Process an IPP request. - */ - -static int /* O - 1 on success, 0 on error */ -process_ipp(ippeve_client_t *client) /* I - Client */ -{ - ipp_tag_t group; /* Current group tag */ - ipp_attribute_t *attr; /* Current attribute */ - ipp_attribute_t *charset; /* Character set attribute */ - ipp_attribute_t *language; /* Language attribute */ - ipp_attribute_t *uri; /* Printer URI attribute */ - int major, minor; /* Version number */ - const char *name; /* Name of attribute */ - - - debug_attributes("Request", client->request, 1); - - /* - * First build an empty response message for this request... - */ - - client->operation_id = ippGetOperation(client->request); - client->response = ippNewResponse(client->request); - - /* - * Then validate the request header and required attributes... - */ - - major = ippGetVersion(client->request, &minor); - - if (major < 1 || major > 2) - { - /* - * Return an error, since we only support IPP 1.x and 2.x. - */ - - respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor); - } - else if ((major * 10 + minor) > MaxVersion) - { - if (httpGetState(client->http) != HTTP_STATE_POST_SEND) - httpFlush(client->http); /* Flush trailing (junk) data */ - - respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); - return (0); - } - else if (ippGetRequestId(client->request) <= 0) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request)); - } - else if (!ippFirstAttribute(client->request)) - { - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request."); - } - else - { - /* - * Make sure that the attributes are provided in the correct order and - * don't repeat groups... - */ - - for (attr = ippFirstAttribute(client->request), - group = ippGetGroupTag(attr); - attr; - attr = ippNextAttribute(client->request)) - { - if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO) - { - /* - * Out of order; return an error... - */ - - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, - "Attribute groups are out of order (%x < %x).", - ippGetGroupTag(attr), group); - break; - } - else - group = ippGetGroupTag(attr); - } - - if (!attr) - { - /* - * Then make sure that the first three attributes are: - * - * attributes-charset - * attributes-natural-language - * printer-uri/job-uri - */ - - attr = ippFirstAttribute(client->request); - name = ippGetName(attr); - if (attr && name && !strcmp(name, "attributes-charset") && - ippGetValueTag(attr) == IPP_TAG_CHARSET) - charset = attr; - else - charset = NULL; - - attr = ippNextAttribute(client->request); - name = ippGetName(attr); - - if (attr && name && !strcmp(name, "attributes-natural-language") && - ippGetValueTag(attr) == IPP_TAG_LANGUAGE) - language = attr; - else - language = NULL; - - if ((attr = ippFindAttribute(client->request, "printer-uri", - IPP_TAG_URI)) != NULL) - uri = attr; - else if ((attr = ippFindAttribute(client->request, "job-uri", - IPP_TAG_URI)) != NULL) - uri = attr; - else - uri = NULL; - - if (charset && - strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") && - strcasecmp(ippGetString(charset, 0, NULL), "utf-8")) - { - /* - * Bad character set... - */ - - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, - "Unsupported character set \"%s\".", - ippGetString(charset, 0, NULL)); - } - else if (!charset || !language || !uri) - { - /* - * Return an error, since attributes-charset, - * attributes-natural-language, and printer-uri/job-uri are required - * for all operations. - */ - - respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, - "Missing required attributes."); - } - else - { - char scheme[32], /* URI scheme */ - userpass[32], /* Username/password in URI */ - host[256], /* Host name in URI */ - resource[256]; /* Resource path in URI */ - int port; /* Port number in URI */ - - name = ippGetName(uri); - - if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL), - scheme, sizeof(scheme), - userpass, sizeof(userpass), - host, sizeof(host), &port, - resource, sizeof(resource)) < HTTP_URI_STATUS_OK) - respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, - "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL)); - else if ((!strcmp(name, "job-uri") && - strncmp(resource, "/ipp/print/", 11)) || - (!strcmp(name, "printer-uri") && - strcmp(resource, "/ipp/print"))) - respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.", - name, ippGetString(uri, 0, NULL)); - else - { - /* - * Try processing the operation... - */ - - switch (ippGetOperation(client->request)) - { - case IPP_OP_PRINT_JOB : - ipp_print_job(client); - break; - - case IPP_OP_PRINT_URI : - ipp_print_uri(client); - break; - - case IPP_OP_VALIDATE_JOB : - ipp_validate_job(client); - break; - - case IPP_OP_CREATE_JOB : - ipp_create_job(client); - break; - - case IPP_OP_SEND_DOCUMENT : - ipp_send_document(client); - break; - - case IPP_OP_SEND_URI : - ipp_send_uri(client); - break; - - case IPP_OP_CANCEL_JOB : - ipp_cancel_job(client); - break; - - case IPP_OP_GET_JOB_ATTRIBUTES : - ipp_get_job_attributes(client); - break; - - case IPP_OP_GET_JOBS : - ipp_get_jobs(client); - break; - - case IPP_OP_GET_PRINTER_ATTRIBUTES : - ipp_get_printer_attributes(client); - break; - - case IPP_OP_CLOSE_JOB : - ipp_close_job(client); - break; - - case IPP_OP_IDENTIFY_PRINTER : - ipp_identify_printer(client); - break; - - default : - respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, - "Operation not supported."); - break; - } - } - } - } - } - - /* - * Send the HTTP header and return... - */ - - if (httpGetState(client->http) != HTTP_STATE_POST_SEND) - httpFlush(client->http); /* Flush trailing (junk) data */ - - return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp", - ippLength(client->response))); -} - - -/* - * 'process_job()' - Process a print job. - */ - -static void * /* O - Thread exit status */ -process_job(ippeve_job_t *job) /* I - Job */ -{ - job->state = IPP_JSTATE_PROCESSING; - job->printer->state = IPP_PSTATE_PROCESSING; - job->processing = time(NULL); - - while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY) - { - job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED; - - sleep(1); - } - - job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED; - - if (job->printer->command) - { - /* - * Execute a command with the job spool file and wait for it to complete... - */ - - int pid, /* Process ID */ - status; /* Exit status */ - struct timeval start, /* Start time */ - end; /* End time */ - char *myargv[3], /* Command-line arguments */ - *myenvp[400]; /* Environment variables */ - int myenvc; /* Number of environment variables */ - ipp_attribute_t *attr; /* Job attribute */ - char val[1280], /* IPP_NAME=value */ - *valptr; /* Pointer into string */ -#ifndef _WIN32 - int mystdout = -1; /* File for stdout */ - int mypipe[2]; /* Pipe for stderr */ - char line[2048], /* Line from stderr */ - *ptr, /* Pointer into line */ - *endptr; /* End of line */ - ssize_t bytes; /* Bytes read */ -#endif /* !_WIN32 */ - - fprintf(stderr, "[Job %d] Running command \"%s %s\".\n", job->id, job->printer->command, job->filename); - gettimeofday(&start, NULL); - - /* - * Setup the command-line arguments... - */ - - myargv[0] = job->printer->command; - myargv[1] = job->filename; - myargv[2] = NULL; - - /* - * Copy the current environment, then add environment variables for every - * Job attribute and Printer -default attributes... - */ - - for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++) - myenvp[myenvc] = strdup(environ[myenvc]); - - if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32)) - { - fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id); - job->state = IPP_JSTATE_ABORTED; - goto error; - } - - if (asprintf(myenvp + myenvc, "CONTENT_TYPE=%s", job->format) > 0) - myenvc ++; - - if (job->printer->device_uri && asprintf(myenvp + myenvc, "DEVICE_URI=%s", job->printer->device_uri) > 0) - myenvc ++; - -#if !CUPS_LITE - if (job->printer->ppdfile && asprintf(myenvp + myenvc, "PPD=%s", job->printer->ppdfile) > 0) - myenvc ++; -#endif /* !CUPS_LITE */ - - for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs)) - { - /* - * Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and - * then add the value(s) from the attribute. - */ - - const char *name = ippGetName(attr), - /* Attribute name */ - *suffix = strstr(name, "-default"); - /* Suffix on attribute name */ - - if (!suffix || suffix[8]) - continue; - - valptr = val; - *valptr++ = 'I'; - *valptr++ = 'P'; - *valptr++ = 'P'; - *valptr++ = '_'; - while (*name && valptr < (val + sizeof(val) - 2)) - { - if (*name == '-') - *valptr++ = '_'; - else - *valptr++ = (char)toupper(*name & 255); - - name ++; - } - *valptr++ = '='; - ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val)); - - myenvp[myenvc++] = strdup(val); - } - - for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs)) - { - /* - * Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the - * value(s) from the attribute. - */ - - const char *name = ippGetName(attr); - /* Attribute name */ - - if (!name) - continue; - - valptr = val; - *valptr++ = 'I'; - *valptr++ = 'P'; - *valptr++ = 'P'; - *valptr++ = '_'; - while (*name && valptr < (val + sizeof(val) - 2)) - { - if (*name == '-') - *valptr++ = '_'; - else - *valptr++ = (char)toupper(*name & 255); - - name ++; - } - *valptr++ = '='; - ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val)); - - myenvp[myenvc++] = strdup(val); - } - - if (attr) - { - fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id); - job->state = IPP_JSTATE_ABORTED; - goto error; - } - - myenvp[myenvc] = NULL; - - /* - * Now run the program... - */ - -#ifdef _WIN32 - status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp); - -#else - if (job->printer->device_uri) - { - char scheme[32], /* URI scheme */ - userpass[256], /* username:password (unused) */ - host[256], /* Hostname or IP address */ - resource[256]; /* Resource path */ - int port; /* Port number */ - - - 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) - { - fprintf(stderr, "[Job %d] Bad device URI \"%s\".\n", job->id, job->printer->device_uri); - } - else if (!strcmp(scheme, "file")) - { - struct stat fileinfo; /* See if this is a file or directory... */ - - if (stat(resource, &fileinfo)) - { - if (errno == ENOENT) - { - if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0) - fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource); - else - fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno)); - } - else - fprintf(stderr, "[Job %d] Unable to access \"%s\": %s\n", job->id, resource, strerror(errno)); - } - else if (S_ISDIR(fileinfo.st_mode)) - { - if ((mystdout = create_job_file(job, line, sizeof(line), resource, "prn")) >= 0) - fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line); - else - fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, line, strerror(errno)); - } - else if (!S_ISREG(fileinfo.st_mode)) - { - if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC, 0666)) >= 0) - fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource); - else - fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno)); - } - else if ((mystdout = open(resource, O_WRONLY)) >= 0) - fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource); - else - fprintf(stderr, "[Job %d] Unable to open \"%s\": %s\n", job->id, resource, strerror(errno)); - } - else if (!strcmp(scheme, "socket")) - { - http_addrlist_t *addrlist; /* List of addresses */ - char service[32]; /* Service number */ - - snprintf(service, sizeof(service), "%d", port); - - if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL) - fprintf(stderr, "[Job %d] Unable to find \"%s\": %s\n", job->id, host, cupsLastErrorString()); - else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel))) - fprintf(stderr, "[Job %d] Unable to connect to \"%s\": %s\n", job->id, host, cupsLastErrorString()); - - httpAddrFreeList(addrlist); - } - else - { - fprintf(stderr, "[Job %d] Unsupported device URI scheme \"%s\".\n", job->id, scheme); - } - } - else if ((mystdout = create_job_file(job, line, sizeof(line), job->printer->directory, "prn")) >= 0) - { - fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line); - } - - if (mystdout < 0) - mystdout = open("/dev/null", O_WRONLY); - - if (pipe(mypipe)) - { - fprintf(stderr, "[Job %d] Unable to create pipe for stderr: %s\n", job->id, strerror(errno)); - mypipe[0] = mypipe[1] = -1; - } - - if ((pid = fork()) == 0) - { - /* - * Child comes here... - */ - - close(1); - dup2(mystdout, 1); - close(mystdout); - - close(2); - dup2(mypipe[1], 2); - close(mypipe[0]); - close(mypipe[1]); - - execve(job->printer->command, myargv, myenvp); - exit(errno); - } - else if (pid < 0) - { - /* - * Unable to fork process... - */ - - fprintf(stderr, "[Job %d] Unable to start job processing command: %s\n", job->id, strerror(errno)); - status = -1; - - close(mystdout); - close(mypipe[0]); - close(mypipe[1]); - - /* - * Free memory used for environment... - */ - - while (myenvc > 0) - free(myenvp[-- myenvc]); - } - else - { - /* - * Free memory used for environment... - */ - - while (myenvc > 0) - free(myenvp[-- myenvc]); - - /* - * Close the output file in the parent process... - */ - - close(mystdout); - - /* - * If the pipe exists, read from it until EOF... - */ - - if (mypipe[0] >= 0) - { - close(mypipe[1]); - - endptr = line; - while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0) - { - endptr += bytes; - *endptr = '\0'; - - while ((ptr = strchr(line, '\n')) != NULL) - { - int level = 3; /* Message log level */ - - *ptr++ = '\0'; - - if (!strncmp(line, "ATTR:", 5)) - { - /* - * Process job/printer attribute updates. - */ - - process_attr_message(job, line); - } - else if (!strncmp(line, "DEBUG:", 6)) - { - /* - * Debug message... - */ - - level = 2; - } - else if (!strncmp(line, "ERROR:", 6)) - { - /* - * Error message... - */ - - level = 0; - job->message = strdup(line + 6); - job->msglevel = 0; - } - else if (!strncmp(line, "INFO:", 5)) - { - /* - * Informational/progress message... - */ - - level = 1; - if (job->msglevel) - { - job->message = strdup(line + 5); - job->msglevel = 1; - } - } - else if (!strncmp(line, "STATE:", 6)) - { - /* - * Process printer-state-reasons keywords. - */ - - process_state_message(job, line); - } - - if (Verbosity >= level) - fprintf(stderr, "[Job %d] Command - %s\n", job->id, line); - - bytes = ptr - line; - if (ptr < endptr) - memmove(line, ptr, (size_t)(endptr - ptr)); - endptr -= bytes; - *endptr = '\0'; - } - } - - close(mypipe[0]); - } - - /* - * Wait for child to complete... - */ - -# ifdef HAVE_WAITPID - while (waitpid(pid, &status, 0) < 0); -# else - while (wait(&status) < 0); -# endif /* HAVE_WAITPID */ - } -#endif /* _WIN32 */ - - if (status) - { -#ifndef _WIN32 - if (WIFEXITED(status)) -#endif /* !_WIN32 */ - fprintf(stderr, "[Job %d] Command \"%s\" exited with status %d.\n", job->id, job->printer->command, WEXITSTATUS(status)); -#ifndef _WIN32 - else - fprintf(stderr, "[Job %d] Command \"%s\" terminated with signal %d.\n", job->id, job->printer->command, WTERMSIG(status)); -#endif /* !_WIN32 */ - job->state = IPP_JSTATE_ABORTED; - } - else if (status < 0) - job->state = IPP_JSTATE_ABORTED; - else - fprintf(stderr, "[Job %d] Command \"%s\" completed successfully.\n", job->id, job->printer->command); - - /* - * Report the total processing time... - */ - - gettimeofday(&end, NULL); - - 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)); - } - else - { - /* - * Sleep for a random amount of time to simulate job processing. - */ - - sleep((unsigned)(5 + (rand() % 11))); - } - - if (job->cancel) - job->state = IPP_JSTATE_CANCELED; - else if (job->state == IPP_JSTATE_PROCESSING) - job->state = IPP_JSTATE_COMPLETED; - - error: - - job->completed = time(NULL); - job->printer->state = IPP_PSTATE_IDLE; - job->printer->active_job = NULL; - - return (NULL); -} - - -/* - * 'process_state_message()' - Process a STATE: message from a command. - */ - -static void -process_state_message( - ippeve_job_t *job, /* I - Job */ - char *message) /* I - Message */ -{ - int i; /* Looping var */ - ippeve_preason_t state_reasons, /* printer-state-reasons values */ - bit; /* Current reason bit */ - char *ptr, /* Pointer into message */ - *next; /* Next keyword in message */ - int remove; /* Non-zero if we are removing keywords */ - - - /* - * Skip leading "STATE:" and any whitespace... - */ - - for (message += 6; *message; message ++) - if (*message != ' ' && *message != '\t') - break; - - /* - * Support the following forms of message: - * - * "keyword[,keyword,...]" to set the printer-state-reasons value(s). - * - * "-keyword[,keyword,...]" to remove keywords. - * - * "+keyword[,keyword,...]" to add keywords. - * - * Keywords may or may not have a suffix (-report, -warning, -error) per - * RFC 8011. - */ - - if (*message == '-') - { - remove = 1; - state_reasons = job->printer->state_reasons; - message ++; - } - else if (*message == '+') - { - remove = 0; - state_reasons = job->printer->state_reasons; - message ++; - } - else - { - remove = 0; - state_reasons = IPPEVE_PREASON_NONE; - } - - while (*message) - { - if ((next = strchr(message, ',')) != NULL) - *next++ = '\0'; - - if ((ptr = strstr(message, "-error")) != NULL) - *ptr = '\0'; - else if ((ptr = strstr(message, "-report")) != NULL) - *ptr = '\0'; - else if ((ptr = strstr(message, "-warning")) != NULL) - *ptr = '\0'; - - for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2) - { - if (!strcmp(message, ippeve_preason_strings[i])) - { - if (remove) - state_reasons &= ~bit; - else - state_reasons |= bit; - } - } - - if (next) - message = next; - else - break; - } - - job->printer->state_reasons = state_reasons; -} - - -/* - * 'register_printer()' - Register a printer object via Bonjour. - */ - -static int /* O - 1 on success, 0 on error */ -register_printer( - ippeve_printer_t *printer, /* I - Printer */ - const char *subtypes) /* I - Service subtype(s) */ -{ -#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) - ippeve_txt_t ipp_txt; /* Bonjour IPP TXT record */ - int i, /* Looping var */ - count; /* Number of values */ - ipp_attribute_t *color_supported, - *document_format_supported, - *printer_location, - *printer_make_and_model, - *printer_more_info, - *printer_uuid, - *sides_supported, - *urf_supported; /* Printer attributes */ - const char *value; /* Value string */ - char formats[252], /* List of supported formats */ - urf[252], /* List of supported URF values */ - *ptr; /* Pointer into string */ - - color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN); - document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE); - printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT); - printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT); - printer_more_info = ippFindAttribute(printer->attrs, "printer-more-info", IPP_TAG_URI); - printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI); - sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD); - urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD); - - for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++) - { - value = ippGetString(document_format_supported, i, NULL); - - if (!strcasecmp(value, "application/octet-stream")) - continue; - - if (ptr > formats && ptr < (formats + sizeof(formats) - 1)) - *ptr++ = ','; - - strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats)); - ptr += strlen(ptr); - - if (ptr >= (formats + sizeof(formats) - 1)) - break; - } - - urf[0] = '\0'; - for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++) - { - value = ippGetString(urf_supported, i, NULL); - - if (ptr > urf && ptr < (urf + sizeof(urf) - 1)) - *ptr++ = ','; - - strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf)); - ptr += strlen(ptr); - - if (ptr >= (urf + sizeof(urf) - 1)) - break; - } - -#endif /* HAVE_DNSSD || HAVE_AVAHI */ -#ifdef HAVE_DNSSD - DNSServiceErrorType error; /* Error from Bonjour */ - char regtype[256]; /* Bonjour service type */ - - - /* - * Build the TXT record for IPP... - */ - - TXTRecordCreate(&ipp_txt, 1024, NULL); - TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print"); - if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL) - TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value); - if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL) - TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(value), value); - if ((value = ippGetString(printer_location, 0, NULL)) != NULL) - TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value); - TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats); - TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F"); - TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F"); - if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL) - TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9); -# ifdef HAVE_SSL - TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2"); -# endif /* HAVE_SSL */ - if (urf[0]) - TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf); - TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1"); - TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1"); - - /* - * Register the _printer._tcp (LPD) service type with a port number of 0 to - * defend our service name but not actually support LPD... - */ - - printer->printer_ref = DNSSDMaster; - - if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError) - { - _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error); - return (0); - } - - /* - * Then register the _ipp._tcp (IPP) service type with the real port number to - * advertise our IPP printer... - */ - - printer->ipp_ref = DNSSDMaster; - - if (subtypes && *subtypes) - snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtypes); - else - strlcpy(regtype, "_ipp._tcp", sizeof(regtype)); - - if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError) - { - _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error); - return (0); - } - -# ifdef HAVE_SSL - /* - * Then register the _ipps._tcp (IPP) service type with the real port number to - * advertise our IPPS printer... - */ - - printer->ipps_ref = DNSSDMaster; - - if (subtypes && *subtypes) - snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtypes); - else - strlcpy(regtype, "_ipps._tcp", sizeof(regtype)); - - if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError) - { - _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error); - return (0); - } -# endif /* HAVE_SSL */ - - /* - * Similarly, register the _http._tcp,_printer (HTTP) service type with the - * real port number to advertise our IPP printer... - */ - - printer->http_ref = DNSSDMaster; - - if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection, 0 /* interfaceIndex */, printer->dnssd_name, "_http._tcp,_printer", NULL /* domain */, NULL /* host */, htons(printer->port), 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError) - { - _cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error); - return (0); - } - - TXTRecordDeallocate(&ipp_txt); - -#elif defined(HAVE_AVAHI) - char temp[256]; /* Subtype service string */ - - /* - * Create the TXT record... - */ - - ipp_txt = NULL; - ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print"); - if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL) - ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value); - if ((value = ippGetString(printer_more_info, 0, NULL)) != NULL) - ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", value); - if ((value = ippGetString(printer_location, 0, NULL)) != NULL) - ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value); - ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats); - ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F"); - ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F"); - if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL) - ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9); -# ifdef HAVE_SSL - ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2"); -# endif /* HAVE_SSL */ - if (urf[0]) - ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf); - ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1"); - ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1"); - - /* - * Register _printer._tcp (LPD) with port 0 to reserve the service name... - */ - - avahi_threaded_poll_lock(DNSSDMaster); - - printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL); - - 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); - - /* - * Then register the _ipp._tcp (IPP)... - */ - - 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); - if (subtypes && *subtypes) - { - char *temptypes = strdup(subtypes), *start, *end; - - for (start = temptypes; *start; start = end) - { - if ((end = strchr(start, ',')) != NULL) - *end++ = '\0'; - else - end = start + strlen(start); - - snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start); - avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp); - } - - free(temptypes); - } - -#ifdef HAVE_SSL - /* - * _ipps._tcp (IPPS) for secure printing... - */ - - 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); - if (subtypes && *subtypes) - { - char *temptypes = strdup(subtypes), *start, *end; - - for (start = temptypes; *start; start = end) - { - if ((end = strchr(start, ',')) != NULL) - *end++ = '\0'; - else - end = start + strlen(start); - - snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start); - avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp); - } - - free(temptypes); - } -#endif /* HAVE_SSL */ - - /* - * Finally _http.tcp (HTTP) for the web interface... - */ - - 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); - 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"); - - /* - * Commit it... - */ - - avahi_entry_group_commit(printer->ipp_ref); - avahi_threaded_poll_unlock(DNSSDMaster); - - avahi_string_list_free(ipp_txt); -#endif /* HAVE_DNSSD */ - - return (1); -} - - -/* - * 'respond_http()' - Send a HTTP response. - */ - -int /* O - 1 on success, 0 on failure */ -respond_http( - ippeve_client_t *client, /* I - Client */ - http_status_t code, /* I - HTTP status of response */ - const char *content_encoding, /* I - Content-Encoding of response */ - const char *type, /* I - MIME media type of response */ - size_t length) /* I - Length of response */ -{ - char message[1024]; /* Text message */ - - - fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code)); - - if (code == HTTP_STATUS_CONTINUE) - { - /* - * 100-continue doesn't send any headers... - */ - - return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0); - } - - /* - * Format an error message... - */ - - if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS) - { - snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code)); - - type = "text/plain"; - length = strlen(message); - } - else - message[0] = '\0'; - - /* - * Send the HTTP response header... - */ - - httpClearFields(client->http); - - if (code == HTTP_STATUS_METHOD_NOT_ALLOWED || - client->operation == HTTP_STATE_OPTIONS) - httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST"); - - if (type) - { - if (!strcmp(type, "text/html")) - httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, - "text/html; charset=utf-8"); - else - httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type); - - if (content_encoding) - httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding); - } - - httpSetLength(client->http, length); - - if (httpWriteResponse(client->http, code) < 0) - return (0); - - /* - * Send the response data... - */ - - if (message[0]) - { - /* - * Send a plain text message. - */ - - if (httpPrintf(client->http, "%s", message) < 0) - return (0); - - if (httpWrite2(client->http, "", 0) < 0) - return (0); - } - else if (client->response) - { - /* - * Send an IPP response... - */ - - debug_attributes("Response", client->response, 2); - - ippSetState(client->response, IPP_STATE_IDLE); - - if (ippWrite(client->http, client->response) != IPP_STATE_DATA) - return (0); - } - - return (1); -} - - -/* - * 'respond_ipp()' - Send an IPP response. - */ - -static void -respond_ipp(ippeve_client_t *client, /* I - Client */ - ipp_status_t status, /* I - status-code */ - const char *message, /* I - printf-style status-message */ - ...) /* I - Additional args as needed */ -{ - const char *formatted = NULL; /* Formatted message */ - - - ippSetStatusCode(client->response, status); - - if (message) - { - va_list ap; /* Pointer to additional args */ - ipp_attribute_t *attr; /* New status-message attribute */ - - va_start(ap, message); - if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL) - ippSetStringfv(client->response, &attr, 0, message, ap); - else - attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap); - va_end(ap); - - formatted = ippGetString(attr, 0, NULL); - } - - if (formatted) - fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted); - else - fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status)); -} - - -/* - * 'respond_unsupported()' - Respond with an unsupported attribute. - */ - -static void -respond_unsupported( - ippeve_client_t *client, /* I - Client */ - ipp_attribute_t *attr) /* I - Atribute */ -{ - ipp_attribute_t *temp; /* Copy of attribute */ - - - respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); - - temp = ippCopyAttribute(client->response, attr, 0); - ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP); -} - - -/* - * 'run_printer()' - Run the printer service. - */ - -static void -run_printer(ippeve_printer_t *printer) /* I - Printer */ -{ - int num_fds; /* Number of file descriptors */ - struct pollfd polldata[3]; /* poll() data */ - int timeout; /* Timeout for poll() */ - ippeve_client_t *client; /* New client */ - - - /* - * Setup poll() data for the Bonjour service socket and IPv4/6 listeners... - */ - - polldata[0].fd = printer->ipv4; - polldata[0].events = POLLIN; - - polldata[1].fd = printer->ipv6; - polldata[1].events = POLLIN; - - num_fds = 2; - -#ifdef HAVE_DNSSD - polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster); - polldata[num_fds ++].events = POLLIN; -#endif /* HAVE_DNSSD */ - - /* - * Loop until we are killed or have a hard error... - */ - - for (;;) - { - if (cupsArrayCount(printer->jobs)) - timeout = 10; - else - timeout = -1; - - if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR) - { - perror("poll() failed"); - break; - } - - if (polldata[0].revents & POLLIN) - { - if ((client = create_client(printer, printer->ipv4)) != NULL) - { - _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client); - - if (t) - { - _cupsThreadDetach(t); - } - else - { - perror("Unable to create client thread"); - delete_client(client); - } - } - } - - if (polldata[1].revents & POLLIN) - { - if ((client = create_client(printer, printer->ipv6)) != NULL) - { - _cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client); - - if (t) - { - _cupsThreadDetach(t); - } - else - { - perror("Unable to create client thread"); - delete_client(client); - } - } - } - -#ifdef HAVE_DNSSD - if (polldata[2].revents & POLLIN) - DNSServiceProcessResult(DNSSDMaster); -#endif /* HAVE_DNSSD */ - - /* - * Clean out old jobs... - */ - - clean_jobs(printer); - } -} - - -/* - * 'show_media()' - Show media load state. - */ - -static int /* O - 1 on success, 0 on failure */ -show_media(ippeve_client_t *client) /* I - Client connection */ -{ - ippeve_printer_t *printer = client->printer; - /* Printer */ - int i, j, /* Looping vars */ - num_ready, /* Number of ready media */ - num_sizes, /* Number of media sizes */ - num_sources, /* Number of media sources */ - num_types; /* Number of media types */ - ipp_attribute_t *media_col_ready,/* media-col-ready attribute */ - *media_ready, /* media-ready attribute */ - *media_sizes, /* media-supported attribute */ - *media_sources, /* media-source-supported attribute */ - *media_types, /* media-type-supported attribute */ - *input_tray; /* printer-input-tray attribute */ - ipp_t *media_col; /* media-col value */ - const char *media_size, /* media value */ - *media_source, /* media-source value */ - *media_type, /* media-type value */ - *ready_size, /* media-col-ready media-size[-name] value */ - *ready_source, /* media-col-ready media-source value */ - *ready_tray, /* printer-input-tray value */ - *ready_type; /* media-col-ready media-type value */ - char tray_str[1024], /* printer-input-tray string value */ - *tray_ptr; /* Pointer into value */ - int tray_len; /* Length of printer-input-tray value */ - int ready_sheets; /* printer-input-tray sheets value */ - int num_options; /* Number of form options */ - cups_option_t *options; /* Form options */ - static const int sheets[] = /* Number of sheets */ - { - 250, - 125, - 50, - 25, - 5, - 0, - -2 - }; - - - if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0)) - return (0); - - html_header(client, printer->name, 0); - - if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL) - { - html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO); - - if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL) - { - html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL) - { - html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL) - { - html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL) - { - html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - num_ready = ippGetCount(media_col_ready); - num_sizes = ippGetCount(media_sizes); - num_sources = ippGetCount(media_sources); - num_types = ippGetCount(media_types); - - if (num_sources != ippGetCount(input_tray)) - { - html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - /* - * Process form data if present... - */ - - if (printer->web_forms) - num_options = parse_options(client, &options); - else - num_options = 0; - - if (num_options > 0) - { - /* - * WARNING: A real printer/server implementation MUST NOT implement - * media updates via a GET request - GET requests are supposed to be - * idempotent (without side-effects) and we obviously are not - * authenticating access here. This form is provided solely to - * enable testing and development! - */ - - char name[255]; /* Form name */ - const char *val; /* Form value */ - pwg_media_t *media; /* Media info */ - - _cupsRWLockWrite(&printer->rwlock); - - ippDeleteAttribute(printer->attrs, media_col_ready); - media_col_ready = NULL; - - if (media_ready) - { - ippDeleteAttribute(printer->attrs, media_ready); - media_ready = NULL; - } - - printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED); - - for (i = 0; i < num_sources; i ++) - { - media_source = ippGetString(media_sources, i, NULL); - - if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL) - continue; - - snprintf(name, sizeof(name), "size%d", i); - if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL) - { - snprintf(name, sizeof(name), "type%d", i); - if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type) - media_type = NULL; - - if (media_ready) - ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size); - else - media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size); - - media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1); - - if (media_col_ready) - ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col); - else - media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col); - ippDelete(media_col); - } - else - media = NULL; - - snprintf(name, sizeof(name), "level%d", i); - if ((val = cupsGetOption(name, num_options, options)) != NULL) - ready_sheets = atoi(val); - else - ready_sheets = 0; - - 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); - - ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str)); - - if (ready_sheets == 0) - { - printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY; - if (printer->active_job) - printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED; - } - else if (ready_sheets < 25 && ready_sheets > 0) - printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW; - } - - if (!media_col_ready) - media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready"); - - if (!media_ready) - media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready"); - - _cupsRWUnlock(&printer->rwlock); - } - - if (printer->web_forms) - html_printf(client, "<form method=\"GET\" action=\"/media\">\n"); - - html_printf(client, "<table class=\"form\" summary=\"Media\">\n"); - for (i = 0; i < num_sources; i ++) - { - media_source = ippGetString(media_sources, i, NULL); - - if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL) - continue; - - for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++) - { - media_col = ippGetCollection(media_col_ready, j); - ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL); - ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL); - ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL); - - if (ready_source && !strcmp(ready_source, media_source)) - break; - - ready_source = NULL; - ready_size = NULL; - ready_type = NULL; - } - - html_printf(client, "<tr><th>%s:</th>", media_source); - - /* - * Media size... - */ - - if (printer->web_forms) - { - html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i); - for (j = 0; j < num_sizes; j ++) - { - media_size = ippGetString(media_sizes, j, NULL); - - html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size); - } - html_printf(client, "</select>"); - } - else - html_printf(client, "<td>%s", ready_size); - - /* - * Media type... - */ - - if (printer->web_forms) - { - html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i); - for (j = 0; j < num_types; j ++) - { - media_type = ippGetString(media_types, j, NULL); - - html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type); - } - html_printf(client, "</select>"); - } - else - html_printf(client, ", %s", ready_type); - - /* - * Level/sheets loaded... - */ - - if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL) - { - if (tray_len > (int)(sizeof(tray_str) - 1)) - tray_len = (int)sizeof(tray_str) - 1; - memcpy(tray_str, ready_tray, (size_t)tray_len); - tray_str[tray_len] = '\0'; - - if ((tray_ptr = strstr(tray_str, "level=")) != NULL) - ready_sheets = atoi(tray_ptr + 6); - else - ready_sheets = 0; - } - else - ready_sheets = 0; - - if (printer->web_forms) - { - html_printf(client, " <select name=\"level%d\">", i); - for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++) - { - if (!strcmp(media_source, "by-pass-tray") && sheets[j] > 25) - continue; - - if (sheets[j] < 0) - html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : ""); - else - html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]); - } - html_printf(client, "</select></td></tr>\n"); - } - else if (ready_sheets > 0) - html_printf(client, ", %d sheets</td></tr>\n", ready_sheets); - else - html_printf(client, "</td></tr>\n"); - } - - if (printer->web_forms) - { - html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">"); - if (num_options > 0) - html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n"); - html_printf(client, "</td></tr></table></form>\n"); - - if (num_options > 0) - html_printf(client, "<script>\n" - "setTimeout(hide_status, 3000);\n" - "function hide_status() {\n" - " var status = document.getElementById('status');\n" - " status.style.display = 'none';\n" - "}\n" - "</script>\n"); - } - else - html_printf(client, "</table>\n"); - - html_footer(client); - - return (1); -} - - -/* - * 'show_status()' - Show printer/system state. - */ - -static int /* O - 1 on success, 0 on failure */ -show_status(ippeve_client_t *client) /* I - Client connection */ -{ - ippeve_printer_t *printer = client->printer; - /* Printer */ - ippeve_job_t *job; /* Current job */ - int i; /* Looping var */ - ippeve_preason_t reason; /* Current reason */ - static const char * const reasons[] = /* Reason strings */ - { - "Other", - "Cover Open", - "Input Tray Missing", - "Marker Supply Empty", - "Marker Supply Low", - "Marker Waste Almost Full", - "Marker Waste Full", - "Media Empty", - "Media Jam", - "Media Low", - "Media Needed", - "Moving to Paused", - "Paused", - "Spool Area Full", - "Toner Empty", - "Toner Low" - }; - static const char * const state_colors[] = - { /* State colors */ - "#0C0", /* Idle */ - "#EE0", /* Processing */ - "#C00" /* Stopped */ - }; - - - if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0)) - return (0); - - html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15); - 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); - html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs)); - for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1) - if (printer->state_reasons & reason) - html_printf(client, "\n<br> %s", reasons[i]); - html_printf(client, "</p>\n"); - - if (cupsArrayCount(printer->jobs) > 0) - { - _cupsRWLockRead(&(printer->rwlock)); - - 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"); - for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs)) - { - char when[256], /* When job queued/started/finished */ - hhmmss[64]; /* Time HH:MM:SS */ - - switch (job->state) - { - case IPP_JSTATE_PENDING : - case IPP_JSTATE_HELD : - snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss))); - break; - case IPP_JSTATE_PROCESSING : - case IPP_JSTATE_STOPPED : - snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss))); - break; - case IPP_JSTATE_ABORTED : - snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss))); - break; - case IPP_JSTATE_CANCELED : - snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss))); - break; - case IPP_JSTATE_COMPLETED : - snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss))); - break; - } - - 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); - } - html_printf(client, "</tbody></table>\n"); - - _cupsRWUnlock(&(printer->rwlock)); - } - - html_footer(client); - - return (1); -} - - -/* - * 'show_supplies()' - Show printer supplies. - */ - -static int /* O - 1 on success, 0 on failure */ -show_supplies( - ippeve_client_t *client) /* I - Client connection */ -{ - ippeve_printer_t *printer = client->printer; - /* Printer */ - int i, /* Looping var */ - num_supply; /* Number of supplies */ - ipp_attribute_t *supply, /* printer-supply attribute */ - *supply_desc; /* printer-supply-description attribute */ - int num_options; /* Number of form options */ - cups_option_t *options; /* Form options */ - int supply_len, /* Length of supply value */ - level; /* Supply level */ - const char *supply_value; /* Supply value */ - char supply_text[1024], /* Supply string */ - *supply_ptr; /* Pointer into supply string */ - static const char * const printer_supply[] = - { /* printer-supply values */ - "index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;" - "maxcapacity=100;level=%d;colorantname=unknown;", - "index=2;class=supplyThatIsConsumed;type=toner;unit=percent;" - "maxcapacity=100;level=%d;colorantname=black;", - "index=3;class=supplyThatIsConsumed;type=toner;unit=percent;" - "maxcapacity=100;level=%d;colorantname=cyan;", - "index=4;class=supplyThatIsConsumed;type=toner;unit=percent;" - "maxcapacity=100;level=%d;colorantname=magenta;", - "index=5;class=supplyThatIsConsumed;type=toner;unit=percent;" - "maxcapacity=100;level=%d;colorantname=yellow;" - }; - static const char * const backgrounds[] = - { /* Background colors for the supply-level bars */ - "#777 linear-gradient(#333,#777)", - "#000 linear-gradient(#666,#000)", - "#0FF linear-gradient(#6FF,#0FF)", - "#F0F linear-gradient(#F6F,#F0F)", - "#CC0 linear-gradient(#EE6,#EE0)" - }; - static const char * const colors[] = /* Text colors for the supply-level bars */ - { - "#fff", - "#fff", - "#000", - "#000", - "#000" - }; - - - if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0)) - return (0); - - html_header(client, printer->name, 0); - - if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL) - { - html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - num_supply = ippGetCount(supply); - - if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL) - { - html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - if (num_supply != ippGetCount(supply_desc)) - { - html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n"); - html_footer(client); - return (1); - } - - if (printer->web_forms) - num_options = parse_options(client, &options); - else - num_options = 0; - - if (num_options > 0) - { - /* - * WARNING: A real printer/server implementation MUST NOT implement - * supply updates via a GET request - GET requests are supposed to be - * idempotent (without side-effects) and we obviously are not - * authenticating access here. This form is provided solely to - * enable testing and development! - */ - - char name[64]; /* Form field */ - const char *val; /* Form value */ - - _cupsRWLockWrite(&printer->rwlock); - - ippDeleteAttribute(printer->attrs, supply); - supply = NULL; - - 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); - - for (i = 0; i < num_supply; i ++) - { - snprintf(name, sizeof(name), "supply%d", i); - if ((val = cupsGetOption(name, num_options, options)) != NULL) - { - level = atoi(val); /* New level */ - - snprintf(supply_text, sizeof(supply_text), printer_supply[i], level); - if (supply) - ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text)); - else - supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text)); - - if (i == 0) - { - if (level == 100) - printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL; - else if (level > 90) - printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL; - } - else - { - if (level == 0) - printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY; - else if (level < 10) - printer->state_reasons |= IPPEVE_PREASON_TONER_LOW; - } - } - } - - _cupsRWUnlock(&printer->rwlock); - } - - if (printer->web_forms) - html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n"); - - html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n"); - for (i = 0; i < num_supply; i ++) - { - supply_value = ippGetOctetString(supply, i, &supply_len); - if (supply_len > (int)(sizeof(supply_text) - 1)) - supply_len = (int)sizeof(supply_text) - 1; - - memcpy(supply_text, supply_value, (size_t)supply_len); - supply_text[supply_len] = '\0'; - - if ((supply_ptr = strstr(supply_text, "level=")) != NULL) - level = atoi(supply_ptr + 6); - else - level = 50; - - if (printer->web_forms) - html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level); - else - html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL)); - - if (level < 10) - 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); - else - 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); - } - - if (printer->web_forms) - { - html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">"); - if (num_options > 0) - html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n"); - html_printf(client, "</td></tr>\n</table>\n</form>\n"); - - if (num_options > 0) - html_printf(client, "<script>\n" - "setTimeout(hide_status, 3000);\n" - "function hide_status() {\n" - " var status = document.getElementById('status');\n" - " status.style.display = 'none';\n" - "}\n" - "</script>\n"); - } - else - html_printf(client, "</table>\n"); - - html_footer(client); - - return (1); -} - - -/* - * 'time_string()' - Return the local time in hours, minutes, and seconds. - */ - -static char * -time_string(time_t tv, /* I - Time value */ - char *buffer, /* I - Buffer */ - size_t bufsize) /* I - Size of buffer */ -{ - struct tm *curtime = localtime(&tv); - /* Local time */ - - strftime(buffer, bufsize, "%X", curtime); - return (buffer); -} - - -/* - * 'usage()' - Show program usage. - */ - -static void -usage(int status) /* O - Exit status */ -{ - _cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\"")); - _cupsLangPuts(stdout, _("Options:")); - _cupsLangPuts(stderr, _("--help Show program help")); - _cupsLangPuts(stderr, _("--no-web-forms Disable web forms for media and supplies")); - _cupsLangPuts(stderr, _("--version Show program version")); - _cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)")); - _cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer")); -#ifdef HAVE_SSL - _cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys.")); -#endif /* HAVE_SSL */ - _cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)")); - _cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file")); - _cupsLangPuts(stdout, _("-V version Set default IPP version")); - _cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file")); - _cupsLangPuts(stdout, _("-c command Set print command")); - _cupsLangPuts(stdout, _("-d spool-directory Set spool directory")); - _cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types")); - _cupsLangPuts(stdout, _("-i iconfile.png Set icon file")); - _cupsLangPuts(stdout, _("-k Keep job spool files")); - _cupsLangPuts(stdout, _("-l location Set location of printer")); - _cupsLangPuts(stdout, _("-m model Set model name (default=Printer)")); - _cupsLangPuts(stdout, _("-n hostname Set hostname for printer")); - _cupsLangPuts(stdout, _("-p port Set port number for printer")); - _cupsLangPuts(stdout, _("-r subtype,[subtype] Set DNS-SD service subtype")); - _cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute")); - _cupsLangPuts(stderr, _("-v Be verbose")); - - exit(status); -} - - -/* - * 'valid_doc_attributes()' - Determine whether the document attributes are - * valid. - * - * When one or more document attributes are invalid, this function adds a - * suitable response and attributes to the unsupported group. - */ - -static int /* O - 1 if valid, 0 if not */ -valid_doc_attributes( - ippeve_client_t *client) /* I - Client */ -{ - int valid = 1; /* Valid attributes? */ - ipp_op_t op = ippGetOperation(client->request); - /* IPP operation */ - const char *op_name = ippOpString(op); - /* IPP operation name */ - ipp_attribute_t *attr, /* Current attribute */ - *supported; /* xxx-supported attribute */ - const char *compression = NULL, - /* compression value */ - *format = NULL; /* document-format value */ - - - /* - * Check operation attributes... - */ - - if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL) - { - /* - * If compression is specified, only accept a supported value in a Print-Job - * or Send-Document request... - */ - - compression = ippGetString(attr, 0, NULL); - supported = ippFindAttribute(client->printer->attrs, - "compression-supported", IPP_TAG_KEYWORD); - - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || - ippGetGroupTag(attr) != IPP_TAG_OPERATION || - (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT && - op != IPP_OP_VALIDATE_JOB) || - !ippContainsString(supported, compression)) - { - respond_unsupported(client, attr); - valid = 0; - } - else - { - fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression); - - ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression); - - if (strcmp(compression, "none")) - { - if (Verbosity) - fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression); - httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression); - } - } - } - - /* - * Is it a format we support? - */ - - if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE || - ippGetGroupTag(attr) != IPP_TAG_OPERATION) - { - respond_unsupported(client, attr); - valid = 0; - } - else - { - format = ippGetString(attr, 0, NULL); - - fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format); - - ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format); - } - } - else - { - format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL); - if (!format) - format = "application/octet-stream"; /* Should never happen */ - - attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format); - } - - if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT)) - { - /* - * Auto-type the file using the first 8 bytes of the file... - */ - - unsigned char header[8]; /* First 8 bytes of file */ - - memset(header, 0, sizeof(header)); - httpPeek(client->http, (char *)header, sizeof(header)); - - if (!memcmp(header, "%PDF", 4)) - format = "application/pdf"; - else if (!memcmp(header, "%!", 2)) - format = "application/postscript"; - else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef) - format = "image/jpeg"; - else if (!memcmp(header, "\211PNG", 4)) - format = "image/png"; - else if (!memcmp(header, "RAS2", 4)) - format = "image/pwg-raster"; - else if (!memcmp(header, "UNIRAST", 8)) - format = "image/urf"; - else - format = NULL; - - if (format) - { - fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format); - - ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format); - } - } - - if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format)) - { - respond_unsupported(client, attr); - valid = 0; - } - - /* - * document-name - */ - - if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL) - ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL)); - - return (valid); -} - - -/* - * 'valid_job_attributes()' - Determine whether the job attributes are valid. - * - * When one or more job attributes are invalid, this function adds a suitable - * response and attributes to the unsupported group. - */ - -static int /* O - 1 if valid, 0 if not */ -valid_job_attributes( - ippeve_client_t *client) /* I - Client */ -{ - int i, /* Looping var */ - count, /* Number of values */ - valid = 1; /* Valid attributes? */ - ipp_attribute_t *attr, /* Current attribute */ - *supported; /* xxx-supported attribute */ - - - /* - * Check operation attributes... - */ - - valid = valid_doc_attributes(client); - - /* - * Check the various job template attributes... - */ - - if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || - ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || - (ippGetValueTag(attr) != IPP_TAG_NAME && - ippGetValueTag(attr) != IPP_TAG_NAMELANG && - ippGetValueTag(attr) != IPP_TAG_KEYWORD) || - strcmp(ippGetString(attr, 0, NULL), "no-hold")) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || - (ippGetValueTag(attr) != IPP_TAG_NAME && - ippGetValueTag(attr) != IPP_TAG_NAMELANG)) - { - respond_unsupported(client, attr); - valid = 0; - } - - ippSetGroupTag(client->request, &attr, IPP_TAG_JOB); - } - else - ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled"); - - if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || - ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || - (ippGetValueTag(attr) != IPP_TAG_NAME && - ippGetValueTag(attr) != IPP_TAG_NAMELANG && - ippGetValueTag(attr) != IPP_TAG_KEYWORD) || - strcmp(ippGetString(attr, 0, NULL), "none")) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || - (ippGetValueTag(attr) != IPP_TAG_NAME && - ippGetValueTag(attr) != IPP_TAG_NAMELANG && - ippGetValueTag(attr) != IPP_TAG_KEYWORD)) - { - respond_unsupported(client, attr); - valid = 0; - } - else - { - supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD); - - if (!ippContainsString(supported, ippGetString(attr, 0, NULL))) - { - respond_unsupported(client, attr); - valid = 0; - } - } - } - - if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL) - { - ipp_t *col, /* media-col collection */ - *size; /* media-size collection */ - ipp_attribute_t *member, /* Member attribute */ - *x_dim, /* x-dimension */ - *y_dim; /* y-dimension */ - int x_value, /* y-dimension value */ - y_value; /* x-dimension value */ - - if (ippGetCount(attr) != 1 || - ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION) - { - respond_unsupported(client, attr); - valid = 0; - } - - col = ippGetCollection(attr, 0); - - if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(member) != 1 || - (ippGetValueTag(member) != IPP_TAG_NAME && - ippGetValueTag(member) != IPP_TAG_NAMELANG && - ippGetValueTag(member) != IPP_TAG_KEYWORD)) - { - respond_unsupported(client, attr); - valid = 0; - } - else - { - supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD); - - if (!ippContainsString(supported, ippGetString(member, 0, NULL))) - { - respond_unsupported(client, attr); - valid = 0; - } - } - } - else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL) - { - if (ippGetCount(member) != 1) - { - respond_unsupported(client, attr); - valid = 0; - } - else - { - size = ippGetCollection(member, 0); - - if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 || - (y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1) - { - respond_unsupported(client, attr); - valid = 0; - } - else - { - x_value = ippGetInteger(x_dim, 0); - y_value = ippGetInteger(y_dim, 0); - supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION); - count = ippGetCount(supported); - - for (i = 0; i < count ; i ++) - { - size = ippGetCollection(supported, i); - x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO); - y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO); - - if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value)) - break; - } - - if (i >= count) - { - respond_unsupported(client, attr); - valid = 0; - } - } - } - } - } - - if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || - (strcmp(ippGetString(attr, 0, NULL), - "separate-documents-uncollated-copies") && - strcmp(ippGetString(attr, 0, NULL), - "separate-documents-collated-copies"))) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM || - ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT || - ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL) - { - if (ippGetValueTag(attr) != IPP_TAG_RANGE) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL) - { - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM || - ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT || - ippGetInteger(attr, 0) > IPP_QUALITY_HIGH) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL) - { - supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION); - - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION || - !supported) - { - respond_unsupported(client, attr); - valid = 0; - } - else - { - int xdpi, /* Horizontal resolution for job template attribute */ - ydpi, /* Vertical resolution for job template attribute */ - sydpi; /* Vertical resolution for supported value */ - ipp_res_t units, /* Units for job template attribute */ - sunits; /* Units for supported value */ - - xdpi = ippGetResolution(attr, 0, &ydpi, &units); - count = ippGetCount(supported); - - for (i = 0; i < count; i ++) - { - if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits) - break; - } - - if (i >= count) - { - respond_unsupported(client, attr); - valid = 0; - } - } - } - - if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL) - { - const char *sides = ippGetString(attr, 0, NULL); - /* "sides" value... */ - - if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD) - { - respond_unsupported(client, attr); - valid = 0; - } - else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL) - { - if (!ippContainsString(supported, sides)) - { - respond_unsupported(client, attr); - valid = 0; - } - } - else if (strcmp(sides, "one-sided")) - { - respond_unsupported(client, attr); - valid = 0; - } - } - - return (valid); -} |