#define _IPP_PRIVATE_STRUCTURES 0
#define _CUPS_NO_DEPRECATED 1
#include <config.h>
#include <cups/cups.h>
#include <cups/string-private.h>
#include <cups/thread-private.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#ifdef WIN32
# include <fcntl.h>
# include <io.h>
# include <process.h>
# define WEXITSTATUS(s) (s)
# include <winsock2.h>
typedef ULONG nfds_t;
# define poll WSAPoll
#else
extern char **environ;
# include <sys/fcntl.h>
# include <sys/wait.h>
# include <poll.h>
#endif
#ifdef HAVE_DNSSD
# include <dns_sd.h>
#elif defined(HAVE_AVAHI)
# include <avahi-client/client.h>
# include <avahi-client/publish.h>
# include <avahi-common/error.h>
# include <avahi-common/thread-watch.h>
#endif
#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
enum _ipp_preason_e
{
_IPP_PREASON_NONE = 0x0000,
_IPP_PREASON_OTHER = 0x0001,
_IPP_PREASON_COVER_OPEN = 0x0002,
_IPP_PREASON_INPUT_TRAY_MISSING = 0x0004,
_IPP_PREASON_MARKER_SUPPLY_EMPTY = 0x0008,
_IPP_PREASON_MARKER_SUPPLY_LOW = 0x0010,
_IPP_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020,
_IPP_PREASON_MARKER_WASTE_FULL = 0x0040,
_IPP_PREASON_MEDIA_EMPTY = 0x0080,
_IPP_PREASON_MEDIA_JAM = 0x0100,
_IPP_PREASON_MEDIA_LOW = 0x0200,
_IPP_PREASON_MEDIA_NEEDED = 0x0400,
_IPP_PREASON_MOVING_TO_PAUSED = 0x0800,
_IPP_PREASON_PAUSED = 0x1000,
_IPP_PREASON_SPOOL_AREA_FULL = 0x2000,
_IPP_PREASON_TONER_EMPTY = 0x4000,
_IPP_PREASON_TONER_LOW = 0x8000
};
typedef unsigned int _ipp_preason_t;
static const char * const _ipp_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"
};
typedef enum _ipp_media_class_e
{
_IPP_GENERAL,
_IPP_PHOTO_ONLY,
_IPP_ENV_ONLY
} _ipp_media_class_t;
typedef enum _ipp_media_size_e
{
_IPP_MEDIA_SIZE_NONE = -1,
_IPP_MEDIA_SIZE_A4,
_IPP_MEDIA_SIZE_A5,
_IPP_MEDIA_SIZE_A6,
_IPP_MEDIA_SIZE_DL,
_IPP_MEDIA_SIZE_LEGAL,
_IPP_MEDIA_SIZE_LETTER,
_IPP_MEDIA_SIZE_COM10,
_IPP_MEDIA_SIZE_3x5,
_IPP_MEDIA_SIZE_L,
_IPP_MEDIA_SIZE_4x6,
_IPP_MEDIA_SIZE_5x7
} _ipp_media_size_t;
static const char * const media_supported[] =
{
"iso_a4_210x297mm",
"iso_a5_148x210mm",
"iso_a6_105x148mm",
"iso_dl_110x220mm",
"na_legal_8.5x14in",
"na_letter_8.5x11in",
"na_number-10_4.125x9.5in",
"na_index-3x5_3x5in",
"oe_photo-l_3.5x5in",
"na_index-4x6_4x6in",
"na_5x7_5x7in"
};
static const int media_col_sizes[][3] =
{
{ 21000, 29700, _IPP_GENERAL },
{ 14800, 21000, _IPP_PHOTO_ONLY },
{ 10500, 14800, _IPP_PHOTO_ONLY },
{ 11000, 22000, _IPP_ENV_ONLY },
{ 21590, 35560, _IPP_GENERAL },
{ 21590, 27940, _IPP_GENERAL },
{ 10477, 24130, _IPP_ENV_ONLY },
{ 7630, 12700, _IPP_PHOTO_ONLY },
{ 8890, 12700, _IPP_PHOTO_ONLY },
{ 10160, 15240, _IPP_PHOTO_ONLY },
{ 12700, 17780, _IPP_PHOTO_ONLY }
};
typedef enum _ipp_media_source_e
{
_IPP_MEDIA_SOURCE_NONE = -1,
_IPP_MEDIA_SOURCE_AUTO,
_IPP_MEDIA_SOURCE_MAIN,
_IPP_MEDIA_SOURCE_MANUAL,
_IPP_MEDIA_SOURCE_ENVELOPE,
_IPP_MEDIA_SOURCE_PHOTO
} _ipp_media_source_t;
static const char * const media_source_supported[] =
{
"auto",
"main",
"manual",
"envelope",
"photo"
};
typedef enum _ipp_media_type_e
{
_IPP_MEDIA_TYPE_NONE = -1,
_IPP_MEDIA_TYPE_AUTO,
_IPP_MEDIA_TYPE_CARDSTOCK,
_IPP_MEDIA_TYPE_ENVELOPE,
_IPP_MEDIA_TYPE_LABELS,
_IPP_MEDIA_TYPE_OTHER,
_IPP_MEDIA_TYPE_GLOSSY,
_IPP_MEDIA_TYPE_HIGH_GLOSS,
_IPP_MEDIA_TYPE_MATTE,
_IPP_MEDIA_TYPE_SATIN,
_IPP_MEDIA_TYPE_SEMI_GLOSS,
_IPP_MEDIA_TYPE_STATIONERY,
_IPP_MEDIA_TYPE_LETTERHEAD,
_IPP_MEDIA_TYPE_TRANSPARENCY
} _ipp_media_type_t;
static const char * const media_type_supported[] =
{
"auto",
"cardstock",
"envelope",
"labels",
"other",
"photographic-glossy",
"photographic-high-gloss",
"photographic-matte",
"photographic-satin",
"photographic-semi-gloss",
"stationery",
"stationery-letterhead",
"transparency"
};
typedef enum _ipp_supply_e
{
_IPP_SUPPLY_CYAN,
_IPP_SUPPLY_MAGENTA,
_IPP_SUPPLY_YELLOW,
_IPP_SUPPLY_BLACK,
_IPP_SUPPLY_WASTE
} _ipp_supply_t;
static const char * const printer_supplies[] =
{
"Cyan Toner",
"Magenta Toner",
"Yellow Toner",
"Black Toner",
"Toner Waste"
};
#ifdef HAVE_SSL
# define WEB_SCHEME "https"
#else
# define WEB_SCHEME "http"
#endif
#ifdef HAVE_DNSSD
typedef DNSServiceRef _ipp_srv_t;
typedef TXTRecordRef _ipp_txt_t;
#elif defined(HAVE_AVAHI)
typedef AvahiEntryGroup *_ipp_srv_t;
typedef AvahiStringList *_ipp_txt_t;
#else
typedef void *_ipp_srv_t;
typedef void *_ipp_txt_t;
#endif
typedef struct _ipp_filter_s
{
cups_array_t *ra;
ipp_tag_t group_tag;
} _ipp_filter_t;
typedef struct _ipp_job_s _ipp_job_t;
typedef struct _ipp_printer_s
{
int ipv4,
ipv6;
_ipp_srv_t ipp_ref,
ipps_ref,
http_ref,
printer_ref;
char *dnssd_name,
*name,
*icon,
*directory,
*hostname,
*uri,
*command;
int port;
size_t urilen;
ipp_t *attrs;
time_t start_time;
time_t config_time;
ipp_pstate_t state;
_ipp_preason_t state_reasons;
time_t state_time;
cups_array_t *jobs;
_ipp_job_t *active_job;
int next_job_id;
_cups_rwlock_t rwlock;
_ipp_media_size_t main_size;
_ipp_media_type_t main_type;
int main_level;
_ipp_media_size_t envelope_size;
int envelope_level;
_ipp_media_size_t photo_size;
_ipp_media_type_t photo_type;
int photo_level;
int supplies[5];
} _ipp_printer_t;
struct _ipp_job_s
{
int id;
const char *name,
*username,
*format;
ipp_jstate_t state;
time_t created,
processing,
completed;
int impressions,
impcompleted;
ipp_t *attrs;
int cancel;
char *filename;
int fd;
_ipp_printer_t *printer;
};
typedef struct _ipp_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;
http_addr_t addr;
char hostname[256];
_ipp_printer_t *printer;
_ipp_job_t *job;
} _ipp_client_t;
static void clean_jobs(_ipp_printer_t *printer);
static int compare_jobs(_ipp_job_t *a, _ipp_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(_ipp_client_t *client,
_ipp_job_t *job, cups_array_t *ra);
static _ipp_client_t *create_client(_ipp_printer_t *printer, int sock);
static _ipp_job_t *create_job(_ipp_client_t *client);
static int create_listener(int family, int port);
static ipp_t *create_media_col(const char *media, const char *source, const char *type, int width, int length, int margins);
static ipp_t *create_media_size(int width, int length);
static _ipp_printer_t *create_printer(const char *servername,
const char *name, const char *location,
const char *make, const char *model,
const char *icon,
const char *docformats, int ppm,
int ppm_color, int duplex, int port,
int pin, const char *subtype,
const char *directory,
const char *command,
const char *attrfile);
static void debug_attributes(const char *title, ipp_t *ipp,
int response);
static void delete_client(_ipp_client_t *client);
static void delete_job(_ipp_job_t *job);
static void delete_printer(_ipp_printer_t *printer);
#ifdef HAVE_DNSSD
static void DNSSD_API dnssd_callback(DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
_ipp_printer_t *printer);
#elif defined(HAVE_AVAHI)
static void dnssd_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, void *context);
static void dnssd_client_cb(AvahiClient *c, AvahiClientState state, void *userdata);
#endif
static void dnssd_init(void);
static int filter_cb(_ipp_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr);
static _ipp_job_t *find_job(_ipp_client_t *client);
static ipp_t *get_collection(FILE *fp, const char *filename, int *linenum);
static char *get_token(FILE *fp, char *buf, int buflen, int *linenum);
static void html_escape(_ipp_client_t *client, const char *s,
size_t slen);
static void html_footer(_ipp_client_t *client);
static void html_header(_ipp_client_t *client, const char *title);
static void html_printf(_ipp_client_t *client, const char *format,
...) __attribute__((__format__(__printf__,
2, 3)));
static void ipp_cancel_job(_ipp_client_t *client);
static void ipp_close_job(_ipp_client_t *client);
static void ipp_create_job(_ipp_client_t *client);
static void ipp_get_job_attributes(_ipp_client_t *client);
static void ipp_get_jobs(_ipp_client_t *client);
static void ipp_get_printer_attributes(_ipp_client_t *client);
static void ipp_identify_printer(_ipp_client_t *client);
static void ipp_print_job(_ipp_client_t *client);
static void ipp_print_uri(_ipp_client_t *client);
static void ipp_send_document(_ipp_client_t *client);
static void ipp_send_uri(_ipp_client_t *client);
static void ipp_validate_job(_ipp_client_t *client);
static void load_attributes(const char *filename, ipp_t *attrs);
static int parse_options(_ipp_client_t *client, cups_option_t **options);
static void process_attr_message(_ipp_job_t *job, char *message);
static void *process_client(_ipp_client_t *client);
static int process_http(_ipp_client_t *client);
static int process_ipp(_ipp_client_t *client);
static void *process_job(_ipp_job_t *job);
static void process_state_message(_ipp_job_t *job, char *message);
static int register_printer(_ipp_printer_t *printer, const char *location, const char *make, const char *model, const char *formats, const char *adminurl, const char *uuid, int color, int duplex, const char *regtype);
static int respond_http(_ipp_client_t *client, http_status_t code,
const char *content_coding,
const char *type, size_t length);
static void respond_ipp(_ipp_client_t *client, ipp_status_t status,
const char *message, ...)
__attribute__ ((__format__ (__printf__, 3, 4)));
static void respond_unsupported(_ipp_client_t *client,
ipp_attribute_t *attr);
static void run_printer(_ipp_printer_t *printer);
static char *time_string(time_t tv, char *buffer, size_t bufsize);
static void usage(int status) __attribute__((noreturn));
static int valid_doc_attributes(_ipp_client_t *client);
static int valid_job_attributes(_ipp_client_t *client);
#ifdef HAVE_DNSSD
static DNSServiceRef DNSSDMaster = NULL;
#elif defined(HAVE_AVAHI)
static AvahiThreadedPoll *DNSSDMaster = NULL;
static AvahiClient *DNSSDClient = NULL;
#endif
static int KeepFiles = 0,
Verbosity = 0;
int
main(int argc,
char *argv[])
{
int i;
const char *opt,
*attrfile = NULL,
*command = NULL,
*servername = NULL,
*name = NULL,
*location = "",
*make = "Test",
*model = "Printer",
*icon = "printer.png",
*formats = "application/pdf,image/jpeg,image/pwg-raster";
#ifdef HAVE_SSL
const char *keypath = NULL;
#endif
const char *subtype = "_print";
int port = 0,
duplex = 0,
ppm = 10,
ppm_color = 0,
pin = 0;
char directory[1024] = "",
hostname[1024];
_ipp_printer_t *printer;
for (i = 1; i < argc; i ++)
if (argv[i][0] == '-')
{
for (opt = argv[i] + 1; *opt; opt ++)
{
switch (*opt)
{
case '2' :
duplex = 1;
break;
#ifdef HAVE_SSL
case 'K' :
i ++;
if (i >= argc)
usage(1);
keypath = argv[i];
break;
#endif
case 'M' :
i ++;
if (i >= argc)
usage(1);
make = argv[i];
break;
case 'P' :
pin = 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);
formats = argv[i];
break;
case 'h' :
usage(0);
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];
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);
port = atoi(argv[i]);
break;
case 'r' :
i ++;
if (i >= argc)
usage(1);
subtype = argv[i];
break;
case 's' :
i ++;
if (i >= argc)
usage(1);
if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1)
usage(1);
break;
case 'v' :
Verbosity ++;
break;
default :
fprintf(stderr, "Unknown option \"-%c\".\n", *opt);
usage(1);
}
}
}
else if (!name)
{
name = argv[i];
}
else
{
fprintf(stderr, "Unexpected command-line argument \"%s\"\n", argv[i]);
usage(1);
}
if (!name)
usage(1);
if (!servername)
servername = httpGetHostname(NULL, hostname, sizeof(hostname));
if (!port)
{
#ifdef WIN32
port = 8631;
#else
port = 8000 + ((int)getuid() % 1000);
#endif
fprintf(stderr, "Listening on port %d.\n", port);
}
if (!directory[0])
{
const char *tmpdir;
#ifdef WIN32
if ((tmpdir = getenv("TEMP")) == NULL)
tmpdir = "C:/TEMP";
#elif defined(__APPLE__)
if ((tmpdir = getenv("TMPDIR")) == NULL)
tmpdir = "/private/tmp";
#else
if ((tmpdir = getenv("TMPDIR")) == NULL)
tmpdir = "/tmp";
#endif
snprintf(directory, sizeof(directory), "%s/ippserver.%d", tmpdir, (int)getpid());
if (mkdir(directory, 0755) && errno != EEXIST)
{
fprintf(stderr, "Unable to create spool directory \"%s\": %s\n",
directory, strerror(errno));
usage(1);
}
if (Verbosity)
fprintf(stderr, "Using spool directory \"%s\".\n", directory);
}
#ifdef HAVE_SSL
cupsSetServerCredentials(keypath, servername, 1);
#endif
dnssd_init();
if ((printer = create_printer(servername, name, location, make, model, icon,
formats, ppm, ppm_color, duplex, port, pin,
subtype, directory, command, attrfile)) == NULL)
return (1);
run_printer(printer);
delete_printer(printer);
return (0);
}
static void
clean_jobs(_ipp_printer_t *printer)
{
_ipp_job_t *job;
time_t cleantime;
if (cupsArrayCount(printer->jobs) == 0)
return;
cleantime = time(NULL) - 60;
_cupsRWLockWrite(&(printer->rwlock));
for (job = (_ipp_job_t *)cupsArrayFirst(printer->jobs);
job;
job = (_ipp_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(_ipp_job_t *a,
_ipp_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)
{
_ipp_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(
_ipp_client_t *client,
_ipp_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", job->state);
if (!ra || cupsArrayFind(ra, "job-state-message"))
{
switch (job->state)
{
case IPP_JSTATE_PENDING :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job pending.");
break;
case IPP_JSTATE_HELD :
if (job->fd >= 0)
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job incoming.");
else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job held.");
else
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job created.");
break;
case IPP_JSTATE_PROCESSING :
if (job->cancel)
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceling.");
else
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job printing.");
break;
case IPP_JSTATE_STOPPED :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job stopped.");
break;
case IPP_JSTATE_CANCELED :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceled.");
break;
case IPP_JSTATE_ABORTED :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job aborted.");
break;
case IPP_JSTATE_COMPLETED :
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job completed.");
break;
}
}
if (!ra || cupsArrayFind(ra, "job-state-reasons"))
{
switch (job->state)
{
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 _ipp_client_t *
create_client(_ipp_printer_t *printer,
int sock)
{
_ipp_client_t *client;
if ((client = calloc(1, sizeof(_ipp_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 _ipp_job_t *
create_job(_ipp_client_t *client)
{
_ipp_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)
{
_cupsRWLockWrite(&(client->printer->rwlock));
return (NULL);
}
if ((job = calloc(1, sizeof(_ipp_job_t))) == NULL)
{
perror("Unable to allocate memory for job");
return (NULL);
}
job->printer = client->printer;
job->attrs = ippNew();
job->state = IPP_JSTATE_HELD;
job->fd = -1;
copy_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 ++;
snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id);
httpAssembleUUID(client->printer->hostname, client->printer->port, client->printer->name, job->id, uuid, sizeof(uuid));
ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(&job->created)));
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, ippGetString(attr, 0, NULL));
else
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, client->printer->uri);
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time));
cupsArrayAdd(client->printer->jobs, job);
client->printer->active_job = job;
_cupsRWUnlock(&(client->printer->rwlock));
return (job);
}
static void create_job_filename(
_ipp_printer_t *printer,
_ipp_job_t *job,
char *fname,
size_t fnamesize)
{
char name[256],
*nameptr;
const char *ext,
*job_name;
ipp_attribute_t *job_name_attr;
if ((job_name_attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME)) != NULL)
job_name = ippGetString(job_name_attr, 0, NULL);
else
job_name = "untitled";
for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++)
if (isalnum(*job_name & 255) || *job_name == '-')
*nameptr++ = (char)tolower(*job_name & 255);
else
*nameptr++ = '_';
*nameptr = '\0';
if (!strcasecmp(job->format, "image/jpeg"))
ext = "jpg";
else if (!strcasecmp(job->format, "image/png"))
ext = "png";
else if (!strcasecmp(job->format, "image/pwg-raster"))
ext = "ras";
else if (!strcasecmp(job->format, "image/urf"))
ext = "urf";
else if (!strcasecmp(job->format, "application/pdf"))
ext = "pdf";
else if (!strcasecmp(job->format, "application/postscript"))
ext = "ps";
else
ext = "prn";
snprintf(fname, fnamesize, "%s/%d-%s.%s", printer->directory, job->id, name, ext);
}
static int
create_listener(int family,
int port)
{
int sock;
http_addrlist_t *addrlist;
char service[255];
snprintf(service, sizeof(service), "%d", port);
if ((addrlist = httpAddrGetList(NULL, 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 margins)
{
ipp_t *media_col = ippNew(),
*media_size = create_media_size(width, length);
char media_key[256];
if (type && source)
snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, type, margins == 0 ? "_borderless" : "");
else if (type)
snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, margins == 0 ? "_borderless" : "");
else if (source)
snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, margins == 0 ? "_borderless" : "");
else
snprintf(media_key, sizeof(media_key), "%s%s", media, margins == 0 ? "_borderless" : "");
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL,
media_key);
ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-size-name", NULL, media);
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"media-bottom-margin", margins);
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"media-left-margin", margins);
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"media-right-margin", margins);
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"media-top-margin", margins);
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 _ipp_printer_t *
create_printer(const char *servername,
const char *name,
const char *location,
const char *make,
const char *model,
const char *icon,
const char *docformats,
int ppm,
int ppm_color,
int duplex,
int port,
int pin,
const char *subtype,
const char *directory,
const char *command,
const char *attrfile)
{
int i, j;
_ipp_printer_t *printer;
#ifndef WIN32
char path[1024];
#endif
char uri[1024],
#ifdef HAVE_SSL
securi[1024],
*uris[2],
#endif
icons[1024],
adminurl[1024],
supplyurl[1024],
device_id[1024],
make_model[128],
uuid[128];
int num_formats;
char *defformat,
*formats[100],
*ptr;
const char *prefix;
int num_database;
ipp_attribute_t *media_col_database,
*media_size_supported;
ipp_t *media_col_default;
int media_col_index;
int k_supported;
#ifdef HAVE_STATVFS
struct statvfs spoolinfo;
double spoolsize;
#elif defined(HAVE_STATFS)
struct statfs spoolinfo;
double spoolsize;
#endif
static const int orients[4] =
{
IPP_ORIENT_PORTRAIT,
IPP_ORIENT_LANDSCAPE,
IPP_ORIENT_REVERSE_LANDSCAPE,
IPP_ORIENT_REVERSE_PORTRAIT
};
static const char * const versions[] =
{
"1.0",
"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",
"ipp-attribute-fidelity",
"job-account-id",
"job-accounting-user-id",
"job-name",
"job-password",
"job-priority",
"media",
"media-col",
"multiple-document-handling",
"orientation-requested",
"print-quality",
"sides"
};
static const char * const media_col_supported[] =
{
"media-bottom-margin",
"media-left-margin",
"media-right-margin",
"media-size",
"media-source",
"media-top-margin",
"media-type"
};
static const int media_xxx_margin_supported[] =
{
0,
635
};
static const char * const multiple_document_handling[] =
{
"separate-documents-uncollated-copies",
"separate-documents-collated-copies"
};
static const char * const overrides[] =
{
"document-number",
"pages"
};
static const char * const print_color_mode_supported[] =
{
"auto",
"color",
"monochrome"
};
static const int print_quality_supported[] =
{
IPP_QUALITY_DRAFT,
IPP_QUALITY_NORMAL,
IPP_QUALITY_HIGH
};
static const int pwg_raster_document_resolution_supported[] =
{
150,
300
};
static const char * const pwg_raster_document_type_supported[] =
{
"black_1",
"cmyk_8",
"sgray_8",
"srgb_8",
"srgb_16"
};
static const char * const reference_uri_schemes_supported[] =
{
"file",
"ftp",
"http"
#ifdef HAVE_SSL
, "https"
#endif
};
static const char * const sides_supported[] =
{
"one-sided",
"two-sided-long-edge",
"two-sided-short-edge"
};
static const char * const urf_supported[] =
{
"CP1",
"IS1-5-7",
"MT1-2-3-4-5-6-8-9-10-11-12-13",
"RS300",
"SRGB24",
"V1.4",
"W8",
"DM1"
};
#ifdef HAVE_SSL
static const char * const uri_authentication_supported[] =
{
"none",
"none"
};
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))
{
fprintf(stderr, "ippserver: Unable to execute command \"%s\": %s\n", command, strerror(errno));
return (NULL);
}
}
else
{
if (!cupsFileFind(command, getenv("PATH"), 1, path, sizeof(path)))
{
fprintf(stderr, "ippserver: Unable to find command \"%s\".\n", command);
return (NULL);
}
command = path;
}
}
#endif
if ((printer = calloc(1, sizeof(_ipp_printer_t))) == NULL)
{
perror("ippserver: Unable to allocate memory for printer");
return (NULL);
}
printer->ipv4 = -1;
printer->ipv6 = -1;
printer->name = strdup(name);
printer->dnssd_name = strdup(printer->name);
printer->command = command ? strdup(command) : NULL;
printer->directory = strdup(directory);
printer->hostname = strdup(servername);
printer->port = port;
printer->start_time = time(NULL);
printer->config_time = printer->start_time;
printer->state = IPP_PSTATE_IDLE;
printer->state_reasons = _IPP_PREASON_NONE;
printer->state_time = printer->start_time;
printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
printer->next_job_id = 1;
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, printer->hostname, printer->port, "/ipp/print");
printer->uri = strdup(uri);
printer->urilen = strlen(uri);
#ifdef HAVE_SSL
httpAssembleURI(HTTP_URI_CODING_ALL, securi, sizeof(securi), "ipps", NULL, printer->hostname, printer->port, "/ipp/print");
#endif
if (icon)
printer->icon = strdup(icon);
printer->main_size = _IPP_MEDIA_SIZE_A4;
printer->main_type = _IPP_MEDIA_TYPE_STATIONERY;
printer->main_level = 500;
printer->envelope_size = _IPP_MEDIA_SIZE_NONE;
printer->envelope_level = 0;
printer->photo_size = _IPP_MEDIA_SIZE_NONE;
printer->photo_type = _IPP_MEDIA_TYPE_NONE;
printer->photo_level = 0;
printer->supplies[_IPP_SUPPLY_CYAN] = 100;
printer->supplies[_IPP_SUPPLY_MAGENTA] = 100;
printer->supplies[_IPP_SUPPLY_YELLOW] = 100;
printer->supplies[_IPP_SUPPLY_BLACK] = 100;
printer->supplies[_IPP_SUPPLY_WASTE] = 0;
_cupsRWInit(&(printer->rwlock));
if ((printer->ipv4 = create_listener(AF_INET, printer->port)) < 0)
{
perror("Unable to create IPv4 listener");
goto bad_printer;
}
if ((printer->ipv6 = create_listener(AF_INET6, printer->port)) < 0)
{
perror("Unable to create IPv6 listener");
goto bad_printer;
}
httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), WEB_SCHEME, NULL, printer->hostname, printer->port, "/icon.png");
httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/");
httpAssembleURI(HTTP_URI_CODING_ALL, supplyurl, sizeof(supplyurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/supplies");
if (Verbosity)
{
fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
fprintf(stderr, "printer-supply-info-uri=\"%s\"\n", supplyurl);
fprintf(stderr, "printer-uri=\"%s\"\n", uri);
}
snprintf(make_model, sizeof(make_model), "%s %s", make, model);
num_formats = 1;
formats[0] = strdup(docformats);
defformat = formats[0];
for (ptr = strchr(formats[0], ','); ptr; ptr = strchr(ptr, ','))
{
*ptr++ = '\0';
formats[num_formats++] = ptr;
if (!strcasecmp(ptr, "application/octet-stream"))
defformat = ptr;
}
snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
ptr = device_id + strlen(device_id);
prefix = "CMD:";
for (i = 0; i < num_formats; i ++)
{
if (!strcasecmp(formats[i], "application/pdf"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix);
else if (!strcasecmp(formats[i], "application/postscript"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix);
else if (!strcasecmp(formats[i], "application/vnd.hp-PCL"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix);
else if (!strcasecmp(formats[i], "image/jpeg"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix);
else if (!strcasecmp(formats[i], "image/png"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix);
else if (strcasecmp(formats[i], "application/octet-stream"))
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s%s", prefix, formats[i]);
ptr += strlen(ptr);
prefix = ",";
}
if (ptr < (device_id + sizeof(device_id) - 1))
{
*ptr++ = ';';
*ptr = '\0';
}
#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
printer->attrs = ippNew();
if (attrfile)
load_attributes(attrfile, printer->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, "color-supported", IPP_TAG_ZERO))
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0);
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);
if (!ippFindAttribute(printer->attrs, "copies-default", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
if (!ippFindAttribute(printer->attrs, "copies-supported", IPP_TAG_ZERO))
ippAddRange(printer->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
"document-format-default", NULL, defformat);
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
"document-format-supported", num_formats, NULL,
(const char * const *)formats);
if (!ippFindAttribute(printer->attrs, "document-password-supported", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
if (!ippFindAttribute(printer->attrs, "finishings-default", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
if (!ippFindAttribute(printer->attrs, "finishings-supported", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "generated-natural-language-supported", NULL, "en");
if (!ippFindAttribute(printer->attrs, "identify-actions-default", IPP_TAG_ZERO))
ippAddString (printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", NULL, "sound");
if (!ippFindAttribute(printer->attrs, "identify-actions-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-supported", sizeof(identify_actions) / sizeof(identify_actions[0]), NULL, identify_actions);
if (!ippFindAttribute(printer->attrs, "ipp-features-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", sizeof(features) / sizeof(features[0]), NULL, features);
if (!ippFindAttribute(printer->attrs, "ipp-versions-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", sizeof(versions) / sizeof(versions[0]), NULL, versions);
if (!ippFindAttribute(printer->attrs, "job-account-id-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-account-id-default", NULL, "");
if (!ippFindAttribute(printer->attrs, "job-account-id-supported", IPP_TAG_ZERO))
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-account-id-supported", 1);
if (!ippFindAttribute(printer->attrs, "job-accounting-user-id-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-accounting-user-id-default", NULL, "");
if (!ippFindAttribute(printer->attrs, "job-accounting-user-id-supported", IPP_TAG_ZERO))
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-accounting-user-id-supported", 1);
if (!ippFindAttribute(printer->attrs, "job-creation-attributes-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", sizeof(job_creation) / sizeof(job_creation[0]), NULL, job_creation);
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1);
ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0,
k_supported);
if (!ippFindAttribute(printer->attrs, "job-password-supported", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-password-supported", 4);
if (!ippFindAttribute(printer->attrs, "job-priority-default", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-default", 50);
if (!ippFindAttribute(printer->attrs, "job-priority-supported", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 100);
if (!ippFindAttribute(printer->attrs, "job-sheets-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-default", NULL, "none");
if (!ippFindAttribute(printer->attrs, "job-sheets-supported", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none");
if (!ippFindAttribute(printer->attrs, "media-bottom-margin-supported", IPP_TAG_ZERO))
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_xxx_margin_supported) / sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported);
if (!ippFindAttribute(printer->attrs, "media-col-database", IPP_TAG_ZERO))
{
for (num_database = 0, i = 0;
i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
i ++)
{
if (media_col_sizes[i][2] == _IPP_ENV_ONLY)
num_database += 3;
else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY)
num_database += 6 * 3;
else
num_database += 2;
}
media_col_database = ippAddCollections(printer->attrs, IPP_TAG_PRINTER, "media-col-database", num_database, NULL);
for (media_col_index = 0, i = 0;
i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
i ++)
{
switch (media_col_sizes[i][2])
{
case _IPP_GENERAL :
ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], NULL, NULL, media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[1]));
ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], NULL, NULL, media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[0]));
break;
case _IPP_ENV_ONLY :
ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "auto", "envelope", media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[1]));
ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "manual", "envelope", media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[1]));
ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "envelope", "envelope", media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[1]));
break;
case _IPP_PHOTO_ONLY :
for (j = 0;
j < (int)(sizeof(media_type_supported) /
sizeof(media_type_supported[0]));
j ++)
{
if (strcmp(media_type_supported[j], "auto") && strncmp(media_type_supported[j], "photographic-", 13))
continue;
ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "auto", media_type_supported[j], media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[0]));
ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "manual", media_type_supported[j], media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[0]));
ippSetCollection(printer->attrs, &media_col_database, media_col_index ++, create_media_col(media_supported[i], "photo", media_type_supported[j], media_col_sizes[i][0], media_col_sizes[i][1], media_xxx_margin_supported[0]));
}
break;
}
}
}
if (!ippFindAttribute(printer->attrs, "media-col-default", IPP_TAG_ZERO))
{
media_col_default = create_media_col(media_supported[0], media_source_supported[0], media_type_supported[0], media_col_sizes[0][0], media_col_sizes[0][1],media_xxx_margin_supported[1]);
ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-default",
media_col_default);
ippDelete(media_col_default);
}
if (!ippFindAttribute(printer->attrs, "media-col-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-col-supported", (int)(sizeof(media_col_supported) / sizeof(media_col_supported[0])), NULL, media_col_supported);
if (!ippFindAttribute(printer->attrs, "media-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media_supported[0]);
if (!ippFindAttribute(printer->attrs, "media-left-margin-supported", IPP_TAG_ZERO))
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_xxx_margin_supported) / sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported);
if (!ippFindAttribute(printer->attrs, "media-right-margin-supported", IPP_TAG_ZERO))
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_xxx_margin_supported) / sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported);
if (!ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", (int)(sizeof(media_supported) / sizeof(media_supported[0])), NULL, media_supported);
if (!ippFindAttribute(printer->attrs, "media-size-supported", IPP_TAG_ZERO))
{
media_size_supported = ippAddCollections(printer->attrs, IPP_TAG_PRINTER, "media-size-supported", (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0])), NULL);
for (i = 0;
i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
i ++)
{
ipp_t *size = create_media_size(media_col_sizes[i][0], media_col_sizes[i][1]);
ippSetCollection(printer->attrs, &media_size_supported, i, size);
ippDelete(size);
}
}
if (!ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported) / sizeof(media_source_supported[0])), NULL, media_source_supported);
if (!ippFindAttribute(printer->attrs, "media-top-margin-supported", IPP_TAG_ZERO))
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_xxx_margin_supported) / sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported);
if (!ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported) / sizeof(media_type_supported[0])), NULL, media_type_supported);
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");
if (!ippFindAttribute(printer->attrs, "number-up-default", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "number-up-default", 1);
if (!ippFindAttribute(printer->attrs, "number-up-supported", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "number-up-supported", 1);
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
if (!ippFindAttribute(printer->attrs, "orientation-requested-default", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "orientation-requested-default", 0);
if (!ippFindAttribute(printer->attrs, "orientation-requested-supported", IPP_TAG_ZERO))
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", 4, orients);
if (!ippFindAttribute(printer->attrs, "output-bin-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
if (!ippFindAttribute(printer->attrs, "output-bin-supported", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
if (!ippFindAttribute(printer->attrs, "overrides-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides) / sizeof(overrides[0])), NULL, overrides);
if (!ippFindAttribute(printer->attrs, "page-ranges-supported", IPP_TAG_ZERO))
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"pages-per-minute", ppm);
if (ppm_color > 0)
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"pages-per-minute-color", ppm_color);
if (!ippFindAttribute(printer->attrs, "pdl-override-supported", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdl-override-supported", NULL, "attempted");
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "preferred-attributes-supported", 0);
if (!ippFindAttribute(printer->attrs, "print-color-mode-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, "auto");
if (!ippFindAttribute(printer->attrs, "print-color-mode-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
if (!ippFindAttribute(printer->attrs, "print-content-optimize-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
if (!ippFindAttribute(printer->attrs, "print-content-optimize-supported", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
if (!ippFindAttribute(printer->attrs, "print-rendering-intent-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
if (!ippFindAttribute(printer->attrs, "print-rendering-intent-supported", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
if (!ippFindAttribute(printer->attrs, "print-quality-default", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
if (!ippFindAttribute(printer->attrs, "print-quality-supported", IPP_TAG_ZERO))
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"printer-device-id", NULL, device_id);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format");
if (!ippFindAttribute(printer->attrs, "printer-geo-location", IPP_TAG_ZERO))
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location", 0);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
"printer-icons", NULL, icons);
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_TEXT,
"printer-make-and-model", NULL, make_model);
if (pin && !ippFindAttribute(printer->attrs, "", IPP_TAG_ZERO))
{
static const char * const names[] =
{
"job-account-id",
"job-accounting-user-id",
"job-password"
};
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
"printer-mandatory-job-attributes",
(int)(sizeof(names) / sizeof(names[0])), NULL, names);
}
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, adminurl);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
if (!ippFindAttribute(printer->attrs, "printer-organization", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organization", NULL, "Apple Inc.");
if (!ippFindAttribute(printer->attrs, "printer-organizational-unit", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organizational-unit", NULL, "Printing Engineering");
if (!ippFindAttribute(printer->attrs, "printer-resolution-default", IPP_TAG_ZERO))
ippAddResolution(printer->attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
if (!ippFindAttribute(printer->attrs, "printer-resolutions-supported", IPP_TAG_ZERO))
ippAddResolution(printer->attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])), NULL, printer_supplies);
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, supplyurl);
#ifdef HAVE_SSL
uris[0] = uri;
uris[1] = securi;
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", 2, NULL, (const char **)uris);
#else
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
#endif
httpAssembleUUID(printer->hostname, port, name, 0, uuid, sizeof(uuid));
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid);
for (i = 0; i < num_formats; i ++)
if (!strcasecmp(formats[i], "image/pwg-raster"))
break;
if (i < num_formats)
{
if (!ippFindAttribute(printer->attrs, "pwg-raster-document-resolution-supported", IPP_TAG_ZERO))
ippAddResolutions(printer->attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", (int)(sizeof(pwg_raster_document_resolution_supported) / sizeof(pwg_raster_document_resolution_supported[0])), IPP_RES_PER_INCH, pwg_raster_document_resolution_supported, pwg_raster_document_resolution_supported);
if (!ippFindAttribute(printer->attrs, "pwg-raster-document-sheet-back", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "pwg-raster-document-sheet-back", NULL, "normal");
if (!ippFindAttribute(printer->attrs, "pwg-raster-document-type-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
}
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);
if (!ippFindAttribute(printer->attrs, "sides-default", IPP_TAG_ZERO))
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
if (!ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", duplex ? 3 : 1, NULL, sides_supported);
for (i = 0; i < num_formats; i ++)
if (!strcasecmp(formats[i], "image/urf"))
break;
if (i < num_formats && !ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_ZERO))
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])) - !duplex, NULL, urf_supported);
#ifdef HAVE_SSL
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_supported);
#else
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "none");
#endif
#ifdef HAVE_SSL
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", 2, NULL, uri_security_supported);
#else
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", NULL, "none");
#endif
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "which-jobs-supported", sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
free(formats[0]);
debug_attributes("Printer", printer->attrs, 0);
if (!register_printer(printer, location, make, model, docformats, adminurl, uuid + 9, ppm_color > 0, duplex, subtype))
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(_ipp_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(_ipp_job_t *job)
{
if (Verbosity)
fprintf(stderr, "Removing job #%d from history.\n", job->id);
ippDelete(job->attrs);
if (job->filename)
{
if (!KeepFiles)
unlink(job->filename);
free(job->filename);
}
free(job);
}
static void
delete_printer(_ipp_printer_t *printer)
{
if (printer->ipv4 >= 0)
close(printer->ipv4);
if (printer->ipv6 >= 0)
close(printer->ipv6);
#if HAVE_DNSSD
if (printer->printer_ref)
DNSServiceRefDeallocate(printer->printer_ref);
if (printer->ipp_ref)
DNSServiceRefDeallocate(printer->ipp_ref);
if (printer->ipps_ref)
DNSServiceRefDeallocate(printer->ipps_ref);
if (printer->http_ref)
DNSServiceRefDeallocate(printer->http_ref);
#elif defined(HAVE_AVAHI)
avahi_threaded_poll_lock(DNSSDMaster);
if (printer->printer_ref)
avahi_entry_group_free(printer->printer_ref);
if (printer->ipp_ref)
avahi_entry_group_free(printer->ipp_ref);
if (printer->ipps_ref)
avahi_entry_group_free(printer->ipps_ref);
if (printer->http_ref)
avahi_entry_group_free(printer->http_ref);
avahi_threaded_poll_unlock(DNSSDMaster);
#endif
if (printer->dnssd_name)
free(printer->dnssd_name);
if (printer->name)
free(printer->name);
if (printer->icon)
free(printer->icon);
if (printer->command)
free(printer->command);
if (printer->directory)
free(printer->directory);
if (printer->hostname)
free(printer->hostname);
if (printer->uri)
free(printer->uri);
ippDelete(printer->attrs);
cupsArrayDelete(printer->jobs);
free(printer);
}
#ifdef HAVE_DNSSD
static void DNSSD_API
dnssd_callback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
_ipp_printer_t *printer)
{
(void)sdRef;
(void)flags;
(void)domain;
if (errorCode)
{
fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n",
regtype, (int)errorCode);
return;
}
else if (strcasecmp(name, printer->dnssd_name))
{
if (Verbosity)
fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name);
free(printer->dnssd_name);
printer->dnssd_name = strdup(name);
}
}
#elif defined(HAVE_AVAHI)
static void
dnssd_callback(
AvahiEntryGroup *srv,
AvahiEntryGroupState state,
void *context)
{
(void)srv;
(void)state;
(void)context;
}
static void
dnssd_client_cb(
AvahiClient *c,
AvahiClientState state,
void *userdata)
{
(void)userdata;
if (!c)
return;
switch (state)
{
default :
fprintf(stderr, "Ignore Avahi state %d.\n", state);
break;
case AVAHI_CLIENT_FAILURE:
if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
{
fputs("Avahi server crashed, exiting.\n", stderr);
exit(1);
}
break;
}
}
#endif
static void
dnssd_init(void)
{
#ifdef HAVE_DNSSD
if (DNSServiceCreateConnection(&DNSSDMaster) != kDNSServiceErr_NoError)
{
fputs("Error: Unable to initialize Bonjour.\n", stderr);
exit(1);
}
#elif defined(HAVE_AVAHI)
int error;
if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
{
fputs("Error: Unable to initialize Bonjour.\n", stderr);
exit(1);
}
if ((DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssd_client_cb, NULL, &error)) == NULL)
{
fputs("Error: Unable to initialize Bonjour.\n", stderr);
exit(1);
}
avahi_threaded_poll_start(DNSSDMaster);
#endif
}
static int
filter_cb(_ipp_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 _ipp_job_t *
find_job(_ipp_client_t *client)
{
ipp_attribute_t *attr;
_ipp_job_t key,
*job;
if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
{
const char *uri = ippGetString(attr, 0, NULL);
if (!strncmp(uri, client->printer->uri, client->printer->urilen) &&
uri[client->printer->urilen] == '/')
key.id = atoi(uri + client->printer->urilen + 1);
else
return (NULL);
}
else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
key.id = ippGetInteger(attr, 0);
_cupsRWLockRead(&(client->printer->rwlock));
job = (_ipp_job_t *)cupsArrayFind(client->printer->jobs, &key);
_cupsRWUnlock(&(client->printer->rwlock));
return (job);
}
static ipp_t *
get_collection(FILE *fp,
const char *filename,
int *linenum)
{
char token[1024],
attr[128];
ipp_tag_t value;
ipp_t *col = ippNew();
ipp_attribute_t *lastcol = NULL;
while (get_token(fp, token, sizeof(token), linenum) != NULL)
{
if (!strcmp(token, "}"))
break;
else if (!strcmp(token, "{") && lastcol)
{
ipp_t *subcol = get_collection(fp, filename, linenum);
if (subcol)
ippSetCollection(col, &lastcol, ippGetCount(lastcol), subcol);
else
goto col_error;
}
else if (!_cups_strcasecmp(token, "MEMBER"))
{
lastcol = NULL;
if (!get_token(fp, token, sizeof(token), linenum))
{
fprintf(stderr, "ippserver: Missing MEMBER value tag on line %d of \"%s\".\n", *linenum, filename);
goto col_error;
}
if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
{
fprintf(stderr, "ippserver: Bad MEMBER value tag \"%s\" on line %d of \"%s\".\n", token, *linenum, filename);
goto col_error;
}
if (!get_token(fp, attr, sizeof(attr), linenum))
{
fprintf(stderr, "ippserver: Missing MEMBER name on line %d of \"%s\".\n", *linenum, filename);
goto col_error;
}
if (!get_token(fp, token, sizeof(token), linenum))
{
fprintf(stderr, "ippserver: Missing MEMBER value on line %d of \"%s\".\n", *linenum, filename);
goto col_error;
}
switch (value)
{
case IPP_TAG_BOOLEAN :
if (!_cups_strcasecmp(token, "true"))
ippAddBoolean(col, IPP_TAG_ZERO, attr, 1);
else
ippAddBoolean(col, IPP_TAG_ZERO, attr, (char)atoi(token));
break;
case IPP_TAG_INTEGER :
case IPP_TAG_ENUM :
ippAddInteger(col, IPP_TAG_ZERO, value, attr, atoi(token));
break;
case IPP_TAG_RESOLUTION :
{
int xres,
yres;
char units[6];
if (sscanf(token, "%dx%d%5s", &xres, &yres, units) != 3 ||
(_cups_strcasecmp(units, "dpi") &&
_cups_strcasecmp(units, "dpc") &&
_cups_strcasecmp(units, "dpcm") &&
_cups_strcasecmp(units, "other")))
{
fprintf(stderr, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token, *linenum, filename);
goto col_error;
}
if (!_cups_strcasecmp(units, "dpi"))
ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_INCH, xres, yres);
else if (!_cups_strcasecmp(units, "dpc") ||
!_cups_strcasecmp(units, "dpcm"))
ippAddResolution(col, IPP_TAG_ZERO, attr, IPP_RES_PER_CM, xres, yres);
else
ippAddResolution(col, IPP_TAG_ZERO, attr, (ipp_res_t)0, xres, yres);
}
break;
case IPP_TAG_RANGE :
{
int lowers[4],
uppers[4],
num_vals;
num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
lowers + 0, uppers + 0,
lowers + 1, uppers + 1,
lowers + 2, uppers + 2,
lowers + 3, uppers + 3);
if ((num_vals & 1) || num_vals == 0)
{
fprintf(stderr, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".\n", token, *linenum, filename);
goto col_error;
}
ippAddRanges(col, IPP_TAG_ZERO, attr, num_vals / 2, lowers,
uppers);
}
break;
case IPP_TAG_BEGIN_COLLECTION :
if (!strcmp(token, "{"))
{
ipp_t *subcol = get_collection(fp, filename, linenum);
if (subcol)
{
lastcol = ippAddCollection(col, IPP_TAG_ZERO, attr, subcol);
ippDelete(subcol);
}
else
goto col_error;
}
else
{
fprintf(stderr, "ippserver: Bad collection value on line %d of \"%s\".\n", *linenum, filename);
goto col_error;
}
break;
case IPP_TAG_STRING :
ippAddOctetString(col, IPP_TAG_ZERO, attr, token, (int)strlen(token));
break;
default :
if (!strchr(token, ','))
ippAddString(col, IPP_TAG_ZERO, value, attr, NULL, token);
else
{
int num_values;
char *values[100],
*ptr;
values[0] = token;
num_values = 1;
for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
{
*ptr++ = '\0';
values[num_values] = ptr;
num_values ++;
if (num_values >= (int)(sizeof(values) / sizeof(values[0])))
break;
}
ippAddStrings(col, IPP_TAG_ZERO, value, attr, num_values,
NULL, (const char **)values);
}
break;
}
}
}
return (col);
col_error:
ippDelete(col);
return (NULL);
}
static char *
get_token(FILE *fp,
char *buf,
int buflen,
int *linenum)
{
int ch,
quote;
char *bufptr,
*bufend;
for (;;)
{
while (isspace(ch = getc(fp)))
{
if (ch == '\n')
(*linenum) ++;
}
if (ch == EOF)
return (NULL);
else if (ch == '\'' || ch == '\"')
{
quote = ch;
bufptr = buf;
bufend = buf + buflen - 1;
while ((ch = getc(fp)) != EOF)
{
if (ch == '\\')
{
if (bufptr < bufend)
*bufptr++ = (char)ch;
if ((ch = getc(fp)) != EOF && bufptr < bufend)
*bufptr++ = (char)ch;
}
else if (ch == quote)
break;
else if (bufptr < bufend)
*bufptr++ = (char)ch;
}
*bufptr = '\0';
return (buf);
}
else if (ch == '#')
{
while ((ch = getc(fp)) != EOF)
if (ch == '\n')
break;
(*linenum) ++;
}
else if (ch == '{' || ch == '}' || ch == ',')
{
buf[0] = (char)ch;
buf[1] = '\0';
return (buf);
}
else
{
ungetc(ch, fp);
bufptr = buf;
bufend = buf + buflen - 1;
while ((ch = getc(fp)) != EOF)
if (isspace(ch) || ch == '#')
break;
else if (bufptr < bufend)
*bufptr++ = (char)ch;
if (ch == '#')
ungetc(ch, fp);
else if (ch == '\n')
(*linenum) ++;
*bufptr = '\0';
return (buf);
}
}
}
static void
html_escape(_ipp_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(_ipp_client_t *client)
{
html_printf(client,
"</div>\n"
"</body>\n"
"</html>\n");
httpWrite2(client->http, "", 0);
}
static void
html_header(_ipp_client_t *client,
const char *title)
{
html_printf(client,
"<!doctype html>\n"
"<html>\n"
"<head>\n"
"<title>%s</title>\n"
"<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
"<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n"
"<meta name=\"viewport\" content=\"width=device-width\">\n"
"<style>\n"
"body { font-family: sans-serif; margin: 0; }\n"
"div.body { padding: 0px 10px 10px; }\n"
"blockquote { background: #dfd; border-radius: 5px; color: #006; padding: 10px; }\n"
"table.form { border-collapse: collapse; margin-top: 10px; width: 100%%; }\n"
"table.form td, table.form th { padding: 5px 2px; width: 50%%; }\n"
"table.form th { text-align: right; }\n"
"table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
"table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
"table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
"table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
"table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
"table.nav { border-collapse: collapse; width: 100%%; }\n"
"table.nav td { margin: 0; text-align: center; }\n"
"td.nav a, td.nav a:active, td.nav a:hover, td.nav a:hover:link, td.nav a:hover:link:visited, td.nav a:link, td.nav a:link:visited, td.nav a:visited { background: inherit; color: inherit; font-size: 80%%; text-decoration: none; }\n"
"td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
"td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
"td.nav:hover { background: #666; color: #fff; }\n"
"td.nav:active { background: #000; color: #ff0; }\n"
"</style>\n"
"</head>\n"
"<body>\n"
"<table class=\"nav\"><tr>"
"<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
"<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
"<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
"</tr></table>\n"
"<div class=\"body\">\n", title, !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
}
static void
html_printf(_ipp_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;
sprintf(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')
sprintf(temp, tformat, va_arg(ap, long long));
else
# endif
if (size == 'l')
sprintf(temp, tformat, va_arg(ap, long));
else
sprintf(temp, tformat, va_arg(ap, int));
httpWrite2(client->http, temp, strlen(temp));
break;
case 'p' :
if ((size_t)(width + 2) > sizeof(temp))
break;
sprintf(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(_ipp_client_t *client)
{
_ipp_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_close_job(_ipp_client_t *client)
{
_ipp_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(_ipp_client_t *client)
{
_ipp_job_t *job;
cups_array_t *ra;
if (!valid_job_attributes(client))
{
httpFlush(client->http);
return;
}
if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Unexpected document data following request.");
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(
_ipp_client_t *client)
{
_ipp_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(_ipp_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;
_ipp_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 = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs);
(limit <= 0 || count < limit) && job;
job = (_ipp_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(
_ipp_client_t *client)
{
cups_array_t *ra;
_ipp_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, "media-col-ready"))
{
int i,
num_ready = 0;
ipp_t *ready[3];
if (printer->main_size != _IPP_MEDIA_SIZE_NONE)
{
if (printer->main_type != _IPP_MEDIA_TYPE_NONE)
ready[num_ready ++] = create_media_col(media_supported[printer->main_size], "main", media_type_supported[printer->main_type], media_col_sizes[printer->main_size][0], media_col_sizes[printer->main_size][1], 635);
else
ready[num_ready ++] = create_media_col(media_supported[printer->main_size], "main", NULL, media_col_sizes[printer->main_size][0], media_col_sizes[printer->main_size][1], 635);
}
if (printer->envelope_size != _IPP_MEDIA_SIZE_NONE)
ready[num_ready ++] = create_media_col(media_supported[printer->envelope_size], "envelope", NULL, media_col_sizes[printer->envelope_size][0], media_col_sizes[printer->envelope_size][1], 635);
if (printer->photo_size != _IPP_MEDIA_SIZE_NONE)
{
if (printer->photo_type != _IPP_MEDIA_TYPE_NONE)
ready[num_ready ++] = create_media_col(media_supported[printer->photo_size], "photo", media_type_supported[printer->photo_type], media_col_sizes[printer->photo_size][0], media_col_sizes[printer->photo_size][1], 0);
else
ready[num_ready ++] = create_media_col(media_supported[printer->photo_size], "photo", NULL, media_col_sizes[printer->photo_size][0], media_col_sizes[printer->photo_size][1], 0);
}
if (num_ready)
{
ippAddCollections(client->response, IPP_TAG_PRINTER, "media-col-ready", num_ready, (const ipp_t **)ready);
for (i = 0; i < num_ready; i ++)
ippDelete(ready[i]);
}
else
ippAddOutOfBand(client->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
}
if (!ra || cupsArrayFind(ra, "media-ready"))
{
int num_ready = 0;
const char *ready[3];
if (printer->main_size != _IPP_MEDIA_SIZE_NONE)
ready[num_ready ++] = media_supported[printer->main_size];
if (printer->envelope_size != _IPP_MEDIA_SIZE_NONE)
ready[num_ready ++] = media_supported[printer->envelope_size];
if (printer->photo_size != _IPP_MEDIA_SIZE_NONE)
ready[num_ready ++] = media_supported[printer->photo_size];
if (num_ready)
ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-ready", num_ready, NULL, ready);
else
ippAddOutOfBand(client->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
}
if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
if (!ra || cupsArrayFind(ra, "printer-current-time"))
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
if (!ra || cupsArrayFind(ra, "printer-state"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
"printer-state", printer->state);
if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
if (!ra || cupsArrayFind(ra, "printer-state-message"))
{
static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
}
if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
{
if (printer->state_reasons == _IPP_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;
_ipp_preason_t bit;
int i;
char reason[32];
for (i = 0, bit = 1; i < (int)(sizeof(_ipp_preason_strings) / sizeof(_ipp_preason_strings[0])); i ++, bit *= 2)
{
if (printer->state_reasons & bit)
{
snprintf(reason, sizeof(reason), "%s-%s", _ipp_preason_strings[i], printer->state == IPP_PSTATE_IDLE ? "report" : printer->state == IPP_PSTATE_PROCESSING ? "warning" : "error");
if (attr)
ippSetString(client->response, &attr, ippGetCount(attr), reason);
else
attr = ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, reason);
}
}
}
}
if (!ra || cupsArrayFind(ra, "printer-supply"))
{
int i;
char buffer[256];
ipp_attribute_t *attr = NULL;
static const char * const colorants[] = { "cyan", "magenta", "yellow", "black", "unknown" };
for (i = 0; i < 5; i ++)
{
snprintf(buffer, sizeof(buffer), "index=%d;class=%s;type=%s;unit=percent;maxcapacity=100;level=%d;colorantname=%s;", i + 1, i < 4 ? "supplyThatIsConsumed" : "receptacleThatIsFilled", i < 4 ? "toner" : "wasteToner", printer->supplies[i], colorants[i]);
if (!attr)
attr = ippAddOctetString(client->response, IPP_TAG_PRINTER, "printer-supply", buffer, (int)strlen(buffer));
else
ippSetOctetString(client->response, &attr, i, buffer, (int)strlen(buffer));
}
}
if (!ra || cupsArrayFind(ra, "printer-up-time"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
if (!ra || cupsArrayFind(ra, "queued-job-count"))
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
_cupsRWUnlock(&(printer->rwlock));
cupsArrayDelete(ra);
}
static void
ipp_identify_printer(
_ipp_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"))
{
putchar(0x07);
fflush(stdout);
}
if (ippContainsString(actions, "display"))
printf("IDENTIFY from %s: %s\n", client->hostname, message ? ippGetString(message, 0, NULL) : "No message supplied");
respond_ipp(client, IPP_STATUS_OK, NULL);
}
static void
ipp_print_job(_ipp_client_t *client)
{
_ipp_job_t *job;
char filename[1024],
buffer[4096];
ssize_t bytes;
cups_array_t *ra;
if (!valid_job_attributes(client))
{
httpFlush(client->http);
return;
}
if (httpGetState(client->http) == HTTP_STATE_POST_SEND)
{
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;
}
create_job_filename(client->printer, job, filename, sizeof(filename));
if (Verbosity)
fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
{
job->state = IPP_JSTATE_ABORTED;
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to create print file: %s", strerror(errno));
return;
}
while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
{
if (write(job->fd, buffer, (size_t)bytes) < bytes)
{
int error = errno;
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to write print file: %s", strerror(error));
return;
}
}
if (bytes < 0)
{
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to read print file.");
return;
}
if (close(job->fd))
{
int error = errno;
job->state = IPP_JSTATE_ABORTED;
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to write print file: %s", strerror(error));
return;
}
job->fd = -1;
job->filename = strdup(filename);
job->state = IPP_JSTATE_PENDING;
if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
{
job->state = IPP_JSTATE_ABORTED;
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process 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_print_uri(_ipp_client_t *client)
{
_ipp_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;
cups_array_t *ra;
static const char * const uri_status_strings[] =
{
"URI too large.",
"Bad arguments to function.",
"Bad resource in URI.",
"Bad port number in URI.",
"Bad hostname in URI.",
"Bad username in URI.",
"Bad scheme in URI.",
"Bad/empty URI."
};
if (!valid_job_attributes(client))
{
httpFlush(client->http);
return;
}
if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Unexpected document data following request.");
return;
}
if ((uri = ippFindAttribute(client->request, "document-uri",
IPP_TAG_URI)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
return;
}
if (ippGetCount(uri) != 1)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Too many document-uri values.");
return;
}
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",
uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
return;
}
if (strcmp(scheme, "file") &&
#ifdef HAVE_SSL
strcmp(scheme, "https") &&
#endif
strcmp(scheme, "http"))
{
respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
"URI scheme \"%s\" not supported.", scheme);
return;
}
if (!strcmp(scheme, "file") && access(resource, R_OK))
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
"Unable to access URI: %s", strerror(errno));
return;
}
if ((job = create_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BUSY,
"Currently printing another job.");
return;
}
if (!strcasecmp(job->format, "image/jpeg"))
snprintf(filename, sizeof(filename), "%s/%d.jpg",
client->printer->directory, job->id);
else if (!strcasecmp(job->format, "image/png"))
snprintf(filename, sizeof(filename), "%s/%d.png",
client->printer->directory, job->id);
else if (!strcasecmp(job->format, "application/pdf"))
snprintf(filename, sizeof(filename), "%s/%d.pdf",
client->printer->directory, job->id);
else if (!strcasecmp(job->format, "application/postscript"))
snprintf(filename, sizeof(filename), "%s/%d.ps",
client->printer->directory, job->id);
else
snprintf(filename, sizeof(filename), "%s/%d.prn",
client->printer->directory, job->id);
if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
{
job->state = IPP_JSTATE_ABORTED;
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to create print file: %s", strerror(errno));
return;
}
if (!strcmp(scheme, "file"))
{
if ((infile = open(resource, O_RDONLY)) < 0)
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
"Unable to access URI: %s", strerror(errno));
return;
}
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;
job->state = IPP_JSTATE_ABORTED;
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));
return;
}
}
while (bytes > 0);
close(infile);
}
else
{
#ifdef HAVE_SSL
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());
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
return;
}
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));
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
httpClose(http);
return;
}
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));
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
httpClose(http);
return;
}
while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
{
if (write(job->fd, buffer, (size_t)bytes) < bytes)
{
int error = errno;
job->state = IPP_JSTATE_ABORTED;
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));
return;
}
}
httpClose(http);
}
if (close(job->fd))
{
int error = errno;
job->state = IPP_JSTATE_ABORTED;
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to write print file: %s", strerror(error));
return;
}
job->fd = -1;
job->filename = strdup(filename);
job->state = IPP_JSTATE_PENDING;
#if 0
if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
{
job->state = IPP_JSTATE_ABORTED;
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
return;
}
#else
process_job(job);
#endif
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);
}
static void
ipp_send_document(_ipp_client_t *client)
{
_ipp_job_t *job;
char filename[1024],
buffer[4096];
ssize_t bytes;
ipp_attribute_t *attr;
cups_array_t *ra;
if ((job = find_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
httpFlush(client->http);
return;
}
if (job->state > IPP_JSTATE_HELD)
{
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
"Job is not in a pending state.");
httpFlush(client->http);
return;
}
else if (job->filename || job->fd >= 0)
{
respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
"Multiple document jobs are not supported.");
httpFlush(client->http);
return;
}
if ((attr = ippFindAttribute(client->request, "last-document",
IPP_TAG_ZERO)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Missing required last-document attribute.");
httpFlush(client->http);
return;
}
else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
!ippGetBoolean(attr, 0))
{
respond_unsupported(client, attr);
httpFlush(client->http);
return;
}
if (!valid_doc_attributes(client))
{
httpFlush(client->http);
return;
}
copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
_cupsRWLockWrite(&(client->printer->rwlock));
if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
job->format = ippGetString(attr, 0, NULL);
else
job->format = "application/octet-stream";
create_job_filename(client->printer, job, filename, sizeof(filename));
if (Verbosity)
fprintf(stderr, "Creating job file \"%s\", format \"%s\".\n", filename, job->format);
job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
_cupsRWUnlock(&(client->printer->rwlock));
if (job->fd < 0)
{
job->state = IPP_JSTATE_ABORTED;
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to create print file: %s", strerror(errno));
return;
}
while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
{
if (write(job->fd, buffer, (size_t)bytes) < bytes)
{
int error = errno;
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to write print file: %s", strerror(error));
return;
}
}
if (bytes < 0)
{
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to read print file.");
return;
}
if (close(job->fd))
{
int error = errno;
job->state = IPP_JSTATE_ABORTED;
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to write print file: %s", strerror(error));
return;
}
_cupsRWLockWrite(&(client->printer->rwlock));
job->fd = -1;
job->filename = strdup(filename);
job->state = IPP_JSTATE_PENDING;
_cupsRWUnlock(&(client->printer->rwlock));
#if 0
if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
{
job->state = IPP_JSTATE_ABORTED;
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
return;
}
#else
process_job(job);
#endif
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);
}
static void
ipp_send_uri(_ipp_client_t *client)
{
_ipp_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;
static const char * const uri_status_strings[] =
{
"URI too large.",
"Bad arguments to function.",
"Bad resource in URI.",
"Bad port number in URI.",
"Bad hostname in URI.",
"Bad username in URI.",
"Bad scheme in URI.",
"Bad/empty URI."
};
if ((job = find_job(client)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
httpFlush(client->http);
return;
}
if (job->state > IPP_JSTATE_HELD)
{
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
"Job is not in a pending state.");
httpFlush(client->http);
return;
}
else if (job->filename || job->fd >= 0)
{
respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
"Multiple document jobs are not supported.");
httpFlush(client->http);
return;
}
if ((attr = ippFindAttribute(client->request, "last-document",
IPP_TAG_ZERO)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Missing required last-document attribute.");
httpFlush(client->http);
return;
}
else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
!ippGetBoolean(attr, 0))
{
respond_unsupported(client, attr);
httpFlush(client->http);
return;
}
if (!valid_doc_attributes(client))
{
httpFlush(client->http);
return;
}
if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Unexpected document data following request.");
return;
}
if ((uri = ippFindAttribute(client->request, "document-uri",
IPP_TAG_URI)) == NULL)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
return;
}
if (ippGetCount(uri) != 1)
{
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
"Too many document-uri values.");
return;
}
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",
uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
return;
}
if (strcmp(scheme, "file") &&
#ifdef HAVE_SSL
strcmp(scheme, "https") &&
#endif
strcmp(scheme, "http"))
{
respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
"URI scheme \"%s\" not supported.", scheme);
return;
}
if (!strcmp(scheme, "file") && access(resource, R_OK))
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
"Unable to access URI: %s", strerror(errno));
return;
}
_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 (!strcasecmp(job->format, "image/jpeg"))
snprintf(filename, sizeof(filename), "%s/%d.jpg",
client->printer->directory, job->id);
else if (!strcasecmp(job->format, "image/png"))
snprintf(filename, sizeof(filename), "%s/%d.png",
client->printer->directory, job->id);
else if (!strcasecmp(job->format, "application/pdf"))
snprintf(filename, sizeof(filename), "%s/%d.pdf",
client->printer->directory, job->id);
else if (!strcasecmp(job->format, "application/postscript"))
snprintf(filename, sizeof(filename), "%s/%d.ps",
client->printer->directory, job->id);
else
snprintf(filename, sizeof(filename), "%s/%d.prn",
client->printer->directory, job->id);
job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
_cupsRWUnlock(&(client->printer->rwlock));
if (job->fd < 0)
{
job->state = IPP_JSTATE_ABORTED;
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to create print file: %s", strerror(errno));
return;
}
if (!strcmp(scheme, "file"))
{
if ((infile = open(resource, O_RDONLY)) < 0)
{
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
"Unable to access URI: %s", strerror(errno));
return;
}
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;
job->state = IPP_JSTATE_ABORTED;
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));
return;
}
}
while (bytes > 0);
close(infile);
}
else
{
#ifdef HAVE_SSL
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());
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
return;
}
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));
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
httpClose(http);
return;
}
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));
job->state = IPP_JSTATE_ABORTED;
close(job->fd);
job->fd = -1;
unlink(filename);
httpClose(http);
return;
}
while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
{
if (write(job->fd, buffer, (size_t)bytes) < bytes)
{
int error = errno;
job->state = IPP_JSTATE_ABORTED;
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));
return;
}
}
httpClose(http);
}
if (close(job->fd))
{
int error = errno;
job->state = IPP_JSTATE_ABORTED;
job->fd = -1;
unlink(filename);
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
"Unable to write print file: %s", strerror(error));
return;
}
_cupsRWLockWrite(&(client->printer->rwlock));
job->fd = -1;
job->filename = strdup(filename);
job->state = IPP_JSTATE_PENDING;
_cupsRWUnlock(&(client->printer->rwlock));
#if 0
if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
{
job->state = IPP_JSTATE_ABORTED;
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
return;
}
#else
process_job(job);
#endif
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);
}
static void
ipp_validate_job(_ipp_client_t *client)
{
if (valid_job_attributes(client))
respond_ipp(client, IPP_STATUS_OK, NULL);
}
static void
load_attributes(const char *filename,
ipp_t *attrs)
{
int linenum = 0;
FILE *fp = NULL;
char attr[128],
token[1024],
*tokenptr;
ipp_tag_t value;
ipp_attribute_t *attrptr,
*lastcol = NULL;
if ((fp = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "ippserver: Unable to open \"%s\": %s\n", filename, strerror(errno));
exit(1);
}
while (get_token(fp, token, sizeof(token), &linenum) != NULL)
{
if (!_cups_strcasecmp(token, "ATTR"))
{
if (!get_token(fp, token, sizeof(token), &linenum))
{
fprintf(stderr, "ippserver: Missing ATTR value tag on line %d of \"%s\".\n", linenum, filename);
exit(1);
}
if ((value = ippTagValue(token)) == IPP_TAG_ZERO)
{
fprintf(stderr, "ippserver: Bad ATTR value tag \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
exit(1);
}
if (!get_token(fp, attr, sizeof(attr), &linenum))
{
fprintf(stderr, "ippserver: Missing ATTR name on line %d of \"%s\".\n", linenum, filename);
exit(1);
}
if (!get_token(fp, token, sizeof(token), &linenum))
{
fprintf(stderr, "ippserver: Missing ATTR value on line %d of \"%s\".\n", linenum, filename);
exit(1);
}
attrptr = NULL;
switch (value)
{
case IPP_TAG_BOOLEAN :
if (!_cups_strcasecmp(token, "true"))
attrptr = ippAddBoolean(attrs, IPP_TAG_PRINTER, attr, 1);
else
attrptr = ippAddBoolean(attrs, IPP_TAG_PRINTER, attr, (char)atoi(token));
break;
case IPP_TAG_INTEGER :
case IPP_TAG_ENUM :
if (!strchr(token, ','))
attrptr = ippAddInteger(attrs, IPP_TAG_PRINTER, value, attr, (int)strtol(token, &tokenptr, 0));
else
{
int values[100],
num_values = 1;
values[0] = (int)strtol(token, &tokenptr, 10);
while (tokenptr && *tokenptr &&
num_values < (int)(sizeof(values) / sizeof(values[0])))
{
if (*tokenptr == ',')
tokenptr ++;
else if (!isdigit(*tokenptr & 255) && *tokenptr != '-')
break;
values[num_values] = (int)strtol(tokenptr, &tokenptr, 0);
num_values ++;
}
attrptr = ippAddIntegers(attrs, IPP_TAG_PRINTER, value, attr, num_values, values);
}
if (!tokenptr || *tokenptr)
{
fprintf(stderr, "ippserver: Bad %s value \"%s\" on line %d of \"%s\".\n", ippTagString(value), token, linenum, filename);
exit(1);
}
break;
case IPP_TAG_RESOLUTION :
{
int xres,
yres;
ipp_res_t units;
char *start,
*ptr,
*next = NULL;
for (start = token; start; start = next)
{
xres = yres = (int)strtol(start, (char **)&ptr, 10);
if (ptr > start && xres > 0)
{
if (*ptr == 'x')
yres = (int)strtol(ptr + 1, (char **)&ptr, 10);
}
if (ptr && (next = strchr(ptr, ',')) != NULL)
*next++ = '\0';
if (ptr <= start || xres <= 0 || yres <= 0 || !ptr ||
(_cups_strcasecmp(ptr, "dpi") &&
_cups_strcasecmp(ptr, "dpc") &&
_cups_strcasecmp(ptr, "dpcm") &&
_cups_strcasecmp(ptr, "other")))
{
fprintf(stderr, "ippserver: Bad resolution value \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
exit(1);
}
if (!_cups_strcasecmp(ptr, "dpc") || !_cups_strcasecmp(ptr, "dpcm"))
units = IPP_RES_PER_CM;
else
units = IPP_RES_PER_INCH;
if (attrptr)
ippSetResolution(attrs, &attrptr, ippGetCount(attrptr), units, xres, yres);
else
attrptr = ippAddResolution(attrs, IPP_TAG_PRINTER, attr, units, xres, yres);
}
}
break;
case IPP_TAG_RANGE :
{
int lowers[4],
uppers[4],
num_vals;
num_vals = sscanf(token, "%d-%d,%d-%d,%d-%d,%d-%d",
lowers + 0, uppers + 0,
lowers + 1, uppers + 1,
lowers + 2, uppers + 2,
lowers + 3, uppers + 3);
if ((num_vals & 1) || num_vals == 0)
{
fprintf(stderr, "ippserver: Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", token, linenum, filename);
exit(1);
}
attrptr = ippAddRanges(attrs, IPP_TAG_PRINTER, attr, num_vals / 2, lowers,
uppers);
}
break;
case IPP_TAG_BEGIN_COLLECTION :
if (!strcmp(token, "{"))
{
ipp_t *col = get_collection(fp, filename, &linenum);
if (col)
{
attrptr = lastcol = ippAddCollection(attrs, IPP_TAG_PRINTER, attr, col);
ippDelete(col);
}
else
exit(1);
}
else
{
fprintf(stderr, "ippserver: Bad ATTR collection value on line %d of \"%s\".\n", linenum, filename);
exit(1);
}
do
{
ipp_t *col;
long pos = ftell(fp);
if (!get_token(fp, token, sizeof(token), &linenum))
break;
if (strcmp(token, ","))
{
fseek(fp, pos, SEEK_SET);
break;
}
if (!get_token(fp, token, sizeof(token), &linenum) || strcmp(token, "{"))
{
fprintf(stderr, "ippserver: Unexpected \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
exit(1);
}
if ((col = get_collection(fp, filename, &linenum)) == NULL)
break;
ippSetCollection(attrs, &attrptr, ippGetCount(attrptr), col);
lastcol = attrptr;
}
while (!strcmp(token, "{"));
break;
case IPP_TAG_STRING :
attrptr = ippAddOctetString(attrs, IPP_TAG_PRINTER, attr, token, (int)strlen(token));
break;
default :
fprintf(stderr, "ippserver: Unsupported ATTR value tag %s on line %d of \"%s\".\n", ippTagString(value), linenum, filename);
exit(1);
case IPP_TAG_TEXTLANG :
case IPP_TAG_NAMELANG :
case IPP_TAG_TEXT :
case IPP_TAG_NAME :
case IPP_TAG_KEYWORD :
case IPP_TAG_URI :
case IPP_TAG_URISCHEME :
case IPP_TAG_CHARSET :
case IPP_TAG_LANGUAGE :
case IPP_TAG_MIMETYPE :
if (!strchr(token, ','))
attrptr = ippAddString(attrs, IPP_TAG_PRINTER, value, attr, NULL, token);
else
{
int num_values;
char *values[100],
*ptr;
values[0] = token;
num_values = 1;
for (ptr = strchr(token, ','); ptr; ptr = strchr(ptr, ','))
{
if (ptr > token && ptr[-1] == '\\')
_cups_strcpy(ptr - 1, ptr);
else
{
*ptr++ = '\0';
values[num_values] = ptr;
num_values ++;
if (num_values >= (int)(sizeof(values) / sizeof(values[0])))
break;
}
}
attrptr = ippAddStrings(attrs, IPP_TAG_PRINTER, value, attr, num_values, NULL, (const char **)values);
}
break;
}
if (!attrptr)
{
fprintf(stderr, "ippserver: Unable to add attribute on line %d of \"%s\": %s\n", linenum, filename, cupsLastErrorString());
exit(1);
}
}
else
{
fprintf(stderr, "ippserver: Unknown directive \"%s\" on line %d of \"%s\".\n", token, linenum, filename);
exit(1);
}
}
fclose(fp);
}
static int
parse_options(_ipp_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(
_ipp_job_t *job,
char *message)
{
(void)job;
(void)message;
}
static void *
process_client(_ipp_client_t *client)
{
#ifdef HAVE_SSL
int first_time = 1;
#endif
while (httpWait(client->http, 30000))
{
#ifdef HAVE_SSL
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(_ipp_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];
int port;
const char *encoding;
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"
};
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)
{
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
return (0);
}
if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
"Upgrade"))
{
#ifdef HAVE_SSL
if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
{
if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
return (0);
fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
{
fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
return (0);
}
fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
}
else
#endif
if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
return (0);
}
if (httpGetExpect(client->http) &&
(client->operation == HTTP_STATE_POST ||
client->operation == HTTP_STATE_PUT))
{
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);
}
}
encoding = httpGetContentEncoding(client->http);
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, "/icon.png"))
return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
case HTTP_STATE_GET :
if (!strcmp(client->uri, "/icon.png"))
{
int fd;
struct stat fileinfo;
char buffer[4096];
ssize_t bytes;
fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
if (!stat(client->printer->icon, &fileinfo) &&
(fd = open(client->printer->icon, O_RDONLY)) >= 0)
{
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png",
(size_t)fileinfo.st_size))
{
close(fd);
return (0);
}
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
httpWrite2(client->http, buffer, (size_t)bytes);
httpFlushWrite(client->http);
close(fd);
}
else
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
}
else if (!strcmp(client->uri, "/"))
{
_ipp_job_t *job;
int i;
_ipp_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"
};
if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
return (0);
html_header(client, client->printer->name);
html_printf(client,
"<p><img align=\"right\" src=\"/icon.png\" width=\"64\" height=\"64\"><b>ippserver (" CUPS_SVERSION ")</b></p>\n"
"<p>%s, %d job(s).", client->printer->state == IPP_PSTATE_IDLE ? "Idle" : client->printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(client->printer->jobs));
for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
if (client->printer->state_reasons & reason)
html_printf(client, "\n<br> %s", reasons[i]);
html_printf(client, "</p>\n");
if (cupsArrayCount(client->printer->jobs) > 0)
{
_cupsRWLockRead(&(client->printer->rwlock));
html_printf(client, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>When</th></tr></thead><tbody>\n");
for (job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs); job; job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
{
char when[256],
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(&(client->printer->rwlock));
}
html_footer(client);
return (1);
}
else if (!strcmp(client->uri, "/media"))
{
int i,
num_options;
cups_option_t *options;
static const char * const sizes[] =
{
"ISO A4",
"ISO A5",
"ISO A6",
"DL Envelope",
"US Legal",
"US Letter",
"#10 Envelope",
"3x5 Photo",
"3.5x5 Photo",
"4x6 Photo",
"5x7 Photo"
};
static const char * const types[] =
{
"Auto",
"Cardstock",
"Envelope",
"Labels",
"Other",
"Glossy Photo",
"High-Gloss Photo",
"Matte Photo",
"Satin Photo",
"Semi-Gloss Photo",
"Plain",
"Letterhead",
"Transparency"
};
static const int sheets[] =
{
250,
100,
25,
5,
0
};
if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
return (0);
html_header(client, client->printer->name);
if ((num_options = parse_options(client, &options)) > 0)
{
const char *val;
if ((val = cupsGetOption("main_size", num_options, options)) != NULL)
client->printer->main_size = atoi(val);
if ((val = cupsGetOption("main_type", num_options, options)) != NULL)
client->printer->main_type = atoi(val);
if ((val = cupsGetOption("main_level", num_options, options)) != NULL)
client->printer->main_level = atoi(val);
if ((val = cupsGetOption("envelope_size", num_options, options)) != NULL)
client->printer->envelope_size = atoi(val);
if ((val = cupsGetOption("envelope_level", num_options, options)) != NULL)
client->printer->envelope_level = atoi(val);
if ((val = cupsGetOption("photo_size", num_options, options)) != NULL)
client->printer->photo_size = atoi(val);
if ((val = cupsGetOption("photo_type", num_options, options)) != NULL)
client->printer->photo_type = atoi(val);
if ((val = cupsGetOption("photo_level", num_options, options)) != NULL)
client->printer->photo_level = atoi(val);
if ((client->printer->main_level < 100 && client->printer->main_level > 0) || (client->printer->envelope_level < 25 && client->printer->envelope_level > 0) || (client->printer->photo_level < 25 && client->printer->photo_level > 0))
client->printer->state_reasons |= _IPP_PREASON_MEDIA_LOW;
else
client->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_LOW;
if ((client->printer->main_level == 0 && client->printer->main_size > _IPP_MEDIA_SIZE_NONE) || (client->printer->envelope_level == 0 && client->printer->envelope_size > _IPP_MEDIA_SIZE_NONE) || (client->printer->photo_level == 0 && client->printer->photo_size > _IPP_MEDIA_SIZE_NONE))
{
client->printer->state_reasons |= _IPP_PREASON_MEDIA_EMPTY;
if (client->printer->active_job)
client->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
}
else
client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MEDIA_EMPTY | _IPP_PREASON_MEDIA_NEEDED);
html_printf(client, "<blockquote>Media updated.</blockquote>\n");
}
html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
html_printf(client, "<tr><th>Main Tray:</th><td><select name=\"main_size\"><option value=\"-1\">None</option>");
for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
if (!strstr(sizes[i], "Envelope") && !strstr(sizes[i], "Photo"))
html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_size ? " selected" : "", sizes[i]);
html_printf(client, "</select> <select name=\"main_type\"><option value=\"-1\">None</option>");
for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
if (!strstr(types[i], "Photo"))
html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->main_type ? " selected" : "", types[i]);
html_printf(client, "</select> <select name=\"main_level\">");
for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->main_level ? " selected" : "", sheets[i]);
html_printf(client, "</select></td></tr>\n");
html_printf(client,
"<tr><th>Envelope Feeder:</th><td><select name=\"envelope_size\"><option value=\"-1\">None</option>");
for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
if (strstr(sizes[i], "Envelope"))
html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->envelope_size ? " selected" : "", sizes[i]);
html_printf(client, "</select> <select name=\"envelope_level\">");
for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->envelope_level ? " selected" : "", sheets[i]);
html_printf(client, "</select></td></tr>\n");
html_printf(client,
"<tr><th>Photo Tray:</th><td><select name=\"photo_size\"><option value=\"-1\">None</option>");
for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i ++)
if (strstr(sizes[i], "Photo"))
html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_size ? " selected" : "", sizes[i]);
html_printf(client, "</select> <select name=\"photo_type\"><option value=\"-1\">None</option>");
for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i ++)
if (strstr(types[i], "Photo"))
html_printf(client, "<option value=\"%d\"%s>%s</option>", i, i == client->printer->photo_type ? " selected" : "", types[i]);
html_printf(client, "</select> <select name=\"photo_level\">");
for (i = 0; i < (int)(sizeof(sheets) / sizeof(sheets[0])); i ++)
html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[i], sheets[i] == client->printer->photo_level ? " selected" : "", sheets[i]);
html_printf(client, "</select></td></tr>\n");
html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\"></td></tr></table></form>\n");
html_footer(client);
return (1);
}
else if (!strcmp(client->uri, "/supplies"))
{
int i, j,
num_options;
cups_option_t *options;
static const int levels[] = { 0, 5, 10, 25, 50, 75, 90, 95, 100 };
if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
return (0);
html_header(client, client->printer->name);
if ((num_options = parse_options(client, &options)) > 0)
{
char name[64];
const char *val;
client->printer->state_reasons &= (_ipp_preason_t)~(_IPP_PREASON_MARKER_SUPPLY_EMPTY | _IPP_PREASON_MARKER_SUPPLY_LOW | _IPP_PREASON_MARKER_WASTE_ALMOST_FULL | _IPP_PREASON_MARKER_WASTE_FULL | _IPP_PREASON_TONER_EMPTY | _IPP_PREASON_TONER_LOW);
for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
{
snprintf(name, sizeof(name), "supply_%d", i);
if ((val = cupsGetOption(name, num_options, options)) != NULL)
{
int level = client->printer->supplies[i] = atoi(val);
if (i < 4)
{
if (level == 0)
client->printer->state_reasons |= _IPP_PREASON_TONER_EMPTY;
else if (level < 10)
client->printer->state_reasons |= _IPP_PREASON_TONER_LOW;
}
else
{
if (level == 100)
client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_FULL;
else if (level > 90)
client->printer->state_reasons |= _IPP_PREASON_MARKER_WASTE_ALMOST_FULL;
}
}
}
html_printf(client, "<blockquote>Supplies updated.</blockquote>\n");
}
html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
for (i = 0; i < (int)(sizeof(printer_supplies) / sizeof(printer_supplies[0])); i ++)
{
html_printf(client, "<tr><th>%s:</th><td><select name=\"supply_%d\">", printer_supplies[i], i);
for (j = 0; j < (int)(sizeof(levels) / sizeof(levels[0])); j ++)
html_printf(client, "<option value=\"%d\"%s>%d%%</option>", levels[j], levels[j] == client->printer->supplies[i] ? " selected" : "", levels[j]);
html_printf(client, "</select></td></tr>\n");
}
html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Supplies\"></td></tr>\n</table>\n</form>\n");
html_footer(client);
return (1);
}
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(_ipp_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;
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 (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")))
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
name, ippGetString(uri, 0, NULL));
else
{
switch (ippGetOperation(client->request))
{
case IPP_OP_PRINT_JOB :
ipp_print_job(client);
break;
case IPP_OP_PRINT_URI :
ipp_print_uri(client);
break;
case IPP_OP_VALIDATE_JOB :
ipp_validate_job(client);
break;
case IPP_OP_CREATE_JOB :
ipp_create_job(client);
break;
case IPP_OP_SEND_DOCUMENT :
ipp_send_document(client);
break;
case IPP_OP_SEND_URI :
ipp_send_uri(client);
break;
case IPP_OP_CANCEL_JOB :
ipp_cancel_job(client);
break;
case IPP_OP_GET_JOB_ATTRIBUTES :
ipp_get_job_attributes(client);
break;
case IPP_OP_GET_JOBS :
ipp_get_jobs(client);
break;
case IPP_OP_GET_PRINTER_ATTRIBUTES :
ipp_get_printer_attributes(client);
break;
case IPP_OP_CLOSE_JOB :
ipp_close_job(client);
break;
case IPP_OP_IDENTIFY_PRINTER :
ipp_identify_printer(client);
break;
default :
respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
"Operation not supported.");
break;
}
}
}
}
}
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(_ipp_job_t *job)
{
job->state = IPP_JSTATE_PROCESSING;
job->printer->state = IPP_PSTATE_PROCESSING;
job->processing = time(NULL);
while (job->printer->state_reasons & _IPP_PREASON_MEDIA_EMPTY)
{
job->printer->state_reasons |= _IPP_PREASON_MEDIA_NEEDED;
sleep(1);
}
job->printer->state_reasons &= (_ipp_preason_t)~_IPP_PREASON_MEDIA_NEEDED;
if (job->printer->command)
{
int pid,
status;
time_t start,
end;
char *myargv[3],
*myenvp[200];
int myenvc;
ipp_attribute_t *attr;
char val[1280],
*valptr;
#ifndef WIN32
int mypipe[2];
char line[2048],
*ptr,
*endptr;
ssize_t bytes;
#endif
fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command,
job->filename);
time(&start);
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]);
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);
}
myenvp[myenvc] = NULL;
#ifdef WIN32
status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
#else
if (pipe(mypipe))
{
perror("Unable to create pipe for stderr");
mypipe[0] = mypipe[1] = -1;
}
if ((pid = fork()) == 0)
{
close(2);
dup2(mypipe[1], 2);
close(mypipe[0]);
close(mypipe[1]);
execve(job->printer->command, myargv, myenvp);
exit(errno);
}
else if (pid < 0)
{
perror("Unable to start job processing command");
status = -1;
close(mypipe[0]);
close(mypipe[1]);
while (myenvc > 0)
free(myenvp[-- myenvc]);
}
else
{
while (myenvc > 0)
free(myenvp[-- myenvc]);
if (mypipe[0] >= 0)
{
close(mypipe[1]);
endptr = line;
while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
{
endptr += bytes;
*endptr = '\0';
while ((ptr = strchr(line, '\n')) != NULL)
{
*ptr++ = '\0';
if (!strncmp(line, "STATE:", 6))
{
process_state_message(job, line);
}
else if (!strncmp(line, "ATTR:", 5))
{
process_attr_message(job, line);
}
else if (Verbosity > 1)
fprintf(stderr, "%s: %s\n", job->printer->command, line);
bytes = ptr - line;
if (ptr < endptr)
memmove(line, ptr, (size_t)(endptr - ptr));
endptr -= bytes;
*endptr = '\0';
}
}
close(mypipe[0]);
}
# 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, "Command \"%s\" exited with status %d.\n",
job->printer->command, WEXITSTATUS(status));
#ifndef WIN32
else
fprintf(stderr, "Command \"%s\" terminated with signal %d.\n",
job->printer->command, WTERMSIG(status));
#endif
job->state = IPP_JSTATE_ABORTED;
}
else if (status < 0)
job->state = IPP_JSTATE_ABORTED;
else
fprintf(stderr, "Command \"%s\" completed successfully.\n",
job->printer->command);
time(&end);
if ((end - start) < 5)
sleep(5);
}
else
{
sleep((unsigned)(5 + (rand() % 11)));
}
if (job->cancel)
job->state = IPP_JSTATE_CANCELED;
else if (job->state == IPP_JSTATE_PROCESSING)
job->state = IPP_JSTATE_COMPLETED;
job->completed = time(NULL);
job->printer->state = IPP_PSTATE_IDLE;
job->printer->active_job = NULL;
return (NULL);
}
static void
process_state_message(
_ipp_job_t *job,
char *message)
{
int i;
_ipp_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 = _IPP_PREASON_NONE;
}
while (*message)
{
if ((next = strchr(message, ',')) != NULL)
*next++ = '\0';
if ((ptr = strstr(message, "-error")) != NULL)
*ptr = '\0';
else if ((ptr = strstr(message, "-report")) != NULL)
*ptr = '\0';
else if ((ptr = strstr(message, "-warning")) != NULL)
*ptr = '\0';
for (i = 0, bit = 1; i < (int)(sizeof(_ipp_preason_strings) / sizeof(_ipp_preason_strings[0])); i ++, bit *= 2)
{
if (!strcmp(message, _ipp_preason_strings[i]))
{
if (remove)
state_reasons &= ~bit;
else
state_reasons |= bit;
}
}
if (next)
message = next;
else
break;
}
job->printer->state_reasons = state_reasons;
}
static int
register_printer(
_ipp_printer_t *printer,
const char *location,
const char *make,
const char *model,
const char *formats,
const char *adminurl,
const char *uuid,
int color,
int duplex,
const char *subtype)
{
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
_ipp_txt_t ipp_txt;
#endif
#ifdef HAVE_DNSSD
DNSServiceErrorType error;
char make_model[256],
product[256],
regtype[256];
snprintf(make_model, sizeof(make_model), "%s %s", make, model);
snprintf(product, sizeof(product), "(%s)", model);
TXTRecordCreate(&ipp_txt, 1024, NULL);
TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(make_model),
make_model);
TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(adminurl),
adminurl);
if (*location)
TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(location),
location);
TXTRecordSetValue(&ipp_txt, "product", (uint8_t)strlen(product),
product);
TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats),
formats);
TXTRecordSetValue(&ipp_txt, "Color", 1, color ? "T" : "F");
TXTRecordSetValue(&ipp_txt, "Duplex", 1, duplex ? "T" : "F");
TXTRecordSetValue(&ipp_txt, "usb_MFG", (uint8_t)strlen(make),
make);
TXTRecordSetValue(&ipp_txt, "usb_MDL", (uint8_t)strlen(model),
model);
TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(uuid), uuid);
# ifdef HAVE_SSL
TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
# endif
if (strstr(formats, "image/urf"))
TXTRecordSetValue(&ipp_txt, "URF", 66, "CP1,IS1-5-7,MT1-2-3-4-5-6-8-9-10-11-12-13,RS300,SRGB24,V1.4,W8,DM1");
TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
printer->printer_ref = DNSSDMaster;
if ((error = DNSServiceRegister(&(printer->printer_ref),
kDNSServiceFlagsShareConnection,
0 , printer->dnssd_name,
"_printer._tcp", NULL ,
NULL , 0 , 0 ,
NULL ,
(DNSServiceRegisterReply)dnssd_callback,
printer)) != kDNSServiceErr_NoError)
{
fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
printer->dnssd_name, error);
return (0);
}
printer->ipp_ref = DNSSDMaster;
if (subtype && *subtype)
snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtype);
else
strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
if ((error = DNSServiceRegister(&(printer->ipp_ref),
kDNSServiceFlagsShareConnection,
0 , printer->dnssd_name,
regtype, NULL ,
NULL , htons(printer->port),
TXTRecordGetLength(&ipp_txt),
TXTRecordGetBytesPtr(&ipp_txt),
(DNSServiceRegisterReply)dnssd_callback,
printer)) != kDNSServiceErr_NoError)
{
fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
printer->dnssd_name, regtype, error);
return (0);
}
# ifdef HAVE_SSL
printer->ipps_ref = DNSSDMaster;
if (subtype && *subtype)
snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtype);
else
strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
if ((error = DNSServiceRegister(&(printer->ipps_ref),
kDNSServiceFlagsShareConnection,
0 , printer->dnssd_name,
regtype, NULL ,
NULL , htons(printer->port),
TXTRecordGetLength(&ipp_txt),
TXTRecordGetBytesPtr(&ipp_txt),
(DNSServiceRegisterReply)dnssd_callback,
printer)) != kDNSServiceErr_NoError)
{
fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
printer->dnssd_name, regtype, error);
return (0);
}
# endif
printer->http_ref = DNSSDMaster;
if ((error = DNSServiceRegister(&(printer->http_ref),
kDNSServiceFlagsShareConnection,
0 , printer->dnssd_name,
"_http._tcp,_printer", NULL ,
NULL , htons(printer->port),
0 , NULL,
(DNSServiceRegisterReply)dnssd_callback,
printer)) != kDNSServiceErr_NoError)
{
fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
printer->dnssd_name, regtype, 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");
ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s %s", make, model);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", adminurl);
if (*location)
ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", location);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "product=(%s)", model);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", color ? "T" : "F");
ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", duplex ? "T" : "F");
ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MFG=%s", make);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MDL=%s", model);
ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", uuid);
# ifdef HAVE_SSL
ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
# endif
avahi_threaded_poll_lock(DNSSDMaster);
printer->ipp_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, NULL);
avahi_entry_group_add_service_strlst(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_printer._tcp", NULL, NULL, 0, NULL);
avahi_entry_group_add_service_strlst(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, NULL, printer->port, ipp_txt);
if (subtype && *subtype)
{
snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", subtype);
avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
}
#ifdef HAVE_SSL
avahi_entry_group_add_service_strlst(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, NULL, printer->port, ipp_txt);
if (subtype && *subtype)
{
snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", subtype);
avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
}
#endif
avahi_entry_group_add_service_strlst(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, NULL, printer->port, NULL);
avahi_entry_group_add_service_subtype(printer->ipp_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, "_printer._sub._http._tcp");
avahi_entry_group_commit(printer->ipp_ref);
avahi_threaded_poll_unlock(DNSSDMaster);
avahi_string_list_free(ipp_txt);
#endif
return (1);
}
int
respond_http(
_ipp_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 (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(_ipp_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(
_ipp_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(_ipp_printer_t *printer)
{
int num_fds;
struct pollfd polldata[3];
int timeout;
_ipp_client_t *client;
polldata[0].fd = printer->ipv4;
polldata[0].events = POLLIN;
polldata[1].fd = printer->ipv6;
polldata[1].events = POLLIN;
num_fds = 2;
#ifdef HAVE_DNSSD
polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
polldata[num_fds ++].events = POLLIN;
#endif
for (;;)
{
if (cupsArrayCount(printer->jobs))
timeout = 10;
else
timeout = -1;
if (poll(polldata, (nfds_t)num_fds, timeout) < 0 && errno != EINTR)
{
perror("poll() failed");
break;
}
if (polldata[0].revents & POLLIN)
{
if ((client = create_client(printer, printer->ipv4)) != NULL)
{
if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
{
perror("Unable to create client thread");
delete_client(client);
}
}
}
if (polldata[1].revents & POLLIN)
{
if ((client = create_client(printer, printer->ipv6)) != NULL)
{
if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
{
perror("Unable to create client thread");
delete_client(client);
}
}
}
#ifdef HAVE_DNSSD
if (polldata[2].revents & POLLIN)
DNSServiceProcessResult(DNSSDMaster);
#endif
clean_jobs(printer);
}
}
static char *
time_string(time_t tv,
char *buffer,
size_t bufsize)
{
struct tm *curtime = localtime(&tv);
strftime(buffer, bufsize, "%X", curtime);
return (buffer);
}
static void
usage(int status)
{
if (!status)
{
puts(CUPS_SVERSION " - Copyright 2010-2015 by Apple Inc. All rights "
"reserved.");
puts("");
}
puts("Usage: ippserver [options] \"name\"");
puts("");
puts("Options:");
puts("-2 Supports 2-sided printing (default=1-sided)");
puts("-M manufacturer Manufacturer name (default=Test)");
puts("-P PIN printing mode");
puts("-a attributes-file Load printer attributes from file");
puts("-c command Run command for every print job");
printf("-d spool-directory Spool directory "
"(default=/tmp/ippserver.%d)\n", (int)getpid());
puts("-f type/subtype[,...] List of supported types "
"(default=application/pdf,image/jpeg)");
puts("-h Show program help");
puts("-i iconfile.png PNG icon file (default=printer.png)");
puts("-k Keep job spool files");
puts("-l location Location of printer (default=empty string)");
puts("-m model Model name (default=Printer)");
puts("-n hostname Hostname for printer");
puts("-p port Port number (default=auto)");
puts("-r subtype Bonjour service subtype (default=_print)");
puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
puts("-v[vvv] Be (very) verbose");
exit(status);
}
static int
valid_doc_attributes(
_ipp_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 (!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));
if (!memcmp(header, "%PDF", 4))
format = "application/pdf";
else if (!memcmp(header, "%!", 2))
format = "application/postscript";
else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
format = "image/jpeg";
else if (!memcmp(header, "\211PNG", 4))
format = "image/png";
else if (!memcmp(header, "RAS2", 4))
format = "image/pwg-raster";
else if (!memcmp(header, "UNIRAST", 8))
format = "image/urf";
else
format = NULL;
if (format)
{
fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
client->hostname, op_name, format);
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
}
}
if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
{
respond_unsupported(client, attr);
valid = 0;
}
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(
_ipp_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);
}