#include <cups/cups-private.h>
#include <cups/debug-private.h>
#if !CUPS_LITE
# include <cups/ppd-private.h>
#endif
#include <limits.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef _WIN32
# 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 <spawn.h>
# include <sys/wait.h>
# include <poll.h>
#endif
#ifndef O_BINARY
# define O_BINARY 0
#endif
#ifdef HAVE_MDNSRESPONDER
# include <dns_sd.h>
#elif defined(HAVE_AVAHI)
# include <avahi-client/client.h>
# include <avahi-client/publish.h>
# include <avahi-common/alternative.h>
# include <avahi-common/error.h>
# include <avahi-common/malloc.h>
# include <avahi-common/thread-watch.h>
#endif
#ifdef HAVE_SYS_MOUNT_H
# include <sys/mount.h>
#endif
#ifdef HAVE_SYS_STATFS_H
# include <sys/statfs.h>
#endif
#ifdef HAVE_SYS_STATVFS_H
# include <sys/statvfs.h>
#endif
#ifdef HAVE_SYS_VFS_H
# include <sys/vfs.h>
#endif
#if HAVE_LIBPAM
# ifdef HAVE_PAM_PAM_APPL_H
# include <pam/pam_appl.h>
# else
# include <security/pam_appl.h>
# endif
#endif
#include "printer-png.h"
#include "printer-lg-png.h"
#include "printer-sm-png.h"
enum ippeve_preason_e
{
IPPEVE_PREASON_NONE = 0x0000,
IPPEVE_PREASON_OTHER = 0x0001,
IPPEVE_PREASON_COVER_OPEN = 0x0002,
IPPEVE_PREASON_INPUT_TRAY_MISSING = 0x0004,
IPPEVE_PREASON_MARKER_SUPPLY_EMPTY = 0x0008,
IPPEVE_PREASON_MARKER_SUPPLY_LOW = 0x0010,
IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020,
IPPEVE_PREASON_MARKER_WASTE_FULL = 0x0040,
IPPEVE_PREASON_MEDIA_EMPTY = 0x0080,
IPPEVE_PREASON_MEDIA_JAM = 0x0100,
IPPEVE_PREASON_MEDIA_LOW = 0x0200,
IPPEVE_PREASON_MEDIA_NEEDED = 0x0400,
IPPEVE_PREASON_MOVING_TO_PAUSED = 0x0800,
IPPEVE_PREASON_PAUSED = 0x1000,
IPPEVE_PREASON_SPOOL_AREA_FULL = 0x2000,
IPPEVE_PREASON_TONER_EMPTY = 0x4000,
IPPEVE_PREASON_TONER_LOW = 0x8000
};
typedef unsigned int ippeve_preason_t;
static const char * const ippeve_preason_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"
};
#ifdef HAVE_TLS
# define WEB_SCHEME "https"
#else
# define WEB_SCHEME "http"
#endif
#ifdef HAVE_MDNSRESPONDER
typedef DNSServiceRef ippeve_srv_t;
typedef TXTRecordRef ippeve_txt_t;
#elif defined(HAVE_AVAHI)
typedef AvahiEntryGroup *ippeve_srv_t;
typedef AvahiStringList *ippeve_txt_t;
#else
typedef void *ippeve_srv_t;
typedef void *ippeve_txt_t;
#endif
#if HAVE_LIBPAM
typedef struct ippeve_authdata_s
{
char username[HTTP_MAX_VALUE],
*password;
} ippeve_authdata_t;
#endif
typedef struct ippeve_filter_s
{
cups_array_t *ra;
ipp_tag_t group_tag;
} ippeve_filter_t;
typedef struct ippeve_job_s ippeve_job_t;
typedef struct ippeve_printer_s
{
int ipv4,
ipv6;
#ifdef HAVE_MDNSRESPONDER
ippeve_srv_t ipp_ref,
ipps_ref,
http_ref,
printer_ref;
#elif defined(HAVE_AVAHI)
ippeve_srv_t dnssd_ref;
#endif
char *dnssd_subtypes;
int dnssd_collision;
char *dnssd_name,
*name,
*icons[3],
*strings,
*directory,
*hostname,
*device_uri,
*output_format,
#if !CUPS_LITE
*ppdfile,
#endif
*command;
int port;
int web_forms;
size_t urilen;
ipp_t *attrs;
time_t start_time;
time_t config_time;
ipp_pstate_t state;
ippeve_preason_t state_reasons;
time_t state_time;
cups_array_t *jobs;
ippeve_job_t *active_job;
int next_job_id;
_cups_rwlock_t rwlock;
} ippeve_printer_t;
struct ippeve_job_s
{
int id;
const char *name,
*username,
*format;
ipp_jstate_t state;
char *message;
int msglevel;
time_t created,
processing,
completed;
int impressions,
impcompleted;
ipp_t *attrs;
int cancel;
char *filename;
int fd;
ippeve_printer_t *printer;
};
typedef struct ippeve_client_s
{
http_t *http;
ipp_t *request,
*response;
time_t start;
http_state_t operation;
ipp_op_t operation_id;
char uri[1024],
*options,
host_field[HTTP_MAX_VALUE];
int host_port;
http_addr_t addr;
char hostname[256],
username[HTTP_MAX_VALUE];
ippeve_printer_t *printer;
ippeve_job_t *job;
} ippeve_client_t;
static http_status_t authenticate_request(ippeve_client_t *client);
static void clean_jobs(ippeve_printer_t *printer);
static int compare_jobs(ippeve_job_t *a, ippeve_job_t *b);
static void copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra, ipp_tag_t group_tag, int quickcopy);
static void copy_job_attributes(ippeve_client_t *client, ippeve_job_t *job, cups_array_t *ra);
static ippeve_client_t *create_client(ippeve_printer_t *printer, int sock);
static ippeve_job_t *create_job(ippeve_client_t *client);
static int create_job_file(ippeve_job_t *job, char *fname, size_t fnamesize, const char *dir, const char *ext);
static int create_listener(const char *name, int port, int family);
static ipp_t *create_media_col(const char *media, const char *source, const char *type, int width, int length, int bottom, int left, int right, int top);
static ipp_t *create_media_size(int width, int length);
static ippeve_printer_t *create_printer(const char *servername, int serverport, const char *name, const char *location, const char *icons, const char *strings, cups_array_t *docformats, const char *subtypes, const char *directory, const char *command, const char *device_uri, const char *output_format, ipp_t *attrs);
static void debug_attributes(const char *title, ipp_t *ipp, int response);
static void delete_client(ippeve_client_t *client);
static void delete_job(ippeve_job_t *job);
static void delete_printer(ippeve_printer_t *printer);
#ifdef HAVE_MDNSRESPONDER
static void DNSSD_API dnssd_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, ippeve_printer_t *printer);
#elif defined(HAVE_AVAHI)
static void dnssd_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, void *context);
static void dnssd_client_cb(AvahiClient *c, AvahiClientState state, void *userdata);
#endif
static void dnssd_init(void);
static int filter_cb(ippeve_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr);
static ippeve_job_t *find_job(ippeve_client_t *client);
static void finish_document_data(ippeve_client_t *client, ippeve_job_t *job);
static void finish_document_uri(ippeve_client_t *client, ippeve_job_t *job);
static void flush_document_data(ippeve_client_t *client);
static int have_document_data(ippeve_client_t *client);
static void html_escape(ippeve_client_t *client, const char *s, size_t slen);
static void html_footer(ippeve_client_t *client);
static void html_header(ippeve_client_t *client, const char *title, int refresh);
static void html_printf(ippeve_client_t *client, const char *format, ...) _CUPS_FORMAT(2, 3);
static void ipp_cancel_job(ippeve_client_t *client);
static void ipp_cancel_my_jobs(ippeve_client_t *client);
static void ipp_close_job(ippeve_client_t *client);
static void ipp_create_job(ippeve_client_t *client);
static void ipp_get_job_attributes(ippeve_client_t *client);
static void ipp_get_jobs(ippeve_client_t *client);
static void ipp_get_printer_attributes(ippeve_client_t *client);
static void ipp_identify_printer(ippeve_client_t *client);
static void ipp_print_job(ippeve_client_t *client);
static void ipp_print_uri(ippeve_client_t *client);
static void ipp_send_document(ippeve_client_t *client);
static void ipp_send_uri(ippeve_client_t *client);
static void ipp_validate_job(ippeve_client_t *client);
static ipp_t *load_ippserver_attributes(const char *servername, int serverport, const char *filename, cups_array_t *docformats);
static ipp_t *load_legacy_attributes(const char *make, const char *model, int ppm, int ppm_color, int duplex, cups_array_t *docformats);
#if !CUPS_LITE
static ipp_t *load_ppd_attributes(const char *ppdfile, cups_array_t *docformats);
#endif
#if HAVE_LIBPAM
static int pam_func(int, const struct pam_message **, struct pam_response **, void *);
#endif
static int parse_options(ippeve_client_t *client, cups_option_t **options);
static void process_attr_message(ippeve_job_t *job, char *message);
static void *process_client(ippeve_client_t *client);
static int process_http(ippeve_client_t *client);
static int process_ipp(ippeve_client_t *client);
static void *process_job(ippeve_job_t *job);
static void process_state_message(ippeve_job_t *job, char *message);
static int register_printer(ippeve_printer_t *printer);
static int respond_http(ippeve_client_t *client, http_status_t code, const char *content_coding, const char *type, size_t length);
static void respond_ipp(ippeve_client_t *client, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4);
static void respond_unsupported(ippeve_client_t *client, ipp_attribute_t *attr);
static void run_printer(ippeve_printer_t *printer);
static int show_media(ippeve_client_t *client);
static int show_status(ippeve_client_t *client);
static int show_supplies(ippeve_client_t *client);
#ifndef _WIN32
static void signal_handler(int signum);
#endif
static char *time_string(time_t tv, char *buffer, size_t bufsize);
static void usage(int status) _CUPS_NORETURN;
static int valid_doc_attributes(ippeve_client_t *client);
static int valid_job_attributes(ippeve_client_t *client);
#ifdef HAVE_MDNSRESPONDER
static DNSServiceRef DNSSDMaster = NULL;
#elif defined(HAVE_AVAHI)
static AvahiThreadedPoll *DNSSDMaster = NULL;
static AvahiClient *DNSSDClient = NULL;
#endif
static int KeepFiles = 0,
MaxVersion = 20,
Verbosity = 0;
static const char *PAMService = NULL;
#ifndef _WIN32
static int StopPrinter = 0;
#endif
int
main(int argc,
char *argv[])
{
int i;
const char *opt,
*attrfile = NULL,
*command = NULL,
*device_uri = NULL,
*output_format = NULL,
*icon = NULL,
#ifdef HAVE_TLS
*keypath = NULL,
#endif
*location = "",
*make = "Example",
*model = "Printer",
*name = NULL,
#if !CUPS_LITE
*ppdfile = NULL,
#endif
*strings = NULL,
*subtypes = "_print";
int legacy = 0,
duplex = 0,
ppm = 10,
ppm_color = 0,
web_forms = 1;
ipp_t *attrs = NULL;
char directory[1024] = "";
cups_array_t *docformats = NULL;
const char *servername = NULL;
int serverport = 0;
ippeve_printer_t *printer;
for (i = 1; i < argc; i ++)
{
if (!strcmp(argv[i], "--help"))
{
usage(0);
}
else if (!strcmp(argv[i], "--no-web-forms"))
{
web_forms = 0;
}
else if (!strcmp(argv[i], "--pam-service"))
{
i ++;
if (i >= argc)
usage(1);
PAMService = argv[i];
}
else if (!strcmp(argv[i], "--version"))
{
puts(CUPS_SVERSION);
return (0);
}
else if (!strncmp(argv[i], "--", 2))
{
_cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]);
usage(1);
}
else if (argv[i][0] == '-')
{
for (opt = argv[i] + 1; *opt; opt ++)
{
switch (*opt)
{
case '2' :
duplex = 1;
legacy = 1;
break;
case 'A' :
if (!PAMService)
PAMService = "cups";
break;
case 'D' :
i ++;
if (i >= argc)
usage(1);
device_uri = argv[i];
break;
case 'F' :
i ++;
if (i >= argc)
usage(1);
output_format = argv[i];
break;
#ifdef HAVE_TLS
case 'K' :
i ++;
if (i >= argc)
usage(1);
keypath = argv[i];
break;
#endif
case 'M' :
i ++;
if (i >= argc)
usage(1);
make = argv[i];
legacy = 1;
break;
#if !CUPS_LITE
case 'P' :
i ++;
if (i >= argc)
usage(1);
ppdfile = argv[i];
break;
#endif
case 'S' :
i ++;
if (i >= argc)
usage(1);
strings = argv[i];
break;
case 'V' :
i ++;
if (i >= argc)
usage(1);
if (!strcmp(argv[i], "2.0"))
MaxVersion = 20;
else if (!strcmp(argv[i], "1.1"))
MaxVersion = 11;
else
usage(1);
break;
case 'a' :
i ++;
if (i >= argc)
usage(1);
attrfile = argv[i];
break;
case 'c' :
i ++;
if (i >= argc)
usage(1);
command = argv[i];
break;
case 'd' :
i ++;
if (i >= argc)
usage(1);
strlcpy(directory, argv[i], sizeof(directory));
break;
case 'f' :
i ++;
if (i >= argc)
usage(1);
docformats = _cupsArrayNewStrings(argv[i], ',');
legacy = 1;
break;
case 'i' :
i ++;
if (i >= argc)
usage(1);
icon = argv[i];
break;
case 'k' :
KeepFiles = 1;
break;
case 'l' :
i ++;
if (i >= argc)
usage(1);
location = argv[i];
break;
case 'm' :
i ++;
if (i >= argc)
usage(1);
model = argv[i];
legacy = 1;
break;
case 'n' :
i ++;
if (i >= argc)
usage(1);
servername = argv[i];
break;
case 'p' :
i ++;
if (i >= argc || !isdigit(argv[i][0] & 255))
usage(1);
serverport = atoi(argv[i]);
break;
case 'r' :
i ++;
if (i >= argc)
usage(1);
subtypes = argv[i];
break;
case 's' :
i ++;
if (i >= argc)
usage(1);
if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1)
usage(1);
legacy = 1;
break;
case 'v' :
Verbosity ++;
break;
default :
_cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."), argv[0], *opt);
usage(1);
}
}
}
else if (!name)
{
name = argv[i];
}
else
{
_cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]);
usage(1);
}
}
if (!name)
usage(1);
#if CUPS_LITE
if (attrfile != NULL && legacy)
usage(1);
#else
if (((ppdfile != NULL) + (attrfile != NULL) + legacy) > 1)
usage(1);
#endif
if (!directory[0])
{
const char *tmpdir;
#ifdef _WIN32
if ((tmpdir = getenv("TEMP")) == NULL)
tmpdir = "C:/TEMP";
#elif defined(__APPLE__) && TARGET_OS_OSX
if ((tmpdir = getenv("TMPDIR")) == NULL)
tmpdir = "/private/tmp";
#else
if ((tmpdir = getenv("TMPDIR")) == NULL)
tmpdir = "/tmp";
#endif
snprintf(directory, sizeof(directory), "%s/ippeveprinter.%d", tmpdir, (int)getpid());
if (mkdir(directory, 0755) && errno != EEXIST)
{
_cupsLangPrintf(stderr, _("Unable to create spool directory \"%s\": %s"), directory, strerror(errno));
usage(1);
}
if (Verbosity)
_cupsLangPrintf(stderr, _("Using spool directory \"%s\"."), directory);
}
dnssd_init();
if (!docformats)
docformats = _cupsArrayNewStrings(ppm_color > 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ',');
if (attrfile)
attrs = load_ippserver_attributes(servername, serverport, attrfile, docformats);
#if !CUPS_LITE
else if (ppdfile)
{
attrs = load_ppd_attributes(ppdfile, docformats);
if (!command)
command = "ippeveps";
if (!output_format)
output_format = "application/postscript";
}
#endif
else
attrs = load_legacy_attributes(make, model, ppm, ppm_color, duplex, docformats);
if ((printer = create_printer(servername, serverport, name, location, icon, strings, docformats, subtypes, directory, command, device_uri, output_format, attrs)) == NULL)
return (1);
printer->web_forms = web_forms;
#if !CUPS_LITE
if (ppdfile)
printer->ppdfile = strdup(ppdfile);
#endif
#ifdef HAVE_TLS
cupsSetServerCredentials(keypath, printer->hostname, 1);
#endif
run_printer(printer);
delete_printer(printer);
return (0);
}
static http_status_t
authenticate_request(
ippeve_client_t *client)
{
#if HAVE_LIBPAM
const char *authorization;
int userlen;
pam_handle_t *pamh;
int pamerr;
struct pam_conv pamdata;
ippeve_authdata_t data;
if (!PAMService)
return (HTTP_STATUS_CONTINUE);
authorization = httpGetField(client->http, HTTP_FIELD_AUTHORIZATION);
if (!*authorization)
return (HTTP_STATUS_UNAUTHORIZED);
if (strncmp(authorization, "Basic ", 6))
{
fputs("Unsupported scheme in Authorization header.\n", stderr);
return (HTTP_STATUS_BAD_REQUEST);
}
authorization += 5;
while (isspace(*authorization & 255))
authorization ++;
userlen = sizeof(data.username);
httpDecode64_2(data.username, &userlen, authorization);
if ((data.password = strchr(data.username, ':')) == NULL)
{
fputs("No password in Authorization header.\n", stderr);
return (HTTP_STATUS_BAD_REQUEST);
}
*(data.password)++ = '\0';
if (!data.username[0])
{
fputs("No username in Authorization header.\n", stderr);
return (HTTP_STATUS_BAD_REQUEST);
}
pamdata.conv = pam_func;
pamdata.appdata_ptr = &data;
if ((pamerr = pam_start(PAMService, data.username, &pamdata, &pamh)) != PAM_SUCCESS)
{
fprintf(stderr, "pam_start() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
return (HTTP_STATUS_SERVER_ERROR);
}
if ((pamerr = pam_authenticate(pamh, PAM_SILENT)) != PAM_SUCCESS)
{
fprintf(stderr, "pam_authenticate() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
pam_end(pamh, 0);
return (HTTP_STATUS_UNAUTHORIZED);
}
if ((pamerr = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS)
{
fprintf(stderr, "pam_acct_mgmt() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
pam_end(pamh, 0);
return (HTTP_STATUS_SERVER_ERROR);
}
strlcpy(client->username, data.username, sizeof(client->username));
pam_end(pamh, PAM_SUCCESS);
return (HTTP_STATUS_CONTINUE);
#else
return (HTTP_STATUS_CONTINUE);
#endif
}
static void
clean_jobs(ippeve_printer_t *printer)
{
ippeve_job_t *job;
time_t cleantime;
if (cupsArrayCount(printer->jobs) == 0)
return;
cleantime = time(NULL) - 60;
_cupsRWLockWrite(&(printer->rwlock));
for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs);
job;
job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
if (job->completed && job->completed < cleantime)
{
cupsArrayRemove(printer->jobs, job);
delete_job(job);
}
else
break;
_cupsRWUnlock(&(printer->rwlock));
}
static int
compare_jobs(ippeve_job_t *a,
ippeve_job_t *b)
{
return (b->id - a->id);
}
static void
copy_attributes(ipp_t *to,
ipp_t *from,
cups_array_t *ra,
ipp_tag_t group_tag,
int quickcopy)
{
ippeve_filter_t filter;
filter.ra = ra;
filter.group_tag = group_tag;
ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter);
}
static void
copy_job_attributes(
ippeve_client_t *client,
ippeve_job_t *job,
cups_array_t *ra)
{
copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
if (!ra || cupsArrayFind(ra, "date-time-at-completed"))
{
if (job->completed)
ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed));
else
ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed");
}
if (!ra || cupsArrayFind(ra, "date-time-at-processing"))
{
if (job->processing)
ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-processing", ippTimeToDate(job->processing));
else
ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing");
}
if (!ra || cupsArrayFind(ra, "job-impressions"))
ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions", job->impressions);
if (!ra || cupsArrayFind(ra, "job-impressions-completed"))
ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", job->impcompleted);
if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-printer-up-time", (int)(time(NULL) - client->printer->start_time));
if (!ra || cupsArrayFind(ra, "job-state"))
ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state);
if (!ra || cupsArrayFind(ra, "job-state-message"))
{
if (job->message)
{
ippAddString(client->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, job->message);
}
else
{
switch (job->state)
{
case IPP_JSTATE_PENDING :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job pending.");
break;
case IPP_JSTATE_HELD :
if (job->fd >= 0)
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job incoming.");
else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job held.");
else
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job created.");
break;
case IPP_JSTATE_PROCESSING :
if (job->cancel)
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceling.");
else
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job printing.");
break;
case IPP_JSTATE_STOPPED :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job stopped.");
break;
case IPP_JSTATE_CANCELED :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceled.");
break;
case IPP_JSTATE_ABORTED :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job aborted.");
break;
case IPP_JSTATE_COMPLETED :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job completed.");
break;
}
}
}
if (!ra || cupsArrayFind(ra, "job-state-reasons"))
{
switch (job->state)
{
case IPP_JSTATE_PENDING :
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
NULL, "none");
break;
case IPP_JSTATE_HELD :
if (job->fd >= 0)
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD),
"job-state-reasons", NULL, "job-incoming");
else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD),
"job-state-reasons", NULL, "job-hold-until-specified");
else
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD),
"job-state-reasons", NULL, "job-data-insufficient");
break;
case IPP_JSTATE_PROCESSING :
if (job->cancel)
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD),
"job-state-reasons", NULL, "processing-to-stop-point");
else
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD),
"job-state-reasons", NULL, "job-printing");
break;
case IPP_JSTATE_STOPPED :
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
NULL, "job-stopped");
break;
case IPP_JSTATE_CANCELED :
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
NULL, "job-canceled-by-user");
break;
case IPP_JSTATE_ABORTED :
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
NULL, "aborted-by-system");
break;
case IPP_JSTATE_COMPLETED :
ippAddString(client->response, IPP_TAG_JOB,
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
NULL, "job-completed-successfully");
break;
}
}
if (!ra || cupsArrayFind(ra, "time-at-completed"))
ippAddInteger(client->response, IPP_TAG_JOB,
job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
"time-at-completed", (int)(job->completed - client->printer->start_time));
if (!ra || cupsArrayFind(ra, "time-at-processing"))
ippAddInteger(client->response, IPP_TAG_JOB,
job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
"time-at-processing", (int)(job->processing - client->printer->start_time));
}
static ippeve_client_t *
create_client(ippeve_printer_t *printer,
int sock)
{
ippeve_client_t *client;
if ((client = calloc(1, sizeof(ippeve_client_t))) == NULL)
{
perror("Unable to allocate memory for client");
return (NULL);
}
client->printer = printer;
if ((client->http = httpAcceptConnection(sock, 1)) == NULL)
{
perror("Unable to accept client connection");
free(client);
return (NULL);
}
httpGetHostname(client->http, client->hostname, sizeof(client->hostname));
if (Verbosity)
fprintf(stderr, "Accepted connection from %s\n", client->hostname);
return (client);
}
static ippeve_job_t *
create_job(ippeve_client_t *client)
{
ippeve_job_t *job;
ipp_attribute_t *attr;
char uri[1024],
uuid[64];
_cupsRWLockWrite(&(client->printer->rwlock));
if (client->printer->active_job &&
client->printer->active_job->state < IPP_JSTATE_CANCELED)
{
_cupsRWUnlock(&(client->printer->rwlock));
return (NULL);
}
if ((job = calloc(1, sizeof(ippeve_job_t))) == NULL)
{
perror("Unable to allocate memory for job");
_cupsRWUnlock(&(client->printer->rwlock));
return (NULL);
}
job->printer = client->printer;
job->attrs = ippNew();
job->state = IPP_JSTATE_HELD;
job->fd = -1;
copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
job->username = ippGetString(attr, 0, NULL);
else
job->username = "anonymous";
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
if (ippGetOperation(client->request) != IPP_OP_CREATE_JOB)
{
if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else
job->format = "application/octet-stream";
}
if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_INTEGER)) != NULL)
job->impressions = ippGetInteger(attr, 0);
if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_NAME)) != NULL)
job->name = ippGetString(attr, 0, NULL);
job->id = client->printer->next_job_id ++;
if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
snprintf(uri, sizeof(uri), "%s/%d", ippGetString(attr, 0, NULL), job->id);
else
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, client->printer->hostname, client->printer->port, "/ipp/print/%d", job->id);
httpAssembleUUID(client->printer->hostname, client->printer->port, client->printer->name, job->id, uuid, sizeof(uuid));
ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(&job->created)));
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
{
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, ippGetString(attr, 0, NULL));
}
else
{
char printer_uri[1024];
httpAssembleURI(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", NULL, client->printer->hostname, client->printer->port, "/ipp/print");
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, printer_uri);
}
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time));
cupsArrayAdd(client->printer->jobs, job);
client->printer->active_job = job;
_cupsRWUnlock(&(client->printer->rwlock));
return (job);
}
static int
create_job_file(
ippeve_job_t *job,
char *fname,
size_t fnamesize,
const char *directory,
const char *ext)
{
char name[256],
*nameptr;
const char *job_name;
if ((job_name = ippGetString(ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME), 0, NULL)) == NULL)
job_name = "untitled";
for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++)
{
if (isalnum(*job_name & 255) || *job_name == '-')
{
*nameptr++ = (char)tolower(*job_name & 255);
}
else
{
*nameptr++ = '_';
while (job_name[1] && !isalnum(job_name[1] & 255) && job_name[1] != '-')
job_name ++;
}
}
*nameptr = '\0';
if (!ext)
{
if (!strcasecmp(job->format, "image/jpeg"))
ext = "jpg";
else if (!strcasecmp(job->format, "image/png"))
ext = "png";
else if (!strcasecmp(job->format, "image/pwg-raster"))
ext = "pwg";
else if (!strcasecmp(job->format, "image/urf"))
ext = "urf";
else if (!strcasecmp(job->format, "application/pdf"))
ext = "pdf";
else if (!strcasecmp(job->format, "application/postscript"))
ext = "ps";
else if (!strcasecmp(job->format, "application/vnd.hp-pcl"))
ext = "pcl";
else
ext = "dat";
}
snprintf(fname, fnamesize, "%s/%d-%s.%s", directory, job->id, name, ext);
return (open(fname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
}
static int
create_listener(const char *name,
int port,
int family)
{
int sock;
http_addrlist_t *addrlist;
char service[255];
snprintf(service, sizeof(service), "%d", port);
if ((addrlist = httpAddrGetList(name, family, service)) == NULL)
return (-1);
sock = httpAddrListen(&(addrlist->addr), port);
httpAddrFreeList(addrlist);
return (sock);
}
static ipp_t *
create_media_col(const char *media,
const char *source,
const char *type,
int width,
int length,
int bottom,
int left,
int right,
int top)
{
ipp_t *media_col = ippNew(),
*media_size = create_media_size(width, length);
char media_key[256];
const char *media_key_suffix = "";
if (bottom == 0 && left == 0 && right == 0 && top == 0)
media_key_suffix = "_borderless";
if (type && source)
snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, type, media_key_suffix);
else if (type)
snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, media_key_suffix);
else if (source)
snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, media_key_suffix);
else
snprintf(media_key, sizeof(media_key), "%s%s", media, media_key_suffix);
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL, media_key);
ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-size-name", NULL, media);
if (bottom >= 0)
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin", bottom);
if (left >= 0)
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin", left);
if (right >= 0)
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin", right);
if (top >= 0)
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin", top);
if (source)
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source", NULL, source);
if (type)
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type", NULL, type);
ippDelete(media_size);
return (media_col);
}
static ipp_t *
create_media_size(int width,
int length)
{
ipp_t *media_size = ippNew();
ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", width);
ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", length);
return (media_size);
}
static ippeve_printer_t *
create_printer(
const char *servername,
int serverport,
const char *name,
const char *location,
const char *icons,
const char *strings,
cups_array_t *docformats,
const char *subtypes,
const char *directory,
const char *command,
const char *device_uri,
const char *output_format,
ipp_t *attrs)
{
ippeve_printer_t *printer;
int i;
#ifndef _WIN32
char path[1024];
#endif
unsigned char sha256[32];
char uuid_data[1024],
uuid[128],
*iconsptr;
int k_supported;
int num_formats;
const char *formats[100],
*format;
int num_sup_attrs;
const char *sup_attrs[100];
char xxx_supported[256];
_cups_globals_t *cg = _cupsGlobals();
#ifdef HAVE_STATVFS
struct statvfs spoolinfo;
double spoolsize;
#elif defined(HAVE_STATFS)
struct statfs spoolinfo;
double spoolsize;
#endif
static const char * const versions[] =
{
"1.1",
"2.0"
};
static const char * const features[] =
{
"ipp-everywhere"
};
static const int ops[] =
{
IPP_OP_PRINT_JOB,
IPP_OP_PRINT_URI,
IPP_OP_VALIDATE_JOB,
IPP_OP_CREATE_JOB,
IPP_OP_SEND_DOCUMENT,
IPP_OP_SEND_URI,
IPP_OP_CANCEL_JOB,
IPP_OP_GET_JOB_ATTRIBUTES,
IPP_OP_GET_JOBS,
IPP_OP_GET_PRINTER_ATTRIBUTES,
IPP_OP_CANCEL_MY_JOBS,
IPP_OP_CLOSE_JOB,
IPP_OP_IDENTIFY_PRINTER
};
static const char * const charsets[] =
{
"us-ascii",
"utf-8"
};
static const char * const compressions[] =
{
#ifdef HAVE_LIBZ
"deflate",
"gzip",
#endif
"none"
};
static const char * const identify_actions[] =
{
"display",
"sound"
};
static const char * const job_creation[] =
{
"copies",
"document-access",
"document-charset",
"document-format",
"document-message",
"document-metadata",
"document-name",
"document-natural-language",
"document-password",
"finishings",
"finishings-col",
"ipp-attribute-fidelity",
"job-account-id",
"job-account-type",
"job-accouunting-sheets",
"job-accounting-user-id",
"job-authorization-uri",
"job-error-action",
"job-error-sheet",
"job-hold-until",
"job-hold-until-time",
"job-mandatory-attributes",
"job-message-to-operator",
"job-name",
"job-pages-per-set",
"job-password",
"job-password-encryption",
"job-phone-number",
"job-priority",
"job-recipient-name",
"job-resource-ids",
"job-sheet-message",
"job-sheets",
"job-sheets-col",
"media",
"media-col",
"multiple-document-handling",
"number-up",
"orientation-requested",
"output-bin",
"output-device",
"overrides",
"page-delivery",
"page-ranges",
"presentation-direction-number-up",
"print-color-mode",
"print-content-optimize",
"print-quality",
"print-rendering-intent",
"print-scaling",
"printer-resolution",
"proof-print",
"separator-sheets",
"sides",
"x-image-position",
"x-image-shift",
"x-side1-image-shift",
"x-side2-image-shift",
"y-image-position",
"y-image-shift",
"y-side1-image-shift",
"y-side2-image-shift"
};
static const char * const media_col_supported[] =
{
"media-bottom-margin",
"media-left-margin",
"media-right-margin",
"media-size",
"media-size-name",
"media-source",
"media-top-margin",
"media-type"
};
static const char * const multiple_document_handling[] =
{
"separate-documents-uncollated-copies",
"separate-documents-collated-copies"
};
static const char * const reference_uri_schemes_supported[] =
{
"file",
"ftp",
"http"
#ifdef HAVE_TLS
, "https"
#endif
};
#ifdef HAVE_TLS
static const char * const uri_authentication_supported[] =
{
"none",
"none"
};
static const char * const uri_authentication_basic[] =
{
"basic",
"basic"
};
static const char * const uri_security_supported[] =
{
"none",
"tls"
};
#endif
static const char * const which_jobs[] =
{
"completed",
"not-completed",
"aborted",
"all",
"canceled",
"pending",
"pending-held",
"processing",
"processing-stopped"
};
#ifndef _WIN32
if (command)
{
if (*command == '/' || !strncmp(command, "./", 2))
{
if (access(command, X_OK))
{
_cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
return (NULL);
}
}
else
{
snprintf(path, sizeof(path), "%s/command/%s", cg->cups_serverbin, command);
if (access(command, X_OK))
{
_cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
return (NULL);
}
command = path;
}
}
#endif
if ((printer = calloc(1, sizeof(ippeve_printer_t))) == NULL)
{
_cupsLangPrintError(NULL, _("Unable to allocate memory for printer"));
return (NULL);
}
printer->ipv4 = -1;
printer->ipv6 = -1;
printer->name = strdup(name);
printer->dnssd_name = strdup(name);
printer->dnssd_subtypes = subtypes ? strdup(subtypes) : NULL;
printer->command = command ? strdup(command) : NULL;
printer->device_uri = device_uri ? strdup(device_uri) : NULL;
printer->output_format = output_format ? strdup(output_format) : NULL;
printer->directory = strdup(directory);
printer->icons[0] = icons ? strdup(icons) : NULL;
printer->strings = strings ? strdup(strings) : NULL;
printer->port = serverport;
printer->start_time = time(NULL);
printer->config_time = printer->start_time;
printer->state = IPP_PSTATE_IDLE;
printer->state_reasons = IPPEVE_PREASON_NONE;
printer->state_time = printer->start_time;
printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
printer->next_job_id = 1;
if (printer->icons[0])
{
for (i = 1, iconsptr = strchr(icons, ','); iconsptr && i < 3; i ++, iconsptr = strchr(iconsptr, ','))
{
*iconsptr++ = '\0';
printer->icons[i] = iconsptr;
}
if (iconsptr)
*iconsptr = '\0';
while (i < 3)
{
printer->icons[i] = printer->icons[i - 1];
i ++;
}
}
if (servername)
{
printer->hostname = strdup(servername);
}
else
{
char temp[1024],
*tempptr;
#ifdef HAVE_AVAHI
const char *avahi_name = avahi_client_get_host_name_fqdn(DNSSDClient);
if (avahi_name)
strlcpy(temp, avahi_name, sizeof(temp));
else
#endif
if ((tempptr = strstr(httpGetHostname(NULL, temp, sizeof(temp)), ".lan")) != NULL && !tempptr[5])
strlcpy(tempptr, ".local", sizeof(temp) - (size_t)(tempptr - temp));
printer->hostname = strdup(temp);
}
_cupsRWInit(&(printer->rwlock));
if (printer->port)
{
if ((printer->ipv4 = create_listener(servername, printer->port, AF_INET)) < 0)
{
perror("Unable to create IPv4 listener");
goto bad_printer;
}
}
else
{
#ifdef _WIN32
serverport = 8631;
#else
serverport = 8000 + ((int)getuid() % 1000);
#endif
while (serverport < 10000)
{
if ((printer->ipv4 = create_listener(servername, serverport, AF_INET)) >= 0)
break;
serverport ++;
}
if (serverport < 10000)
{
_cupsLangPrintf(stderr, _("Listening on port %d."), serverport);
printer->port = serverport;
}
else
{
perror("Unable to create IPv4 listener");
goto bad_printer;
}
}
if ((printer->ipv6 = create_listener(servername, printer->port, AF_INET6)) < 0)
{
perror("Unable to create IPv6 listener");
goto bad_printer;
}
snprintf(uuid_data, sizeof(uuid_data), "_IPPEVEPRINTER_:%s:%d:%s", printer->hostname, printer->port, printer->name);
cupsHashData("sha2-256", (unsigned char *)uuid_data, strlen(uuid_data), sha256, sizeof(sha256));
snprintf(uuid, sizeof(uuid), "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", sha256[0], sha256[1], sha256[3], sha256[4], sha256[5], sha256[6], (sha256[10] & 15) | 0x30, sha256[11], (sha256[15] & 0x3f) | 0x40, sha256[16], sha256[20], sha256[21], sha256[25], sha256[26], sha256[30], sha256[31]);
if (Verbosity)
{
#ifdef HAVE_TLS
fprintf(stderr, "printer-uri-supported=\"ipp://%s:%d/ipp/print\",\"ipps://%s:%d/ipp/print\"\n", printer->hostname, printer->port, printer->hostname, printer->port);
#else
fprintf(stderr, "printer-uri-supported=\"ipp://%s:%d/ipp/print\"\n", printer->hostname, printer->port);
#endif
fprintf(stderr, "printer-uuid=\"%s\"\n", uuid);
}
#ifdef HAVE_STATVFS
if (statvfs(printer->directory, &spoolinfo))
k_supported = INT_MAX;
else if ((spoolsize = (double)spoolinfo.f_frsize *
spoolinfo.f_blocks / 1024) > INT_MAX)
k_supported = INT_MAX;
else
k_supported = (int)spoolsize;
#elif defined(HAVE_STATFS)
if (statfs(printer->directory, &spoolinfo))
k_supported = INT_MAX;
else if ((spoolsize = (double)spoolinfo.f_bsize *
spoolinfo.f_blocks / 1024) > INT_MAX)
k_supported = INT_MAX;
else
k_supported = (int)spoolsize;
#else
k_supported = INT_MAX;
#endif
if (!cupsArrayFind(docformats, (void *)"application/octet-stream"))
cupsArrayAdd(docformats, (void *)"application/octet-stream");
for (num_formats = 0, format = (const char *)cupsArrayFirst(docformats); format && num_formats < (int)(sizeof(formats) / sizeof(formats[0])); format = (const char *)cupsArrayNext(docformats))
formats[num_formats ++] = format;
num_sup_attrs = 0;
sup_attrs[num_sup_attrs ++] = "document-access";
sup_attrs[num_sup_attrs ++] = "document-charset";
sup_attrs[num_sup_attrs ++] = "document-format";
sup_attrs[num_sup_attrs ++] = "document-message";
sup_attrs[num_sup_attrs ++] = "document-metadata";
sup_attrs[num_sup_attrs ++] = "document-name";
sup_attrs[num_sup_attrs ++] = "document-natural-language";
sup_attrs[num_sup_attrs ++] = "ipp-attribute-fidelity";
sup_attrs[num_sup_attrs ++] = "job-name";
sup_attrs[num_sup_attrs ++] = "job-priority";
for (i = 0; i < (int)(sizeof(job_creation) / sizeof(job_creation[0])) && num_sup_attrs < (int)(sizeof(sup_attrs) / sizeof(sup_attrs[0])); i ++)
{
snprintf(xxx_supported, sizeof(xxx_supported), "%s-supported", job_creation[i]);
if (ippFindAttribute(attrs, xxx_supported, IPP_TAG_ZERO))
sup_attrs[num_sup_attrs ++] = job_creation[i];
}
printer->attrs = attrs;
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-configured", NULL, "utf-8");
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-supported", sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
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);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format-default", NULL, "application/octet-stream");
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, "document-format-supported", num_formats, NULL, formats);
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_KEYWORD), "identify-actions-default", NULL, "sound");
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);
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", sizeof(features) / sizeof(features[0]), NULL, features);
if (MaxVersion == 11)
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", NULL, "1.1");
else
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", (int)(sizeof(versions) / sizeof(versions[0])), NULL, versions);
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", num_sup_attrs, NULL, sup_attrs);
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1);
ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0, k_supported);
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-default", 50);
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 1);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-default", NULL, "none");
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none");
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);
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);
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 0);
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "multiple-operation-time-out", 60);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-operation-time-out-action", NULL, "abort-job");
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "natural-language-configured", NULL, "en");
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdl-override-supported", NULL, "attempted");
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "preferred-attributes-supported", 0);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format");
ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location");
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, name);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, location);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organization", NULL, "");
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organizational-unit", NULL, "");
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE, "printer-strings-languages-supported", NULL, "en");
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid);
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);
#ifdef HAVE_TLS
if (PAMService)
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_basic);
else
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_supported);
#else
if (PAMService)
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "basic");
else
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "none");
#endif
#ifdef HAVE_TLS
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
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "which-jobs-supported", sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
debug_attributes("Printer", printer->attrs, 0);
if (!register_printer(printer))
goto bad_printer;
return (printer);
bad_printer:
delete_printer(printer);
return (NULL);
}
static void
debug_attributes(const char *title,
ipp_t *ipp,
int type)
{
ipp_tag_t group_tag;
ipp_attribute_t *attr;
char buffer[2048];
int major, minor;
if (Verbosity <= 1)
return;
fprintf(stderr, "%s:\n", title);
major = ippGetVersion(ipp, &minor);
fprintf(stderr, " version=%d.%d\n", major, minor);
if (type == 1)
fprintf(stderr, " operation-id=%s(%04x)\n",
ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp));
else if (type == 2)
fprintf(stderr, " status-code=%s(%04x)\n",
ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp));
fprintf(stderr, " request-id=%d\n\n", ippGetRequestId(ipp));
for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO;
attr;
attr = ippNextAttribute(ipp))
{
if (ippGetGroupTag(attr) != group_tag)
{
group_tag = ippGetGroupTag(attr);
fprintf(stderr, " %s\n", ippTagString(group_tag));
}
if (ippGetName(attr))
{
ippAttributeString(attr, buffer, sizeof(buffer));
fprintf(stderr, " %s (%s%s) %s\n", ippGetName(attr),
ippGetCount(attr) > 1 ? "1setOf " : "",
ippTagString(ippGetValueTag(attr)), buffer);
}
}
}
static void
delete_client(ippeve_client_t *client)
{
if (Verbosity)
fprintf(stderr, "Closing connection from %s\n", client->hostname);
httpFlushWrite(client->http);
httpClose(client->http);
ippDelete(client->request);
ippDelete(client->response);
free(client);
}
static void
delete_job(ippeve_job_t *job)
{
if (Verbosity)
fprintf(stderr, "[Job %d] Removing job from history.\n", job->id);
ippDelete(job->attrs);
if (job->message)
free(job->message);
if (job->filename)
{
if (!KeepFiles)
unlink(job->filename);
free(job->filename);
}
free(job);
}
static void
delete_printer(ippeve_printer_t *printer)
{
if (printer->ipv4 >= 0)
close(printer->ipv4);
if (printer->ipv6 >= 0)
close(printer->ipv6);
#if HAVE_MDNSRESPONDER
if (printer->printer_ref)
DNSServiceRefDeallocate(printer->printer_ref);
if (printer->ipp_ref)
DNSServiceRefDeallocate(printer->ipp_ref);
if (printer->ipps_ref)
DNSServiceRefDeallocate(printer->ipps_ref);
if (printer->http_ref)
DNSServiceRefDeallocate(printer->http_ref);
#elif defined(HAVE_AVAHI)
avahi_threaded_poll_lock(DNSSDMaster);
if (printer->dnssd_ref)
avahi_entry_group_free(printer->dnssd_ref);
avahi_threaded_poll_unlock(DNSSDMaster);
#endif
if (printer->dnssd_name)
free(printer->dnssd_name);
if (printer->name)
free(printer->name);
if (printer->icons[0])
free(printer->icons[0]);
if (printer->strings)
free(printer->strings);
if (printer->command)
free(printer->command);
if (printer->device_uri)
free(printer->device_uri);
#if !CUPS_LITE
if (printer->ppdfile)
free(printer->ppdfile);
#endif
if (printer->directory)
free(printer->directory);
if (printer->hostname)
free(printer->hostname);
ippDelete(printer->attrs);
cupsArrayDelete(printer->jobs);
free(printer);
}
#ifdef HAVE_MDNSRESPONDER
static void DNSSD_API
dnssd_callback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
ippeve_printer_t *printer)
{
(void)sdRef;
(void)flags;
(void)domain;
(void)name;
if (errorCode == kDNSServiceErr_NameConflict)
{
fputs("DNS-SD service name collision detected.\n", stderr);
printer->dnssd_collision = 1;
}
else if (errorCode)
{
fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n", regtype, (int)errorCode);
return;
}
}
#elif defined(HAVE_AVAHI)
static void
dnssd_callback(
AvahiEntryGroup *srv,
AvahiEntryGroupState state,
void *context)
{
ippeve_printer_t *printer = (ippeve_printer_t *)context;
(void)srv;
if (state == AVAHI_ENTRY_GROUP_COLLISION)
{
fputs("DNS-SD service name collision detected.\n", stderr);
printer->dnssd_collision = 1;
}
}
static void
dnssd_client_cb(
AvahiClient *c,
AvahiClientState state,
void *userdata)
{
if (!c)
return;
switch (state)
{
default :
fprintf(stderr, "Ignored Avahi state %d.\n", state);
break;
case AVAHI_CLIENT_FAILURE:
if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
{
fputs("Avahi server crashed, exiting.\n", stderr);
exit(1);
}
break;
}
}
#endif
static void
dnssd_init(void)
{
#ifdef HAVE_MDNSRESPONDER
if (DNSServiceCreateConnection(&DNSSDMaster) != kDNSServiceErr_NoError)
{
fputs("Error: Unable to initialize DNS-SD.\n", stderr);
exit(1);
}
#elif defined(HAVE_AVAHI)
int error;
if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
{
fputs("Error: Unable to initialize DNS-SD.\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 DNS-SD.\n", stderr);
exit(1);
}
avahi_threaded_poll_start(DNSSDMaster);
#endif
}
static int
filter_cb(ippeve_filter_t *filter,
ipp_t *dst,
ipp_attribute_t *attr)
{
#ifndef _WIN32
(void)dst;
#endif
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);
}
static ippeve_job_t *
find_job(ippeve_client_t *client)
{
ipp_attribute_t *attr;
ippeve_job_t key,
*job;
if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
{
const char *uri = ippGetString(attr, 0, NULL);
const char *uriptr = strrchr(uri, '/');
if (uriptr && isdigit(uriptr[1] & 255))
key.id = atoi(uriptr + 1);
else
return (NULL);
}
else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
key.id = ippGetInteger(attr, 0);
_cupsRWLockRead(&(client->printer->rwlock));
job = (ippeve_job_t *)cupsArrayFind(client->printer->jobs, &key);
_cupsRWUnlock(&(client->printer->rwlock));
return (job);
}
static void
finish_document_data(
ippeve_client_t *client,
ippeve_job_t *job)
{
char filename[1024],
buffer[4096];
ssize_t bytes;
cups_array_t *ra;
_cups_thread_t t;
if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
{
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
goto abort_job;
}
if (Verbosity)
fprintf(stderr, "Created job file \"%s\", format \"%s\".\n", filename, job->format);
while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
{
if (write(job->fd, buffer, (size_t)bytes) < bytes)
{
int error = errno;
close(job->fd);
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
goto abort_job;
}
}
if (bytes < 0)
{
close(job->fd);
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to read print file.");
goto abort_job;
}
if (close(job->fd))
{
int error = errno;
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
goto abort_job;
}
job->fd = -1;
job->filename = strdup(filename);
job->state = IPP_JSTATE_PENDING;
t = _cupsThreadCreate((_cups_thread_func_t)process_job, job);
if (t)
{
_cupsThreadDetach(t);
}
else
{
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
goto abort_job;
}
respond_ipp(client, IPP_STATUS_OK, NULL);
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
cupsArrayAdd(ra, "job-id");
cupsArrayAdd(ra, "job-state");
cupsArrayAdd(ra, "job-state-message");
cupsArrayAdd(ra, "job-state-reasons");
cupsArrayAdd(ra, "job-uri");
copy_job_attributes(client, job, ra);
cupsArrayDelete(ra);
return;
abort_job:
job->state = IPP_JSTATE_ABORTED;
job->completed = time(NULL);
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
cupsArrayAdd(ra, "job-id");
cupsArrayAdd(ra, "job-state");
cupsArrayAdd(ra, "job-state-reasons");
cupsArrayAdd(ra, "job-uri");
copy_job_attributes(client, job, ra);
cupsArrayDelete(ra);
}
static void
finish_document_uri(
ippeve_client_t *client,
ippeve_job_t *job)
{
ipp_attribute_t *uri;
char scheme[256],
userpass[256],
hostname[256],
resource[1024];
int port;
http_uri_status_t uri_status;
http_encryption_t encryption;
http_t *http;
http_status_t status;
int infile;
char filename[1024],
buffer[4096];
ssize_t bytes;
ipp_attribute_t *attr;
cups_array_t *ra;
if (have_document_data(client))
{
flush_document_data(client);
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unexpected document data following request.");
goto abort_job;
}
if ((uri = ippFindAttribute(client->request, "document-uri", IPP_TAG_URI)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
goto abort_job;
}
if (ippGetCount(uri) != 1)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Too many document-uri values.");
goto abort_job;
}
uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
scheme, sizeof(scheme), userpass,
sizeof(userpass), hostname, sizeof(hostname),
&port, resource, sizeof(resource));
if (uri_status < HTTP_URI_STATUS_OK)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", httpURIStatusString(uri_status));
goto abort_job;
}
if (strcmp(scheme, "file") &&
#ifdef HAVE_TLS
strcmp(scheme, "https") &&
#endif
strcmp(scheme, "http"))
{
respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, "URI scheme \"%s\" not supported.", scheme);
goto abort_job;
}
if (!strcmp(scheme, "file") && access(resource, R_OK))
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
goto abort_job;
}
_cupsRWLockWrite(&(client->printer->rwlock));
if ((attr = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else
job->format = "application/octet-stream";
if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
{
_cupsRWUnlock(&(client->printer->rwlock));
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
goto abort_job;
}
_cupsRWUnlock(&(client->printer->rwlock));
if (!strcmp(scheme, "file"))
{
if ((infile = open(resource, O_RDONLY | O_BINARY)) < 0)
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
goto abort_job;
}
do
{
if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
(errno == EAGAIN || errno == EINTR))
{
bytes = 1;
}
else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
{
int error = errno;
close(job->fd);
job->fd = -1;
unlink(filename);
close(infile);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
goto abort_job;
}
}
while (bytes > 0);
close(infile);
}
else
{
#ifdef HAVE_TLS
if (port == 443 || !strcmp(scheme, "https"))
encryption = HTTP_ENCRYPTION_ALWAYS;
else
#endif
encryption = HTTP_ENCRYPTION_IF_REQUESTED;
if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to connect to %s: %s", hostname, cupsLastErrorString());
close(job->fd);
job->fd = -1;
unlink(filename);
goto abort_job;
}
httpClearFields(http);
httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
if (httpGet(http, resource))
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", strerror(errno));
close(job->fd);
job->fd = -1;
unlink(filename);
httpClose(http);
goto abort_job;
}
while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
if (status != HTTP_STATUS_OK)
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", httpStatus(status));
close(job->fd);
job->fd = -1;
unlink(filename);
httpClose(http);
goto abort_job;
}
while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
{
if (write(job->fd, buffer, (size_t)bytes) < bytes)
{
int error = errno;
close(job->fd);
job->fd = -1;
unlink(filename);
httpClose(http);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to write print file: %s", strerror(error));
goto abort_job;
}
}
httpClose(http);
}
if (close(job->fd))
{
int error = errno;
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
goto abort_job;
}
_cupsRWLockWrite(&(client->printer->rwlock));
job->fd = -1;
job->filename = strdup(filename);
job->state = IPP_JSTATE_PENDING;
_cupsRWUnlock(&(client->printer->rwlock));
process_job(job);
respond_ipp(client, IPP_STATUS_OK, NULL);
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
cupsArrayAdd(ra, "job-id");
cupsArrayAdd(ra, "job-state");
cupsArrayAdd(ra, "job-state-reasons");
cupsArrayAdd(ra, "job-uri");
copy_job_attributes(client, job, ra);
cupsArrayDelete(ra);
return;
abort_job:
job->state = IPP_JSTATE_ABORTED;
job->completed = time(NULL);
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
cupsArrayAdd(ra, "job-id");
cupsArrayAdd(ra, "job-state");
cupsArrayAdd(ra, "job-state-reasons");
cupsArrayAdd(ra, "job-uri");
copy_job_attributes(client, job, ra);
cupsArrayDelete(ra);
}
static void
flush_document_data(
ippeve_client_t *client)
{
char buffer[8192];
if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
{
while (httpRead2(client->http, buffer, sizeof(buffer)) > 0);
}
}
static int
have_document_data(
ippeve_client_t *client)
{
char temp;
if (httpGetState(client->http) != HTTP_STATE_POST_RECV)
return (0);
else
return (httpPeek(client->http, &temp, 1) > 0);
}
static void
html_escape(ippeve_client_t *client,
const char *s,
size_t slen)
{
const char *start,
*end;
start = s;
end = s + (slen > 0 ? slen : strlen(s));
while (*s && s < end)
{
if (*s == '&' || *s == '<')
{
if (s > start)
httpWrite2(client->http, start, (size_t)(s - start));
if (*s == '&')
httpWrite2(client->http, "&", 5);
else
httpWrite2(client->http, "<", 4);
start = s + 1;
}
s ++;
}
if (s > start)
httpWrite2(client->http, start, (size_t)(s - start));
}
static void
html_footer(ippeve_client_t *client)
{
html_printf(client,
"</div>\n"
"</body>\n"
"</html>\n");
httpWrite2(client->http, "", 0);
}
static void
html_header(ippeve_client_t *client,
const char *title,
int refresh)
{
html_printf(client,
"<!doctype html>\n"
"<html>\n"
"<head>\n"
"<title>%s</title>\n"
"<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
"<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title);
if (refresh > 0)
html_printf(client, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh);
html_printf(client,
"<meta name=\"viewport\" content=\"width=device-width\">\n"
"<style>\n"
"body { font-family: sans-serif; margin: 0; }\n"
"div.body { padding: 0px 10px 10px; }\n"
"span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
"span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
"table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
"table.form td, table.form th { padding: 5px 2px; }\n"
"table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
"table.form th { text-align: right; }\n"
"table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
"table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
"table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
"table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
"table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
"table.nav { border-collapse: collapse; width: 100%%; }\n"
"table.nav td { margin: 0; text-align: center; }\n"
"td.nav a, td.nav a:active, td.nav a:hover, td.nav a:hover:link, td.nav a:hover:link:visited, td.nav a:link, td.nav a:link:visited, td.nav a:visited { background: inherit; color: inherit; font-size: 80%%; text-decoration: none; }\n"
"td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
"td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
"td.nav:hover { background: #666; color: #fff; }\n"
"td.nav:active { background: #000; color: #ff0; }\n"
"</style>\n"
"</head>\n"
"<body>\n"
"<table class=\"nav\"><tr>"
"<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
"<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
"<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
"</tr></table>\n"
"<div class=\"body\">\n", !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
}
static void
html_printf(ippeve_client_t *client,
const char *format,
...)
{
va_list ap;
const char *start;
char size,
type;
int width,
prec;
char tformat[100],
*tptr,
temp[1024];
char *s;
va_start(ap, format);
start = format;
while (*format)
{
if (*format == '%')
{
if (format > start)
httpWrite2(client->http, start, (size_t)(format - start));
tptr = tformat;
*tptr++ = *format++;
if (*format == '%')
{
httpWrite2(client->http, "%", 1);
format ++;
start = format;
continue;
}
else if (strchr(" -+#\'", *format))
*tptr++ = *format++;
if (*format == '*')
{
format ++;
width = va_arg(ap, int);
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
tptr += strlen(tptr);
}
else
{
width = 0;
while (isdigit(*format & 255))
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
width = width * 10 + *format++ - '0';
}
}
if (*format == '.')
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
format ++;
if (*format == '*')
{
format ++;
prec = va_arg(ap, int);
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
tptr += strlen(tptr);
}
else
{
prec = 0;
while (isdigit(*format & 255))
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
prec = prec * 10 + *format++ - '0';
}
}
}
if (*format == 'l' && format[1] == 'l')
{
size = 'L';
if (tptr < (tformat + sizeof(tformat) - 2))
{
*tptr++ = 'l';
*tptr++ = 'l';
}
format += 2;
}
else if (*format == 'h' || *format == 'l' || *format == 'L')
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
size = *format++;
}
else
size = 0;
if (!*format)
{
start = format;
break;
}
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
type = *format++;
*tptr = '\0';
start = format;
switch (type)
{
case 'E' :
case 'G' :
case 'e' :
case 'f' :
case 'g' :
if ((size_t)(width + 2) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
httpWrite2(client->http, temp, strlen(temp));
break;
case 'B' :
case 'X' :
case 'b' :
case 'd' :
case 'i' :
case 'o' :
case 'u' :
case 'x' :
if ((size_t)(width + 2) > sizeof(temp))
break;
# ifdef HAVE_LONG_LONG
if (size == 'L')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
else
# endif
if (size == 'l')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
else
snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
httpWrite2(client->http, temp, strlen(temp));
break;
case 'p' :
if ((size_t)(width + 2) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
httpWrite2(client->http, temp, strlen(temp));
break;
case 'c' :
if (width <= 1)
{
temp[0] = (char)va_arg(ap, int);
temp[1] = '\0';
html_escape(client, temp, 1);
}
else
html_escape(client, va_arg(ap, char *), (size_t)width);
break;
case 's' :
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
html_escape(client, s, strlen(s));
break;
}
}
else
format ++;
}
if (format > start)
httpWrite2(client->http, start, (size_t)(format - start));
va_end(ap);
}
static void
ipp_cancel_job(ippeve_client_t *client)
{
ippeve_job_t *job;
if ((job = find_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
return;
}
switch (job->state)
{
case IPP_JSTATE_CANCELED :
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
"Job #%d is already canceled - can\'t cancel.", job->id);
break;
case IPP_JSTATE_ABORTED :
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
"Job #%d is already aborted - can\'t cancel.", job->id);
break;
case IPP_JSTATE_COMPLETED :
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
"Job #%d is already completed - can\'t cancel.", job->id);
break;
default :
_cupsRWLockWrite(&(client->printer->rwlock));
if (job->state == IPP_JSTATE_PROCESSING ||
(job->state == IPP_JSTATE_HELD && job->fd >= 0))
job->cancel = 1;
else
{
job->state = IPP_JSTATE_CANCELED;
job->completed = time(NULL);
}
_cupsRWUnlock(&(client->printer->rwlock));
respond_ipp(client, IPP_STATUS_OK, NULL);
break;
}
}
static void
ipp_cancel_my_jobs(
ippeve_client_t *client)
{
ippeve_job_t *job;
_cupsRWLockWrite(&client->printer->rwlock);
if ((job = client->printer->active_job) != NULL)
{
if (job->state < IPP_JSTATE_CANCELED)
{
if (job->state == IPP_JSTATE_PROCESSING || (job->state == IPP_JSTATE_HELD && job->fd >= 0))
{
job->cancel = 1;
}
else
{
job->state = IPP_JSTATE_CANCELED;
job->completed = time(NULL);
}
}
}
respond_ipp(client, IPP_STATUS_OK, NULL);
_cupsRWUnlock(&client->printer->rwlock);
}
static void
ipp_close_job(ippeve_client_t *client)
{
ippeve_job_t *job;
if ((job = find_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
return;
}
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;
}
}
static void
ipp_create_job(ippeve_client_t *client)
{
ippeve_job_t *job;
cups_array_t *ra;
if (have_document_data(client))
{
flush_document_data(client);
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Unexpected document data following request.");
return;
}
if (!valid_job_attributes(client))
return;
if ((job = create_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BUSY,
"Currently printing another job.");
return;
}
respond_ipp(client, IPP_STATUS_OK, NULL);
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
cupsArrayAdd(ra, "job-id");
cupsArrayAdd(ra, "job-state");
cupsArrayAdd(ra, "job-state-message");
cupsArrayAdd(ra, "job-state-reasons");
cupsArrayAdd(ra, "job-uri");
copy_job_attributes(client, job, ra);
cupsArrayDelete(ra);
}
static void
ipp_get_job_attributes(
ippeve_client_t *client)
{
ippeve_job_t *job;
cups_array_t *ra;
if ((job = find_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
return;
}
respond_ipp(client, IPP_STATUS_OK, NULL);
ra = ippCreateRequestedArray(client->request);
copy_job_attributes(client, job, ra);
cupsArrayDelete(ra);
}
static void
ipp_get_jobs(ippeve_client_t *client)
{
ipp_attribute_t *attr;
const char *which_jobs = NULL;
int job_comparison;
ipp_jstate_t job_state;
int first_job_id,
limit,
count;
const char *username;
ippeve_job_t *job;
cups_array_t *ra;
if ((attr = ippFindAttribute(client->request, "which-jobs",
IPP_TAG_KEYWORD)) != NULL)
{
which_jobs = ippGetString(attr, 0, NULL);
fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
}
if (!which_jobs || !strcmp(which_jobs, "not-completed"))
{
job_comparison = -1;
job_state = IPP_JSTATE_STOPPED;
}
else if (!strcmp(which_jobs, "completed"))
{
job_comparison = 1;
job_state = IPP_JSTATE_CANCELED;
}
else if (!strcmp(which_jobs, "aborted"))
{
job_comparison = 0;
job_state = IPP_JSTATE_ABORTED;
}
else if (!strcmp(which_jobs, "all"))
{
job_comparison = 1;
job_state = IPP_JSTATE_PENDING;
}
else if (!strcmp(which_jobs, "canceled"))
{
job_comparison = 0;
job_state = IPP_JSTATE_CANCELED;
}
else if (!strcmp(which_jobs, "pending"))
{
job_comparison = 0;
job_state = IPP_JSTATE_PENDING;
}
else if (!strcmp(which_jobs, "pending-held"))
{
job_comparison = 0;
job_state = IPP_JSTATE_HELD;
}
else if (!strcmp(which_jobs, "processing"))
{
job_comparison = 0;
job_state = IPP_JSTATE_PROCESSING;
}
else if (!strcmp(which_jobs, "processing-stopped"))
{
job_comparison = 0;
job_state = IPP_JSTATE_STOPPED;
}
else
{
respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
"The which-jobs value \"%s\" is not supported.", which_jobs);
ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
"which-jobs", NULL, which_jobs);
return;
}
if ((attr = ippFindAttribute(client->request, "limit",
IPP_TAG_INTEGER)) != NULL)
{
limit = ippGetInteger(attr, 0);
fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
}
else
limit = 0;
if ((attr = ippFindAttribute(client->request, "first-job-id",
IPP_TAG_INTEGER)) != NULL)
{
first_job_id = ippGetInteger(attr, 0);
fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, first_job_id);
}
else
first_job_id = 1;
username = NULL;
if ((attr = ippFindAttribute(client->request, "my-jobs",
IPP_TAG_BOOLEAN)) != NULL)
{
int my_jobs = ippGetBoolean(attr, 0);
fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, my_jobs ? "true" : "false");
if (my_jobs)
{
if ((attr = ippFindAttribute(client->request, "requesting-user-name",
IPP_TAG_NAME)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Need requesting-user-name with my-jobs.");
return;
}
username = ippGetString(attr, 0, NULL);
fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", client->hostname, username);
}
}
ra = ippCreateRequestedArray(client->request);
respond_ipp(client, IPP_STATUS_OK, NULL);
_cupsRWLockRead(&(client->printer->rwlock));
for (count = 0, job = (ippeve_job_t *)cupsArrayFirst(client->printer->jobs);
(limit <= 0 || count < limit) && job;
job = (ippeve_job_t *)cupsArrayNext(client->printer->jobs))
{
if ((job_comparison < 0 && job->state > job_state) ||
(job_comparison == 0 && job->state != job_state) ||
(job_comparison > 0 && job->state < job_state) ||
job->id < first_job_id ||
(username && job->username &&
strcasecmp(username, job->username)))
continue;
if (count > 0)
ippAddSeparator(client->response);
count ++;
copy_job_attributes(client, job, ra);
}
cupsArrayDelete(ra);
_cupsRWUnlock(&(client->printer->rwlock));
}
static void
ipp_get_printer_attributes(
ippeve_client_t *client)
{
cups_array_t *ra;
ippeve_printer_t *printer;
ra = ippCreateRequestedArray(client->request);
printer = client->printer;
respond_ipp(client, IPP_STATUS_OK, NULL);
_cupsRWLockRead(&(printer->rwlock));
copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
IPP_TAG_CUPS_CONST);
if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
if (!ra || cupsArrayFind(ra, "printer-current-time"))
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
if (!ra || cupsArrayFind(ra, "printer-icons"))
{
char uris[3][1024];
const char *values[3];
httpAssembleURI(HTTP_URI_CODING_ALL, uris[0], sizeof(uris[0]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon-sm.png");
httpAssembleURI(HTTP_URI_CODING_ALL, uris[1], sizeof(uris[1]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon.png");
httpAssembleURI(HTTP_URI_CODING_ALL, uris[2], sizeof(uris[2]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon-lg.png");
values[0] = uris[0];
values[1] = uris[1];
values[2] = uris[2];
ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons", 3, NULL, values);
}
if (!ra || cupsArrayFind(ra, "printer-more-info"))
{
char uri[1024];
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/");
ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, uri);
}
if (!ra || cupsArrayFind(ra, "printer-state"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
if (!ra || cupsArrayFind(ra, "printer-state-message"))
{
static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
}
if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
{
if (printer->state_reasons == IPPEVE_PREASON_NONE)
{
ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none");
}
else
{
ipp_attribute_t *attr = NULL;
ippeve_preason_t bit;
int i;
char reason[32];
for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
{
if (printer->state_reasons & bit)
{
snprintf(reason, sizeof(reason), "%s-%s", ippeve_preason_strings[i], printer->state == IPP_PSTATE_IDLE ? "report" : printer->state == IPP_PSTATE_PROCESSING ? "warning" : "error");
if (attr)
ippSetString(client->response, &attr, ippGetCount(attr), reason);
else
attr = ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, reason);
}
}
}
}
if (!ra || cupsArrayFind(ra, "printer-strings-uri"))
{
char uri[1024];
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/en.strings");
ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-strings-uri", NULL, uri);
}
if (!ra || cupsArrayFind(ra, "printer-supply-info-uri"))
{
char uri[1024];
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/supplies");
ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, uri);
}
if (!ra || cupsArrayFind(ra, "printer-up-time"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
if (!ra || cupsArrayFind(ra, "printer-uri-supported"))
{
char uris[2][1024];
const char *values[2];
int num_values = 0;
httpAssembleURI(HTTP_URI_CODING_ALL, uris[0], sizeof(uris[0]), "ipp", NULL, client->host_field, client->host_port, "/ipp/print");
values[num_values ++] = uris[0];
#ifdef HAVE_TLS
httpAssembleURI(HTTP_URI_CODING_ALL, uris[1], sizeof(uris[1]), "ipps", NULL, client->host_field, client->host_port, "/ipp/print");
values[num_values ++] = uris[1];
#endif
ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", num_values, NULL, values);
}
if (!ra || cupsArrayFind(ra, "queued-job-count"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
_cupsRWUnlock(&(printer->rwlock));
cupsArrayDelete(ra);
}
static void
ipp_identify_printer(
ippeve_client_t *client)
{
ipp_attribute_t *actions,
*message;
actions = ippFindAttribute(client->request, "identify-actions", IPP_TAG_KEYWORD);
message = ippFindAttribute(client->request, "message", IPP_TAG_TEXT);
if (!actions || ippContainsString(actions, "sound"))
{
#ifdef __APPLE__
pid_t pid;
static const char * const afplay[3] =
{
"/usr/bin/afplay",
"/System/Library/Sounds/Ping.aiff",
NULL
};
posix_spawn(&pid, afplay[0], NULL, NULL, (char **)afplay, NULL);
#else
putchar(0x07);
fflush(stdout);
#endif
}
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);
}
static void
ipp_print_job(ippeve_client_t *client)
{
ippeve_job_t *job;
if (!valid_job_attributes(client))
{
flush_document_data(client);
return;
}
if (!have_document_data(client))
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
return;
}
if ((job = create_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
return;
}
finish_document_data(client, job);
}
static void
ipp_print_uri(ippeve_client_t *client)
{
ippeve_job_t *job;
if (!valid_job_attributes(client))
{
flush_document_data(client);
return;
}
if ((job = create_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
return;
}
finish_document_uri(client, job);
}
static void
ipp_send_document(
ippeve_client_t *client)
{
ippeve_job_t *job;
ipp_attribute_t *attr;
int have_data;
if ((job = find_job(client)) == NULL)
{
flush_document_data(client);
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
return;
}
have_data = have_document_data(client);
if ((job->filename || job->fd >= 0) && have_data)
{
flush_document_data(client);
respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
return;
}
else if (job->state > IPP_JSTATE_HELD && have_data)
{
flush_document_data(client);
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
return;
}
if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
{
flush_document_data(client);
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
return;
}
else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
{
flush_document_data(client);
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
return;
}
else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1)
{
flush_document_data(client);
respond_unsupported(client, attr);
return;
}
if (have_data && !valid_doc_attributes(client))
{
flush_document_data(client);
return;
}
if (!have_data && !job->filename)
job->state = IPP_JSTATE_ABORTED;
_cupsRWLockWrite(&(client->printer->rwlock));
copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else
job->format = "application/octet-stream";
_cupsRWUnlock(&(client->printer->rwlock));
if (have_data)
finish_document_data(client, job);
}
static void
ipp_send_uri(ippeve_client_t *client)
{
ippeve_job_t *job;
ipp_attribute_t *attr;
if ((job = find_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
return;
}
if (job->filename || job->fd >= 0)
{
respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
return;
}
else if (job->state > IPP_JSTATE_HELD)
{
flush_document_data(client);
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
return;
}
if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
return;
}
else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
return;
}
else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1)
{
respond_unsupported(client, attr);
return;
}
if (!valid_doc_attributes(client))
{
flush_document_data(client);
return;
}
_cupsRWLockWrite(&(client->printer->rwlock));
copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else
job->format = "application/octet-stream";
_cupsRWUnlock(&(client->printer->rwlock));
finish_document_uri(client, job);
}
static void
ipp_validate_job(ippeve_client_t *client)
{
if (valid_job_attributes(client))
respond_ipp(client, IPP_STATUS_OK, NULL);
}
static int
ippserver_attr_cb(
_ipp_file_t *f,
void *user_data,
const char *attr)
{
int i,
result;
static const char * const ignored[] =
{
"attributes-charset",
"attributes-natural-language",
"charset-configured",
"charset-supported",
"device-service-count",
"device-uuid",
"document-format-varying-attributes",
"generated-natural-language-supported",
"identify-actions-default",
"identify-actions-supported",
"ipp-features-supported",
"ipp-versions-supproted",
"ippget-event-life",
"job-hold-until-supported",
"job-hold-until-time-supported",
"job-ids-supported",
"job-k-octets-supported",
"job-settable-attributes-supported",
"multiple-document-jobs-supported",
"multiple-operation-time-out",
"multiple-operation-time-out-action",
"natural-language-configured",
"notify-attributes-supported",
"notify-events-default",
"notify-events-supported",
"notify-lease-duration-default",
"notify-lease-duration-supported",
"notify-max-events-supported",
"notify-pull-method-supported",
"operations-supported",
"printer-alert",
"printer-alert-description",
"printer-camera-image-uri",
"printer-charge-info",
"printer-charge-info-uri",
"printer-config-change-date-time",
"printer-config-change-time",
"printer-current-time",
"printer-detailed-status-messages",
"printer-dns-sd-name",
"printer-fax-log-uri",
"printer-get-attributes-supported",
"printer-icons",
"printer-id",
"printer-info",
"printer-is-accepting-jobs",
"printer-message-date-time",
"printer-message-from-operator",
"printer-message-time",
"printer-more-info",
"printer-service-type",
"printer-settable-attributes-supported",
"printer-state",
"printer-state-message",
"printer-state-reasons",
"printer-static-resource-directory-uri",
"printer-static-resource-k-octets-free",
"printer-static-resource-k-octets-supported",
"printer-strings-languages-supported",
"printer-strings-uri",
"printer-supply-info-uri",
"printer-up-time",
"printer-uri-supported",
"printer-xri-supported",
"queued-job-count",
"reference-uri-scheme-supported",
"uri-authentication-supported",
"uri-security-supported",
"which-jobs-supported",
"xri-authentication-supported",
"xri-security-supported",
"xri-uri-scheme-supported"
};
(void)f;
(void)user_data;
for (i = 0, result = 1; i < (int)(sizeof(ignored) / sizeof(ignored[0])); i ++)
{
if ((result = strcmp(attr, ignored[i])) <= 0)
break;
}
return (result != 0);
}
static int
ippserver_error_cb(
_ipp_file_t *f,
void *user_data,
const char *error)
{
(void)f;
(void)user_data;
_cupsLangPrintf(stderr, "%s\n", error);
return (1);
}
static int
ippserver_token_cb(
_ipp_file_t *f,
_ipp_vars_t *vars,
void *user_data,
const char *token)
{
(void)vars;
(void)user_data;
if (!token)
{
f->attrs = ippNew();
f->group_tag = IPP_TAG_PRINTER;
}
else
{
_cupsLangPrintf(stderr, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token, f->linenum, f->filename);
}
return (1);
}
static ipp_t *
load_ippserver_attributes(
const char *servername,
int serverport,
const char *filename,
cups_array_t *docformats)
{
ipp_t *attrs;
_ipp_vars_t vars;
char temp[256];
(void)docformats;
_ippVarsInit(&vars, (_ipp_fattr_cb_t)ippserver_attr_cb, (_ipp_ferror_cb_t)ippserver_error_cb, (_ipp_ftoken_cb_t)ippserver_token_cb);
if (servername)
{
_ippVarsSet(&vars, "SERVERNAME", servername);
}
else
{
httpGetHostname(NULL, temp, sizeof(temp));
_ippVarsSet(&vars, "SERVERNAME", temp);
}
snprintf(temp, sizeof(temp), "%d", serverport);
_ippVarsSet(&vars, "SERVERPORT", temp);
attrs = _ippFileParse(&vars, filename, NULL);
_ippVarsDeinit(&vars);
return (attrs);
}
static ipp_t *
load_legacy_attributes(
const char *make,
const char *model,
int ppm,
int ppm_color,
int duplex,
cups_array_t *docformats)
{
int i;
ipp_t *attrs,
*col;
ipp_attribute_t *attr;
char device_id[1024],
*ptr,
make_model[128];
const char *format,
*prefix;
int num_media;
const char * const *media;
int num_ready;
const char * const *ready;
pwg_media_t *pwg;
static const char * const media_supported[] =
{
"na_letter_8.5x11in",
"na_legal_8.5x14in",
"iso_a4_210x297mm",
"na_number-10_4.125x9.5in",
"iso_dl_110x220mm"
};
static const char * const media_supported_color[] =
{
"na_letter_8.5x11in",
"na_legal_8.5x14in",
"iso_a4_210x297mm",
"na_number-10_4.125x9.5in",
"iso_dl_110x220mm",
"na_index-3x5_3x5in",
"oe_photo-l_3.5x5in",
"na_index-4x6_4x6in",
"iso_a6_105x148mm",
"na_5x7_5x7in",
"iso_a5_148x210mm",
};
static const char * const media_ready[] =
{
"na_letter_8.5x11in",
"na_number-10_4.125x9.5in"
};
static const char * const media_ready_color[] =
{
"na_letter_8.5x11in",
"na_index-4x6_4x6in"
};
static const char * const media_source_supported[] =
{
"auto",
"main",
"manual",
"by-pass-tray"
};
static const char * const media_source_supported_color[] =
{
"auto",
"main",
"photo"
};
static const char * const media_type_supported[] =
{
"auto",
"cardstock",
"envelope",
"labels",
"other",
"stationery",
"stationery-letterhead",
"transparency"
};
static const char * const media_type_supported_color[] =
{
"auto",
"cardstock",
"envelope",
"labels",
"other",
"stationery",
"stationery-letterhead",
"transparency",
"photographic-glossy",
"photographic-high-gloss",
"photographic-matte",
"photographic-satin",
"photographic-semi-gloss"
};
static const int media_bottom_margin_supported[] =
{
635
};
static const int media_bottom_margin_supported_color[] =
{
0,
1168
};
static const int media_lr_margin_supported[] =
{
340,
635
};
static const int media_lr_margin_supported_color[] =
{
0,
340,
635
};
static const int media_top_margin_supported[] =
{
635
};
static const int media_top_margin_supported_color[] =
{
0,
102
};
static const int orientation_requested_supported[4] =
{
IPP_ORIENT_PORTRAIT,
IPP_ORIENT_LANDSCAPE,
IPP_ORIENT_REVERSE_LANDSCAPE,
IPP_ORIENT_REVERSE_PORTRAIT
};
static const char * const overrides_supported[] =
{
"document-numbers",
"media",
"media-col",
"orientation-requested",
"pages"
};
static const char * const print_color_mode_supported[] =
{
"monochrome"
};
static const char * const print_color_mode_supported_color[] =
{
"auto",
"color",
"monochrome"
};
static const int print_quality_supported[] =
{
IPP_QUALITY_DRAFT,
IPP_QUALITY_NORMAL,
IPP_QUALITY_HIGH
};
static const char * const printer_input_tray[] =
{
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
"type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
"type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
};
static const char * const printer_input_tray_color[] =
{
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
};
static const char * const printer_supply[] =
{
"index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
"maxcapacity=100;level=25;colorantname=unknown;",
"index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
"maxcapacity=100;level=75;colorantname=black;"
};
static const char * const printer_supply_color[] =
{
"index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
"maxcapacity=100;level=25;colorantname=unknown;",
"index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
"maxcapacity=100;level=75;colorantname=black;",
"index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
"maxcapacity=100;level=50;colorantname=cyan;",
"index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
"maxcapacity=100;level=33;colorantname=magenta;",
"index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
"maxcapacity=100;level=67;colorantname=yellow;"
};
static const char * const printer_supply_description[] =
{
"Toner Waste Tank",
"Black Toner"
};
static const char * const printer_supply_description_color[] =
{
"Ink Waste Tank",
"Black Ink",
"Cyan Ink",
"Magenta Ink",
"Yellow Ink"
};
static const int pwg_raster_document_resolution_supported[] =
{
300,
600
};
static const char * const pwg_raster_document_type_supported[] =
{
"black_1",
"sgray_8"
};
static const char * const pwg_raster_document_type_supported_color[] =
{
"black_1",
"sgray_8",
"srgb_8",
"srgb_16"
};
static const char * const sides_supported[] =
{
"one-sided",
"two-sided-long-edge",
"two-sided-short-edge"
};
static const char * const urf_supported[] =
{
"CP1",
"IS1-4-5-19",
"MT1-2-3-4-5-6",
"RS600",
"V1.4",
"W8"
};
static const char * const urf_supported_color[] =
{
"CP1",
"IS1-4-5-7-19",
"MT1-2-3-4-5-6-8-9-10-11-12-13",
"RS600",
"SRGB24",
"V1.4",
"W8"
};
static const char * const urf_supported_color_duplex[] =
{
"CP1",
"IS1-4-5-7-19",
"MT1-2-3-4-5-6-8-9-10-11-12-13",
"RS600",
"SRGB24",
"V1.4",
"W8",
"DM3"
};
static const char * const urf_supported_duplex[] =
{
"CP1",
"IS1-4-5-19",
"MT1-2-3-4-5-6",
"RS600",
"V1.4",
"W8",
"DM1"
};
attrs = ippNew();
if (ppm_color > 0)
{
num_media = (int)(sizeof(media_supported_color) / sizeof(media_supported_color[0]));
media = media_supported_color;
num_ready = (int)(sizeof(media_ready_color) / sizeof(media_ready_color[0]));
ready = media_ready_color;
}
else
{
num_media = (int)(sizeof(media_supported) / sizeof(media_supported[0]));
media = media_supported;
num_ready = (int)(sizeof(media_ready) / sizeof(media_ready[0]));
ready = media_ready;
}
ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0);
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, (cupsArrayFind(docformats, (void *)"application/pdf") != NULL || cupsArrayFind(docformats, (void *)"image/jpeg") != NULL) ? 999 : 1);
if (cupsArrayFind(docformats, (void *)"application/pdf"))
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 1023);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template-supported", NULL, "none");
col = ippNew();
ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-database", col);
ippDelete(col);
col = ippNew();
ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
ippDelete(col);
col = ippNew();
ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-ready", col);
ippDelete(col);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishings-col-supported", NULL, "finishing-template");
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", IPP_FINISHINGS_NONE);
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE);
if (ppm_color > 0)
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported) / sizeof(media_bottom_margin_supported[0])), media_bottom_margin_supported);
else
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported_color) / sizeof(media_bottom_margin_supported_color[0])), media_bottom_margin_supported_color);
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_media, NULL);
for (i = 0; i < num_media; i ++)
{
int bottom, left,
right, top;
const char *source;
pwg = pwgMediaForPWG(media[i]);
if (pwg->width < 21000 && pwg->length < 21000)
{
source = "photo";
bottom =
left =
right =
top = 0;
}
else if (pwg->width < 21000)
{
source = "by-pass-tray";
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
left =
right = media_lr_margin_supported[1];
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
}
else if (pwg->width == 21000)
{
source = NULL;
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
left =
right = media_lr_margin_supported[0];
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
}
else
{
source = NULL;
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
left =
right = media_lr_margin_supported[1];
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
}
col = create_media_col(media[i], source, NULL, pwg->width, pwg->length, bottom, left, right, top);
ippSetCollection(attrs, &attr, i, col);
ippDelete(col);
}
pwg = pwgMediaForPWG(ready[0]);
if (pwg->width == 21000)
col = create_media_col(ready[0], "main", "stationery", pwg->width, pwg->length, ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0], media_lr_margin_supported[0], media_lr_margin_supported[0], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
else
col = create_media_col(ready[0], "main", "stationery", pwg->width, pwg->length, ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0], media_lr_margin_supported[1], media_lr_margin_supported[1], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
ippDelete(col);
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-ready", num_ready, NULL);
for (i = 0; i < num_ready; i ++)
{
int bottom, left,
right, top;
const char *source,
*type;
pwg = pwgMediaForPWG(ready[i]);
if (pwg->width < 21000 && pwg->length < 21000)
{
source = "photo";
type = "photographic-glossy";
bottom =
left =
right =
top = 0;
}
else if (pwg->width < 21000)
{
source = "by-pass-tray";
type = "envelope";
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
left =
right = media_lr_margin_supported[1];
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
}
else if (pwg->width == 21000)
{
source = "main";
type = "stationery";
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
left =
right = media_lr_margin_supported[0];
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
}
else
{
source = "main";
type = "stationery";
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
left =
right = media_lr_margin_supported[1];
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
}
col = create_media_col(ready[i], source, type, pwg->width, pwg->length, bottom, left, right, top);
ippSetCollection(attrs, &attr, i, col);
ippDelete(col);
}
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media[0]);
if (ppm_color > 0)
{
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported_color) / sizeof(media_lr_margin_supported_color[0])), media_lr_margin_supported_color);
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported_color) / sizeof(media_lr_margin_supported_color[0])), media_lr_margin_supported_color);
}
else
{
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported) / sizeof(media_lr_margin_supported[0])), media_lr_margin_supported);
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported) / sizeof(media_lr_margin_supported[0])), media_lr_margin_supported);
}
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", num_ready, NULL, ready);
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", num_media, NULL, media);
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", num_media, NULL);
for (i = 0; i < num_media; i ++)
{
pwg = pwgMediaForPWG(media[i]);
col = create_media_size(pwg->width, pwg->length);
ippSetCollection(attrs, &attr, i, col);
ippDelete(col);
}
if (ppm_color > 0)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported_color) / sizeof(media_source_supported_color[0])), NULL, media_source_supported_color);
else
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported) / sizeof(media_source_supported[0])), NULL, media_source_supported);
if (ppm_color > 0)
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported) / sizeof(media_top_margin_supported[0])), media_top_margin_supported);
else
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported_color) / sizeof(media_top_margin_supported_color[0])), media_top_margin_supported_color);
if (ppm_color > 0)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported_color) / sizeof(media_type_supported_color[0])), NULL, media_type_supported_color);
else
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported) / sizeof(media_type_supported[0])), NULL, media_type_supported);
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
if (cupsArrayFind(docformats, (void *)"application/pdf") || cupsArrayFind(docformats, (void *)"image/jpeg"))
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
else
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", IPP_ORIENT_PORTRAIT);
if (ppm_color > 0)
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-up");
else
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
if (ppm_color > 0)
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-up");
else
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
if (cupsArrayFind(docformats, (void *)"application/pdf"))
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", cupsArrayFind(docformats, (void *)"application/pdf") != NULL);
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppm);
if (ppm_color > 0)
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppm_color);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppm_color > 0 ? "auto" : "monochrome");
if (ppm_color > 0)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color) / sizeof(print_color_mode_supported_color[0])), NULL, print_color_mode_supported_color);
else
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
ptr = device_id + strlen(device_id);
prefix = "CMD:";
for (format = (const char *)cupsArrayFirst(docformats); format; format = (const char *)cupsArrayNext(docformats))
{
if (!strcasecmp(format, "application/pdf"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix);
else if (!strcasecmp(format, "application/postscript"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix);
else if (!strcasecmp(format, "application/vnd.hp-PCL"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix);
else if (!strcasecmp(format, "image/jpeg"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix);
else if (!strcasecmp(format, "image/png"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix);
else if (!strcasecmp(format, "image/pwg-raster"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPWG", prefix);
else if (!strcasecmp(format, "image/urf"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sURF", prefix);
else
continue;
ptr += strlen(ptr);
prefix = ",";
}
if (ptr < (device_id + sizeof(device_id) - 1))
{
*ptr++ = ';';
*ptr = '\0';
}
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
if (ppm_color > 0)
{
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray_color[0], (int)strlen(printer_input_tray_color[0]));
for (i = 1; i < (int)(sizeof(printer_input_tray_color) / sizeof(printer_input_tray_color[0])); i ++)
ippSetOctetString(attrs, &attr, i, printer_input_tray_color[i], (int)strlen(printer_input_tray_color[i]));
}
else
{
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray[0], (int)strlen(printer_input_tray[0]));
for (i = 1; i < (int)(sizeof(printer_input_tray) / sizeof(printer_input_tray[0])); i ++)
ippSetOctetString(attrs, &attr, i, printer_input_tray[i], (int)strlen(printer_input_tray[i]));
}
snprintf(make_model, sizeof(make_model), "%s %s", make, model);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, make_model);
ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
if (ppm_color > 0)
{
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], (int)strlen(printer_supply_color[0]));
for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
ippSetOctetString(attrs, &attr, i, printer_supply_color[i], (int)strlen(printer_supply_color[i]));
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description_color) / sizeof(printer_supply_description_color[0])), NULL, printer_supply_description_color);
}
else
{
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], (int)strlen(printer_supply[0]));
for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
ippSetOctetString(attrs, &attr, i, printer_supply[i], (int)strlen(printer_supply[i]));
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description) / sizeof(printer_supply_description[0])), NULL, printer_supply_description);
}
if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
{
ippAddResolutions(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", (int)(sizeof(pwg_raster_document_resolution_supported) / sizeof(pwg_raster_document_resolution_supported[0])), IPP_RES_PER_INCH, pwg_raster_document_resolution_supported, pwg_raster_document_resolution_supported);
if (ppm_color > 0 && duplex)
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "rotated");
else if (duplex)
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
if (ppm_color > 0)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color) / sizeof(pwg_raster_document_type_supported_color[0])), NULL, pwg_raster_document_type_supported_color);
else
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
}
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
if (duplex)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
else
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
if (cupsArrayFind(docformats, (void *)"image/urf"))
{
if (ppm_color > 0)
{
if (duplex)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color_duplex) / sizeof(urf_supported_color_duplex[0])), NULL, urf_supported_color_duplex);
else
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color) / sizeof(urf_supported_color[0])), NULL, urf_supported_color);
}
else if (duplex)
{
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_duplex) / sizeof(urf_supported_duplex[0])), NULL, urf_supported_duplex);
}
else
{
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])), NULL, urf_supported);
}
}
return (attrs);
}
#if !CUPS_LITE
static ipp_t *
load_ppd_attributes(
const char *ppdfile,
cups_array_t *docformats)
{
int i, j;
ipp_t *attrs;
ipp_attribute_t *attr;
ipp_t *col;
ppd_file_t *ppd;
ppd_attr_t *ppd_attr;
ppd_choice_t *ppd_choice;
ppd_size_t *ppd_size;
pwg_size_t *pwg_size,
*default_size = NULL;
const char *default_source = NULL,
*default_type = NULL;
pwg_map_t *pwg_map;
_ppd_cache_t *pc;
_pwg_finishings_t *finishings;
const char *template;
int num_margins;
int margins[10];
int xres,
yres;
int num_urf;
const char *urf[10];
char urf_rs[32];
static const int orientation_requested_supported[4] =
{
IPP_ORIENT_PORTRAIT,
IPP_ORIENT_LANDSCAPE,
IPP_ORIENT_REVERSE_LANDSCAPE,
IPP_ORIENT_REVERSE_PORTRAIT
};
static const char * const overrides_supported[] =
{
"document-numbers",
"media",
"media-col",
"orientation-requested",
"pages"
};
static const char * const print_color_mode_supported[] =
{
"monochrome"
};
static const char * const print_color_mode_supported_color[] =
{
"auto",
"color",
"monochrome"
};
static const int print_quality_supported[] =
{
IPP_QUALITY_DRAFT,
IPP_QUALITY_NORMAL,
IPP_QUALITY_HIGH
};
static const char * const printer_supply[] =
{
"index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
"maxcapacity=100;level=25;colorantname=unknown;",
"index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
"maxcapacity=100;level=75;colorantname=black;"
};
static const char * const printer_supply_color[] =
{
"index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
"maxcapacity=100;level=25;colorantname=unknown;",
"index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
"maxcapacity=100;level=75;colorantname=black;",
"index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
"maxcapacity=100;level=50;colorantname=cyan;",
"index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
"maxcapacity=100;level=33;colorantname=magenta;",
"index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
"maxcapacity=100;level=67;colorantname=yellow;"
};
static const char * const printer_supply_description[] =
{
"Toner Waste Tank",
"Black Toner"
};
static const char * const printer_supply_description_color[] =
{
"Ink Waste Tank",
"Black Ink",
"Cyan Ink",
"Magenta Ink",
"Yellow Ink"
};
static const char * const pwg_raster_document_type_supported[] =
{
"black_1",
"sgray_8"
};
static const char * const pwg_raster_document_type_supported_color[] =
{
"black_1",
"sgray_8",
"srgb_8",
"srgb_16"
};
static const char * const sides_supported[] =
{
"one-sided",
"two-sided-long-edge",
"two-sided-short-edge"
};
if ((ppd = ppdOpenFile(ppdfile)) == NULL)
{
ppd_status_t status;
status = ppdLastError(&i);
_cupsLangPrintf(stderr, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile, ppdErrorString(status), i);
return (NULL);
}
ppdMarkDefaults(ppd);
pc = _ppdCacheCreateWithPPD(ppd);
if ((ppd_size = ppdPageSize(ppd, NULL)) != NULL)
{
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
{
if (!strcmp(pwg_size->map.ppd, ppd_size->name))
{
default_size = pwg_size;
break;
}
}
}
if (!default_size)
{
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
{
if (!strcmp(pwg_size->map.ppd, "Letter") || !strcmp(pwg_size->map.ppd, "A4"))
{
default_size = pwg_size;
break;
}
}
if (!default_size)
default_size = pc->sizes;
}
if ((ppd_choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
default_source = _ppdCacheGetSource(pc, ppd_choice->choice);
if ((ppd_choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
default_source = _ppdCacheGetType(pc, ppd_choice->choice);
if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL)
{
if ((i = sscanf(ppd_attr->value, "%dx%d", &xres, &yres)) == 1)
yres = xres;
else if (i < 0)
xres = yres = 300;
}
else
{
xres = yres = 300;
}
snprintf(urf_rs, sizeof(urf_rs), "RS%d", yres < xres ? yres : xres);
num_urf = 0;
urf[num_urf ++] = "V1.4";
urf[num_urf ++] = "CP1";
urf[num_urf ++] = urf_rs;
urf[num_urf ++] = "W8";
if (pc->sides_2sided_long)
urf[num_urf ++] = "DM1";
if (ppd->color_device)
urf[num_urf ++] = "SRGB24";
cupsArrayAdd(docformats, "application/pdf");
cupsArrayAdd(docformats, "application/postscript");
cupsArrayAdd(docformats, "image/jpeg");
attrs = ippNew();
ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", (char)ppd->color_device);
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template-supported", cupsArrayCount(pc->templates) + 1, NULL, NULL);
ippSetString(attrs, &attr, 0, "none");
for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
ippSetString(attrs, &attr, i, template);
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-database", cupsArrayCount(pc->templates) + 1, NULL);
col = ippNew();
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
ippSetCollection(attrs, &attr, 0, col);
ippDelete(col);
for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
{
col = ippNew();
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
ippSetCollection(attrs, &attr, i, col);
ippDelete(col);
}
col = ippNew();
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
ippDelete(col);
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-ready", cupsArrayCount(pc->templates) + 1, NULL);
col = ippNew();
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
ippSetCollection(attrs, &attr, 0, col);
ippDelete(col);
for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
{
col = ippNew();
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
ippSetCollection(attrs, &attr, i, col);
ippDelete(col);
}
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishings-col-supported", NULL, "finishing-template");
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", cupsArrayCount(pc->finishings) + 1, NULL);
ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
ippSetInteger(attrs, &attr, i, (int)finishings->value);
attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", cupsArrayCount(pc->finishings) + 1, NULL);
ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
ippSetInteger(attrs, &attr, i, (int)finishings->value);
for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
{
for (j = 0; j < num_margins; j ++)
{
if (margins[j] == pwg_size->bottom)
break;
}
if (j >= num_margins)
margins[num_margins ++] = pwg_size->bottom;
}
for (i = 0; i < (num_margins - 1); i ++)
{
for (j = i + 1; j < num_margins; j ++)
{
if (margins[i] > margins[j])
{
int mtemp = margins[i];
margins[i] = margins[j];
margins[j] = mtemp;
}
}
}
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", num_margins, margins);
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", pc->num_sizes, NULL);
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
{
col = create_media_col(pwg_size->map.pwg, NULL, NULL, pwg_size->width, pwg_size->length, pwg_size->bottom, pwg_size->left, pwg_size->right, pwg_size->top);
ippSetCollection(attrs, &attr, i, col);
ippDelete(col);
}
col = create_media_col(default_size->map.pwg, default_source, default_type, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
ippDelete(col);
col = create_media_col(default_size->map.pwg, default_source, default_type, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col);
ippDelete(col);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, default_size->map.pwg);
for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
{
for (j = 0; j < num_margins; j ++)
{
if (margins[j] == pwg_size->left)
break;
}
if (j >= num_margins)
margins[num_margins ++] = pwg_size->left;
}
for (i = 0; i < (num_margins - 1); i ++)
{
for (j = i + 1; j < num_margins; j ++)
{
if (margins[i] > margins[j])
{
int mtemp = margins[i];
margins[i] = margins[j];
margins[j] = mtemp;
}
}
}
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", num_margins, margins);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, default_size->map.pwg);
for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
{
for (j = 0; j < num_margins; j ++)
{
if (margins[j] == pwg_size->right)
break;
}
if (j >= num_margins)
margins[num_margins ++] = pwg_size->right;
}
for (i = 0; i < (num_margins - 1); i ++)
{
for (j = i + 1; j < num_margins; j ++)
{
if (margins[i] > margins[j])
{
int mtemp = margins[i];
margins[i] = margins[j];
margins[j] = mtemp;
}
}
}
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", num_margins, margins);
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-supported", pc->num_sizes, NULL, NULL);
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
ippSetString(attrs, &attr, i, pwg_size->map.pwg);
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", pc->num_sizes, NULL);
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
{
col = create_media_size(pwg_size->width, pwg_size->length);
ippSetCollection(attrs, &attr, i, col);
ippDelete(col);
}
if (pc->num_sources > 0)
{
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source-supported", pc->num_sources, NULL, NULL);
for (i = 0, pwg_map = pc->sources; i < pc->num_sources; i ++, pwg_map ++)
ippSetString(attrs, &attr, i, pwg_map->pwg);
}
else
{
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", NULL, "auto");
}
for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
{
for (j = 0; j < num_margins; j ++)
{
if (margins[j] == pwg_size->top)
break;
}
if (j >= num_margins)
margins[num_margins ++] = pwg_size->top;
}
for (i = 0; i < (num_margins - 1); i ++)
{
for (j = i + 1; j < num_margins; j ++)
{
if (margins[i] > margins[j])
{
int mtemp = margins[i];
margins[i] = margins[j];
margins[j] = mtemp;
}
}
}
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", num_margins, margins);
if (pc->num_types > 0)
{
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type-supported", pc->num_types, NULL, NULL);
for (i = 0, pwg_map = pc->types; i < pc->num_types; i ++, pwg_map ++)
ippSetString(attrs, &attr, i, pwg_map->pwg);
}
else
{
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", NULL, "auto");
}
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
if (pc->num_bins > 0)
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-default", NULL, pc->bins->pwg);
else
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
if (pc->num_bins > 0)
{
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-supported", pc->num_bins, NULL, NULL);
for (i = 0, pwg_map = pc->bins; i < pc->num_bins; i ++, pwg_map ++)
ippSetString(attrs, &attr, i, pwg_map->pwg);
}
else
{
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
}
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppd->throughput);
if (ppd->color_device)
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppd->throughput);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppd->color_device ? "auto" : "monochrome");
if (ppd->color_device)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color) / sizeof(print_color_mode_supported_color[0])), NULL, print_color_mode_supported_color);
else
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
if ((ppd_attr = ppdFindAttr(ppd, "1284DeviceId", NULL)) != NULL)
{
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, ppd_attr->value);
}
else
{
char device_id[1024];
snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;CMD:PS;", ppd->manufacturer, ppd->modelname);
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
}
if (pc->num_sources > 0)
{
for (i = 0, attr = NULL; i < pc->num_sources; i ++)
{
char input_tray[1024];
if (!strcmp(pc->sources[i].pwg, "manual") || strstr(pc->sources[i].pwg, "-man") != NULL)
snprintf(input_tray, sizeof(input_tray), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc->sources[i].pwg);
else
snprintf(input_tray, sizeof(input_tray), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc->sources[i].pwg);
if (attr)
ippSetOctetString(attrs, &attr, i, input_tray, (int)strlen(input_tray));
else
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", input_tray, (int)strlen(input_tray));
}
}
else
{
static const char *printer_input_tray = "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto";
ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray, (int)strlen(printer_input_tray));
}
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, ppd->nickname);
ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, xres, yres);
ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, xres, yres);
if (ppd->color_device)
{
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], (int)strlen(printer_supply_color[0]));
for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
ippSetOctetString(attrs, &attr, i, printer_supply_color[i], (int)strlen(printer_supply_color[i]));
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description_color) / sizeof(printer_supply_description_color[0])), NULL, printer_supply_description_color);
}
else
{
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], (int)strlen(printer_supply[0]));
for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
ippSetOctetString(attrs, &attr, i, printer_supply[i], (int)strlen(printer_supply[i]));
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description) / sizeof(printer_supply_description[0])), NULL, printer_supply_description);
}
if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
{
ippAddResolution(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH, xres, yres);
if (pc->sides_2sided_long)
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
if (ppd->color_device)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color) / sizeof(pwg_raster_document_type_supported_color[0])), NULL, pwg_raster_document_type_supported_color);
else
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
}
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
if (pc->sides_2sided_long)
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
else
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
if (cupsArrayFind(docformats, (void *)"image/urf"))
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", num_urf, NULL, urf);
_ppdCacheDestroy(pc);
ppdClose(ppd);
return (attrs);
}
#endif
#if HAVE_LIBPAM
static int
pam_func(
int num_msg,
const struct pam_message **msg,
struct pam_response **resp,
void *appdata_ptr)
{
int i;
struct pam_response *replies;
ippeve_authdata_t *data;
if ((replies = malloc(sizeof(struct pam_response) * (size_t)num_msg)) == NULL)
return (PAM_CONV_ERR);
data = (ippeve_authdata_t *)appdata_ptr;
for (i = 0; i < num_msg; i ++)
{
switch (msg[i]->msg_style)
{
case PAM_PROMPT_ECHO_ON:
replies[i].resp_retcode = PAM_SUCCESS;
replies[i].resp = strdup(data->username);
break;
case PAM_PROMPT_ECHO_OFF:
replies[i].resp_retcode = PAM_SUCCESS;
replies[i].resp = strdup(data->password);
break;
case PAM_TEXT_INFO:
replies[i].resp_retcode = PAM_SUCCESS;
replies[i].resp = NULL;
break;
case PAM_ERROR_MSG:
replies[i].resp_retcode = PAM_SUCCESS;
replies[i].resp = NULL;
break;
default:
free(replies);
return (PAM_CONV_ERR);
}
}
*resp = replies;
return (PAM_SUCCESS);
}
#endif
static int
parse_options(ippeve_client_t *client,
cups_option_t **options)
{
char *name,
*value,
*next;
int num_options = 0;
*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);
}
static void
process_attr_message(
ippeve_job_t *job,
char *message)
{
int i,
num_options = 0;
cups_option_t *options = NULL,
*option;
ipp_attribute_t *attr;
num_options = cupsParseOptions(message + 5, num_options, &options);
for (i = num_options, option = options; i > 0; i --, option ++)
{
if (!strcmp(option->name, "job-impressions"))
{
job->impressions = atoi(option->value);
}
else if (!strcmp(option->name, "job-impressions-completed"))
{
job->impcompleted = atoi(option->value);
}
else if (!strncmp(option->name, "marker-", 7) || !strcmp(option->name, "printer-alert") || !strcmp(option->name, "printer-alert-description") || !strcmp(option->name, "printer-supply") || !strcmp(option->name, "printer-supply-description"))
{
_cupsRWLockWrite(&job->printer->rwlock);
if ((attr = ippFindAttribute(job->printer->attrs, option->name, IPP_TAG_ZERO)) != NULL)
ippDeleteAttribute(job->printer->attrs, attr);
cupsEncodeOption(job->printer->attrs, IPP_TAG_PRINTER, option->name, option->value);
_cupsRWUnlock(&job->printer->rwlock);
}
else
{
fprintf(stderr, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job->id, option->name, option->value);
}
}
cupsFreeOptions(num_options, options);
}
static void *
process_client(ippeve_client_t *client)
{
#ifdef HAVE_TLS
int first_time = 1;
#endif
while (httpWait(client->http, 30000))
{
#ifdef HAVE_TLS
if (first_time)
{
char buf[1];
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
if (!process_http(client))
break;
}
delete_client(client);
return (NULL);
}
int
process_http(ippeve_client_t *client)
{
char uri[1024];
http_state_t http_state;
http_status_t http_status;
ipp_state_t ipp_state;
char scheme[32],
userpass[128],
hostname[HTTP_MAX_HOST],
*ptr;
int port;
static const char * const http_states[] =
{
"WAITING",
"OPTIONS",
"GET",
"GET_SEND",
"HEAD",
"POST",
"POST_RECV",
"POST_SEND",
"PUT",
"PUT_RECV",
"DELETE",
"TRACE",
"CONNECT",
"STATUS",
"UNKNOWN_METHOD",
"UNKNOWN_VERSION"
};
client->username[0] = '\0';
ippDelete(client->request);
ippDelete(client->response);
client->request = NULL;
client->response = NULL;
client->operation = HTTP_STATE_WAITING;
while ((http_state = httpReadRequest(client->http, uri,
sizeof(uri))) == HTTP_STATE_WAITING)
usleep(1);
if (http_state == HTTP_STATE_ERROR)
{
if (httpError(client->http) == EPIPE)
fprintf(stderr, "%s Client closed connection.\n", client->hostname);
else
fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, strerror(httpError(client->http)));
return (0);
}
else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
{
fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
{
fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], uri);
if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
userpass, sizeof(userpass),
hostname, sizeof(hostname), &port,
client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
(http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
{
fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
if ((client->options = strchr(client->uri, '?')) != NULL)
*(client->options)++ = '\0';
client->start = time(NULL);
client->operation = httpGetState(client->http);
while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
if (http_status != HTTP_STATUS_OK)
{
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
httpGetVersion(client->http) >= HTTP_VERSION_1_1)
{
fprintf(stderr, "%s Missing Host: header.\n", client->hostname);
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
strlcpy(client->host_field, httpGetField(client->http, HTTP_FIELD_HOST), sizeof(client->host_field));
if ((ptr = strrchr(client->host_field, ':')) != NULL)
{
*ptr++ = '\0';
client->host_port = atoi(ptr);
}
else
{
client->host_port = client->printer->port;
}
if ((ptr = strstr(client->host_field, ".local")) == NULL)
ptr = strrchr(client->host_field, '.');
if (!isdigit(client->host_field[0] & 255) && client->host_field[0] != '[' && strcmp(client->host_field, client->printer->hostname) && strcmp(client->host_field, "localhost") &&
(!ptr || (strcmp(ptr, ".local") && strcmp(ptr, ".local."))))
{
fprintf(stderr, "%s Bad Host: header '%s'.\n", client->hostname, client->host_field);
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), "Upgrade"))
{
#ifdef HAVE_TLS
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
if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
return (0);
}
switch (client->operation)
{
case HTTP_STATE_OPTIONS :
return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
case HTTP_STATE_HEAD :
if (!strcmp(client->uri, "/en.strings"))
return (respond_http(client, HTTP_STATUS_OK, NULL, "text/strings", 0));
else if (!strcmp(client->uri, "/icon.png") || !strcmp(client->uri, "/icon-lg.png") || !strcmp(client->uri, "/icon-sm.png"))
return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
case HTTP_STATE_GET :
if (!strcmp(client->uri, "/en.strings"))
{
if (client->printer->strings)
{
int fd;
struct stat fileinfo;
char buffer[4096];
ssize_t bytes;
if (!stat(client->printer->strings, &fileinfo) && (fd = open(client->printer->strings, O_RDONLY | O_BINARY)) >= 0)
{
if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/strings", (size_t)fileinfo.st_size))
{
close(fd);
return (0);
}
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
httpWrite2(client->http, buffer, (size_t)bytes);
httpFlushWrite(client->http);
close(fd);
}
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
}
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
}
else if (!strcmp(client->uri, "/icon.png"))
{
if (client->printer->icons[1])
{
int fd;
struct stat fileinfo;
char buffer[4096];
ssize_t bytes;
if (!stat(client->printer->icons[1], &fileinfo) && (fd = open(client->printer->icons[1], O_RDONLY | O_BINARY)) >= 0)
{
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
{
close(fd);
return (0);
}
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
httpWrite2(client->http, buffer, (size_t)bytes);
httpFlushWrite(client->http);
close(fd);
}
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
}
else
{
fputs("Icon file is internal printer.png.\n", stderr);
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png)))
return (0);
httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png));
httpFlushWrite(client->http);
}
}
else if (!strcmp(client->uri, "/icon-lg.png"))
{
if (client->printer->icons[2])
{
int fd;
struct stat fileinfo;
char buffer[4096];
ssize_t bytes;
if (!stat(client->printer->icons[2], &fileinfo) && (fd = open(client->printer->icons[2], O_RDONLY | O_BINARY)) >= 0)
{
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
{
close(fd);
return (0);
}
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
httpWrite2(client->http, buffer, (size_t)bytes);
httpFlushWrite(client->http);
close(fd);
}
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
}
else
{
fputs("Icon file is internal printer-lg.png.\n", stderr);
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_lg_png)))
return (0);
httpWrite2(client->http, (const char *)printer_lg_png, sizeof(printer_lg_png));
httpFlushWrite(client->http);
}
}
else if (!strcmp(client->uri, "/icon-sm.png"))
{
if (client->printer->icons[0])
{
int fd;
struct stat fileinfo;
char buffer[4096];
ssize_t bytes;
if (!stat(client->printer->icons[0], &fileinfo) && (fd = open(client->printer->icons[0], O_RDONLY | O_BINARY)) >= 0)
{
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
{
close(fd);
return (0);
}
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
httpWrite2(client->http, buffer, (size_t)bytes);
httpFlushWrite(client->http);
close(fd);
}
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
}
else
{
fputs("Icon file is internal printer-sm.png.\n", stderr);
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_sm_png)))
return (0);
httpWrite2(client->http, (const char *)printer_sm_png, sizeof(printer_sm_png));
httpFlushWrite(client->http);
}
}
else
{
if ((http_status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
{
return (respond_http(client, http_status, NULL, NULL, 0));
}
if (!strcmp(client->uri, "/"))
{
return (show_status(client));
}
else if (!strcmp(client->uri, "/media"))
{
return (show_media(client));
}
else if (!strcmp(client->uri, "/supplies"))
{
return (show_supplies(client));
}
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
}
break;
case HTTP_STATE_POST :
if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
"application/ipp"))
{
return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
}
client->request = ippNew();
while ((ipp_state = ippRead(client->http,
client->request)) != IPP_STATE_DATA)
{
if (ipp_state == IPP_STATE_ERROR)
{
fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString());
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
}
return (process_ipp(client));
default :
break;
}
return (1);
}
static int
process_ipp(ippeve_client_t *client)
{
ipp_tag_t group;
ipp_attribute_t *attr;
ipp_attribute_t *charset;
ipp_attribute_t *language;
ipp_attribute_t *uri;
int major, minor;
const char *name;
http_status_t status;
debug_attributes("Request", client->request, 1);
client->operation_id = ippGetOperation(client->request);
client->response = ippNewResponse(client->request);
major = ippGetVersion(client->request, &minor);
if (major < 1 || major > 2)
{
respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
}
else if ((major * 10 + minor) > MaxVersion)
{
if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
httpFlush(client->http);
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
else if (ippGetRequestId(client->request) <= 0)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
}
else if (!ippFirstAttribute(client->request))
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
}
else
{
for (attr = ippFirstAttribute(client->request),
group = ippGetGroupTag(attr);
attr;
attr = ippNextAttribute(client->request))
{
if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Attribute groups are out of order (%x < %x).",
ippGetGroupTag(attr), group);
break;
}
else
group = ippGetGroupTag(attr);
}
if (!attr)
{
attr = ippFirstAttribute(client->request);
name = ippGetName(attr);
if (attr && name && !strcmp(name, "attributes-charset") &&
ippGetValueTag(attr) == IPP_TAG_CHARSET)
charset = attr;
else
charset = NULL;
attr = ippNextAttribute(client->request);
name = ippGetName(attr);
if (attr && name && !strcmp(name, "attributes-natural-language") &&
ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
language = attr;
else
language = NULL;
if ((attr = ippFindAttribute(client->request, "printer-uri",
IPP_TAG_URI)) != NULL)
uri = attr;
else if ((attr = ippFindAttribute(client->request, "job-uri",
IPP_TAG_URI)) != NULL)
uri = attr;
else
uri = NULL;
if (charset &&
strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Unsupported character set \"%s\".",
ippGetString(charset, 0, NULL));
}
else if (!charset || !language || !uri)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Missing required attributes.");
}
else
{
char scheme[32],
userpass[32],
host[256],
resource[256];
int port;
name = ippGetName(uri);
if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
scheme, sizeof(scheme),
userpass, sizeof(userpass),
host, sizeof(host), &port,
resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
"Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
else if ((!strcmp(name, "job-uri") &&
strncmp(resource, "/ipp/print/", 11)) ||
(!strcmp(name, "printer-uri") &&
strcmp(resource, "/ipp/print") &&
(strcmp(resource, "/") || ippGetOperation(client->request) != IPP_OP_GET_PRINTER_ATTRIBUTES)))
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
name, ippGetString(uri, 0, NULL));
else if (client->operation_id != IPP_OP_GET_PRINTER_ATTRIBUTES && (status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
{
flush_document_data(client);
return (respond_http(client, status, NULL, NULL, 0));
}
else
{
if (httpGetExpect(client->http))
{
if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
{
if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
return (0);
}
else
{
if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
return (0);
flush_document_data(client);
return (1);
}
}
switch (client->operation_id)
{
case IPP_OP_PRINT_JOB :
ipp_print_job(client);
break;
case IPP_OP_PRINT_URI :
ipp_print_uri(client);
break;
case IPP_OP_VALIDATE_JOB :
ipp_validate_job(client);
break;
case IPP_OP_CREATE_JOB :
ipp_create_job(client);
break;
case IPP_OP_SEND_DOCUMENT :
ipp_send_document(client);
break;
case IPP_OP_SEND_URI :
ipp_send_uri(client);
break;
case IPP_OP_CANCEL_JOB :
ipp_cancel_job(client);
break;
case IPP_OP_CANCEL_MY_JOBS :
ipp_cancel_my_jobs(client);
break;
case IPP_OP_GET_JOB_ATTRIBUTES :
ipp_get_job_attributes(client);
break;
case IPP_OP_GET_JOBS :
ipp_get_jobs(client);
break;
case IPP_OP_GET_PRINTER_ATTRIBUTES :
ipp_get_printer_attributes(client);
break;
case IPP_OP_CLOSE_JOB :
ipp_close_job(client);
break;
case IPP_OP_IDENTIFY_PRINTER :
ipp_identify_printer(client);
break;
default :
respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
"Operation not supported.");
break;
}
}
}
}
}
if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
httpFlush(client->http);
return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
ippLength(client->response)));
}
static void *
process_job(ippeve_job_t *job)
{
job->state = IPP_JSTATE_PROCESSING;
job->printer->state = IPP_PSTATE_PROCESSING;
job->processing = time(NULL);
while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY)
{
job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
sleep(1);
}
job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED;
if (job->printer->command)
{
int pid,
status;
struct timeval start,
end;
char *myargv[3],
*myenvp[400];
int myenvc;
ipp_attribute_t *attr;
char val[1280],
*valptr;
#ifndef _WIN32
int mystdout = -1;
int mypipe[2];
char line[2048],
*ptr,
*endptr;
ssize_t bytes;
#endif
fprintf(stderr, "[Job %d] Running command \"%s %s\".\n", job->id, job->printer->command, job->filename);
gettimeofday(&start, NULL);
myargv[0] = job->printer->command;
myargv[1] = job->filename;
myargv[2] = NULL;
for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
myenvp[myenvc] = strdup(environ[myenvc]);
if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32))
{
fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
job->state = IPP_JSTATE_ABORTED;
goto error;
}
snprintf(val, sizeof(val), "CONTENT_TYPE=%s", job->format);
myenvp[myenvc ++] = strdup(val);
if (job->printer->device_uri)
{
snprintf(val, sizeof(val), "DEVICE_URI=%s", job->printer->device_uri);
myenvp[myenvc ++] = strdup(val);
}
if (job->printer->output_format)
{
snprintf(val, sizeof(val), "OUTPUT_TYPE=%s", job->printer->output_format);
myenvp[myenvc ++] = strdup(val);
}
#if !CUPS_LITE
if (job->printer->ppdfile)
{
snprintf(val, sizeof(val), "PPD=%s", job->printer->ppdfile);
myenvp[myenvc++] = strdup(val);
}
#endif
for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
{
const char *name = ippGetName(attr),
*suffix = strstr(name, "-default");
if (strncmp(name, "pwg-", 4) && (!suffix || suffix[8]))
continue;
valptr = val;
*valptr++ = 'I';
*valptr++ = 'P';
*valptr++ = 'P';
*valptr++ = '_';
while (*name && valptr < (val + sizeof(val) - 2))
{
if (*name == '-')
*valptr++ = '_';
else
*valptr++ = (char)toupper(*name & 255);
name ++;
}
*valptr++ = '=';
ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
myenvp[myenvc++] = strdup(val);
}
for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
{
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);
name ++;
}
*valptr++ = '=';
ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
myenvp[myenvc++] = strdup(val);
}
if (attr)
{
fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
job->state = IPP_JSTATE_ABORTED;
goto error;
}
myenvp[myenvc] = NULL;
#ifdef _WIN32
status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
#else
if (job->printer->device_uri)
{
char scheme[32],
userpass[256],
host[256],
resource[256];
int port;
if (httpSeparateURI(HTTP_URI_CODING_ALL, job->printer->device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
{
fprintf(stderr, "[Job %d] Bad device URI \"%s\".\n", job->id, job->printer->device_uri);
}
else if (!strcmp(scheme, "file"))
{
struct stat fileinfo;
if (stat(resource, &fileinfo))
{
if (errno == ENOENT)
{
if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) >= 0)
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
else
fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
}
else
fprintf(stderr, "[Job %d] Unable to access \"%s\": %s\n", job->id, resource, strerror(errno));
}
else if (S_ISDIR(fileinfo.st_mode))
{
if ((mystdout = create_job_file(job, line, sizeof(line), resource, "prn")) >= 0)
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
else
fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, line, strerror(errno));
}
else if (!S_ISREG(fileinfo.st_mode))
{
if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) >= 0)
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
else
fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
}
else if ((mystdout = open(resource, O_WRONLY | O_BINARY)) >= 0)
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
else
fprintf(stderr, "[Job %d] Unable to open \"%s\": %s\n", job->id, resource, strerror(errno));
}
else if (!strcmp(scheme, "socket"))
{
http_addrlist_t *addrlist;
char service[32];
snprintf(service, sizeof(service), "%d", port);
if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL)
fprintf(stderr, "[Job %d] Unable to find \"%s\": %s\n", job->id, host, cupsLastErrorString());
else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel)))
fprintf(stderr, "[Job %d] Unable to connect to \"%s\": %s\n", job->id, host, cupsLastErrorString());
httpAddrFreeList(addrlist);
}
else
{
fprintf(stderr, "[Job %d] Unsupported device URI scheme \"%s\".\n", job->id, scheme);
}
}
else if ((mystdout = create_job_file(job, line, sizeof(line), job->printer->directory, "prn")) >= 0)
{
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
}
if (mystdout < 0)
mystdout = open("/dev/null", O_WRONLY | O_BINARY);
if (pipe(mypipe))
{
fprintf(stderr, "[Job %d] Unable to create pipe for stderr: %s\n", job->id, strerror(errno));
mypipe[0] = mypipe[1] = -1;
}
if ((pid = fork()) == 0)
{
close(1);
dup2(mystdout, 1);
close(mystdout);
close(2);
dup2(mypipe[1], 2);
close(mypipe[0]);
close(mypipe[1]);
execve(job->printer->command, myargv, myenvp);
exit(errno);
}
else if (pid < 0)
{
fprintf(stderr, "[Job %d] Unable to start job processing command: %s\n", job->id, strerror(errno));
status = -1;
close(mystdout);
close(mypipe[0]);
close(mypipe[1]);
while (myenvc > 0)
free(myenvp[-- myenvc]);
}
else
{
while (myenvc > 0)
free(myenvp[-- myenvc]);
close(mystdout);
if (mypipe[0] >= 0)
{
close(mypipe[1]);
endptr = line;
while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
{
endptr += bytes;
*endptr = '\0';
while ((ptr = strchr(line, '\n')) != NULL)
{
int level = 3;
*ptr++ = '\0';
if (!strncmp(line, "ATTR:", 5))
{
process_attr_message(job, line);
}
else if (!strncmp(line, "DEBUG:", 6))
{
level = 2;
}
else if (!strncmp(line, "ERROR:", 6))
{
level = 0;
job->message = strdup(line + 6);
job->msglevel = 0;
}
else if (!strncmp(line, "INFO:", 5))
{
level = 1;
if (job->msglevel)
{
job->message = strdup(line + 5);
job->msglevel = 1;
}
}
else if (!strncmp(line, "STATE:", 6))
{
process_state_message(job, line);
}
if (Verbosity >= level)
fprintf(stderr, "[Job %d] Command - %s\n", job->id, line);
bytes = ptr - line;
if (ptr < endptr)
memmove(line, ptr, (size_t)(endptr - ptr));
endptr -= bytes;
*endptr = '\0';
}
}
close(mypipe[0]);
}
# ifdef HAVE_WAITPID
while (waitpid(pid, &status, 0) < 0);
# else
while (wait(&status) < 0);
# endif
}
#endif
if (status)
{
#ifndef _WIN32
if (WIFEXITED(status))
#endif
fprintf(stderr, "[Job %d] Command \"%s\" exited with status %d.\n", job->id, job->printer->command, WEXITSTATUS(status));
#ifndef _WIN32
else
fprintf(stderr, "[Job %d] Command \"%s\" terminated with signal %d.\n", job->id, job->printer->command, WTERMSIG(status));
#endif
job->state = IPP_JSTATE_ABORTED;
}
else
fprintf(stderr, "[Job %d] Command \"%s\" completed successfully.\n", job->id, job->printer->command);
gettimeofday(&end, NULL);
fprintf(stderr, "[Job %d] Processing time was %.3f seconds.\n", job->id, end.tv_sec - start.tv_sec + 0.000001 * (end.tv_usec - start.tv_usec));
}
else
{
sleep((unsigned)(5 + (CUPS_RAND() % 11)));
}
if (job->cancel)
job->state = IPP_JSTATE_CANCELED;
else if (job->state == IPP_JSTATE_PROCESSING)
job->state = IPP_JSTATE_COMPLETED;
error:
job->completed = time(NULL);
job->printer->state = IPP_PSTATE_IDLE;
job->printer->active_job = NULL;
return (NULL);
}
static void
process_state_message(
ippeve_job_t *job,
char *message)
{
int i;
ippeve_preason_t state_reasons,
bit;
char *ptr,
*next;
int remove;
for (message += 6; *message; message ++)
if (*message != ' ' && *message != '\t')
break;
if (*message == '-')
{
remove = 1;
state_reasons = job->printer->state_reasons;
message ++;
}
else if (*message == '+')
{
remove = 0;
state_reasons = job->printer->state_reasons;
message ++;
}
else
{
remove = 0;
state_reasons = IPPEVE_PREASON_NONE;
}
while (*message)
{
if ((next = strchr(message, ',')) != NULL)
*next++ = '\0';
if ((ptr = strstr(message, "-error")) != NULL)
*ptr = '\0';
else if ((ptr = strstr(message, "-report")) != NULL)
*ptr = '\0';
else if ((ptr = strstr(message, "-warning")) != NULL)
*ptr = '\0';
for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
{
if (!strcmp(message, ippeve_preason_strings[i]))
{
if (remove)
state_reasons &= ~bit;
else
state_reasons |= bit;
}
}
if (next)
message = next;
else
break;
}
job->printer->state_reasons = state_reasons;
}
static int
register_printer(
ippeve_printer_t *printer)
{
#ifdef HAVE_DNSSD
ippeve_txt_t ipp_txt;
int i,
count;
ipp_attribute_t *color_supported,
*document_format_supported,
*printer_location,
*printer_make_and_model,
*printer_uuid,
*sides_supported,
*urf_supported;
const char *value;
char adminurl[247],
formats[252],
urf[252],
*ptr;
if (printer->dnssd_subtypes && !strcmp(printer->dnssd_subtypes, "off"))
return (1);
color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/");
for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
{
value = ippGetString(document_format_supported, i, NULL);
if (!strcasecmp(value, "application/octet-stream"))
continue;
if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
*ptr++ = ',';
strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
ptr += strlen(ptr);
if (ptr >= (formats + sizeof(formats) - 1))
break;
}
urf[0] = '\0';
for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
{
value = ippGetString(urf_supported, i, NULL);
if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
*ptr++ = ',';
strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
ptr += strlen(ptr);
if (ptr >= (urf + sizeof(urf) - 1))
break;
}
if (printer->dnssd_collision)
{
char new_dnssd_name[256];
const char *uuid = ippGetString(printer_uuid, 0, NULL);
_cupsRWLockWrite(&printer->rwlock);
snprintf(new_dnssd_name, sizeof(new_dnssd_name), "%s (%c%c%c%c%c%c)", printer->dnssd_name, toupper(uuid[39]), toupper(uuid[40]), toupper(uuid[41]), toupper(uuid[42]), toupper(uuid[43]), toupper(uuid[44]));
free(printer->dnssd_name);
printer->dnssd_name = strdup(new_dnssd_name);
fprintf(stderr, "DNS-SD name collision, trying new DNS-SD service name '%s'.\n", printer->dnssd_name);
_cupsRWUnlock(&printer->rwlock);
printer->dnssd_collision = 0;
}
#endif
#ifdef HAVE_MDNSRESPONDER
DNSServiceErrorType error;
char regtype[256];
uint32_t ifindex;
TXTRecordCreate(&ipp_txt, 1024, NULL);
TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(adminurl), adminurl);
if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
# ifdef HAVE_TLS
TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
# endif
if (urf[0])
TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf);
TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
ifindex = !strcmp(printer->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
if (printer->printer_ref)
DNSServiceRefDeallocate(printer->printer_ref);
printer->printer_ref = DNSSDMaster;
if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, "_printer._tcp", NULL , NULL , 0 , 0 , NULL , (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
{
_cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
return (0);
}
if (printer->ipp_ref)
DNSServiceRefDeallocate(printer->ipp_ref);
printer->ipp_ref = DNSSDMaster;
if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", printer->dnssd_subtypes);
else
strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, regtype, NULL , NULL , htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
{
_cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
return (0);
}
# ifdef HAVE_TLS
if (printer->ipps_ref)
DNSServiceRefDeallocate(printer->ipps_ref);
printer->ipps_ref = DNSSDMaster;
if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", printer->dnssd_subtypes);
else
strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, regtype, NULL , NULL , htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
{
_cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
return (0);
}
# endif
if (printer->http_ref)
DNSServiceRefDeallocate(printer->http_ref);
printer->http_ref = DNSSDMaster;
if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, "_http._tcp,_printer", NULL , NULL , htons(printer->port), 0 , NULL , (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
{
_cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
return (0);
}
TXTRecordDeallocate(&ipp_txt);
#elif defined(HAVE_AVAHI)
char temp[256];
ipp_txt = NULL;
ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", adminurl);
if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
# ifdef HAVE_TLS
ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
# endif
if (urf[0])
ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
avahi_threaded_poll_lock(DNSSDMaster);
if (printer->dnssd_ref)
avahi_entry_group_free(printer->dnssd_ref);
printer->dnssd_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, printer);
avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_printer._tcp", NULL, NULL, 0, NULL);
avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, NULL, printer->port, ipp_txt);
if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
{
char *temptypes = strdup(printer->dnssd_subtypes), *start, *end;
for (start = temptypes; *start; start = end)
{
if ((end = strchr(start, ',')) != NULL)
*end++ = '\0';
else
end = start + strlen(start);
snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start);
avahi_entry_group_add_service_subtype(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
}
free(temptypes);
}
#ifdef HAVE_TLS
avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, NULL, printer->port, ipp_txt);
if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
{
char *temptypes = strdup(printer->dnssd_subtypes), *start, *end;
for (start = temptypes; *start; start = end)
{
if ((end = strchr(start, ',')) != NULL)
*end++ = '\0';
else
end = start + strlen(start);
snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start);
avahi_entry_group_add_service_subtype(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
}
free(temptypes);
}
#endif
avahi_entry_group_add_service_strlst(printer->dnssd_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->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, "_printer._sub._http._tcp");
avahi_entry_group_commit(printer->dnssd_ref);
avahi_threaded_poll_unlock(DNSSDMaster);
avahi_string_list_free(ipp_txt);
#endif
return (1);
}
int
respond_http(
ippeve_client_t *client,
http_status_t code,
const char *content_encoding,
const char *type,
size_t length)
{
char message[1024];
fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
if (code == HTTP_STATUS_CONTINUE)
{
return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
}
if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
{
snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
type = "text/plain";
length = strlen(message);
}
else
message[0] = '\0';
httpClearFields(client->http);
if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
client->operation == HTTP_STATE_OPTIONS)
httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
if (code == HTTP_STATUS_UNAUTHORIZED)
{
char value[256];
snprintf(value, sizeof(value), "Basic realm=\"%s\"", PAMService);
httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value);
}
if (type)
{
if (!strcmp(type, "text/html"))
httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
"text/html; charset=utf-8");
else
httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
if (content_encoding)
httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
}
httpSetLength(client->http, length);
if (httpWriteResponse(client->http, code) < 0)
return (0);
if (message[0])
{
if (httpPrintf(client->http, "%s", message) < 0)
return (0);
if (httpWrite2(client->http, "", 0) < 0)
return (0);
}
else if (client->response)
{
debug_attributes("Response", client->response, 2);
ippSetState(client->response, IPP_STATE_IDLE);
if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
return (0);
}
return (1);
}
static void
respond_ipp(ippeve_client_t *client,
ipp_status_t status,
const char *message,
...)
{
const char *formatted = NULL;
ippSetStatusCode(client->response, status);
if (message)
{
va_list ap;
ipp_attribute_t *attr;
va_start(ap, message);
if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
ippSetStringfv(client->response, &attr, 0, message, ap);
else
attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
va_end(ap);
formatted = ippGetString(attr, 0, NULL);
}
if (formatted)
fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
else
fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
}
static void
respond_unsupported(
ippeve_client_t *client,
ipp_attribute_t *attr)
{
ipp_attribute_t *temp;
respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
temp = ippCopyAttribute(client->response, attr, 0);
ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
}
static void
run_printer(ippeve_printer_t *printer)
{
int num_fds;
struct pollfd polldata[3];
ippeve_client_t *client;
#ifndef _WIN32
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
#endif
polldata[0].fd = printer->ipv4;
polldata[0].events = POLLIN;
polldata[1].fd = printer->ipv6;
polldata[1].events = POLLIN;
num_fds = 2;
#ifdef HAVE_MDNSRESPONDER
polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
polldata[num_fds ++].events = POLLIN;
#endif
for (;;)
{
if (poll(polldata, (nfds_t)num_fds, 1000) < 0 && errno != EINTR)
{
perror("poll() failed");
break;
}
#ifndef _WIN32
if (StopPrinter)
break;
#endif
if (polldata[0].revents & POLLIN)
{
if ((client = create_client(printer, printer->ipv4)) != NULL)
{
_cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
if (t)
{
_cupsThreadDetach(t);
}
else
{
perror("Unable to create client thread");
delete_client(client);
}
}
}
if (polldata[1].revents & POLLIN)
{
if ((client = create_client(printer, printer->ipv6)) != NULL)
{
_cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
if (t)
{
_cupsThreadDetach(t);
}
else
{
perror("Unable to create client thread");
delete_client(client);
}
}
}
#ifdef HAVE_MDNSRESPONDER
if (polldata[2].revents & POLLIN)
DNSServiceProcessResult(DNSSDMaster);
#endif
#ifdef HAVE_DNSSD
if (printer->dnssd_collision)
register_printer(printer);
#endif
clean_jobs(printer);
}
}
static int
show_media(ippeve_client_t *client)
{
ippeve_printer_t *printer = client->printer;
int i, j,
num_ready,
num_sizes,
num_sources,
num_types;
ipp_attribute_t *media_col_ready,
*media_ready,
*media_sizes,
*media_sources,
*media_types,
*input_tray;
ipp_t *media_col;
const char *media_size,
*media_source,
*media_type,
*ready_size,
*ready_source,
*ready_tray,
*ready_type;
char tray_str[1024],
*tray_ptr;
int tray_len;
int ready_sheets;
int num_options = 0;
cups_option_t *options = NULL;
static const int sheets[] =
{
250,
125,
50,
25,
5,
0,
-2
};
if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
return (0);
html_header(client, printer->name, 0);
if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
{
html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
html_footer(client);
return (1);
}
media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
{
html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
html_footer(client);
return (1);
}
if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
{
html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
html_footer(client);
return (1);
}
if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
{
html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
html_footer(client);
return (1);
}
if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
{
html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
html_footer(client);
return (1);
}
num_ready = ippGetCount(media_col_ready);
num_sizes = ippGetCount(media_sizes);
num_sources = ippGetCount(media_sources);
num_types = ippGetCount(media_types);
if (num_sources != ippGetCount(input_tray))
{
html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
html_footer(client);
return (1);
}
if (printer->web_forms)
num_options = parse_options(client, &options);
if (num_options > 0)
{
char name[255];
const char *val;
pwg_media_t *media;
_cupsRWLockWrite(&printer->rwlock);
ippDeleteAttribute(printer->attrs, media_col_ready);
media_col_ready = NULL;
if (media_ready)
{
ippDeleteAttribute(printer->attrs, media_ready);
media_ready = NULL;
}
printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
for (i = 0; i < num_sources; i ++)
{
media_source = ippGetString(media_sources, i, NULL);
if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
continue;
snprintf(name, sizeof(name), "size%d", i);
if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
{
snprintf(name, sizeof(name), "type%d", i);
if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
media_type = NULL;
if (media_ready)
ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
else
media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
if (media_col_ready)
ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
else
media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
ippDelete(media_col);
}
else
media = NULL;
snprintf(name, sizeof(name), "level%d", i);
if ((val = cupsGetOption(name, num_options, options)) != NULL)
ready_sheets = atoi(val);
else
ready_sheets = 0;
snprintf(tray_str, sizeof(tray_str), "type=sheetFeedAuto%sRemovableTray;mediafeed=%d;mediaxfeed=%d;maxcapacity=%d;level=%d;status=0;name=%s;", !strcmp(media_source, "by-pass-tray") ? "Non" : "", media ? media->length : 0, media ? media->width : 0, strcmp(media_source, "by-pass-tray") ? 250 : 25, ready_sheets, media_source);
ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str));
if (ready_sheets == 0)
{
printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
if (printer->active_job)
printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
}
else if (ready_sheets < 25 && ready_sheets > 0)
printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
}
if (!media_col_ready)
media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
if (!media_ready)
media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
_cupsRWUnlock(&printer->rwlock);
}
if (printer->web_forms)
html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
for (i = 0; i < num_sources; i ++)
{
media_source = ippGetString(media_sources, i, NULL);
if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
continue;
for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
{
media_col = ippGetCollection(media_col_ready, j);
ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
if (ready_source && !strcmp(ready_source, media_source))
break;
ready_source = NULL;
ready_size = NULL;
ready_type = NULL;
}
html_printf(client, "<tr><th>%s:</th>", media_source);
if (printer->web_forms)
{
html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i);
for (j = 0; j < num_sizes; j ++)
{
media_size = ippGetString(media_sizes, j, NULL);
html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
}
html_printf(client, "</select>");
}
else
html_printf(client, "<td>%s", ready_size);
if (printer->web_forms)
{
html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i);
for (j = 0; j < num_types; j ++)
{
media_type = ippGetString(media_types, j, NULL);
html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
}
html_printf(client, "</select>");
}
else if (ready_type)
html_printf(client, ", %s", ready_type);
if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
{
if (tray_len > (int)(sizeof(tray_str) - 1))
tray_len = (int)sizeof(tray_str) - 1;
memcpy(tray_str, ready_tray, (size_t)tray_len);
tray_str[tray_len] = '\0';
if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
ready_sheets = atoi(tray_ptr + 6);
else
ready_sheets = 0;
}
else
ready_sheets = 0;
if (printer->web_forms)
{
html_printf(client, " <select name=\"level%d\">", i);
for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
{
if (!strcmp(media_source, "by-pass-tray") && sheets[j] > 25)
continue;
if (sheets[j] < 0)
html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "");
else
html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
}
html_printf(client, "</select></td></tr>\n");
}
else if (ready_sheets == 1)
html_printf(client, ", 1 sheet</td></tr>\n");
else if (ready_sheets > 0)
html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
else
html_printf(client, "</td></tr>\n");
}
if (printer->web_forms)
{
html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
if (num_options > 0)
html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
html_printf(client, "</td></tr></table></form>\n");
if (num_options > 0)
html_printf(client, "<script>\n"
"setTimeout(hide_status, 3000);\n"
"function hide_status() {\n"
" var status = document.getElementById('status');\n"
" status.style.display = 'none';\n"
"}\n"
"</script>\n");
}
else
html_printf(client, "</table>\n");
cupsFreeOptions(num_options, options);
html_footer(client);
return (1);
}
static int
show_status(ippeve_client_t *client)
{
ippeve_printer_t *printer = client->printer;
ippeve_job_t *job;
int i;
ippeve_preason_t reason;
static const char * const reasons[] =
{
"Other",
"Cover Open",
"Input Tray Missing",
"Marker Supply Empty",
"Marker Supply Low",
"Marker Waste Almost Full",
"Marker Waste Full",
"Media Empty",
"Media Jam",
"Media Low",
"Media Needed",
"Moving to Paused",
"Paused",
"Spool Area Full",
"Toner Empty",
"Toner Low"
};
static const char * const state_colors[] =
{
"#0C0",
"#EE0",
"#C00"
};
if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
return (0);
html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15);
html_printf(client, "<h1><img style=\"background: %s; border-radius: 10px; float: left; margin-right: 10px; padding: 10px;\" src=\"/icon.png\" width=\"64\" height=\"64\">%s Jobs</h1>\n", state_colors[printer->state - IPP_PSTATE_IDLE], printer->name);
html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
if (printer->state_reasons & reason)
html_printf(client, "\n<br> %s", reasons[i]);
html_printf(client, "</p>\n");
if (cupsArrayCount(printer->jobs) > 0)
{
_cupsRWLockRead(&(printer->rwlock));
html_printf(client, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>Status</th></tr></thead><tbody>\n");
for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
{
char when[256],
hhmmss[64];
switch (job->state)
{
case IPP_JSTATE_PENDING :
case IPP_JSTATE_HELD :
snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
break;
case IPP_JSTATE_PROCESSING :
case IPP_JSTATE_STOPPED :
snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
break;
case IPP_JSTATE_ABORTED :
snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
break;
case IPP_JSTATE_CANCELED :
snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
break;
case IPP_JSTATE_COMPLETED :
snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
break;
}
html_printf(client, "<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", job->id, job->name, job->username, when);
}
html_printf(client, "</tbody></table>\n");
_cupsRWUnlock(&(printer->rwlock));
}
html_footer(client);
return (1);
}
static int
show_supplies(
ippeve_client_t *client)
{
ippeve_printer_t *printer = client->printer;
int i,
num_supply;
ipp_attribute_t *supply,
*supply_desc;
int num_options = 0;
cups_option_t *options = NULL;
int supply_len,
level;
const char *supply_value;
char supply_text[1024],
*supply_ptr;
static const char * const printer_supply[] =
{
"index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
"maxcapacity=100;level=%d;colorantname=unknown;",
"index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
"maxcapacity=100;level=%d;colorantname=black;",
"index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
"maxcapacity=100;level=%d;colorantname=cyan;",
"index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
"maxcapacity=100;level=%d;colorantname=magenta;",
"index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
"maxcapacity=100;level=%d;colorantname=yellow;"
};
static const char * const backgrounds[] =
{
"#777 linear-gradient(#333,#777)",
"#000 linear-gradient(#666,#000)",
"#0FF linear-gradient(#6FF,#0FF)",
"#F0F linear-gradient(#F6F,#F0F)",
"#CC0 linear-gradient(#EE6,#EE0)"
};
static const char * const colors[] =
{
"#fff",
"#fff",
"#000",
"#000",
"#000"
};
if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
return (0);
html_header(client, printer->name, 0);
if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
{
html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
html_footer(client);
return (1);
}
num_supply = ippGetCount(supply);
if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
{
html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
html_footer(client);
return (1);
}
if (num_supply != ippGetCount(supply_desc))
{
html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
html_footer(client);
return (1);
}
if (printer->web_forms)
num_options = parse_options(client, &options);
if (num_options > 0)
{
char name[64];
const char *val;
_cupsRWLockWrite(&printer->rwlock);
ippDeleteAttribute(printer->attrs, supply);
supply = NULL;
printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MARKER_SUPPLY_EMPTY | IPPEVE_PREASON_MARKER_SUPPLY_LOW | IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL | IPPEVE_PREASON_MARKER_WASTE_FULL | IPPEVE_PREASON_TONER_EMPTY | IPPEVE_PREASON_TONER_LOW);
for (i = 0; i < num_supply; i ++)
{
snprintf(name, sizeof(name), "supply%d", i);
if ((val = cupsGetOption(name, num_options, options)) != NULL)
{
level = atoi(val);
snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
if (supply)
ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
else
supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
if (i == 0)
{
if (level == 100)
printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
else if (level > 90)
printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
}
else
{
if (level == 0)
printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
else if (level < 10)
printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
}
}
}
_cupsRWUnlock(&printer->rwlock);
}
if (printer->web_forms)
html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
for (i = 0; i < num_supply; i ++)
{
supply_value = ippGetOctetString(supply, i, &supply_len);
if (supply_len > (int)(sizeof(supply_text) - 1))
supply_len = (int)sizeof(supply_text) - 1;
memcpy(supply_text, supply_value, (size_t)supply_len);
supply_text[supply_len] = '\0';
if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
level = atoi(supply_ptr + 6);
else
level = 50;
if (printer->web_forms)
html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level);
else
html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL));
if (level < 10)
html_printf(client, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; padding: 5px %dpx;\"></span> %d%%</td></tr>\n", backgrounds[i], level * 2, level);
else
html_printf(client, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; color: %s; padding: 5px %dpx;\">%d%%</span></td></tr>\n", backgrounds[i], colors[i], level * 2, level);
}
if (printer->web_forms)
{
html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
if (num_options > 0)
html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
html_printf(client, "</td></tr>\n</table>\n</form>\n");
if (num_options > 0)
html_printf(client, "<script>\n"
"setTimeout(hide_status, 3000);\n"
"function hide_status() {\n"
" var status = document.getElementById('status');\n"
" status.style.display = 'none';\n"
"}\n"
"</script>\n");
}
else
html_printf(client, "</table>\n");
cupsFreeOptions(num_options, options);
html_footer(client);
return (1);
}
#ifndef _WIN32
static void
signal_handler(int signum)
{
(void)signum;
StopPrinter = 1;
}
#endif
static char *
time_string(time_t tv,
char *buffer,
size_t bufsize)
{
struct tm date;
localtime_r(&tv, &date);
strftime(buffer, bufsize, "%X", &date);
return (buffer);
}
static void
usage(int status)
{
_cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
_cupsLangPuts(stdout, _("Options:"));
_cupsLangPuts(stdout, _("--help Show program help"));
_cupsLangPuts(stdout, _("--no-web-forms Disable web forms for media and supplies"));
_cupsLangPuts(stdout, _("--pam-service service Use the named PAM service"));
_cupsLangPuts(stdout, _("--version Show program version"));
_cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
_cupsLangPuts(stdout, _("-A Enable authentication"));
_cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
_cupsLangPuts(stdout, _("-F output-type/subtype Set the output format for the printer"));
#ifdef HAVE_TLS
_cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys."));
#endif
_cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)"));
#if !CUPS_LITE
_cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file"));
#endif
_cupsLangPuts(stdout, _("-S filename.strings Set strings file"));
_cupsLangPuts(stdout, _("-V version Set default IPP version"));
_cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file"));
_cupsLangPuts(stdout, _("-c command Set print command"));
_cupsLangPuts(stdout, _("-d spool-directory Set spool directory"));
_cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types"));
_cupsLangPuts(stdout, _("-i iconfile.png[,...] Set icon file(s)"));
_cupsLangPuts(stdout, _("-k Keep job spool files"));
_cupsLangPuts(stdout, _("-l location Set location of printer"));
_cupsLangPuts(stdout, _("-m model Set model name (default=Printer)"));
_cupsLangPuts(stdout, _("-n hostname Set hostname for printer"));
_cupsLangPuts(stdout, _("-p port Set port number for printer"));
_cupsLangPuts(stdout, _("-r subtype,[subtype] Set DNS-SD service subtype"));
_cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
_cupsLangPuts(stdout, _("-v Be verbose"));
exit(status);
}
static int
valid_doc_attributes(
ippeve_client_t *client)
{
int valid = 1;
ipp_op_t op = ippGetOperation(client->request);
const char *op_name = ippOpString(op);
ipp_attribute_t *attr,
*supported;
const char *compression = NULL,
*format = NULL;
if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
{
compression = ippGetString(attr, 0, NULL);
supported = ippFindAttribute(client->printer->attrs,
"compression-supported", IPP_TAG_KEYWORD);
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
(op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
op != IPP_OP_VALIDATE_JOB) ||
!ippContainsString(supported, compression))
{
respond_unsupported(client, attr);
valid = 0;
}
else
{
fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
if (strcmp(compression, "none"))
{
if (Verbosity)
fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
}
}
}
if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
ippGetGroupTag(attr) != IPP_TAG_OPERATION)
{
respond_unsupported(client, attr);
valid = 0;
}
else
{
format = ippGetString(attr, 0, NULL);
fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
}
}
else
{
format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
if (!format)
format = "application/octet-stream";
attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
}
if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
{
unsigned char header[8];
memset(header, 0, sizeof(header));
httpPeek(client->http, (char *)header, sizeof(header));
fprintf(stderr, "%s %s Auto-type header: %02X%02X%02X%02X%02X%02X%02X%02X\n", client->hostname, op_name, header[0], header[1], header[2], header[3], header[4], header[5], header[6], header[7]);
if (!memcmp(header, "%PDF", 4))
format = "application/pdf";
else if (!memcmp(header, "%!", 2))
format = "application/postscript";
else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
format = "image/jpeg";
else if (!memcmp(header, "\211PNG", 4))
format = "image/png";
else if (!memcmp(header, "RaS2PwgR", 8))
format = "image/pwg-raster";
else if (!memcmp(header, "UNIRAST", 8))
format = "image/urf";
else
format = NULL;
if (format)
{
fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
}
}
if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
{
respond_unsupported(client, attr);
valid = 0;
}
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);
}
static int
valid_job_attributes(
ippeve_client_t *client)
{
int i,
count,
valid = 1;
ipp_attribute_t *attr,
*supported;
valid = valid_doc_attributes(client);
if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 ||
(ippGetValueTag(attr) != IPP_TAG_NAME &&
ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
strcmp(ippGetString(attr, 0, NULL), "no-hold"))
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 ||
(ippGetValueTag(attr) != IPP_TAG_NAME &&
ippGetValueTag(attr) != IPP_TAG_NAMELANG))
{
respond_unsupported(client, attr);
valid = 0;
}
ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
}
else
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 ||
(ippGetValueTag(attr) != IPP_TAG_NAME &&
ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
strcmp(ippGetString(attr, 0, NULL), "none"))
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 ||
(ippGetValueTag(attr) != IPP_TAG_NAME &&
ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
ippGetValueTag(attr) != IPP_TAG_KEYWORD))
{
respond_unsupported(client, attr);
valid = 0;
}
else
{
supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
{
respond_unsupported(client, attr);
valid = 0;
}
}
}
if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
{
ipp_t *col,
*size;
ipp_attribute_t *member,
*x_dim,
*y_dim;
int x_value,
y_value;
if (ippGetCount(attr) != 1 ||
ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
{
respond_unsupported(client, attr);
valid = 0;
}
col = ippGetCollection(attr, 0);
if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(member) != 1 ||
(ippGetValueTag(member) != IPP_TAG_NAME &&
ippGetValueTag(member) != IPP_TAG_NAMELANG &&
ippGetValueTag(member) != IPP_TAG_KEYWORD))
{
respond_unsupported(client, attr);
valid = 0;
}
else
{
supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
{
respond_unsupported(client, attr);
valid = 0;
}
}
}
else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
{
if (ippGetCount(member) != 1)
{
respond_unsupported(client, attr);
valid = 0;
}
else
{
size = ippGetCollection(member, 0);
if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
(y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
{
respond_unsupported(client, attr);
valid = 0;
}
else
{
x_value = ippGetInteger(x_dim, 0);
y_value = ippGetInteger(y_dim, 0);
supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
count = ippGetCount(supported);
for (i = 0; i < count ; i ++)
{
size = ippGetCollection(supported, i);
x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
break;
}
if (i >= count)
{
respond_unsupported(client, attr);
valid = 0;
}
}
}
}
}
if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
(strcmp(ippGetString(attr, 0, NULL),
"separate-documents-uncollated-copies") &&
strcmp(ippGetString(attr, 0, NULL),
"separate-documents-collated-copies")))
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
{
if (ippGetValueTag(attr) != IPP_TAG_RANGE)
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
{
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
{
respond_unsupported(client, attr);
valid = 0;
}
}
if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
{
supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
!supported)
{
respond_unsupported(client, attr);
valid = 0;
}
else
{
int xdpi,
ydpi,
sydpi;
ipp_res_t units,
sunits;
xdpi = ippGetResolution(attr, 0, &ydpi, &units);
count = ippGetCount(supported);
for (i = 0; i < count; i ++)
{
if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
break;
}
if (i >= count)
{
respond_unsupported(client, attr);
valid = 0;
}
}
}
if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
{
const char *sides = ippGetString(attr, 0, NULL);
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
{
respond_unsupported(client, attr);
valid = 0;
}
else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
{
if (!ippContainsString(supported, sides))
{
respond_unsupported(client, attr);
valid = 0;
}
}
else if (strcmp(sides, "one-sided"))
{
respond_unsupported(client, attr);
valid = 0;
}
}
return (valid);
}