/*
- * "$Id$"
+ * "$Id: ippserver.c 13138 2016-03-15 14:59:54Z msweet $"
*
- * Sample IPP/2.0 server for CUPS.
+ * Sample IPP Everywhere server for CUPS.
*
- * Copyright 2010-2014 by Apple Inc.
+ * Copyright 2010-2015 by Apple Inc.
*
* These coded instructions, statements, and computer programs are the
* property of Apple Inc. and are protected by Federal copyright
* Include necessary headers...
*/
-#include <cups/cups.h> /* Public API */
#include <config.h> /* CUPS configuration header */
-#include <cups/string-private.h> /* For string functions */
+#include <cups/cups.h> /* Public API */
+#include <cups/string-private.h> /* CUPS string functions */
#include <cups/thread-private.h> /* For multithreading functions */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
-#include <sys/wait.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 */
-#include <limits.h>
-#include <sys/stat.h>
-#include <sys/fcntl.h>
-#include <poll.h>
#ifdef HAVE_SYS_MOUNT_H
# include <sys/mount.h>
#endif /* HAVE_SYS_MOUNT_H */
* Constants...
*/
-enum _ipp_preasons_e /* printer-state-reasons bit values */
+enum _ipp_preason_e /* printer-state-reasons bit values */
{
- _IPP_PSTATE_NONE = 0x0000, /* none */
- _IPP_PSTATE_OTHER = 0x0001, /* other */
- _IPP_PSTATE_COVER_OPEN = 0x0002, /* cover-open */
- _IPP_PSTATE_INPUT_TRAY_MISSING = 0x0004,
+ _IPP_PREASON_NONE = 0x0000, /* none */
+ _IPP_PREASON_OTHER = 0x0001, /* other */
+ _IPP_PREASON_COVER_OPEN = 0x0002, /* cover-open */
+ _IPP_PREASON_INPUT_TRAY_MISSING = 0x0004,
/* input-tray-missing */
- _IPP_PSTATE_MARKER_SUPPLY_EMPTY = 0x0008,
+ _IPP_PREASON_MARKER_SUPPLY_EMPTY = 0x0008,
/* marker-supply-empty */
- _IPP_PSTATE_MARKER_SUPPLY_LOW = 0x0010,
- /* marker-suply-low */
- _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL = 0x0020,
+ _IPP_PREASON_MARKER_SUPPLY_LOW = 0x0010,
+ /* marker-supply-low */
+ _IPP_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020,
/* marker-waste-almost-full */
- _IPP_PSTATE_MARKER_WASTE_FULL = 0x0040,
+ _IPP_PREASON_MARKER_WASTE_FULL = 0x0040,
/* marker-waste-full */
- _IPP_PSTATE_MEDIA_EMPTY = 0x0080, /* media-empty */
- _IPP_PSTATE_MEDIA_JAM = 0x0100, /* media-jam */
- _IPP_PSTATE_MEDIA_LOW = 0x0200, /* media-low */
- _IPP_PSTATE_MEDIA_NEEDED = 0x0400, /* media-needed */
- _IPP_PSTATE_MOVING_TO_PAUSED = 0x0800,
+ _IPP_PREASON_MEDIA_EMPTY = 0x0080, /* media-empty */
+ _IPP_PREASON_MEDIA_JAM = 0x0100, /* media-jam */
+ _IPP_PREASON_MEDIA_LOW = 0x0200, /* media-low */
+ _IPP_PREASON_MEDIA_NEEDED = 0x0400, /* media-needed */
+ _IPP_PREASON_MOVING_TO_PAUSED = 0x0800,
/* moving-to-paused */
- _IPP_PSTATE_PAUSED = 0x1000, /* paused */
- _IPP_PSTATE_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
- _IPP_PSTATE_TONER_EMPTY = 0x4000, /* toner-empty */
- _IPP_PSTATE_TONER_LOW = 0x8000 /* toner-low */
+ _IPP_PREASON_PAUSED = 0x1000, /* paused */
+ _IPP_PREASON_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
+ _IPP_PREASON_TONER_EMPTY = 0x4000, /* toner-empty */
+ _IPP_PREASON_TONER_LOW = 0x8000 /* toner-low */
+};
+typedef unsigned int _ipp_preason_t; /* Bitfield for printer-state-reasons */
+static const char * const _ipp_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"
};
-typedef unsigned int _ipp_preasons_t; /* Bitfield for printer-state-reasons */
typedef enum _ipp_media_class_e
{
_IPP_ENV_ONLY /* Envelope-only size */
} _ipp_media_class_t;
+typedef enum _ipp_media_size_e
+{
+ _IPP_MEDIA_SIZE_NONE = -1,
+ _IPP_MEDIA_SIZE_A4,
+ _IPP_MEDIA_SIZE_A5,
+ _IPP_MEDIA_SIZE_A6,
+ _IPP_MEDIA_SIZE_DL,
+ _IPP_MEDIA_SIZE_LEGAL,
+ _IPP_MEDIA_SIZE_LETTER,
+ _IPP_MEDIA_SIZE_COM10,
+ _IPP_MEDIA_SIZE_3x5,
+ _IPP_MEDIA_SIZE_L,
+ _IPP_MEDIA_SIZE_4x6,
+ _IPP_MEDIA_SIZE_5x7
+} _ipp_media_size_t;
static const char * const media_supported[] =
{ /* media-supported values */
"iso_a4_210x297mm", /* A4 */
{ 10160, 15240, _IPP_PHOTO_ONLY }, /* 4x6 */
{ 12700, 17780, _IPP_PHOTO_ONLY } /* 5x7 aka 2L */
};
+
+typedef enum _ipp_media_source_e
+{
+ _IPP_MEDIA_SOURCE_NONE = -1,
+ _IPP_MEDIA_SOURCE_AUTO,
+ _IPP_MEDIA_SOURCE_MAIN,
+ _IPP_MEDIA_SOURCE_MANUAL,
+ _IPP_MEDIA_SOURCE_ENVELOPE,
+ _IPP_MEDIA_SOURCE_PHOTO
+} _ipp_media_source_t;
+static const char * const media_source_supported[] =
+ /* media-source-supported values */
+{
+ "auto",
+ "main",
+ "manual",
+ "envelope",
+ "photo"
+};
+
+typedef enum _ipp_media_type_e
+{
+ _IPP_MEDIA_TYPE_NONE = -1,
+ _IPP_MEDIA_TYPE_AUTO,
+ _IPP_MEDIA_TYPE_CARDSTOCK,
+ _IPP_MEDIA_TYPE_ENVELOPE,
+ _IPP_MEDIA_TYPE_LABELS,
+ _IPP_MEDIA_TYPE_OTHER,
+ _IPP_MEDIA_TYPE_GLOSSY,
+ _IPP_MEDIA_TYPE_HIGH_GLOSS,
+ _IPP_MEDIA_TYPE_MATTE,
+ _IPP_MEDIA_TYPE_SATIN,
+ _IPP_MEDIA_TYPE_SEMI_GLOSS,
+ _IPP_MEDIA_TYPE_STATIONERY,
+ _IPP_MEDIA_TYPE_LETTERHEAD,
+ _IPP_MEDIA_TYPE_TRANSPARENCY
+} _ipp_media_type_t;
static const char * const media_type_supported[] =
/* media-type-supported values */
{
"transparency"
};
+typedef enum _ipp_supply_e
+{
+ _IPP_SUPPLY_CYAN, /* Cyan Toner */
+ _IPP_SUPPLY_MAGENTA, /* Magenta Toner */
+ _IPP_SUPPLY_YELLOW, /* Yellow Toner */
+ _IPP_SUPPLY_BLACK, /* Black Toner */
+ _IPP_SUPPLY_WASTE /* Waste Toner */
+} _ipp_supply_t;
+static const char * const printer_supplies[] =
+{ /* printer-supply-description values */
+ "Cyan Toner",
+ "Magenta Toner",
+ "Yellow Toner",
+ "Black Toner",
+ "Toner Waste"
+};
+
+/*
+ * 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 _ipp_srv_t; /* Service reference */
+typedef TXTRecordRef _ipp_txt_t; /* TXT record */
+
+#elif defined(HAVE_AVAHI)
+typedef AvahiEntryGroup *_ipp_srv_t; /* Service reference */
+typedef AvahiStringList *_ipp_txt_t; /* TXT record */
+
+#else
+typedef void *_ipp_srv_t; /* Service reference */
+typedef void *_ipp_txt_t; /* TXT record */
+#endif /* HAVE_DNSSD */
+
+typedef struct _ipp_filter_s /**** Attribute filter ****/
+{
+ cups_array_t *ra; /* Requested attributes */
+ ipp_tag_t group_tag; /* Group to copy */
+} _ipp_filter_t;
+
typedef struct _ipp_job_s _ipp_job_t;
typedef struct _ipp_printer_s /**** Printer data ****/
{
int ipv4, /* IPv4 listener */
ipv6; /* IPv6 listener */
-#ifdef HAVE_DNSSD
- DNSServiceRef common_ref, /* Shared service connection */
- ipp_ref, /* Bonjour IPP service */
-# ifdef HAVE_SSL
+ _ipp_srv_t ipp_ref, /* Bonjour IPP service */
ipps_ref, /* Bonjour IPPS service */
-# endif /* HAVE_SSL */
http_ref, /* Bonjour HTTP service */
printer_ref; /* Bonjour LPD service */
- TXTRecordRef ipp_txt; /* Bonjour IPP TXT record */
- char *dnssd_name; /* printer-dnssd-name */
-#endif /* HAVE_DNSSD */
- char *name, /* printer-name */
+ char *dnssd_name, /* printer-dnssd-name */
+ *name, /* printer-name */
*icon, /* Icon filename */
*directory, /* Spool directory */
*hostname, /* Hostname */
int port; /* Port */
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 */
- _ipp_preasons_t state_reasons; /* printer-state-reasons values */
+ _ipp_preason_t state_reasons; /* printer-state-reasons values */
+ time_t state_time; /* printer-state-change-time */
cups_array_t *jobs; /* Jobs */
_ipp_job_t *active_job; /* Current active/pending job */
int next_job_id; /* Next job-id value */
_cups_rwlock_t rwlock; /* Printer lock */
+ _ipp_media_size_t main_size; /* Ready media */
+ _ipp_media_type_t main_type;
+ int main_level;
+ _ipp_media_size_t envelope_size;
+ int envelope_level;
+ _ipp_media_size_t photo_size;
+ _ipp_media_type_t photo_type;
+ int photo_level;
+ int supplies[5]; /* Supply levels (0-100) */
} _ipp_printer_t;
struct _ipp_job_s /**** Job data ****/
*username, /* job-originating-user-name */
*format; /* document-format */
ipp_jstate_t state; /* job-state value */
- time_t processing, /* time-at-processing value */
+ 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 */
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 */
+ char uri[1024], /* Request URI */
+ *options; /* URI options */
http_addr_t addr; /* Client address */
char hostname[256]; /* Client hostname */
_ipp_printer_t *printer; /* Printer */
_ipp_job_t *job, cups_array_t *ra);
static _ipp_client_t *create_client(_ipp_printer_t *printer, int sock);
static _ipp_job_t *create_job(_ipp_client_t *client);
-static int create_listener(int family, int *port);
-static ipp_t *create_media_col(const char *media, const char *type,
- int width, int length, int margins);
+static int create_listener(int family, int port);
+static ipp_t *create_media_col(const char *media, const char *source, const char *type, int width, int length, int margins);
static ipp_t *create_media_size(int width, int length);
static _ipp_printer_t *create_printer(const char *servername,
const char *name, const char *location,
const char *icon,
const char *docformats, int ppm,
int ppm_color, int duplex, int port,
- int pin,
-#ifdef HAVE_DNSSD
- const char *subtype,
-#endif /* HAVE_DNSSD */
+ int pin, const char *subtype,
const char *directory,
- const char *command);
+ const char *command,
+ const char *attrfile);
static void debug_attributes(const char *title, ipp_t *ipp,
int response);
static void delete_client(_ipp_client_t *client);
static void delete_job(_ipp_job_t *job);
static void delete_printer(_ipp_printer_t *printer);
#ifdef HAVE_DNSSD
-static void dnssd_callback(DNSServiceRef sdRef,
+static void DNSSD_API dnssd_callback(DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
_ipp_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(_ipp_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr);
static _ipp_job_t *find_job(_ipp_client_t *client);
+static ipp_t *get_collection(FILE *fp, const char *filename, int *linenum);
+static char *get_token(FILE *fp, char *buf, int buflen, int *linenum);
static void html_escape(_ipp_client_t *client, const char *s,
size_t slen);
+static void html_footer(_ipp_client_t *client);
+static void html_header(_ipp_client_t *client, const char *title);
static void html_printf(_ipp_client_t *client, const char *format,
...) __attribute__((__format__(__printf__,
2, 3)));
static void ipp_cancel_job(_ipp_client_t *client);
+static void ipp_close_job(_ipp_client_t *client);
static void ipp_create_job(_ipp_client_t *client);
static void ipp_get_job_attributes(_ipp_client_t *client);
static void ipp_get_jobs(_ipp_client_t *client);
static void ipp_get_printer_attributes(_ipp_client_t *client);
+static void ipp_identify_printer(_ipp_client_t *client);
static void ipp_print_job(_ipp_client_t *client);
static void ipp_print_uri(_ipp_client_t *client);
static void ipp_send_document(_ipp_client_t *client);
static void ipp_send_uri(_ipp_client_t *client);
static void ipp_validate_job(_ipp_client_t *client);
+static void load_attributes(const char *filename, ipp_t *attrs);
+static int parse_options(_ipp_client_t *client, cups_option_t **options);
+static void process_attr_message(_ipp_job_t *job, char *message);
static void *process_client(_ipp_client_t *client);
static int process_http(_ipp_client_t *client);
static int process_ipp(_ipp_client_t *client);
static void *process_job(_ipp_job_t *job);
-#ifdef HAVE_DNSSD
-static int register_printer(_ipp_printer_t *printer,
- const char *location, const char *make,
- const char *model, const char *formats,
- const char *adminurl, int color,
- int duplex, const char *regtype);
-#endif /* HAVE_DNSSD */
+static void process_state_message(_ipp_job_t *job, char *message);
+static int register_printer(_ipp_printer_t *printer, const char *location, const char *make, const char *model, const char *formats, const char *adminurl, const char *uuid, int color, int duplex, const char *regtype);
static int respond_http(_ipp_client_t *client, http_status_t code,
const char *content_coding,
const char *type, size_t length);
static void respond_unsupported(_ipp_client_t *client,
ipp_attribute_t *attr);
static void run_printer(_ipp_printer_t *printer);
+static char *time_string(time_t tv, char *buffer, size_t bufsize);
static void usage(int status) __attribute__((noreturn));
static int valid_doc_attributes(_ipp_client_t *client);
static int valid_job_attributes(_ipp_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,
Verbosity = 0;
{
int i; /* Looping var */
const char *opt, /* Current option character */
+ *attrfile = NULL, /* Attributes file */
*command = NULL, /* Command to run with job files */
*servername = NULL, /* Server host name */
*name = NULL, /* Printer name */
*icon = "printer.png", /* Icon file */
*formats = "application/pdf,image/jpeg,image/pwg-raster";
/* Supported formats */
-#ifdef HAVE_DNSSD
+#ifdef HAVE_SSL
+ const char *keypath = NULL; /* Keychain path */
+#endif /* HAVE_SSL */
const char *subtype = "_print"; /* Bonjour service subtype */
-#endif /* HAVE_DNSSD */
- int port = 8631, /* Port number (0 = auto) */
+ int port = 0, /* Port number (0 = auto) */
duplex = 0, /* Duplex mode */
ppm = 10, /* Pages per minute for mono */
ppm_color = 0, /* Pages per minute for color */
pin = 0; /* PIN printing mode? */
- char directory[1024] = ""; /* Spool directory */
+ char directory[1024] = "", /* Spool directory */
+ hostname[1024]; /* Auto-detected hostname */
_ipp_printer_t *printer; /* Printer object */
if (argv[i][0] == '-')
{
for (opt = argv[i] + 1; *opt; opt ++)
+ {
switch (*opt)
{
case '2' : /* -2 (enable 2-sided printing) */
duplex = 1;
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)
pin = 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)
port = atoi(argv[i]);
break;
-#ifdef HAVE_DNSSD
case 'r' : /* -r subtype */
i ++;
if (i >= argc)
usage(1);
subtype = argv[i];
break;
-#endif /* HAVE_DNSSD */
case 's' : /* -s speed[,color-speed] */
i ++;
fprintf(stderr, "Unknown option \"-%c\".\n", *opt);
usage(1);
}
+ }
}
else if (!name)
{
* Apply defaults as needed...
*/
+ if (!servername)
+ servername = httpGetHostname(NULL, hostname, sizeof(hostname));
+
+ if (!port)
+ {
+#ifdef WIN32
+ /*
+ * Windows is almost always used as a single user system, so use a default
+ * port number of 8631.
+ */
+
+ port = 8631;
+
+#else
+ /*
+ * Use 8000 + UID mod 1000 for the default port number...
+ */
+
+ port = 8000 + ((int)getuid() % 1000);
+#endif /* WIN32 */
+
+ fprintf(stderr, "Listening on port %d.\n", port);
+ }
+
if (!directory[0])
{
- snprintf(directory, sizeof(directory), "/tmp/ippserver.%d", (int)getpid());
+ const char *tmpdir; /* Temporary directory */
+
+#ifdef WIN32
+ if ((tmpdir = getenv("TEMP")) == NULL)
+ tmpdir = "C:/TEMP";
+#elif defined(__APPLE__)
+ if ((tmpdir = getenv("TMPDIR")) == NULL)
+ tmpdir = "/private/tmp";
+#else
+ if ((tmpdir = getenv("TMPDIR")) == NULL)
+ tmpdir = "/tmp";
+#endif /* WIN32 */
- if (mkdir(directory, 0777) && errno != EEXIST)
+ snprintf(directory, sizeof(directory), "%s/ippserver.%d", tmpdir, (int)getpid());
+
+ if (mkdir(directory, 0755) && errno != EEXIST)
{
fprintf(stderr, "Unable to create spool directory \"%s\": %s\n",
directory, strerror(errno));
fprintf(stderr, "Using spool directory \"%s\".\n", directory);
}
+#ifdef HAVE_SSL
+ cupsSetServerCredentials(keypath, servername, 1);
+#endif /* HAVE_SSL */
+
+ /*
+ * Initialize Bonjour...
+ */
+
+ dnssd_init();
+
/*
* Create the printer...
*/
if ((printer = create_printer(servername, name, location, make, model, icon,
formats, ppm, ppm_color, duplex, port, pin,
-#ifdef HAVE_DNSSD
- subtype,
-#endif /* HAVE_DNSSD */
- directory, command)) == NULL)
+ subtype, directory, command, attrfile)) == NULL)
return (1);
/*
ipp_tag_t group_tag, /* I - Group to copy */
int quickcopy) /* I - Do a quick copy? */
{
- ipp_attribute_t *fromattr; /* Source attribute */
-
-
- if (!to || !from)
- return;
-
- for (fromattr = ippFirstAttribute(from);
- fromattr;
- fromattr = ippNextAttribute(from))
- {
- /*
- * Filter attributes as needed...
- */
+ _ipp_filter_t filter; /* Filter data */
- ipp_tag_t fromgroup = ippGetGroupTag(fromattr);
- const char *fromname = ippGetName(fromattr);
- if ((group_tag != IPP_TAG_ZERO && fromgroup != group_tag &&
- fromgroup != IPP_TAG_ZERO) || !fromname)
- continue;
+ filter.ra = ra;
+ filter.group_tag = group_tag;
- if (!ra || cupsArrayFind(ra, (void *)fromname))
- ippCopyAttribute(to, fromattr, quickcopy);
- }
+ ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter);
}
{
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));
+ 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", job->state);
+ if (!ra || cupsArrayFind(ra, "job-state-message"))
+ {
+ 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)
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);
+ "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);
+ "time-at-processing", (int)(job->processing - client->printer->start_time));
}
{
_ipp_job_t *job; /* Job */
ipp_attribute_t *attr; /* Job attribute */
- char uri[1024]; /* job-uri value */
+ char uri[1024], /* job-uri value */
+ uuid[64]; /* job-uuid value */
_cupsRWLockWrite(&(client->printer->rwlock));
}
job->printer = client->printer;
- job->attrs = client->request;
+ job->attrs = ippNew();
job->state = IPP_JSTATE_HELD;
job->fd = -1;
- client->request = NULL;
/*
- * Set all but the first two attributes to the job attributes group...
+ * Copy all of the job attributes...
*/
- for (ippFirstAttribute(job->attrs),
- ippNextAttribute(job->attrs),
- attr = ippNextAttribute(job->attrs);
- attr;
- attr = ippNextAttribute(job->attrs))
- ippSetGroupTag(job->attrs, &attr, IPP_TAG_JOB);
+ copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
/*
* Get the requesting-user-name, document format, and priority...
*/
- if ((attr = ippFindAttribute(job->attrs, "requesting-user-name",
- IPP_TAG_NAME)) != NULL)
- ippSetName(job->attrs, &attr, "job-originating-user-name");
- else
- attr = ippAddString(job->attrs, IPP_TAG_JOB,
- IPP_CONST_TAG(IPP_TAG_NAME),
- "job-originating-user-name", NULL, "anonymous");
-
- if (attr)
+ if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
job->username = ippGetString(attr, 0, NULL);
else
job->username = "anonymous";
- if ((attr = ippFindAttribute(job->attrs, "document-format",
- IPP_TAG_MIMETYPE)) != NULL)
- job->format = ippGetString(attr, 0, NULL);
- else
- job->format = "application/octet-stream";
+ 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-printer-uri", NULL,
- client->printer->uri);
- ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
- (int)time(NULL));
+ 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;
}
+/*
+ * 'create_job_filename()' - Create the filename for a document in a job.
+ */
+
+static void create_job_filename(
+ _ipp_printer_t *printer, /* I - Printer */
+ _ipp_job_t *job, /* I - Job */
+ char *fname, /* I - Filename buffer */
+ size_t fnamesize) /* I - Size of filename buffer */
+{
+ char name[256], /* "Safe" filename */
+ *nameptr; /* Pointer into filename */
+ const char *ext, /* Filename extension */
+ *job_name; /* job-name value */
+ ipp_attribute_t *job_name_attr; /* job-name attribute */
+
+
+ /*
+ * Make a name from the job-name attribute...
+ */
+
+ if ((job_name_attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME)) != NULL)
+ job_name = ippGetString(job_name_attr, 0, NULL);
+ else
+ 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++ = '_';
+
+ *nameptr = '\0';
+
+ /*
+ * Figure out the extension...
+ */
+
+ 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 = "ras";
+ 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
+ ext = "prn";
+
+ /*
+ * Create a filename with the job-id, job-name, and document-format (extension)...
+ */
+
+ snprintf(fname, fnamesize, "%s/%d-%s.%s", printer->directory, job->id, name, ext);
+}
+
+
/*
* 'create_listener()' - Create a listener socket.
*/
-static int /* O - Listener socket or -1 on error */
-create_listener(int family, /* I - Address family */
- int *port) /* IO - Port number */
+static int /* O - Listener socket or -1 on error */
+create_listener(int family, /* I - Address family */
+ int port) /* I - Port number */
{
int sock; /* Listener socket */
http_addrlist_t *addrlist; /* Listen address */
char service[255]; /* Service port */
- if (!*port)
- {
- *port = 8000 + (getuid() % 1000);
- fprintf(stderr, "Listening on port %d.\n", *port);
- }
-
- snprintf(service, sizeof(service), "%d", *port);
+ snprintf(service, sizeof(service), "%d", port);
if ((addrlist = httpAddrGetList(NULL, family, service)) == NULL)
return (-1);
- sock = httpAddrListen(&(addrlist->addr), *port);
+ sock = httpAddrListen(&(addrlist->addr), port);
httpAddrFreeList(addrlist);
static ipp_t * /* O - media-col collection */
create_media_col(const char *media, /* I - Media name */
- const char *type, /* I - Nedua type */
+ const char *source, /* I - Media source */
+ const char *type, /* I - Media type */
int width, /* I - x-dimension in 2540ths */
int length, /* I - y-dimension in 2540ths */
int margins) /* I - Value for margins */
char media_key[256]; /* media-key value */
- snprintf(media_key, sizeof(media_key), "%s_%s%s", media, type,
- margins == 0 ? "_borderless" : "");
+ if (type && source)
+ snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, type, margins == 0 ? "_borderless" : "");
+ else if (type)
+ snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, margins == 0 ? "_borderless" : "");
+ else if (source)
+ snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, margins == 0 ? "_borderless" : "");
+ else
+ snprintf(media_key, sizeof(media_key), "%s%s", media, margins == 0 ? "_borderless" : "");
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);
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"media-bottom-margin", margins);
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"media-right-margin", margins);
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"media-top-margin", margins);
- ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type",
- NULL, type);
+ 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);
int duplex, /* I - 1 = duplex, 0 = simplex */
int port, /* I - Port for listeners or 0 for auto */
int pin, /* I - Require PIN printing */
-#ifdef HAVE_DNSSD
const char *subtype, /* I - Bonjour service subtype */
-#endif /* HAVE_DNSSD */
const char *directory, /* I - Spool directory */
- const char *command) /* I - Command to run on job files */
+ const char *command, /* I - Command to run on job files */
+ const char *attrfile) /* I - Attributes file */
{
int i, j; /* Looping vars */
_ipp_printer_t *printer; /* Printer */
- char hostname[256], /* Hostname */
- uri[1024], /* Printer URI */
+#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 */
device_id[1024],/* printer-device-id */
- make_model[128];/* printer-make-and-model */
+ make_model[128],/* printer-make-and-model */
+ uuid[128]; /* printer-uuid */
int num_formats; /* Number of document-format-supported values */
char *defformat, /* document-format-default value */
*formats[100], /* document-format-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_CANCEL_JOB,
IPP_OP_GET_JOB_ATTRIBUTES,
IPP_OP_GET_JOBS,
- IPP_OP_GET_PRINTER_ATTRIBUTES
+ 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 */
{
#endif /* HAVE_LIBZ */
"none"
};
+ static const char * const identify_actions[] =
+ {
+ "display",
+ "sound"
+ };
static const char * const job_creation[] =
{ /* job-creation-attributes-supported values */
"copies",
"media-left-margin",
"media-right-margin",
"media-size",
+ "media-source",
"media-top-margin",
"media-type"
};
"separate-documents-uncollated-copies",
"separate-documents-collated-copies"
};
+ static const char * const overrides[] =
+ { /* overrides-supported */
+ "document-number",
+ "pages"
+ };
+ static const char * const print_color_mode_supported[] =
+ { /* print-color-mode-supported values */
+ "auto",
+ "color",
+ "monochrome"
+ };
static const int print_quality_supported[] =
{ /* print-quality-supported values */
IPP_QUALITY_DRAFT,
static const int pwg_raster_document_resolution_supported[] =
{
150,
- 300,
- 600
+ 300
};
static const char * const pwg_raster_document_type_supported[] =
{
- "black-1",
- "cmyk-8",
- "sgray-8",
- "srgb-8",
- "srgb-16"
+ "black_1",
+ "cmyk_8",
+ "sgray_8",
+ "srgb_8",
+ "srgb_16"
};
static const char * const reference_uri_schemes_supported[] =
{ /* reference-uri-schemes-supported */
"two-sided-long-edge",
"two-sided-short-edge"
};
+ static const char * const urf_supported[] =
+ { /* urf-supported values */
+ "CP1",
+ "IS1-5-7",
+ "MT1-2-3-4-5-6-8-9-10-11-12-13",
+ "RS300",
+ "SRGB24",
+ "V1.4",
+ "W8",
+ "DM1"
+ };
+#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",
};
+#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))
+ {
+ fprintf(stderr, "ippserver: Unable to execute command \"%s\": %s\n", command, strerror(errno));
+ return (NULL);
+ }
+ }
+ else
+ {
+ if (!cupsFileFind(command, getenv("PATH"), 1, path, sizeof(path)))
+ {
+ fprintf(stderr, "ippserver: Unable to find command \"%s\".\n", command);
+ return (NULL);
+ }
+
+ command = path;
+ }
+ }
+#endif /* !WIN32 */
+
/*
* Allocate memory for the printer...
*/
if ((printer = calloc(1, sizeof(_ipp_printer_t))) == NULL)
{
- perror("Unable to allocate memory for printer");
+ perror("ippserver: Unable to allocate memory for printer");
return (NULL);
}
printer->ipv4 = -1;
printer->ipv6 = -1;
printer->name = strdup(name);
-#ifdef HAVE_DNSSD
printer->dnssd_name = strdup(printer->name);
-#endif /* HAVE_DNSSD */
printer->command = command ? strdup(command) : NULL;
printer->directory = strdup(directory);
- printer->hostname = strdup(servername ? servername :
- httpGetHostname(NULL, hostname,
- sizeof(hostname)));
+ printer->hostname = strdup(servername);
printer->port = port;
+ printer->start_time = time(NULL);
+ printer->config_time = printer->start_time;
printer->state = IPP_PSTATE_IDLE;
- printer->state_reasons = _IPP_PSTATE_NONE;
+ printer->state_reasons = _IPP_PREASON_NONE;
+ printer->state_time = printer->start_time;
printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
printer->next_job_id = 1;
- httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
- printer->hostname, printer->port, "/ipp/print");
+ 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 */
+
if (icon)
printer->icon = strdup(icon);
+ printer->main_size = _IPP_MEDIA_SIZE_A4;
+ printer->main_type = _IPP_MEDIA_TYPE_STATIONERY;
+ printer->main_level = 500;
+
+ printer->envelope_size = _IPP_MEDIA_SIZE_NONE;
+ printer->envelope_level = 0;
+
+ printer->photo_size = _IPP_MEDIA_SIZE_NONE;
+ printer->photo_type = _IPP_MEDIA_TYPE_NONE;
+ printer->photo_level = 0;
+
+ printer->supplies[_IPP_SUPPLY_CYAN] = 100;
+ printer->supplies[_IPP_SUPPLY_MAGENTA] = 100;
+ printer->supplies[_IPP_SUPPLY_YELLOW] = 100;
+ printer->supplies[_IPP_SUPPLY_BLACK] = 100;
+ printer->supplies[_IPP_SUPPLY_WASTE] = 0;
+
_cupsRWInit(&(printer->rwlock));
/*
* Create the listener sockets...
*/
- if ((printer->ipv4 = create_listener(AF_INET, &(printer->port))) < 0)
+ if ((printer->ipv4 = create_listener(AF_INET, printer->port)) < 0)
{
perror("Unable to create IPv4 listener");
goto bad_printer;
}
- if ((printer->ipv6 = create_listener(AF_INET6, &(printer->port))) < 0)
+ if ((printer->ipv6 = create_listener(AF_INET6, printer->port)) < 0)
{
perror("Unable to create IPv6 listener");
goto bad_printer;
* Prepare values for the printer attributes...
*/
- httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), "http", NULL,
- printer->hostname, printer->port, "/icon.png");
- httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), "http", NULL,
- printer->hostname, printer->port, "/");
+ 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");
if (Verbosity)
{
fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
+ fprintf(stderr, "printer-supply-info-uri=\"%s\"\n", supplyurl);
fprintf(stderr, "printer-uri=\"%s\"\n", uri);
}
*ptr++ = '\0';
formats[num_formats++] = ptr;
- if (!_cups_strcasecmp(ptr, "application/octet-stream"))
+ if (!strcasecmp(ptr, "application/octet-stream"))
defformat = ptr;
}
prefix = "CMD:";
for (i = 0; i < num_formats; i ++)
{
- if (!_cups_strcasecmp(formats[i], "application/pdf"))
+ if (!strcasecmp(formats[i], "application/pdf"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix);
- else if (!_cups_strcasecmp(formats[i], "application/postscript"))
+ else if (!strcasecmp(formats[i], "application/postscript"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix);
- else if (!_cups_strcasecmp(formats[i], "application/vnd.hp-PCL"))
+ else if (!strcasecmp(formats[i], "application/vnd.hp-PCL"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix);
- else if (!_cups_strcasecmp(formats[i], "image/jpeg"))
+ else if (!strcasecmp(formats[i], "image/jpeg"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix);
- else if (!_cups_strcasecmp(formats[i], "image/png"))
+ else if (!strcasecmp(formats[i], "image/png"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix);
- else if (_cups_strcasecmp(formats[i], "application/octet-stream"))
+ else if (strcasecmp(formats[i], "application/octet-stream"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s%s", prefix, formats[i]);
ptr += strlen(ptr);
prefix = ",";
}
- strlcat(device_id, ";", sizeof(device_id));
+ if (ptr < (device_id + sizeof(device_id) - 1))
+ {
+ *ptr++ = ';';
+ *ptr = '\0';
+ }
/*
* Get the maximum spool size based on the size of the filesystem used for
printer->attrs = ippNew();
+ if (attrfile)
+ load_attributes(attrfile, printer->attrs);
+
/* charset-configured */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_CHARSET),
- "charset-configured", NULL, "utf-8");
+ 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);
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-supported", sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
/* color-supported */
- ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "color-supported",
- ppm_color > 0);
+ if (!ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_ZERO))
+ ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0);
/* compression-supported */
- ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "compression-supported",
- (int)(sizeof(compressions) / sizeof(compressions[0])), NULL,
- compressions);
+ 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);
/* copies-default */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "copies-default", 1);
+ if (!ippFindAttribute(printer->attrs, "copies-default", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
/* copies-supported */
- ippAddRange(printer->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
+ if (!ippFindAttribute(printer->attrs, "copies-supported", IPP_TAG_ZERO))
+ ippAddRange(printer->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
/* document-format-default */
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
"document-format-supported", num_formats, NULL,
(const char * const *)formats);
+ /* document-password-supported */
+ if (!ippFindAttribute(printer->attrs, "document-password-supported", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
+
/* finishings-default */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
- "finishings-default", IPP_FINISHINGS_NONE);
+ if (!ippFindAttribute(printer->attrs, "finishings-default", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
/* finishings-supported */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
- "finishings-supported", IPP_FINISHINGS_NONE);
+ if (!ippFindAttribute(printer->attrs, "finishings-supported", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE);
/* generated-natural-language-supported */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_LANGUAGE),
- "generated-natural-language-supported", NULL, "en");
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "generated-natural-language-supported", NULL, "en");
+
+ /* identify-actions-default */
+ if (!ippFindAttribute(printer->attrs, "identify-actions-default", IPP_TAG_ZERO))
+ ippAddString (printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", NULL, "sound");
+
+ /* identify-actions-supported */
+ if (!ippFindAttribute(printer->attrs, "identify-actions-supported", IPP_TAG_ZERO))
+ 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 */
+ if (!ippFindAttribute(printer->attrs, "ipp-features-supported", IPP_TAG_ZERO))
+ 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 */
- ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "ipp-versions-supported",
- sizeof(versions) / sizeof(versions[0]), NULL, versions);
+ if (!ippFindAttribute(printer->attrs, "ipp-versions-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]), NULL, versions);
+
+ /* job-account-id-default */
+ if (!ippFindAttribute(printer->attrs, "job-account-id-default", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-account-id-default", NULL, "");
/* job-account-id-supported */
- ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-account-id-supported", 1);
+ if (!ippFindAttribute(printer->attrs, "job-account-id-supported", IPP_TAG_ZERO))
+ ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-account-id-supported", 1);
+
+ /* job-accounting-user-id-default */
+ if (!ippFindAttribute(printer->attrs, "job-accounting-user-id-default", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-accounting-user-id-default", NULL, "");
/* job-accounting-user-id-supported */
- ippAddBoolean(printer->attrs, IPP_TAG_PRINTER,
- "job-accounting-user-id-supported", 1);
+ if (!ippFindAttribute(printer->attrs, "job-accounting-user-id-supported", IPP_TAG_ZERO))
+ ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-accounting-user-id-supported", 1);
/* job-creation-attributes-supported */
- ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "job-creation-attributes-supported",
- sizeof(job_creation) / sizeof(job_creation[0]),
- NULL, job_creation);
+ if (!ippFindAttribute(printer->attrs, "job-creation-attributes-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", sizeof(job_creation) / sizeof(job_creation[0]), NULL, job_creation);
+
+ /* 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-password-supported */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "job-password-supported", 4);
+ if (!ippFindAttribute(printer->attrs, "job-password-supported", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-password-supported", 4);
/* job-priority-default */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "job-priority-default", 50);
+ if (!ippFindAttribute(printer->attrs, "job-priority-default", IPP_TAG_ZERO))
+ 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", 100);
+ if (!ippFindAttribute(printer->attrs, "job-priority-supported", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 100);
/* job-sheets-default */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_NAME),
- "job-sheets-default", NULL, "none");
+ if (!ippFindAttribute(printer->attrs, "job-sheets-default", IPP_TAG_ZERO))
+ 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");
+ if (!ippFindAttribute(printer->attrs, "job-sheets-supported", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none");
/* media-bottom-margin-supported */
- ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "media-bottom-margin-supported",
- (int)(sizeof(media_xxx_margin_supported) /
- sizeof(media_xxx_margin_supported[0])),
- media_xxx_margin_supported);
+ if (!ippFindAttribute(printer->attrs, "media-bottom-margin-supported", IPP_TAG_ZERO))
+ ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_xxx_margin_supported) / sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported);
/* media-col-database */
- for (num_database = 0, i = 0;
- i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
- i ++)
- {
- if (media_col_sizes[i][2] == _IPP_ENV_ONLY)
- num_database += 2; /* auto + envelope */
- else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY)
- num_database += 12; /* auto + photographic-* + borderless */
- else
- num_database += (int)(sizeof(media_type_supported) /
- sizeof(media_type_supported[0])) + 6;
- /* All types + borderless */
- }
-
- media_col_database = ippAddCollections(printer->attrs, IPP_TAG_PRINTER,
- "media-col-database", num_database,
- NULL);
- for (media_col_index = 0, i = 0;
- i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
- i ++)
- {
- for (j = 0;
- j < (int)(sizeof(media_type_supported) /
- sizeof(media_type_supported[0]));
- j ++)
+ if (!ippFindAttribute(printer->attrs, "media-col-database", IPP_TAG_ZERO))
+ {
+ for (num_database = 0, i = 0;
+ i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
+ i ++)
{
- if (media_col_sizes[i][2] == _IPP_ENV_ONLY &&
- strcmp(media_type_supported[j], "auto") &&
- strcmp(media_type_supported[j], "envelope"))
- continue;
- else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY &&
- strcmp(media_type_supported[j], "auto") &&
- strncmp(media_type_supported[j], "photographic-", 13))
- continue;
+ if (media_col_sizes[i][2] == _IPP_ENV_ONLY)
+ num_database += 3; /* auto + manual + envelope */
+ else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY)
+ num_database += 6 * 3; /* auto + photographic-* from auto, manual, and photo */
+ else
+ num_database += 2; /* Regular + borderless */
+ }
- ippSetCollection(printer->attrs, &media_col_database, media_col_index,
- create_media_col(media_supported[i],
- media_type_supported[j],
- media_col_sizes[i][0],
- media_col_sizes[i][1],
- media_xxx_margin_supported[1]));
- media_col_index ++;
-
- if (media_col_sizes[i][2] != _IPP_ENV_ONLY &&
- (!strcmp(media_type_supported[j], "auto") ||
- !strncmp(media_type_supported[j], "photographic-", 13)))
+ media_col_database = ippAddCollections(printer->attrs, IPP_TAG_PRINTER, "media-col-database", num_database, NULL);
+ for (media_col_index = 0, i = 0;
+ i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
+ i ++)
+ {
+ switch (media_col_sizes[i][2])
{
- /*
- * Add borderless version for this combination...
- */
+ case _IPP_GENERAL :
+ /*
+ * Regular + borderless for the general class; no source/type
+ * selectors...
+ */
+
+ ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], NULL, NULL, media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[1]));
+ ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], NULL, NULL, media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[0]));
+ break;
+
+ case _IPP_ENV_ONLY :
+ /*
+ * Regular margins for "auto", "manual", and "envelope" sources.
+ */
+
+ ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "auto", "envelope", media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[1]));
+ ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "manual", "envelope", media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[1]));
+ ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "envelope", "envelope", media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[1]));
+ break;
+ case _IPP_PHOTO_ONLY :
+ /*
+ * Photos have specific media types and can only be printed via
+ * the auto, manual, and photo sources...
+ */
+
+ for (j = 0;
+ j < (int)(sizeof(media_type_supported) /
+ sizeof(media_type_supported[0]));
+ j ++)
+ {
+ if (strcmp(media_type_supported[j], "auto") && strncmp(media_type_supported[j], "photographic-", 13))
+ continue;
- ippSetCollection(printer->attrs, &media_col_database, media_col_index,
- create_media_col(media_supported[i],
- media_type_supported[j],
- media_col_sizes[i][0],
- media_col_sizes[i][1],
- media_xxx_margin_supported[0]));
- media_col_index ++;
+ ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "auto", media_type_supported[j], media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[0]));
+ ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "manual", media_type_supported[j], media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[0]));
+ ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "photo", media_type_supported[j], media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[0]));
+ }
+ break;
}
}
}
/* media-col-default */
- media_col_default = create_media_col(media_supported[0],
- media_type_supported[0],
- media_col_sizes[0][0],
- media_col_sizes[0][1],
- media_xxx_margin_supported[1]);
+ if (!ippFindAttribute(printer->attrs, "media-col-default", IPP_TAG_ZERO))
+ {
+ media_col_default = create_media_col(media_supported[0], media_source_supported[0], media_type_supported[0], media_col_sizes[0][0], media_col_sizes[0][1],media_xxx_margin_supported[1]);
- ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-default",
- media_col_default);
- ippDelete(media_col_default);
+ ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-default",
+ media_col_default);
+ ippDelete(media_col_default);
+ }
/* 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);
+ if (!ippFindAttribute(printer->attrs, "media-col-supported", IPP_TAG_ZERO))
+ 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);
/* media-default */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "media-default", NULL, media_supported[0]);
+ if (!ippFindAttribute(printer->attrs, "media-default", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media_supported[0]);
/* media-left-margin-supported */
- ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "media-left-margin-supported",
- (int)(sizeof(media_xxx_margin_supported) /
- sizeof(media_xxx_margin_supported[0])),
- media_xxx_margin_supported);
+ if (!ippFindAttribute(printer->attrs, "media-left-margin-supported", IPP_TAG_ZERO))
+ ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_xxx_margin_supported) / sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported);
/* media-right-margin-supported */
- ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "media-right-margin-supported",
- (int)(sizeof(media_xxx_margin_supported) /
- sizeof(media_xxx_margin_supported[0])),
- media_xxx_margin_supported);
+ if (!ippFindAttribute(printer->attrs, "media-right-margin-supported", IPP_TAG_ZERO))
+ ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_xxx_margin_supported) / sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported);
/* media-supported */
- ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "media-supported",
- (int)(sizeof(media_supported) / sizeof(media_supported[0])),
- NULL, media_supported);
+ if (!ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", (int)(sizeof(media_supported) / sizeof(media_supported[0])), NULL, media_supported);
/* media-size-supported */
- media_size_supported = ippAddCollections(printer->attrs, IPP_TAG_PRINTER,
- "media-size-supported",
- (int)(sizeof(media_col_sizes) /
- sizeof(media_col_sizes[0])),
- NULL);
- for (i = 0;
- i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
- i ++)
- ippSetCollection(printer->attrs, &media_size_supported, i,
- create_media_size(media_col_sizes[i][0],
- media_col_sizes[i][1]));
+ if (!ippFindAttribute(printer->attrs, "media-size-supported", IPP_TAG_ZERO))
+ {
+ media_size_supported = ippAddCollections(printer->attrs, IPP_TAG_PRINTER, "media-size-supported", (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0])), NULL);
+
+ for (i = 0;
+ i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
+ i ++)
+ {
+ ipp_t *size = create_media_size(media_col_sizes[i][0], media_col_sizes[i][1]);
+
+ ippSetCollection(printer->attrs, &media_size_supported, i, size);
+ ippDelete(size);
+ }
+ }
+
+ /* media-source-supported */
+ if (!ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->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 */
- ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "media-top-margin-supported",
- (int)(sizeof(media_xxx_margin_supported) /
- sizeof(media_xxx_margin_supported[0])),
- media_xxx_margin_supported);
+ if (!ippFindAttribute(printer->attrs, "media-top-margin-supported", IPP_TAG_ZERO))
+ ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_xxx_margin_supported) / sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported);
/* media-type-supported */
- ippAddStrings(printer->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);
+ if (!ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->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);
/* 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);
+ 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);
+ 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,
"natural-language-configured", NULL, "en");
/* number-up-default */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "number-up-default", 1);
+ if (!ippFindAttribute(printer->attrs, "number-up-default", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "number-up-default", 1);
/* number-up-supported */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "number-up-supported", 1);
+ if (!ippFindAttribute(printer->attrs, "number-up-supported", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "number-up-supported", 1);
/* operations-supported */
- ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
- "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
+ ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
/* orientation-requested-default */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
- "orientation-requested-default", 0);
+ if (!ippFindAttribute(printer->attrs, "orientation-requested-default", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "orientation-requested-default", 0);
/* orientation-requested-supported */
- ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
- "orientation-requested-supported", 4, orients);
+ if (!ippFindAttribute(printer->attrs, "orientation-requested-supported", IPP_TAG_ZERO))
+ ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", 4, orients);
/* output-bin-default */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "output-bin-default", NULL, "face-down");
+ if (!ippFindAttribute(printer->attrs, "output-bin-default", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
/* output-bin-supported */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "output-bin-supported", NULL, "face-down");
+ if (!ippFindAttribute(printer->attrs, "output-bin-supported", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
+
+ /* overrides-supported */
+ if (!ippFindAttribute(printer->attrs, "overrides-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides) / sizeof(overrides[0])), NULL, overrides);
+
+ /* page-ranges-supported */
+ if (!ippFindAttribute(printer->attrs, "page-ranges-supported", IPP_TAG_ZERO))
+ ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
/* pages-per-minute */
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"pages-per-minute-color", ppm_color);
/* pdl-override-supported */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "pdl-override-supported", NULL, "attempted");
+ if (!ippFindAttribute(printer->attrs, "pdl-override-supported", IPP_TAG_ZERO))
+ 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);
+
+ /* print-color-mode-default */
+ if (!ippFindAttribute(printer->attrs, "print-color-mode-default", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, "auto");
+
+ /* print-color-mode-supported */
+ if (!ippFindAttribute(printer->attrs, "print-color-mode-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->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 */
+ if (!ippFindAttribute(printer->attrs, "print-content-optimize-default", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
+
+ /* print-content-optimize-supported */
+ if (!ippFindAttribute(printer->attrs, "print-content-optimize-supported", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
+
+ /* print-rendering-intent-default */
+ if (!ippFindAttribute(printer->attrs, "print-rendering-intent-default", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
+
+ /* print-rendering-intent-supported */
+ if (!ippFindAttribute(printer->attrs, "print-rendering-intent-supported", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
/* print-quality-default */
- ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
- "print-quality-default", IPP_QUALITY_NORMAL);
+ if (!ippFindAttribute(printer->attrs, "print-quality-default", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
/* print-quality-supported */
- ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
- "print-quality-supported",
- (int)(sizeof(print_quality_supported) /
- sizeof(print_quality_supported[0])),
- print_quality_supported);
+ if (!ippFindAttribute(printer->attrs, "print-quality-supported", IPP_TAG_ZERO))
+ ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
/* printer-device-id */
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"printer-device-id", NULL, device_id);
+ /* 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 */
+ if (!ippFindAttribute(printer->attrs, "printer-geo-location", IPP_TAG_ZERO))
+ ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location", 0);
+
/* 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);
+ 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);
+ 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-make-and-model", NULL, make_model);
/* printer-mandatory-job-attributes */
- if (pin)
+ if (pin && !ippFindAttribute(printer->attrs, "", IPP_TAG_ZERO))
{
static const char * const names[] =
{
+ "job-account-id",
"job-accounting-user-id",
"job-password"
};
}
/* printer-more-info */
- ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
- "printer-more-info", NULL, adminurl);
+ 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);
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
+
+ /* printer-organization */
+ if (!ippFindAttribute(printer->attrs, "printer-organization", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organization", NULL, "Apple Inc.");
+
+ /* printer-organizational-unit */
+ if (!ippFindAttribute(printer->attrs, "printer-organizational-unit", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organizational-unit", NULL, "Printing Engineering");
/* printer-resolution-default */
- ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
- "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
+ if (!ippFindAttribute(printer->attrs, "printer-resolution-default", IPP_TAG_ZERO))
+ ippAddResolution(printer->attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
/* printer-resolution-supported */
- ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
- "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
+ if (!ippFindAttribute(printer->attrs, "printer-resolutions-supported", IPP_TAG_ZERO))
+ ippAddResolution(printer->attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
+
+ /* printer-supply-description */
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])), NULL, printer_supplies);
+
+ /* printer-supply-info-uri */
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, supplyurl);
/* printer-uri-supported */
- ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
- "printer-uri-supported", NULL, uri);
+#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 */
+ httpAssembleUUID(printer->hostname, port, name, 0, uuid, sizeof(uuid));
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid);
/* pwg-raster-document-xxx-supported */
for (i = 0; i < num_formats; i ++)
- if (!_cups_strcasecmp(formats[i], "image/pwg-raster"))
+ if (!strcasecmp(formats[i], "image/pwg-raster"))
break;
if (i < num_formats)
{
- ippAddResolutions(printer->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);
- ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
- "pwg-raster-document-sheet-back", NULL, "normal");
- ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 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);
+ if (!ippFindAttribute(printer->attrs, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO))
+ ippAddResolutions(printer->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 (!ippFindAttribute(printer->attrs, "pwg-raster-document-sheet-back", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "pwg-raster-document-sheet-back", NULL, "normal");
+ if (!ippFindAttribute(printer->attrs, "pwg-raster-document-type-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 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);
}
/* 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);
+ 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);
/* sides-default */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "sides-default", NULL, "one-sided");
+ if (!ippFindAttribute(printer->attrs, "sides-default", IPP_TAG_ZERO))
+ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
/* sides-supported */
- ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "sides-supported", duplex ? 3 : 1, NULL, sides_supported);
+ if (!ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", duplex ? 3 : 1, NULL, sides_supported);
+
+ /* urf-supported */
+ for (i = 0; i < num_formats; i ++)
+ if (!strcasecmp(formats[i], "image/urf"))
+ break;
+
+ if (i < num_formats && !ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_ZERO))
+ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])) - !duplex, NULL, urf_supported);
/* uri-authentication-supported */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "uri-authentication-supported", NULL, "none");
+#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 */
- ippAddString(printer->attrs, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "uri-security-supported", NULL, "none");
+#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);
+ 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);
free(formats[0]);
debug_attributes("Printer", printer->attrs, 0);
-#ifdef HAVE_DNSSD
/*
* Register the printer with Bonjour...
*/
- if (!register_printer(printer, location, make, model, docformats, adminurl,
- ppm_color > 0, duplex, subtype))
+ if (!register_printer(printer, location, make, model, docformats, adminurl, uuid + 9, ppm_color > 0, duplex, subtype))
goto bad_printer;
-#endif /* HAVE_DNSSD */
/*
* Return it!
#if HAVE_DNSSD
if (printer->printer_ref)
DNSServiceRefDeallocate(printer->printer_ref);
-
if (printer->ipp_ref)
DNSServiceRefDeallocate(printer->ipp_ref);
-
-# ifdef HAVE_SSL
if (printer->ipps_ref)
DNSServiceRefDeallocate(printer->ipps_ref);
-# endif /* HAVE_SSL */
if (printer->http_ref)
DNSServiceRefDeallocate(printer->http_ref);
+#elif defined(HAVE_AVAHI)
+ avahi_threaded_poll_lock(DNSSDMaster);
- if (printer->common_ref)
- DNSServiceRefDeallocate(printer->common_ref);
+ 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);
- TXTRecordDeallocate(&(printer->ipp_txt));
+ avahi_threaded_poll_unlock(DNSSDMaster);
+#endif /* HAVE_DNSSD */
if (printer->dnssd_name)
free(printer->dnssd_name);
-#endif /* HAVE_DNSSD */
-
if (printer->name)
free(printer->name);
if (printer->icon)
* 'dnssd_callback()' - Handle Bonjour registration events.
*/
-static void
+static void DNSSD_API
dnssd_callback(
DNSServiceRef sdRef, /* I - Service reference */
DNSServiceFlags flags, /* I - Status flags */
regtype, (int)errorCode);
return;
}
- else if (_cups_strcasecmp(name, printer->dnssd_name))
+ else if (strcasecmp(name, printer->dnssd_name))
{
if (Verbosity)
fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", 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, "Ignore 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(_ipp_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.
*/
*job; /* Matching job, if any */
- key.id = 0;
-
- if ((attr = ippFindAttribute(client->request, "job-uri",
- IPP_TAG_URI)) != NULL)
+ 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)
+ else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
key.id = ippGetInteger(attr, 0);
_cupsRWLockRead(&(client->printer->rwlock));
}
+/*
+ * 'get_collection()' - Get a collection value from a file.
+ */
+
+static ipp_t * /* O - Collection value */
+get_collection(FILE *fp, /* I - File to read from */
+ const char *filename, /* I - Attributes filename */
+ int *linenum) /* IO - Line number */
+{
+ char token[1024], /* Token from file */
+ attr[128]; /* Attribute name */
+ ipp_tag_t value; /* Current value type */
+ ipp_t *col = ippNew(); /* Collection value */
+ ipp_attribute_t *lastcol = NULL; /* Last collection attribute */
+
+
+ while (get_token(fp, token, sizeof(token), linenum) != NULL)
+ {
+ if (!strcmp(token, "}"))
+ break;
+ else if (!strcmp(token, "{") && lastcol)
+ {
+ /*
+ * Another collection value
+ */
+
+ ipp_t *subcol = get_collection(fp, filename, linenum);
+ /* Collection value */
+
+ if (subcol)
+ ippSetCollection(col, &lastcol, ippGetCount(lastcol), subcol);
+ else
+ goto col_error;
+ }
+ else if (!_cups_strcasecmp(token, "MEMBER"))
+ {
+ /*
+ * Attribute...
+ */
+
+ lastcol = NULL;
+
+ if (!get_token(fp, token, sizeof(token), linenum))
+ {
+ fprintf(stderr, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum, filename);
+ goto col_error;
+ }
+
+ if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
+ {
+ fprintf(stderr, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token, *linenum, filename);
+ goto col_error;
+ }
+
+ if (!get_token(fp, attr, sizeof(attr), linenum))
+ {
+ fprintf(stderr, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum, filename);
+ goto col_error;
+ }
+
+ if (!get_token(fp, token, sizeof(token), linenum))
+ {
+ fprintf(stderr, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum, filename);
+ goto col_error;
+ }
+
+ switch (value)
+ {
+ case IPP_TAG_BOOLEAN :
+ if (!_cups_strcasecmp(token, "true"))
+ ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
+ else
+ ippAddBoolean(col, IPP_TAG_ZERO, attr, (char)atoi(token));
+ break;
+
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ {
+ int xres, /* X resolution */
+ yres; /* Y resolution */
+ char units[6]; /* Units */
+
+ if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
+ (_cups_strcasecmp(units, "dpi") &&
+ _cups_strcasecmp(units, "dpc") &&
+ _cups_strcasecmp(units, "dpcm") &&
+ _cups_strcasecmp(units, "other")))
+ {
+ fprintf(stderr, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token, *linenum, filename);
+ goto col_error;
+ }
+
+ if (!_cups_strcasecmp(units, "dpi"))
+ ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_INCH, xres, yres);
+ else if (!_cups_strcasecmp(units, "dpc") ||
+ !_cups_strcasecmp(units, "dpcm"))
+ ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_CM, xres, yres);
+ else
+ ippAddResolution(col, IPP_TAG_ZERO, attr, (ipp_res_t)0, xres, yres);
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ {
+ int lowers[4], /* Lower value */
+ uppers[4], /* Upper values */
+ num_vals; /* Number of values */
+
+
+ num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
+ lowers + 0, uppers + 0,
+ lowers + 1, uppers + 1,
+ lowers + 2, uppers + 2,
+ lowers + 3, uppers + 3);
+
+ if ((num_vals & 1) || num_vals == 0)
+ {
+ fprintf(stderr, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token, *linenum, filename);
+ goto col_error;
+ }
+
+ ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
+ uppers);
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ if (!strcmp(token, "{"))
+ {
+ ipp_t *subcol = get_collection(fp, filename, linenum);
+ /* Collection value */
+
+ if (subcol)
+ {
+ lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
+ ippDelete(subcol);
+ }
+ else
+ goto col_error;
+ }
+ else
+ {
+ fprintf(stderr, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum, filename);
+ goto col_error;
+ }
+ break;
+ case IPP_TAG_STRING :
+ ippAddOctetString(col, IPP_TAG_ZERO, attr, token, (int)strlen(token));
+ break;
+
+ default :
+ if (!strchr(token, ','))
+ ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
+ else
+ {
+ /*
+ * Multiple string values...
+ */
+
+ int num_values; /* Number of values */
+ char *values[100], /* Values */
+ *ptr; /* Pointer to next value */
+
+
+ values[0] = token;
+ num_values = 1;
+
+ for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
+ {
+ *ptr++ = '\0';
+ values[num_values] = ptr;
+ num_values ++;
+ if (num_values >= (int)(sizeof(values) / sizeof(values[0])))
+ break;
+ }
+
+ ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
+ NULL, (const char **)values);
+ }
+ break;
+ }
+ }
+ }
+
+ return (col);
+
+ /*
+ * If we get here there was a parse error; free memory and return.
+ */
+
+ col_error:
+
+ ippDelete(col);
+
+ return (NULL);
+}
+
+
+/*
+ * 'get_token()' - Get a token from a file.
+ */
+
+static char * /* O - Token from file or NULL on EOF */
+get_token(FILE *fp, /* I - File to read from */
+ char *buf, /* I - Buffer to read into */
+ int buflen, /* I - Length of buffer */
+ int *linenum) /* IO - Current line number */
+{
+ int ch, /* Character from file */
+ quote; /* Quoting character */
+ char *bufptr, /* Pointer into buffer */
+ *bufend; /* End of buffer */
+
+
+ for (;;)
+ {
+ /*
+ * Skip whitespace...
+ */
+
+ while (isspace(ch = getc(fp)))
+ {
+ if (ch == '\n')
+ (*linenum) ++;
+ }
+
+ /*
+ * Read a token...
+ */
+
+ if (ch == EOF)
+ return (NULL);
+ else if (ch == '\'' || ch == '\"')
+ {
+ /*
+ * Quoted text or regular expression...
+ */
+
+ quote = ch;
+ bufptr = buf;
+ bufend = buf + buflen - 1;
+
+ while ((ch = getc(fp)) != EOF)
+ {
+ if (ch == '\\')
+ {
+ /*
+ * Escape next character...
+ */
+
+ if (bufptr < bufend)
+ *bufptr++ = (char)ch;
+
+ if ((ch = getc(fp)) != EOF && bufptr < bufend)
+ *bufptr++ = (char)ch;
+ }
+ else if (ch == quote)
+ break;
+ else if (bufptr < bufend)
+ *bufptr++ = (char)ch;
+ }
+
+ *bufptr = '\0';
+
+ return (buf);
+ }
+ else if (ch == '#')
+ {
+ /*
+ * Comment...
+ */
+
+ while ((ch = getc(fp)) != EOF)
+ if (ch == '\n')
+ break;
+
+ (*linenum) ++;
+ }
+ else if (ch == '{' || ch == '}' || ch == ',')
+ {
+ buf[0] = (char)ch;
+ buf[1] = '\0';
+
+ return (buf);
+ }
+ else
+ {
+ /*
+ * Whitespace delimited text...
+ */
+
+ ungetc(ch, fp);
+
+ bufptr = buf;
+ bufend = buf + buflen - 1;
+
+ while ((ch = getc(fp)) != EOF)
+ if (isspace(ch) || ch == '#')
+ break;
+ else if (bufptr < bufend)
+ *bufptr++ = (char)ch;
+
+ if (ch == '#')
+ ungetc(ch, fp);
+ else if (ch == '\n')
+ (*linenum) ++;
+
+ *bufptr = '\0';
+
+ return (buf);
+ }
+ }
+}
+
+
/*
* 'html_escape()' - Write a HTML-safe string.
*/
}
+/*
+ * 'html_footer()' - Show the web interface footer.
+ *
+ * This function also writes the trailing 0-length chunk.
+ */
+
+static void
+html_footer(_ipp_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(_ipp_client_t *client, /* I - Client */
+ const char *title) /* I - Title */
+{
+ 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"
+ "<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"
+ "blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
+ "table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
+ "table.form td, table.form th { padding: 5px 2px; width: 50%%; }\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", title, !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.
*/
{
httpWrite2(client->http, "%", 1);
format ++;
+ start = format;
continue;
}
else if (strchr(" -+#\'", *format))
}
+/*
+ * 'ipp_close_job()' - Close an open job.
+ */
+
+static void
+ipp_close_job(_ipp_client_t *client) /* I - Client */
+{
+ _ipp_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.
*/
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");
(job_comparison > 0 && job->state < job_state) ||
job->id < first_job_id ||
(username && job->username &&
- _cups_strcasecmp(username, job->username)))
+ strcasecmp(username, job->username)))
continue;
if (count > 0)
copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
IPP_TAG_CUPS_CONST);
+ if (!ra || cupsArrayFind(ra, "media-col-ready"))
+ {
+ int i, /* Looping var */
+ num_ready = 0; /* Number of ready media */
+ ipp_t *ready[3]; /* Ready media */
+
+ if (printer->main_size != _IPP_MEDIA_SIZE_NONE)
+ {
+ if (printer->main_type != _IPP_MEDIA_TYPE_NONE)
+ ready[num_ready ++] = create_media_col(media_supported[printer->main_size], "main", media_type_supported[printer->main_type], media_col_sizes[printer->main_size][0], media_col_sizes[printer->main_size][1], 635);
+ else
+ ready[num_ready ++] = create_media_col(media_supported[printer->main_size], "main", NULL, media_col_sizes[printer->main_size][0], media_col_sizes[printer->main_size][1], 635);
+ }
+ if (printer->envelope_size != _IPP_MEDIA_SIZE_NONE)
+ ready[num_ready ++] = create_media_col(media_supported[printer->envelope_size], "envelope", NULL, media_col_sizes[printer->envelope_size][0], media_col_sizes[printer->envelope_size][1], 635);
+ if (printer->photo_size != _IPP_MEDIA_SIZE_NONE)
+ {
+ if (printer->photo_type != _IPP_MEDIA_TYPE_NONE)
+ ready[num_ready ++] = create_media_col(media_supported[printer->photo_size], "photo", media_type_supported[printer->photo_type], media_col_sizes[printer->photo_size][0], media_col_sizes[printer->photo_size][1], 0);
+ else
+ ready[num_ready ++] = create_media_col(media_supported[printer->photo_size], "photo", NULL, media_col_sizes[printer->photo_size][0], media_col_sizes[printer->photo_size][1], 0);
+ }
+
+ if (num_ready)
+ {
+ ippAddCollections(client->response, IPP_TAG_PRINTER, "media-col-ready", num_ready, (const ipp_t **)ready);
+ for (i = 0; i < num_ready; i ++)
+ ippDelete(ready[i]);
+ }
+ else
+ ippAddOutOfBand(client->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
+ }
+
+ if (!ra || cupsArrayFind(ra, "media-ready"))
+ {
+ int num_ready = 0; /* Number of ready media */
+ const char *ready[3]; /* Ready media */
+
+ if (printer->main_size != _IPP_MEDIA_SIZE_NONE)
+ ready[num_ready ++] = media_supported[printer->main_size];
+
+ if (printer->envelope_size != _IPP_MEDIA_SIZE_NONE)
+ ready[num_ready ++] = media_supported[printer->envelope_size];
+
+ if (printer->photo_size != _IPP_MEDIA_SIZE_NONE)
+ ready[num_ready ++] = media_supported[printer->photo_size];
+
+ if (num_ready)
+ ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-ready", num_ready, NULL, ready);
+ else
+ ippAddOutOfBand(client->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
+ }
+
+ 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", 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 == _IPP_PSTATE_NONE)
+ if (printer->state_reasons == _IPP_PREASON_NONE)
ippAddString(client->response, IPP_TAG_PRINTER,
IPP_CONST_TAG(IPP_TAG_KEYWORD),
"printer-state-reasons", NULL, "none");
else
{
- int num_reasons = 0;/* Number of reasons */
- const char *reasons[32]; /* Reason strings */
-
- if (printer->state_reasons & _IPP_PSTATE_OTHER)
- reasons[num_reasons ++] = "other";
- if (printer->state_reasons & _IPP_PSTATE_COVER_OPEN)
- reasons[num_reasons ++] = "cover-open";
- if (printer->state_reasons & _IPP_PSTATE_INPUT_TRAY_MISSING)
- reasons[num_reasons ++] = "input-tray-missing";
- if (printer->state_reasons & _IPP_PSTATE_MARKER_SUPPLY_EMPTY)
- reasons[num_reasons ++] = "marker-supply-empty-warning";
- if (printer->state_reasons & _IPP_PSTATE_MARKER_SUPPLY_LOW)
- reasons[num_reasons ++] = "marker-supply-low-report";
- if (printer->state_reasons & _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL)
- reasons[num_reasons ++] = "marker-waste-almost-full-report";
- if (printer->state_reasons & _IPP_PSTATE_MARKER_WASTE_FULL)
- reasons[num_reasons ++] = "marker-waste-full-warning";
- if (printer->state_reasons & _IPP_PSTATE_MEDIA_EMPTY)
- reasons[num_reasons ++] = "media-empty-warning";
- if (printer->state_reasons & _IPP_PSTATE_MEDIA_JAM)
- reasons[num_reasons ++] = "media-jam-warning";
- if (printer->state_reasons & _IPP_PSTATE_MEDIA_LOW)
- reasons[num_reasons ++] = "media-low-report";
- if (printer->state_reasons & _IPP_PSTATE_MEDIA_NEEDED)
- reasons[num_reasons ++] = "media-needed-report";
- if (printer->state_reasons & _IPP_PSTATE_MOVING_TO_PAUSED)
- reasons[num_reasons ++] = "moving-to-paused";
- if (printer->state_reasons & _IPP_PSTATE_PAUSED)
- reasons[num_reasons ++] = "paused";
- if (printer->state_reasons & _IPP_PSTATE_SPOOL_AREA_FULL)
- reasons[num_reasons ++] = "spool-area-full";
- if (printer->state_reasons & _IPP_PSTATE_TONER_EMPTY)
- reasons[num_reasons ++] = "toner-empty-warning";
- if (printer->state_reasons & _IPP_PSTATE_TONER_LOW)
- reasons[num_reasons ++] = "toner-low-report";
-
- ippAddStrings(client->response, IPP_TAG_PRINTER,
- IPP_CONST_TAG(IPP_TAG_KEYWORD),
- "printer-state-reasons", num_reasons, NULL, reasons);
+ ipp_attribute_t *attr = NULL; /* printer-state-reasons */
+ _ipp_preason_t bit; /* Reason bit */
+ int i; /* Looping var */
+ char reason[32]; /* Reason string */
+
+ for (i = 0, bit = 1; i < (int)(sizeof(_ipp_preason_strings) / sizeof(_ipp_preason_strings[0])); i ++, bit *= 2)
+ {
+ if (printer->state_reasons & bit)
+ {
+ snprintf(reason, sizeof(reason), "%s-%s", _ipp_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-supply"))
+ {
+ int i; /* Looping var */
+ char buffer[256]; /* Supply value buffer */
+ ipp_attribute_t *attr = NULL; /* Attribute */
+ static const char * const colorants[] = { "cyan", "magenta", "yellow", "black", "unknown" };
+
+ for (i = 0; i < 5; i ++)
+ {
+ snprintf(buffer, sizeof(buffer), "index=%d;class=%s;type=%s;unit=percent;maxcapacity=100;level=%d;colorantname=%s;", i + 1, i < 4 ? "supplyThatIsConsumed" : "receptacleThatIsFilled", i < 4 ? "toner" : "wasteToner", printer->supplies[i], colorants[i]);
+
+ if (!attr)
+ attr = ippAddOctetString(client->response, IPP_TAG_PRINTER, "printer-supply", buffer, (int)strlen(buffer));
+ else
+ ippSetOctetString(client->response, &attr, i, buffer, (int)strlen(buffer));
}
}
if (!ra || cupsArrayFind(ra, "printer-up-time"))
- ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
- "printer-up-time", (int)time(NULL));
+ 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);
+ "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
_cupsRWUnlock(&(printer->rwlock));
}
+/*
+ * 'ipp_identify_printer()' - Beep or display a message.
+ */
+
+static void
+ipp_identify_printer(
+ _ipp_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.
*/
* Create a file for the request data...
*/
- if (!_cups_strcasecmp(job->format, "image/jpeg"))
- snprintf(filename, sizeof(filename), "%s/%d.jpg",
- client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "image/png"))
- snprintf(filename, sizeof(filename), "%s/%d.png",
- client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "image/pwg-raster"))
- snprintf(filename, sizeof(filename), "%s/%d.ras",
- client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "application/pdf"))
- snprintf(filename, sizeof(filename), "%s/%d.pdf",
- client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "application/postscript"))
- snprintf(filename, sizeof(filename), "%s/%d.ps",
- client->printer->directory, job->id);
- else
- snprintf(filename, sizeof(filename), "%s/%d.prn",
- client->printer->directory, job->id);
+ create_job_filename(client->printer, job, filename, sizeof(filename));
if (Verbosity)
fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
* Process the job...
*/
-#if 0
if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
{
job->state = IPP_JSTATE_ABORTED;
return;
}
-#else
- process_job(job);
-#endif /* 0 */
-
/*
* Return the job info...
*/
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");
* Create a file for the request data...
*/
- if (!_cups_strcasecmp(job->format, "image/jpeg"))
+ if (!strcasecmp(job->format, "image/jpeg"))
snprintf(filename, sizeof(filename), "%s/%d.jpg",
client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "image/png"))
+ else if (!strcasecmp(job->format, "image/png"))
snprintf(filename, sizeof(filename), "%s/%d.png",
client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "application/pdf"))
+ else if (!strcasecmp(job->format, "application/pdf"))
snprintf(filename, sizeof(filename), "%s/%d.pdf",
client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "application/postscript"))
+ else if (!strcasecmp(job->format, "application/postscript"))
snprintf(filename, sizeof(filename), "%s/%d.ps",
client->printer->directory, job->id);
else
return;
}
+ copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
+
/*
* Get the document format for the job...
*/
_cupsRWLockWrite(&(client->printer->rwlock));
- if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
+ 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";
- /*
- * Create a file for the request data...
- */
-
- if (!_cups_strcasecmp(job->format, "image/jpeg"))
- snprintf(filename, sizeof(filename), "%s/%d.jpg",
- client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "image/png"))
- snprintf(filename, sizeof(filename), "%s/%d.png",
- client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "application/pdf"))
- snprintf(filename, sizeof(filename), "%s/%d.pdf",
- client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "application/postscript"))
- snprintf(filename, sizeof(filename), "%s/%d.ps",
- client->printer->directory, job->id);
- else
- snprintf(filename, sizeof(filename), "%s/%d.prn",
- client->printer->directory, job->id);
-
+ /*
+ * Create a file for the request data...
+ */
+
+ create_job_filename(client->printer, job, filename, sizeof(filename));
+
if (Verbosity)
fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
* Create a file for the request data...
*/
- if (!_cups_strcasecmp(job->format, "image/jpeg"))
+ if (!strcasecmp(job->format, "image/jpeg"))
snprintf(filename, sizeof(filename), "%s/%d.jpg",
client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "image/png"))
+ else if (!strcasecmp(job->format, "image/png"))
snprintf(filename, sizeof(filename), "%s/%d.png",
client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "application/pdf"))
+ else if (!strcasecmp(job->format, "application/pdf"))
snprintf(filename, sizeof(filename), "%s/%d.pdf",
client->printer->directory, job->id);
- else if (!_cups_strcasecmp(job->format, "application/postscript"))
+ else if (!strcasecmp(job->format, "application/postscript"))
snprintf(filename, sizeof(filename), "%s/%d.ps",
client->printer->directory, job->id);
else
}
+/*
+ * 'load_attributes()' - Load printer attributes from a file.
+ *
+ * Syntax is based on ipptool format:
+ *
+ * ATTR value-tag name value
+ */
+
+static void
+load_attributes(const char *filename, /* I - File to load */
+ ipp_t *attrs) /* I - Printer attributes */
+{
+ int linenum = 0; /* Current line number */
+ FILE *fp = NULL; /* Test file */
+ char attr[128], /* Attribute name */
+ token[1024], /* Token from file */
+ *tokenptr; /* Pointer into token */
+ ipp_tag_t value; /* Current value type */
+ ipp_attribute_t *attrptr, /* Attribute pointer */
+ *lastcol = NULL; /* Last collection attribute */
+
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ {
+ fprintf(stderr, "ippserver: Unable to open \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+
+ while (get_token(fp, token, sizeof(token), &linenum) != NULL)
+ {
+ if (!_cups_strcasecmp(token, "ATTR"))
+ {
+ /*
+ * Attribute...
+ */
+
+ if (!get_token(fp, token, sizeof(token), &linenum))
+ {
+ fprintf(stderr, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum, filename);
+ exit(1);
+ }
+
+ if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
+ {
+ fprintf(stderr, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
+ exit(1);
+ }
+
+ if (!get_token(fp, attr, sizeof(attr), &linenum))
+ {
+ fprintf(stderr, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum, filename);
+ exit(1);
+ }
+
+ if (!get_token(fp, token, sizeof(token), &linenum))
+ {
+ fprintf(stderr, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum, filename);
+ exit(1);
+ }
+
+ attrptr = NULL;
+
+ switch (value)
+ {
+ case IPP_TAG_BOOLEAN :
+ if (!_cups_strcasecmp(token, "true"))
+ attrptr = ippAddBoolean(attrs, IPP_TAG_PRINTER, attr, 1);
+ else
+ attrptr = ippAddBoolean(attrs, IPP_TAG_PRINTER, attr, (char)atoi(token));
+ break;
+
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ if (!strchr(token, ','))
+ attrptr = ippAddInteger(attrs, IPP_TAG_PRINTER, value, attr, (int)strtol(token, &tokenptr, 0));
+ else
+ {
+ int values[100], /* Values */
+ num_values = 1; /* Number of values */
+
+ values[0] = (int)strtol(token, &tokenptr, 10);
+ while (tokenptr && *tokenptr &&
+ num_values < (int)(sizeof(values) / sizeof(values[0])))
+ {
+ if (*tokenptr == ',')
+ tokenptr ++;
+ else if (!isdigit(*tokenptr & 255) && *tokenptr != '-')
+ break;
+
+ values[num_values] = (int)strtol(tokenptr, &tokenptr, 0);
+ num_values ++;
+ }
+
+ attrptr = ippAddIntegers(attrs, IPP_TAG_PRINTER, value, attr, num_values, values);
+ }
+
+ if (!tokenptr || *tokenptr)
+ {
+ fprintf(stderr, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value), token, linenum, filename);
+ exit(1);
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ {
+ int xres, /* X resolution */
+ yres; /* Y resolution */
+ ipp_res_t units; /* Units */
+ char *start, /* Start of value */
+ *ptr, /* Pointer into value */
+ *next = NULL; /* Next value */
+
+ for (start = token; start; start = next)
+ {
+ xres = yres = (int)strtol(start, (char **)&ptr, 10);
+ if (ptr > start && xres > 0)
+ {
+ if (*ptr == 'x')
+ yres = (int)strtol(ptr + 1, (char **)&ptr, 10);
+ }
+
+ if (ptr && (next = strchr(ptr, ',')) != NULL)
+ *next++ = '\0';
+
+ if (ptr <= start || xres <= 0 || yres <= 0 || !ptr ||
+ (_cups_strcasecmp(ptr, "dpi") &&
+ _cups_strcasecmp(ptr, "dpc") &&
+ _cups_strcasecmp(ptr, "dpcm") &&
+ _cups_strcasecmp(ptr, "other")))
+ {
+ fprintf(stderr, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
+ exit(1);
+ }
+
+ if (!_cups_strcasecmp(ptr, "dpc") || !_cups_strcasecmp(ptr, "dpcm"))
+ units = IPP_RES_PER_CM;
+ else
+ units = IPP_RES_PER_INCH;
+
+ if (attrptr)
+ ippSetResolution(attrs, &attrptr, ippGetCount(attrptr), units, xres, yres);
+ else
+ attrptr = ippAddResolution(attrs, IPP_TAG_PRINTER, attr, units, xres, yres);
+ }
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ {
+ int lowers[4], /* Lower value */
+ uppers[4], /* Upper values */
+ num_vals; /* Number of values */
+
+
+ num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
+ lowers + 0, uppers + 0,
+ lowers + 1, uppers + 1,
+ lowers + 2, uppers + 2,
+ lowers + 3, uppers + 3);
+
+ if ((num_vals & 1) || num_vals == 0)
+ {
+ fprintf(stderr, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token, linenum, filename);
+ exit(1);
+ }
+
+ attrptr = ippAddRanges(attrs, IPP_TAG_PRINTER, attr, num_vals / 2, lowers,
+ uppers);
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ if (!strcmp(token, "{"))
+ {
+ ipp_t *col = get_collection(fp, filename, &linenum);
+ /* Collection value */
+
+ if (col)
+ {
+ attrptr = lastcol = ippAddCollection(attrs, IPP_TAG_PRINTER, attr, col);
+ ippDelete(col);
+ }
+ else
+ exit(1);
+ }
+ else
+ {
+ fprintf(stderr, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum, filename);
+ exit(1);
+ }
+
+ do
+ {
+ ipp_t *col; /* Collection value */
+ long pos = ftell(fp); /* Save position of file */
+
+ if (!get_token(fp, token, sizeof(token), &linenum))
+ break;
+
+ if (strcmp(token, ","))
+ {
+ fseek(fp, pos, SEEK_SET);
+ break;
+ }
+
+ if (!get_token(fp, token, sizeof(token), &linenum) || strcmp(token, "{"))
+ {
+ fprintf(stderr, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
+ exit(1);
+ }
+
+ if ((col = get_collection(fp, filename, &linenum)) == NULL)
+ break;
+
+ ippSetCollection(attrs, &attrptr, ippGetCount(attrptr), col);
+ lastcol = attrptr;
+ }
+ while (!strcmp(token, "{"));
+ break;
+
+ case IPP_TAG_STRING :
+ attrptr = ippAddOctetString(attrs, IPP_TAG_PRINTER, attr, token, (int)strlen(token));
+ break;
+
+ default :
+ fprintf(stderr, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value), linenum, filename);
+ exit(1);
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ if (!strchr(token, ','))
+ attrptr = ippAddString(attrs, IPP_TAG_PRINTER, value, attr, NULL, token);
+ else
+ {
+ /*
+ * Multiple string values...
+ */
+
+ int num_values; /* Number of values */
+ char *values[100], /* Values */
+ *ptr; /* Pointer to next value */
+
+
+ values[0] = token;
+ num_values = 1;
+
+ for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
+ {
+ if (ptr > token && ptr[-1] == '\\')
+ _cups_strcpy(ptr - 1, ptr);
+ else
+ {
+ *ptr++ = '\0';
+ values[num_values] = ptr;
+ num_values ++;
+ if (num_values >= (int)(sizeof(values) / sizeof(values[0])))
+ break;
+ }
+ }
+
+ attrptr = ippAddStrings(attrs, IPP_TAG_PRINTER, value, attr, num_values, NULL, (const char **)values);
+ }
+ break;
+ }
+
+ if (!attrptr)
+ {
+ fprintf(stderr, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum, filename, cupsLastErrorString());
+ exit(1);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
+ exit(1);
+ }
+ }
+
+ fclose(fp);
+}
+
+
+/*
+ * '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(_ipp_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(
+ _ipp_job_t *job, /* I - Job */
+ char *message) /* I - Message */
+{
+ (void)job;
+ (void)message;
+}
+
+
/*
* 'process_client()' - Process client requests on a thread.
*/
* 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...
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)
+ 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...
*/
* Handle HTTP Upgrade...
*/
- if (!_cups_strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
+ 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);
}
{
case HTTP_STATE_OPTIONS :
/*
- * Do HEAD/OPTIONS command...
+ * 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, "/"))
+ 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));
* Show web status page...
*/
+ _ipp_job_t *job; /* Current job */
+ int i; /* Looping var */
+ _ipp_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"
+ };
+
+ if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
+ return (0);
+
+ html_header(client, client->printer->name);
+ html_printf(client,
+ "<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION ")</b></p>\n"
+ "<p>%s, %d job(s).", client->printer->state == IPP_PSTATE_IDLE ? "Idle" : client->printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(client->printer->jobs));
+ for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
+ if (client->printer->state_reasons & reason)
+ html_printf(client, "\n<br> %s", reasons[i]);
+ html_printf(client, "</p>\n");
+
+ if (cupsArrayCount(client->printer->jobs) > 0)
+ {
+ _cupsRWLockRead(&(client->printer->rwlock));
+
+ html_printf(client, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>When</th></tr></thead><tbody>\n");
+ for (job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs); job; job = (_ipp_job_t *)cupsArrayNext(client->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(&(client->printer->rwlock));
+ }
+ html_footer(client);
+
+ return (1);
+ }
+ else if (!strcmp(client->uri, "/media"))
+ {
+ /*
+ * Show web media page...
+ */
+
+ int i, /* Looping var */
+ num_options; /* Number of form options */
+ cups_option_t *options; /* Form options */
+ static const char * const sizes[] =
+ { /* Size strings */
+ "ISO A4",
+ "ISO A5",
+ "ISO A6",
+ "DL Envelope",
+ "US Legal",
+ "US Letter",
+ "#10 Envelope",
+ "3x5 Photo",
+ "3.5x5 Photo",
+ "4x6 Photo",
+ "5x7 Photo"
+ };
+ static const char * const types[] =
+ /* Type strings */
+ {
+ "Auto",
+ "Cardstock",
+ "Envelope",
+ "Labels",
+ "Other",
+ "Glossy Photo",
+ "High-Gloss Photo",
+ "Matte Photo",
+ "Satin Photo",
+ "Semi-Gloss Photo",
+ "Plain",
+ "Letterhead",
+ "Transparency"
+ };
+ static const int sheets[] = /* Number of sheets */
+ {
+ 250,
+ 100,
+ 25,
+ 5,
+ 0
+ };
+
if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
return (0);
+ html_header(client, client->printer->name);
+
+ if ((num_options = parse_options(client, &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!
+ */
+
+ const char *val; /* Form value */
+
+ if ((val = cupsGetOption("main_size", num_options, options)) != NULL)
+ client->printer->main_size = atoi(val);
+ if ((val = cupsGetOption("main_type", num_options, options)) != NULL)
+ client->printer->main_type = atoi(val);
+ if ((val = cupsGetOption("main_level", num_options, options)) != NULL)
+ client->printer->main_level = atoi(val);
+
+ if ((val = cupsGetOption("envelope_size", num_options, options)) != NULL)
+ client->printer->envelope_size = atoi(val);
+ if ((val = cupsGetOption("envelope_level", num_options, options)) != NULL)
+ client->printer->envelope_level = atoi(val);
+
+ if ((val = cupsGetOption("photo_size", num_options, options)) != NULL)
+ client->printer->photo_size = atoi(val);
+ if ((val = cupsGetOption("photo_type", num_options, options)) != NULL)
+ client->printer->photo_type = atoi(val);
+ if ((val = cupsGetOption("photo_level", num_options, options)) != NULL)
+ client->printer->photo_level = atoi(val);
+
+ if ((client->printer->main_level < 100 && client->printer->main_level > 0) || (client->printer->envelope_level < 25 && client->printer->envelope_level > 0) || (client->printer->photo_level < 25 && client->printer->photo_level > 0))
+ client->printer->state_reasons |= _IPP_PREASON_MEDIA_LOW;
+ else
+ client->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_LOW;
+
+ if ((client->printer->main_level == 0 && client->printer->main_size > _IPP_MEDIA_SIZE_NONE) || (client->printer->envelope_level == 0 && client->printer->envelope_size > _IPP_MEDIA_SIZE_NONE) || (client->printer->photo_level == 0 && client->printer->photo_size > _IPP_MEDIA_SIZE_NONE))
+ {
+ client->printer->state_reasons |= _IPP_PREASON_MEDIA_EMPTY;
+ if (client->printer->active_job)
+ client->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
+ }
+ else
+ client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MEDIA_EMPTY | _IPP_PREASON_MEDIA_NEEDED);
+
+ html_printf(client, "<blockquote>Media updated.</blockquote>\n");
+ }
+
+ html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
+
+ html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
+ html_printf(client, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
+ for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
+ if (!strstr(sizes[i], "Envelope") && !strstr(sizes[i], "Photo"))
+ html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_size ? " selected" : "", sizes[i]);
+ html_printf(client, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
+ for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
+ if (!strstr(types[i], "Photo"))
+ html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_type ? " selected" : "", types[i]);
+ html_printf(client, "</select> <select name=\"main_level\">");
+ for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
+ html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->main_level ? " selected" : "", sheets[i]);
+ html_printf(client, "</select></td></tr>\n");
+
+ html_printf(client,
+ "<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
+ for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
+ if (strstr(sizes[i], "Envelope"))
+ html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->envelope_size ? " selected" : "", sizes[i]);
+ html_printf(client, "</select> <select name=\"envelope_level\">");
+ for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
+ html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->envelope_level ? " selected" : "", sheets[i]);
+ html_printf(client, "</select></td></tr>\n");
+
html_printf(client,
- "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
- "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
- "<html>\n"
- "<head>\n"
- "<title>%s</title>\n"
- "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
- "type=\"image/png\">\n"
- "</head>\n"
- "<body>\n"
- "</body>\n"
- "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
- "<p>%s, %d job(s).</p>\n"
- "</body>\n"
- "</html>\n",
- client->printer->name, client->printer->name,
- client->printer->state == IPP_PSTATE_IDLE ? "Idle" :
- client->printer->state == IPP_PSTATE_PROCESSING ?
- "Printing" : "Stopped",
- cupsArrayCount(client->printer->jobs));
- httpWrite2(client->http, "", 0);
+ "<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
+ for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
+ if (strstr(sizes[i], "Photo"))
+ html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_size ? " selected" : "", sizes[i]);
+ html_printf(client, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
+ for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
+ if (strstr(types[i], "Photo"))
+ html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_type ? " selected" : "", types[i]);
+ html_printf(client, "</select> <select name=\"photo_level\">");
+ for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
+ html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->photo_level ? " selected" : "", sheets[i]);
+ html_printf(client, "</select></td></tr>\n");
+
+ html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
+ html_footer(client);
+
+ return (1);
+ }
+ else if (!strcmp(client->uri, "/supplies"))
+ {
+ /*
+ * Show web supplies page...
+ */
+
+ int i, j, /* Looping vars */
+ num_options; /* Number of form options */
+ cups_option_t *options; /* Form options */
+ static const int levels[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
+
+ if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
+ return (0);
+
+ html_header(client, client->printer->name);
+
+ if ((num_options = parse_options(client, &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 */
+
+ client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MARKER_SUPPLY_EMPTY | _IPP_PREASON_MARKER_SUPPLY_LOW | _IPP_PREASON_MARKER_WASTE_ALMOST_FULL | _IPP_PREASON_MARKER_WASTE_FULL | _IPP_PREASON_TONER_EMPTY | _IPP_PREASON_TONER_LOW);
+
+ for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
+ {
+ snprintf(name, sizeof(name), "supply_%d", i);
+ if ((val = cupsGetOption(name, num_options, options)) != NULL)
+ {
+ int level = client->printer->supplies[i] = atoi(val);
+ /* New level */
+
+ if (i < 4)
+ {
+ if (level == 0)
+ client->printer->state_reasons |= _IPP_PREASON_TONER_EMPTY;
+ else if (level < 10)
+ client->printer->state_reasons |= _IPP_PREASON_TONER_LOW;
+ }
+ else
+ {
+ if (level == 100)
+ client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_FULL;
+ else if (level > 90)
+ client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL;
+ }
+ }
+ }
+
+ html_printf(client, "<blockquote>Supplies updated.</blockquote>\n");
+ }
+
+ html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
+
+ html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
+ for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
+ {
+ html_printf(client, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies[i], i);
+ for (j = 0; j < (int)(sizeof(levels) / sizeof(levels[0])); j ++)
+ html_printf(client, "<option value=\"%d\"%s>%d%%</option>", levels[j], levels[j] == client->printer->supplies[i] ? " selected" : "", levels[j]);
+ html_printf(client, "</select></td></tr>\n");
+ }
+ html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
+ html_footer(client);
return (1);
}
uri = NULL;
if (charset &&
- _cups_strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
- _cups_strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
+ strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
+ strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
{
/*
* Bad character set...
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.");
}
-/*
- * 'process_job()' - Process a print job.
- */
+/*
+ * 'process_job()' - Process a print job.
+ */
+
+static void * /* O - Thread exit status */
+process_job(_ipp_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 & _IPP_PREASON_MEDIA_EMPTY)
+ {
+ job->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
+
+ sleep(1);
+ }
+
+ job->printer->state_reasons &= (_ipp_preason_t)~_IPP_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 */
+ time_t start, /* Start time */
+ end; /* End time */
+ char *myargv[3], /* Command-line arguments */
+ *myenvp[200]; /* 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 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, "Running command \"%s %s\".\n", job->printer->command,
+ job->filename);
+ time(&start);
+
+ /*
+ * Setup the command-line arguments...
+ */
+
+ myargv[0] = job->printer->command;
+ myargv[1] = job->filename;
+ myargv[2] = NULL;
+
+ /*
+ * Copy the current environment, then add ENV variables for every Job
+ * attribute...
+ */
+
+ for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
+ myenvp[myenvc] = strdup(environ[myenvc]);
+
+ 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);
+ 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);
-static void * /* O - Thread exit status */
-process_job(_ipp_job_t *job) /* I - Job */
-{
- job->state = IPP_JSTATE_PROCESSING;
- job->printer->state = IPP_PSTATE_PROCESSING;
+ name ++;
+ }
+ *valptr++ = '=';
+ ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
+
+ myenvp[myenvc++] = strdup(val);
+ }
+ myenvp[myenvc] = NULL;
- if (job->printer->command)
- {
/*
- * Execute a command with the job spool file and wait for it to complete...
+ * Now run the program...
*/
- int pid, /* Process ID */
- status; /* Exit status */
- time_t start, /* Start time */
- end; /* End time */
+#ifdef WIN32
+ status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
- fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command,
- job->filename);
- time(&start);
+#else
+ if (pipe(mypipe))
+ {
+ perror("Unable to create pipe for stderr");
+ mypipe[0] = mypipe[1] = -1;
+ }
if ((pid = fork()) == 0)
{
* Child comes here...
*/
- execlp(job->printer->command, job->printer->command, job->filename,
- (void *)NULL);
+ close(2);
+ dup2(mypipe[1], 2);
+ close(mypipe[0]);
+ close(mypipe[1]);
+
+ execve(job->printer->command, myargv, myenvp);
exit(errno);
}
else if (pid < 0)
*/
perror("Unable to start job processing command");
+ status = -1;
+
+ 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]);
+
+ /*
+ * 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)
+ {
+ *ptr++ = '\0';
+
+ if (!strncmp(line, "STATE:", 6))
+ {
+ /*
+ * Process printer-state-reasons keywords.
+ */
+
+ process_state_message(job, line);
+ }
+ else if (!strncmp(line, "ATTR:", 5))
+ {
+ /*
+ * Process printer attribute update.
+ */
+
+ process_attr_message(job, line);
+ }
+ else if (Verbosity > 1)
+ fprintf(stderr, "%s: %s\n", job->printer->command, 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
+# ifdef HAVE_WAITPID
while (waitpid(pid, &status, 0) < 0);
-#else
+# else
while (wait(&status) < 0);
-#endif /* HAVE_WAITPID */
+# endif /* HAVE_WAITPID */
+ }
+#endif /* WIN32 */
- if (status)
- {
- if (WIFEXITED(status))
- fprintf(stderr, "Command \"%s\" exited with status %d.\n",
- job->printer->command, WEXITSTATUS(status));
- else
- fprintf(stderr, "Command \"%s\" terminated with signal %d.\n",
- job->printer->command, WTERMSIG(status));
- }
+ if (status)
+ {
+#ifndef WIN32
+ if (WIFEXITED(status))
+#endif /* !WIN32 */
+ fprintf(stderr, "Command \"%s\" exited with status %d.\n",
+ job->printer->command, WEXITSTATUS(status));
+#ifndef WIN32
else
- fprintf(stderr, "Command \"%s\" completed successfully.\n",
- job->printer->command);
+ fprintf(stderr, "Command \"%s\" terminated with signal %d.\n",
+ job->printer->command, WTERMSIG(status));
+#endif /* !WIN32 */
+ job->state = IPP_JSTATE_ABORTED;
}
+ else if (status < 0)
+ job->state = IPP_JSTATE_ABORTED;
+ else
+ fprintf(stderr, "Command \"%s\" completed successfully.\n",
+ job->printer->command);
/*
* Make sure processing takes at least 5 seconds...
* Sleep for a random amount of time to simulate job processing.
*/
- sleep(5 + (rand() % 11));
+ sleep((unsigned)(5 + (rand() % 11)));
}
if (job->cancel)
job->state = IPP_JSTATE_CANCELED;
- else
+ else if (job->state == IPP_JSTATE_PROCESSING)
job->state = IPP_JSTATE_COMPLETED;
job->completed = time(NULL);
}
-#ifdef HAVE_DNSSD
+/*
+ * 'process_state_message()' - Process a STATE: message from a command.
+ */
+
+static void
+process_state_message(
+ _ipp_job_t *job, /* I - Job */
+ char *message) /* I - Message */
+{
+ int i; /* Looping var */
+ _ipp_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 2911.
+ */
+
+ 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 = _IPP_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(_ipp_preason_strings) / sizeof(_ipp_preason_strings[0])); i ++, bit *= 2)
+ {
+ if (!strcmp(message, _ipp_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.
*/
const char *model, /* I - Model name */
const char *formats, /* I - Supported formats */
const char *adminurl, /* I - Web interface URL */
+ const char *uuid, /* I - Printer UUID */
int color, /* I - 1 = color, 0 = monochrome */
int duplex, /* I - 1 = duplex, 0 = simplex */
const char *subtype) /* I - Service subtype */
{
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+ _ipp_txt_t ipp_txt; /* Bonjour IPP TXT record */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+#ifdef HAVE_DNSSD
DNSServiceErrorType error; /* Error from Bonjour */
char make_model[256],/* Make and model together */
product[256], /* Product string */
snprintf(make_model, sizeof(make_model), "%s %s", make, model);
snprintf(product, sizeof(product), "(%s)", model);
- TXTRecordCreate(&(printer->ipp_txt), 1024, NULL);
- TXTRecordSetValue(&(printer->ipp_txt), "rp", 9, "ipp/print");
- TXTRecordSetValue(&(printer->ipp_txt), "ty", (uint8_t)strlen(make_model),
+ TXTRecordCreate(&ipp_txt, 1024, NULL);
+ TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
+ TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(make_model),
make_model);
- TXTRecordSetValue(&(printer->ipp_txt), "adminurl", (uint8_t)strlen(adminurl),
+ TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(adminurl),
adminurl);
if (*location)
- TXTRecordSetValue(&(printer->ipp_txt), "note", (uint8_t)strlen(location),
+ TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(location),
location);
- TXTRecordSetValue(&(printer->ipp_txt), "product", (uint8_t)strlen(product),
+ TXTRecordSetValue(&ipp_txt, "product", (uint8_t)strlen(product),
product);
- TXTRecordSetValue(&(printer->ipp_txt), "pdl", (uint8_t)strlen(formats),
+ TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats),
formats);
- TXTRecordSetValue(&(printer->ipp_txt), "Color", 1, color ? "T" : "F");
- TXTRecordSetValue(&(printer->ipp_txt), "Duplex", 1, duplex ? "T" : "F");
- TXTRecordSetValue(&(printer->ipp_txt), "usb_MFG", (uint8_t)strlen(make),
+ TXTRecordSetValue(&ipp_txt, "Color", 1, color ? "T" : "F");
+ TXTRecordSetValue(&ipp_txt, "Duplex", 1, duplex ? "T" : "F");
+ TXTRecordSetValue(&ipp_txt, "usb_MFG", (uint8_t)strlen(make),
make);
- TXTRecordSetValue(&(printer->ipp_txt), "usb_MDL", (uint8_t)strlen(model),
+ TXTRecordSetValue(&ipp_txt, "usb_MDL", (uint8_t)strlen(model),
model);
+ TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(uuid), uuid);
+# ifdef HAVE_SSL
+ TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
+# endif /* HAVE_SSL */
+ if (strstr(formats, "image/urf"))
+ TXTRecordSetValue(&ipp_txt, "URF", 66, "CP1,IS1-5-7,MT1-2-3-4-5-6-8-9-10-11-12-13,RS300,SRGB24,V1.4,W8,DM1");
- /*
- * Create a shared service reference for Bonjour...
- */
-
- if ((error = DNSServiceCreateConnection(&(printer->common_ref)))
- != kDNSServiceErr_NoError)
- {
- fprintf(stderr, "Unable to create mDNSResponder connection: %d\n", error);
- return (0);
- }
+ 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 = printer->common_ref;
+ printer->printer_ref = DNSSDMaster;
if ((error = DNSServiceRegister(&(printer->printer_ref),
kDNSServiceFlagsShareConnection,
* advertise our IPP printer...
*/
- printer->ipp_ref = printer->common_ref;
+ printer->ipp_ref = DNSSDMaster;
if (subtype && *subtype)
snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtype);
0 /* interfaceIndex */, printer->dnssd_name,
regtype, NULL /* domain */,
NULL /* host */, htons(printer->port),
- TXTRecordGetLength(&(printer->ipp_txt)),
- TXTRecordGetBytesPtr(&(printer->ipp_txt)),
+ TXTRecordGetLength(&ipp_txt),
+ TXTRecordGetBytesPtr(&ipp_txt),
(DNSServiceRegisterReply)dnssd_callback,
printer)) != kDNSServiceErr_NoError)
{
return (0);
}
-# if 0 /* ifdef HAVE_SSL */
+# ifdef HAVE_SSL
/*
* Then register the _ipps._tcp (IPP) service type with the real port number to
- * advertise our IPP printer...
+ * advertise our IPPS printer...
*/
- printer->ipps_ref = printer->common_ref;
+ printer->ipps_ref = DNSSDMaster;
if (subtype && *subtype)
snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtype);
0 /* interfaceIndex */, printer->dnssd_name,
regtype, NULL /* domain */,
NULL /* host */, htons(printer->port),
- TXTRecordGetLength(&(printer->ipp_txt)),
- TXTRecordGetBytesPtr(&(printer->ipp_txt)),
+ TXTRecordGetLength(&ipp_txt),
+ TXTRecordGetBytesPtr(&ipp_txt),
(DNSServiceRegisterReply)dnssd_callback,
printer)) != kDNSServiceErr_NoError)
{
* real port number to advertise our IPP printer...
*/
- printer->http_ref = printer->common_ref;
+ printer->http_ref = DNSSDMaster;
if ((error = DNSServiceRegister(&(printer->http_ref),
kDNSServiceFlagsShareConnection,
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");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s %s", make, model);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", adminurl);
+ if (*location)
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", location);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "product=(%s)", model);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", color ? "T" : "F");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", duplex ? "T" : "F");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MFG=%s", make);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MDL=%s", model);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", uuid);
+# ifdef HAVE_SSL
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
+# endif /* HAVE_SSL */
+
+ /*
+ * 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 (subtype && *subtype)
+ {
+ snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", subtype);
+ avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
+ }
+
+#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 (subtype && *subtype)
+ {
+ snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", subtype);
+ avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
+ }
+#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);
}
-#endif /* HAVE_DNSSD */
/*
* Format an error message...
*/
- if (!type && !length && code != HTTP_STATUS_OK)
+ if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
{
snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
num_fds = 2;
#ifdef HAVE_DNSSD
- polldata[num_fds ].fd = DNSServiceRefSockFD(printer->common_ref);
+ polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
polldata[num_fds ++].events = POLLIN;
#endif /* HAVE_DNSSD */
#ifdef HAVE_DNSSD
if (polldata[2].revents & POLLIN)
- DNSServiceProcessResult(printer->common_ref);
+ DNSServiceProcessResult(DNSSDMaster);
#endif /* HAVE_DNSSD */
/*
}
+/*
+ * '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.
*/
{
if (!status)
{
- puts(CUPS_SVERSION " - Copyright 2010-2013 by Apple Inc. All rights "
+ puts(CUPS_SVERSION " - Copyright 2010-2015 by Apple Inc. All rights "
"reserved.");
puts("");
}
puts("-2 Supports 2-sided printing (default=1-sided)");
puts("-M manufacturer Manufacturer name (default=Test)");
puts("-P PIN printing mode");
+ puts("-a attributes-file Load printer attributes from file");
puts("-c command Run command for every print job");
printf("-d spool-directory Spool directory "
"(default=/tmp/ippserver.%d)\n", (int)getpid());
puts("-m model Model name (default=Printer)");
puts("-n hostname Hostname for printer");
puts("-p port Port number (default=auto)");
-#ifdef HAVE_DNSSD
puts("-r subtype Bonjour service subtype (default=_print)");
-#endif /* HAVE_DNSSD */
puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
puts("-v[vvv] Be (very) verbose");
* Check operation attributes...
*/
- if ((attr = ippFindAttribute(client->request, "compression",
- IPP_TAG_ZERO)) != NULL)
+ if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
{
/*
* If compression is specified, only accept a supported value in a Print-Job
}
else
{
- fprintf(stderr, "%s %s compression=\"%s\"\n",
- client->hostname, op_name, compression);
+ 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 ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
ippGetGroupTag(attr) != IPP_TAG_OPERATION)
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);
+ 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_JOB, IPP_TAG_MIMETYPE,
- "document-format", NULL, format);
+ attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
}
- if (!strcmp(format, "application/octet-stream") &&
- (ippGetOperation(client->request) == IPP_OP_PRINT_JOB ||
- ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
+ if (!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 4 bytes of the file...
+ * Auto-type the file using the first 8 bytes of the file...
*/
- unsigned char header[4]; /* First 4 bytes of file */
+ unsigned char header[8]; /* First 8 bytes of file */
memset(header, 0, sizeof(header));
httpPeek(client->http, (char *)header, sizeof(header));
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)
+ 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);
- if (!attr)
- attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
- "document-format", NULL, format);
- else
- ippSetString(client->request, &attr, 0, 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))
+ 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);
}
_ipp_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 the various job template attributes...
*/
- if ((attr = ippFindAttribute(client->request, "copies",
- IPP_TAG_ZERO)) != NULL)
+ 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)
}
}
- if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity",
- IPP_TAG_ZERO)) != NULL)
+ if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
{
}
}
- if ((attr = ippFindAttribute(client->request, "job-hold-until",
- IPP_TAG_ZERO)) != NULL)
+ if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 ||
(ippGetValueTag(attr) != IPP_TAG_NAME &&
}
}
- if ((attr = ippFindAttribute(client->request, "job-name",
- IPP_TAG_ZERO)) != NULL)
+ 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 &&
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 ((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)
}
}
- if ((attr = ippFindAttribute(client->request, "job-sheets",
- IPP_TAG_ZERO)) != NULL)
+ if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 ||
(ippGetValueTag(attr) != IPP_TAG_NAME &&
}
}
- if ((attr = ippFindAttribute(client->request, "media",
- IPP_TAG_ZERO)) != NULL)
+ if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 ||
(ippGetValueTag(attr) != IPP_TAG_NAME &&
}
else
{
- for (i = 0;
- i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
- i ++)
- if (!strcmp(ippGetString(attr, 0, NULL), media_supported[i]))
- break;
+ supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
- if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
+ if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
{
respond_unsupported(client, attr);
valid = 0;
}
}
- if ((attr = ippFindAttribute(client->request, "media-col",
- IPP_TAG_ZERO)) != NULL)
+ 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;
}
- /* TODO: check for valid media-col */
+
+ 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 ((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),
}
}
- if ((attr = ippFindAttribute(client->request, "orientation-requested",
- IPP_TAG_ZERO)) != NULL)
+ 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 ||
}
}
- if ((attr = ippFindAttribute(client->request, "page-ranges",
- IPP_TAG_ZERO)) != NULL)
+ if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
{
- respond_unsupported(client, attr);
+ if (ippGetValueTag(attr) != IPP_TAG_RANGE)
+ {
+ respond_unsupported(client, attr);
valid = 0;
+ }
}
- if ((attr = ippFindAttribute(client->request, "print-quality",
- IPP_TAG_ZERO)) != NULL)
+ 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 ||
}
}
- if ((attr = ippFindAttribute(client->request, "printer-resolution",
- IPP_TAG_ZERO)) != NULL)
+ if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
{
- respond_unsupported(client, attr);
- valid = 0;
+ 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)
+ if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
{
- const char *sides = NULL; /* "sides" value... */
+ const char *sides = ippGetString(attr, 0, NULL);
+ /* "sides" value... */
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
{
respond_unsupported(client, attr);
valid = 0;
}
-
- sides = ippGetString(attr, 0, NULL);
-
- if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported",
- IPP_TAG_KEYWORD)) != NULL)
+ else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
{
if (!ippContainsString(supported, sides))
{
/*
- * End of "$Id$".
+ * End of "$Id: ippserver.c 13138 2016-03-15 14:59:54Z msweet $".
*/