#include "cupsd.h"
#include <pwd.h>
#include <grp.h>
#ifdef HAVE_LIBPAPER
# include <paper.h>
#endif
typedef struct
{
char option[PPD_MAX_NAME];
char choice[PPD_MAX_NAME];
} ppd_default_t;
static void accept_jobs(client_t *con, ipp_attribute_t *uri);
static void add_class(client_t *con, ipp_attribute_t *uri);
static int add_file(client_t *con, job_t *job, mime_type_t *filetype,
int compression);
static void add_job_state_reasons(client_t *con, job_t *job);
static void add_printer(client_t *con, ipp_attribute_t *uri);
static void add_printer_state_reasons(client_t *con, printer_t *p);
static void add_queued_job_count(client_t *con, printer_t *p);
static void cancel_all_jobs(client_t *con, ipp_attribute_t *uri);
static void cancel_job(client_t *con, ipp_attribute_t *uri);
static int check_quotas(client_t *con, printer_t *p);
static ipp_attribute_t *copy_attribute(ipp_t *to, ipp_attribute_t *attr,
int quickcopy);
static void copy_attrs(ipp_t *to, ipp_t *from, ipp_attribute_t *req,
ipp_tag_t group, int quickcopy);
static int copy_banner(client_t *con, job_t *job, const char *name);
static int copy_file(const char *from, const char *to);
static int copy_model(const char *from, const char *to);
static void create_job(client_t *con, ipp_attribute_t *uri);
static void delete_printer(client_t *con, ipp_attribute_t *uri);
static void get_default(client_t *con);
static void get_devices(client_t *con);
static void get_jobs(client_t *con, ipp_attribute_t *uri);
static void get_job_attrs(client_t *con, ipp_attribute_t *uri);
static void get_ppds(client_t *con);
static void get_printers(client_t *con, int type);
static void get_printer_attrs(client_t *con, ipp_attribute_t *uri);
static void hold_job(client_t *con, ipp_attribute_t *uri);
static void move_job(client_t *con, ipp_attribute_t *uri);
static int ppd_add_default(const char *option, const char *choice,
int num_defaults, ppd_default_t **defaults);
static int ppd_parse_line(const char *line, char *option, int olen,
char *choice, int clen);
static void print_job(client_t *con, ipp_attribute_t *uri);
static void read_ps_job_ticket(client_t *con);
static void reject_jobs(client_t *con, ipp_attribute_t *uri);
static void release_job(client_t *con, ipp_attribute_t *uri);
static void restart_job(client_t *con, ipp_attribute_t *uri);
static void send_document(client_t *con, ipp_attribute_t *uri);
static void send_ipp_error(client_t *con, ipp_status_t status);
static void set_default(client_t *con, ipp_attribute_t *uri);
static void set_job_attrs(client_t *con, ipp_attribute_t *uri);
static void start_printer(client_t *con, ipp_attribute_t *uri);
static void stop_printer(client_t *con, ipp_attribute_t *uri);
static void validate_job(client_t *con, ipp_attribute_t *uri);
static int validate_user(client_t *con, const char *owner, char *username,
int userlen);
int
ProcessIPPRequest(client_t *con)
{
ipp_tag_t group;
ipp_attribute_t *attr;
ipp_attribute_t *charset;
ipp_attribute_t *language;
ipp_attribute_t *uri;
ipp_attribute_t *username;
LogMessage(L_DEBUG2, "ProcessIPPRequest(%p[%d]): operation_id = %04x",
con, con->http.fd, con->request->request.op.operation_id);
con->response = ippNew();
con->response->request.status.version[0] = con->request->request.op.version[0];
con->response->request.status.version[1] = con->request->request.op.version[1];
con->response->request.status.request_id = con->request->request.op.request_id;
if (con->request->request.any.version[0] != 1)
{
LogMessage(L_ERROR, "ProcessIPPRequest: bad request version (%d.%d)!",
con->request->request.any.version[0],
con->request->request.any.version[1]);
send_ipp_error(con, IPP_VERSION_NOT_SUPPORTED);
}
else if (con->request->attrs == NULL)
{
LogMessage(L_ERROR, "ProcessIPPRequest: no attributes in request!");
send_ipp_error(con, IPP_BAD_REQUEST);
}
else
{
for (attr = con->request->attrs, group = attr->group_tag;
attr != NULL;
attr = attr->next)
if (attr->group_tag < group)
{
LogMessage(L_ERROR, "ProcessIPPRequest: attribute groups are out of order!");
send_ipp_error(con, IPP_BAD_REQUEST);
break;
}
else
group = attr->group_tag;
if (attr == NULL)
{
attr = con->request->attrs;
if (attr != NULL && strcmp(attr->name, "attributes-charset") == 0 &&
attr->value_tag == IPP_TAG_CHARSET)
charset = attr;
else
charset = NULL;
if (attr)
attr = attr->next;
if (attr != NULL && strcmp(attr->name, "attributes-natural-language") == 0 &&
attr->value_tag == IPP_TAG_LANGUAGE)
language = attr;
else
language = NULL;
if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL)
uri = attr;
else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL)
uri = attr;
else
uri = NULL;
if (charset)
ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, charset->values[0].string.text);
else
ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, DefaultCharset);
if (language)
ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL,
language->values[0].string.text);
else
ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL, DefaultLanguage);
if (charset == NULL || language == NULL ||
(uri == NULL &&
con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
con->request->request.op.operation_id != CUPS_GET_CLASSES &&
con->request->request.op.operation_id != CUPS_GET_DEVICES &&
con->request->request.op.operation_id != CUPS_GET_PPDS))
{
if (charset == NULL)
LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-charset attribute!");
if (language == NULL)
LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-natural-language attribute!");
if (uri == NULL)
LogMessage(L_ERROR, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
LogMessage(L_DEBUG, "Request attributes follow...");
for (attr = con->request->attrs; attr != NULL; attr = attr->next)
LogMessage(L_DEBUG, "attr \"%s\": group_tag = %x, value_tag = %x",
attr->name ? attr->name : "(null)", attr->group_tag,
attr->value_tag);
LogMessage(L_DEBUG, "End of attributes...");
send_ipp_error(con, IPP_BAD_REQUEST);
}
else
{
if ((username = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
{
if (strcmp(username->values[0].string.text, "root") == 0 &&
ntohl(con->http.hostaddr.sin_addr.s_addr) != 0x7f000001 &&
strcmp(con->username, "root") != 0)
{
free(username->values[0].string.text);
username->values[0].string.text = strdup(RemoteRoot);
}
}
switch (con->request->request.op.operation_id)
{
case IPP_PRINT_JOB :
print_job(con, uri);
break;
case IPP_VALIDATE_JOB :
validate_job(con, uri);
break;
case IPP_CREATE_JOB :
create_job(con, uri);
break;
case IPP_SEND_DOCUMENT :
send_document(con, uri);
break;
case IPP_CANCEL_JOB :
cancel_job(con, uri);
break;
case IPP_GET_JOB_ATTRIBUTES :
get_job_attrs(con, uri);
break;
case IPP_GET_JOBS :
get_jobs(con, uri);
break;
case IPP_GET_PRINTER_ATTRIBUTES :
get_printer_attrs(con, uri);
break;
case IPP_HOLD_JOB :
hold_job(con, uri);
break;
case IPP_RELEASE_JOB :
release_job(con, uri);
break;
case IPP_RESTART_JOB :
restart_job(con, uri);
break;
case IPP_PAUSE_PRINTER :
stop_printer(con, uri);
break;
case IPP_RESUME_PRINTER :
start_printer(con, uri);
break;
case IPP_PURGE_JOBS :
cancel_all_jobs(con, uri);
break;
case IPP_SET_JOB_ATTRIBUTES :
set_job_attrs(con, uri);
break;
case CUPS_GET_DEFAULT :
get_default(con);
break;
case CUPS_GET_PRINTERS :
get_printers(con, 0);
break;
case CUPS_GET_CLASSES :
get_printers(con, CUPS_PRINTER_CLASS);
break;
case CUPS_ADD_PRINTER :
add_printer(con, uri);
break;
case CUPS_DELETE_PRINTER :
delete_printer(con, uri);
break;
case CUPS_ADD_CLASS :
add_class(con, uri);
break;
case CUPS_DELETE_CLASS :
delete_printer(con, uri);
break;
case CUPS_ACCEPT_JOBS :
case IPP_ENABLE_PRINTER :
accept_jobs(con, uri);
break;
case CUPS_REJECT_JOBS :
case IPP_DISABLE_PRINTER :
reject_jobs(con, uri);
break;
case CUPS_SET_DEFAULT :
set_default(con, uri);
break;
case CUPS_GET_DEVICES :
get_devices(con);
break;
case CUPS_GET_PPDS :
get_ppds(con);
break;
case CUPS_MOVE_JOB :
move_job(con, uri);
break;
default :
send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
}
}
}
}
LogMessage(L_DEBUG, "ProcessIPPRequest: %d status_code=%x",
con->http.fd, con->response->request.status.status_code);
if (SendHeader(con, HTTP_OK, "application/ipp"))
{
#if 0
if (con->http.version == HTTP_1_1)
{
con->http.data_encoding = HTTP_ENCODE_CHUNKED;
httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n");
}
else
#endif
{
con->http.data_encoding = HTTP_ENCODE_LENGTH;
con->http.data_remaining = ippLength(con->response);
httpPrintf(HTTP(con), "Content-Length: %d\r\n\r\n",
con->http.data_remaining);
}
LogMessage(L_DEBUG2, "ProcessIPPRequest: Adding fd %d to OutputSet...",
con->http.fd);
FD_SET(con->http.fd, OutputSet);
return (1);
}
else
{
return (0);
}
}
static void
accept_jobs(client_t *con,
ipp_attribute_t *uri)
{
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
const char *name;
printer_t *printer;
LogMessage(L_DEBUG2, "accept_jobs(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "accept_jobs: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((name = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "accept_jobs: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
printer = FindClass(name);
else
printer = FindPrinter(name);
printer->accepting = 1;
printer->state_message[0] = '\0';
AddPrinterHistory(printer);
if (dtype & CUPS_PRINTER_CLASS)
SaveAllClasses();
else
SaveAllPrinters();
LogMessage(L_INFO, "Printer \'%s\' now accepting jobs (\'%s\').", name,
con->username);
con->response->request.status.status_code = IPP_OK;
}
static void
add_class(client_t *con,
ipp_attribute_t *uri)
{
int i;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
printer_t *pclass;
cups_ptype_t dtype;
const char *dest;
ipp_attribute_t *attr;
int modify;
LogMessage(L_DEBUG2, "add_class(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "add_class: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/classes/", 9) != 0 || strlen(resource) == 9)
{
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
if ((pclass = FindClass(resource + 9)) == NULL)
{
if ((pclass = FindPrinter(resource + 9)) != NULL &&
!(pclass->type & CUPS_PRINTER_REMOTE))
{
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
pclass = AddClass(resource + 9);
modify = 0;
}
else if (pclass->type & CUPS_PRINTER_IMPLICIT)
{
if (ImplicitAnyClasses)
{
snprintf(pclass->name, sizeof(pclass->name), "Any%s", resource + 9);
SortPrinters();
}
else
DeletePrinter(pclass, 1);
pclass = AddClass(resource + 9);
modify = 0;
}
else if (pclass->type & CUPS_PRINTER_REMOTE)
{
DeletePrinterFilters(pclass);
snprintf(pclass->name, sizeof(pclass->name), "%s@%s", resource + 9,
pclass->hostname);
SetPrinterAttrs(pclass);
SortPrinters();
pclass = AddClass(resource + 9);
modify = 0;
}
else
modify = 1;
if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
SetString(&pclass->location, attr->values[0].string.text);
if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
SetString(&pclass->info, attr->values[0].string.text);
if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
{
LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
pclass->name, attr->values[0].boolean, pclass->accepting);
pclass->accepting = attr->values[0].boolean;
AddPrinterHistory(pclass);
}
if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
{
if (attr->values[0].integer != IPP_PRINTER_IDLE &&
attr->values[0].integer == IPP_PRINTER_STOPPED)
{
LogMessage(L_ERROR, "Attempt to set %s printer-state to bad value %d!",
pclass->name, attr->values[0].integer);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name,
attr->values[0].integer, pclass->state);
SetPrinterState(pclass, attr->values[0].integer, 0);
}
if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
{
strlcpy(pclass->state_message, attr->values[0].string.text,
sizeof(pclass->state_message));
AddPrinterHistory(pclass);
}
if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
!Classification)
{
SetString(&pclass->job_sheets[0], attr->values[0].string.text);
if (attr->num_values > 1)
SetString(&pclass->job_sheets[1], attr->values[1].string.text);
else
SetString(&pclass->job_sheets[1], "none");
}
if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
IPP_TAG_ZERO)) != NULL)
{
FreePrinterUsers(pclass);
pclass->deny_users = 0;
if (attr->value_tag == IPP_TAG_NAME &&
(attr->num_values > 1 ||
strcmp(attr->values[0].string.text, "all") != 0))
for (i = 0; i < attr->num_values; i ++)
AddPrinterUser(pclass, attr->values[i].string.text);
}
else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
IPP_TAG_ZERO)) != NULL)
{
FreePrinterUsers(pclass);
pclass->deny_users = 1;
if (attr->value_tag == IPP_TAG_NAME &&
(attr->num_values > 1 ||
strcmp(attr->values[0].string.text, "none") != 0))
for (i = 0; i < attr->num_values; i ++)
AddPrinterUser(pclass, attr->values[i].string.text);
}
if ((attr = ippFindAttribute(con->request, "job-quota-period",
IPP_TAG_INTEGER)) != NULL)
{
LogMessage(L_DEBUG, "add_class: Setting job-quota-period to %d...",
attr->values[0].integer);
FreeQuotas(pclass);
pclass->quota_period = attr->values[0].integer;
}
if ((attr = ippFindAttribute(con->request, "job-k-limit",
IPP_TAG_INTEGER)) != NULL)
{
LogMessage(L_DEBUG, "add_class: Setting job-k-limit to %d...",
attr->values[0].integer);
FreeQuotas(pclass);
pclass->k_limit = attr->values[0].integer;
}
if ((attr = ippFindAttribute(con->request, "job-page-limit",
IPP_TAG_INTEGER)) != NULL)
{
LogMessage(L_DEBUG, "add_class: Setting job-page-limit to %d...",
attr->values[0].integer);
FreeQuotas(pclass);
pclass->page_limit = attr->values[0].integer;
}
if ((attr = ippFindAttribute(con->request, "member-uris", IPP_TAG_URI)) != NULL)
{
if (pclass->num_printers > 0)
{
free(pclass->printers);
pclass->num_printers = 0;
}
for (i = 0; i < attr->num_values; i ++)
{
httpSeparate(attr->values[i].string.text, method, username, host,
&port, resource);
if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "add_class: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
{
AddPrinterToClass(pclass, FindClass(dest));
LogMessage(L_DEBUG, "add_class: Added class \"%s\" to class \"%s\"...",
dest, pclass->name);
}
else
{
AddPrinterToClass(pclass, FindPrinter(dest));
LogMessage(L_DEBUG, "add_class: Added printer \"%s\" to class \"%s\"...",
dest, pclass->name);
}
}
}
SetPrinterAttrs(pclass);
SaveAllClasses();
CheckJobs();
WritePrintcap();
if (modify)
LogMessage(L_INFO, "Class \'%s\' modified by \'%s\'.", pclass->name,
con->username);
else
{
AddPrinterHistory(pclass);
LogMessage(L_INFO, "New class \'%s\' added by \'%s\'.", pclass->name,
con->username);
}
con->response->request.status.status_code = IPP_OK;
}
static int
add_file(client_t *con,
job_t *job,
mime_type_t *filetype,
int compression)
{
mime_type_t **filetypes;
int *compressions;
LogMessage(L_DEBUG2, "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)\n",
con, con->http.fd, job->id, filetype->super, filetype->type,
compression);
if (job->num_files == 0)
{
compressions = (int *)malloc(sizeof(int));
filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
}
else
{
compressions = (int *)realloc(job->compressions,
(job->num_files + 1) * sizeof(int));
filetypes = (mime_type_t **)realloc(job->filetypes,
(job->num_files + 1) *
sizeof(mime_type_t *));
}
if (compressions == NULL || filetypes == NULL)
{
CancelJob(job->id, 1);
LogMessage(L_ERROR, "add_file: unable to allocate memory for file types!");
send_ipp_error(con, IPP_INTERNAL_ERROR);
return (-1);
}
job->compressions = compressions;
job->compressions[job->num_files] = compression;
job->filetypes = filetypes;
job->filetypes[job->num_files] = filetype;
job->num_files ++;
return (0);
}
static void
add_job_state_reasons(client_t *con,
job_t *job)
{
printer_t *dest;
LogMessage(L_DEBUG2, "add_job_state_reasons(%p[%d], %d)\n", con, con->http.fd,
job->id);
switch (job->state->values[0].integer)
{
case IPP_JOB_PENDING :
if (job->dtype & CUPS_PRINTER_CLASS)
dest = FindClass(job->dest);
else
dest = FindPrinter(job->dest);
if (dest != NULL && dest->state == IPP_PRINTER_STOPPED)
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "printer-stopped");
else
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "none");
break;
case IPP_JOB_HELD :
if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "job-hold-until-specified");
else
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "job-incoming");
break;
case IPP_JOB_PROCESSING :
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "job-printing");
break;
case IPP_JOB_STOPPED :
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "job-stopped");
break;
case IPP_JOB_CANCELLED :
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "job-canceled-by-user");
break;
case IPP_JOB_ABORTED :
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "aborted-by-system");
break;
case IPP_JOB_COMPLETED :
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-state-reasons", NULL, "job-completed-successfully");
break;
}
}
static void
add_printer(client_t *con,
ipp_attribute_t *uri)
{
int i;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
printer_t *printer;
ipp_attribute_t *attr;
cups_file_t *fp;
char line[1024];
char srcfile[1024],
dstfile[1024];
int modify;
LogMessage(L_DEBUG2, "add_printer(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "add_printer: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/printers/", 10) != 0 || strlen(resource) == 10)
{
LogMessage(L_ERROR, "add_printer: bad printer URI \"%s\"!",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
if ((printer = FindPrinter(resource + 10)) == NULL)
{
if ((printer = FindClass(resource + 10)) != NULL &&
!(printer->type & CUPS_PRINTER_REMOTE))
{
LogMessage(L_ERROR, "add_printer: \"%s\" already exists as a class!",
resource + 10);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
printer = AddPrinter(resource + 10);
modify = 0;
}
else if (printer->type & CUPS_PRINTER_IMPLICIT)
{
if (ImplicitAnyClasses)
{
snprintf(printer->name, sizeof(printer->name), "Any%s", resource + 10);
SortPrinters();
}
else
DeletePrinter(printer, 1);
printer = AddPrinter(resource + 10);
modify = 0;
}
else if (printer->type & CUPS_PRINTER_REMOTE)
{
DeletePrinterFilters(printer);
snprintf(printer->name, sizeof(printer->name), "%s@%s", resource + 10,
printer->hostname);
SetPrinterAttrs(printer);
SortPrinters();
printer = AddPrinter(resource + 10);
modify = 0;
}
else
modify = 1;
if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
SetString(&printer->location, attr->values[0].string.text);
if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
SetString(&printer->info, attr->values[0].string.text);
if ((attr = ippFindAttribute(con->request, "device-uri", IPP_TAG_URI)) != NULL)
{
ipp_attribute_t *device;
int methodlen;
httpSeparate(attr->values[0].string.text, method, username, host,
&port, resource);
methodlen = strlen(method);
if (strcmp(method, "file") == 0)
{
if (!FileDevice && strcmp(resource, "/dev/null"))
{
LogMessage(L_ERROR, "add_printer: File device URIs have been disabled! "
"To enable, see the FileDevice directive in cupsd.conf.");
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
}
else
{
for (device = ippFindAttribute(Devices, "device-uri", IPP_TAG_URI);
device != NULL;
device = ippFindNextAttribute(Devices, "device-uri", IPP_TAG_URI))
if (strncmp(method, device->values[0].string.text, methodlen) == 0 &&
(device->values[0].string.text[methodlen] == ':' ||
device->values[0].string.text[methodlen] == '\0'))
break;
if (device == NULL)
{
LogMessage(L_ERROR, "add_printer: bad device-uri attribute \'%s\'!",
attr->values[0].string.text);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
}
LogMessage(L_INFO, "Setting %s device-uri to \"%s\" (was \"%s\".)",
printer->name, attr->values[0].string.text, printer->device_uri);
SetString(&printer->device_uri, attr->values[0].string.text);
}
if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
{
LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
printer->name, attr->values[0].boolean, printer->accepting);
printer->accepting = attr->values[0].boolean;
AddPrinterHistory(printer);
}
if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
{
if (attr->values[0].integer != IPP_PRINTER_IDLE &&
attr->values[0].integer == IPP_PRINTER_STOPPED)
{
LogMessage(L_ERROR, "Attempt to set %s printer-state to bad value %d!",
printer->name, attr->values[0].integer);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", printer->name,
attr->values[0].integer, printer->state);
SetPrinterState(printer, attr->values[0].integer, 0);
}
if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
{
strlcpy(printer->state_message, attr->values[0].string.text,
sizeof(printer->state_message));
AddPrinterHistory(printer);
}
if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
!Classification)
{
SetString(&printer->job_sheets[0], attr->values[0].string.text);
if (attr->num_values > 1)
SetString(&printer->job_sheets[1], attr->values[1].string.text);
else
SetString(&printer->job_sheets[1], "none");
}
if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
IPP_TAG_ZERO)) != NULL)
{
FreePrinterUsers(printer);
printer->deny_users = 0;
if (attr->value_tag == IPP_TAG_NAME &&
(attr->num_values > 1 ||
strcmp(attr->values[0].string.text, "all") != 0))
for (i = 0; i < attr->num_values; i ++)
AddPrinterUser(printer, attr->values[i].string.text);
}
else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
IPP_TAG_ZERO)) != NULL)
{
FreePrinterUsers(printer);
printer->deny_users = 1;
if (attr->value_tag == IPP_TAG_NAME &&
(attr->num_values > 1 ||
strcmp(attr->values[0].string.text, "none") != 0))
for (i = 0; i < attr->num_values; i ++)
AddPrinterUser(printer, attr->values[i].string.text);
}
if ((attr = ippFindAttribute(con->request, "job-quota-period",
IPP_TAG_INTEGER)) != NULL)
{
LogMessage(L_DEBUG, "add_printer: Setting job-quota-period to %d...",
attr->values[0].integer);
FreeQuotas(printer);
printer->quota_period = attr->values[0].integer;
}
if ((attr = ippFindAttribute(con->request, "job-k-limit",
IPP_TAG_INTEGER)) != NULL)
{
LogMessage(L_DEBUG, "add_printer: Setting job-k-limit to %d...",
attr->values[0].integer);
FreeQuotas(printer);
printer->k_limit = attr->values[0].integer;
}
if ((attr = ippFindAttribute(con->request, "job-page-limit",
IPP_TAG_INTEGER)) != NULL)
{
LogMessage(L_DEBUG, "add_printer: Setting job-page-limit to %d...",
attr->values[0].integer);
FreeQuotas(printer);
printer->page_limit = attr->values[0].integer;
}
if (!printer->device_uri)
SetString(&printer->device_uri, "file:/dev/null");
if (con->filename)
{
strlcpy(srcfile, con->filename, sizeof(srcfile));
if ((fp = cupsFileOpen(srcfile, "rb")) != NULL)
{
line[0] = '\0';
cupsFileGets(fp, line, sizeof(line));
cupsFileClose(fp);
snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
printer->name);
if (strncmp(line, "*PPD-Adobe", 10) == 0)
{
unlink(dstfile);
}
else
{
if (copy_file(srcfile, dstfile))
{
LogMessage(L_ERROR, "add_printer: Unable to copy interface script from %s to %s - %s!",
srcfile, dstfile, strerror(errno));
send_ipp_error(con, IPP_INTERNAL_ERROR);
return;
}
else
{
LogMessage(L_DEBUG, "add_printer: Copied interface script successfully!");
chmod(dstfile, 0755);
}
}
snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
printer->name);
if (strncmp(line, "*PPD-Adobe", 10) == 0)
{
if (copy_file(srcfile, dstfile))
{
LogMessage(L_ERROR, "add_printer: Unable to copy PPD file from %s to %s - %s!",
srcfile, dstfile, strerror(errno));
send_ipp_error(con, IPP_INTERNAL_ERROR);
return;
}
else
{
LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
chmod(dstfile, 0644);
}
}
else
{
unlink(dstfile);
}
}
}
else if ((attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL)
{
if (strcmp(attr->values[0].string.text, "raw") == 0)
{
snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
printer->name);
unlink(dstfile);
snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
printer->name);
unlink(dstfile);
}
else
{
snprintf(srcfile, sizeof(srcfile), "%s/model/%s", DataDir,
attr->values[0].string.text);
snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
printer->name);
unlink(dstfile);
snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
printer->name);
if (copy_model(srcfile, dstfile))
{
LogMessage(L_ERROR, "add_printer: Unable to copy PPD file from %s to %s - %s!",
srcfile, dstfile, strerror(errno));
send_ipp_error(con, IPP_INTERNAL_ERROR);
return;
}
else
{
LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
chmod(dstfile, 0644);
}
}
}
if (DefaultPrinter == NULL)
DefaultPrinter = printer;
SetPrinterAttrs(printer);
SaveAllPrinters();
if (printer->job != NULL)
{
job_t *job;
job = (job_t *)printer->job;
StopJob(job->id, 1);
job->state->values[0].integer = IPP_JOB_PENDING;
}
CheckJobs();
WritePrintcap();
if (modify)
LogMessage(L_INFO, "Printer \'%s\' modified by \'%s\'.", printer->name,
con->username);
else
{
AddPrinterHistory(printer);
LogMessage(L_INFO, "New printer \'%s\' added by \'%s\'.", printer->name,
con->username);
}
con->response->request.status.status_code = IPP_OK;
}
static void
add_printer_state_reasons(client_t *con,
printer_t *p)
{
LogMessage(L_DEBUG2, "add_printer_state_reasons(%p[%d], %p[%s])\n",
con, con->http.fd, p, p->name);
if (p->num_reasons == 0)
ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
"printer-state-reasons", NULL,
p->state == IPP_PRINTER_STOPPED ? "paused" : "none");
else
ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
"printer-state-reasons", p->num_reasons, NULL,
(const char * const *)p->reasons);
}
static void
add_queued_job_count(client_t *con,
printer_t *p)
{
int count;
LogMessage(L_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])\n",
con, con->http.fd, p, p->name);
count = GetPrinterJobCount(p->name);
ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"queued-job-count", count);
}
static void
cancel_all_jobs(client_t *con,
ipp_attribute_t *uri)
{
const char *dest;
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
userpass[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
ipp_attribute_t *attr;
const char *username;
int purge;
LogMessage(L_DEBUG2, "cancel_all_jobs(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "cancel_all_jobs: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
if (strcmp(uri->name, "printer-uri") != 0)
{
LogMessage(L_ERROR, "cancel_all_jobs: bad %s attribute \'%s\'!",
uri->name, uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
attr->values[0].boolean)
{
if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
username = attr->values[0].string.text;
else
{
LogMessage(L_ERROR, "cancel_all_jobs: missing requesting-user-name attribute!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
}
else
username = NULL;
if ((attr = ippFindAttribute(con->request, "purge-jobs", IPP_TAG_BOOLEAN)) != NULL)
purge = attr->values[0].boolean;
else
purge = 1;
httpSeparate(uri->values[0].string.text, method, userpass, host, &port,
resource);
if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
if (strcmp(resource, "/printers/") != 0)
{
LogMessage(L_ERROR, "cancel_all_jobs: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
CancelJobs(NULL, username, purge);
LogMessage(L_INFO, "All jobs were %s by \'%s\'.",
purge ? "purged" : "cancelled", con->username);
}
else
{
CancelJobs(dest, username, purge);
LogMessage(L_INFO, "All jobs on \'%s\' were %s by \'%s\'.", dest,
purge ? "purged" : "cancelled", con->username);
}
con->response->request.status.status_code = IPP_OK;
}
static void
cancel_job(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr;
int jobid;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
job_t *job;
const char *dest;
cups_ptype_t dtype;
printer_t *printer;
LogMessage(L_DEBUG2, "cancel_job(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/classes/", 9) != 0 &&
strncmp(con->uri, "/jobs/", 5) != 0 &&
strncmp(con->uri, "/printers/", 10) != 0)
{
LogMessage(L_ERROR, "cancel_job: cancel request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
if (strcmp(uri->name, "printer-uri") == 0)
{
if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
{
LogMessage(L_ERROR, "cancel_job: got a printer-uri attribute but no job-id!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
if ((jobid = attr->values[0].integer) == 0)
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "cancel_job: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
printer = FindClass(dest);
else
printer = FindPrinter(dest);
if (printer->job)
jobid = ((job_t *)printer->job)->id;
else
{
for (job = Jobs; job != NULL; job = job->next)
if (job->state->values[0].integer <= IPP_JOB_PROCESSING &&
strcasecmp(job->dest, dest) == 0)
break;
if (job != NULL)
jobid = job->id;
else
{
LogMessage(L_ERROR, "cancel_job: No active jobs on %s!", dest);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
}
}
}
else
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/jobs/", 6) != 0)
{
LogMessage(L_ERROR, "cancel_job: bad job-uri attribute \'%s\'!",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = atoi(resource + 6);
}
if ((job = FindJob(jobid)) == NULL)
{
LogMessage(L_ERROR, "cancel_job: job #%d doesn't exist!", jobid);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (!validate_user(con, job->username, username, sizeof(username)))
{
LogMessage(L_ERROR, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
username, jobid, job->username);
send_ipp_error(con, IPP_FORBIDDEN);
return;
}
if (job->state->values[0].integer >= IPP_JOB_CANCELLED)
{
LogMessage(L_ERROR, "cancel_job: job id %d is %s - can't cancel!",
jobid,
job->state->values[0].integer == IPP_JOB_CANCELLED ? "cancelled" :
job->state->values[0].integer == IPP_JOB_ABORTED ? "aborted" :
"completed");
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
CancelJob(jobid, 0);
CheckJobs();
LogMessage(L_INFO, "Job %d was cancelled by \'%s\'.", jobid, username);
con->response->request.status.status_code = IPP_OK;
}
static int
check_quotas(client_t *con,
printer_t *p)
{
int i, j;
ipp_attribute_t *attr;
char username[33];
quota_t *q;
struct passwd *pw;
struct group *grp;
LogMessage(L_DEBUG2, "check_quotas(%p[%d], %p[%s])\n",
con, con->http.fd, p, p->name);
if (con == NULL || p == NULL)
return (0);
attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
if (con->username[0])
strlcpy(username, con->username, sizeof(username));
else if (attr != NULL)
{
LogMessage(L_DEBUG, "check_quotas: requesting-user-name = \'%s\'",
attr->values[0].string.text);
strlcpy(username, attr->values[0].string.text, sizeof(username));
}
else
strcpy(username, "anonymous");
if (MaxJobsPerPrinter)
{
if (GetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
{
LogMessage(L_INFO, "Too many jobs for printer \"%s\"...", p->name);
return (0);
}
}
if (MaxJobsPerUser)
{
if (GetUserJobCount(username) >= MaxJobsPerUser)
{
LogMessage(L_INFO, "Too many jobs for user \"%s\"...", username);
return (0);
}
}
if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
return (1);
if (p->num_users)
{
pw = getpwnam(username);
endpwent();
for (i = 0; i < p->num_users; i ++)
if (p->users[i][0] == '@')
{
grp = getgrnam(p->users[i] + 1);
endgrent();
if (grp)
{
if (pw && grp->gr_gid == pw->pw_gid)
break;
for (j = 0; grp->gr_mem[j]; j ++)
if (!strcmp(username, grp->gr_mem[j]))
break;
if (grp->gr_mem[j])
break;
}
}
else if (!strcasecmp(username, p->users[i]))
break;
if ((i < p->num_users) == p->deny_users)
{
LogMessage(L_INFO, "Denying user \"%s\" access to printer \"%s\"...",
username, p->name);
return (0);
}
}
if (p->k_limit || p->page_limit)
{
if ((q = UpdateQuota(p, username, 0, 0)) == NULL)
{
LogMessage(L_ERROR, "Unable to allocate quota data for user \"%s\"!",
username);
return (0);
}
if ((q->k_count >= p->k_limit && p->k_limit) ||
(q->page_count >= p->page_limit && p->page_limit))
{
LogMessage(L_INFO, "User \"%s\" is over the quota limit...",
username);
return (0);
}
}
return (1);
}
static ipp_attribute_t *
copy_attribute(ipp_t *to,
ipp_attribute_t *attr,
int quickcopy)
{
int i;
ipp_attribute_t *toattr;
LogMessage(L_DEBUG2, "copy_attribute(%p, %p[%s,%x,%x])\n", to, attr,
attr->name ? attr->name : "(null)", attr->group_tag,
attr->value_tag);
switch (attr->value_tag & ~IPP_TAG_COPY)
{
case IPP_TAG_ZERO :
toattr = ippAddSeparator(to);
break;
case IPP_TAG_INTEGER :
case IPP_TAG_ENUM :
toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
attr->name, attr->num_values, NULL);
for (i = 0; i < attr->num_values; i ++)
toattr->values[i].integer = attr->values[i].integer;
break;
case IPP_TAG_BOOLEAN :
toattr = ippAddBooleans(to, attr->group_tag, attr->name,
attr->num_values, NULL);
for (i = 0; i < attr->num_values; i ++)
toattr->values[i].boolean = attr->values[i].boolean;
break;
case IPP_TAG_STRING :
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 :
toattr = ippAddStrings(to, attr->group_tag,
(ipp_tag_t)(attr->value_tag | quickcopy),
attr->name, attr->num_values, NULL, NULL);
if (quickcopy)
{
for (i = 0; i < attr->num_values; i ++)
toattr->values[i].string.text = attr->values[i].string.text;
}
else
{
for (i = 0; i < attr->num_values; i ++)
toattr->values[i].string.text = strdup(attr->values[i].string.text);
}
break;
case IPP_TAG_DATE :
toattr = ippAddDate(to, attr->group_tag, attr->name,
attr->values[0].date);
break;
case IPP_TAG_RESOLUTION :
toattr = ippAddResolutions(to, attr->group_tag, attr->name,
attr->num_values, IPP_RES_PER_INCH,
NULL, NULL);
for (i = 0; i < attr->num_values; i ++)
{
toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
toattr->values[i].resolution.units = attr->values[i].resolution.units;
}
break;
case IPP_TAG_RANGE :
toattr = ippAddRanges(to, attr->group_tag, attr->name,
attr->num_values, NULL, NULL);
for (i = 0; i < attr->num_values; i ++)
{
toattr->values[i].range.lower = attr->values[i].range.lower;
toattr->values[i].range.upper = attr->values[i].range.upper;
}
break;
case IPP_TAG_TEXTLANG :
case IPP_TAG_NAMELANG :
toattr = ippAddStrings(to, attr->group_tag,
(ipp_tag_t)(attr->value_tag | quickcopy),
attr->name, attr->num_values, NULL, NULL);
if (quickcopy)
{
for (i = 0; i < attr->num_values; i ++)
{
toattr->values[i].string.charset = attr->values[i].string.charset;
toattr->values[i].string.text = attr->values[i].string.text;
}
}
else
{
for (i = 0; i < attr->num_values; i ++)
{
if (!i)
toattr->values[i].string.charset =
strdup(attr->values[i].string.charset);
else
toattr->values[i].string.charset =
toattr->values[0].string.charset;
toattr->values[i].string.text = strdup(attr->values[i].string.text);
}
}
break;
case IPP_TAG_BEGIN_COLLECTION :
toattr = ippAddCollections(to, attr->group_tag, attr->name,
attr->num_values, NULL);
for (i = 0; i < attr->num_values; i ++)
{
toattr->values[i].collection = ippNew();
copy_attrs(toattr->values[i].collection, attr->values[i].collection,
NULL, IPP_TAG_ZERO, 0);
}
break;
default :
toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
attr->name, attr->num_values, NULL);
for (i = 0; i < attr->num_values; i ++)
{
toattr->values[i].unknown.length = attr->values[i].unknown.length;
if (toattr->values[i].unknown.length > 0)
{
if ((toattr->values[i].unknown.data = malloc(toattr->values[i].unknown.length)) == NULL)
toattr->values[i].unknown.length = 0;
else
memcpy(toattr->values[i].unknown.data,
attr->values[i].unknown.data,
toattr->values[i].unknown.length);
}
}
break;
}
return (toattr);
}
static void
copy_attrs(ipp_t *to,
ipp_t *from,
ipp_attribute_t *req,
ipp_tag_t group,
int quickcopy)
{
int i;
ipp_attribute_t *fromattr;
LogMessage(L_DEBUG2, "copy_attrs(%p, %p, %p, %x)\n", to, from, req, group);
if (to == NULL || from == NULL)
return;
if (req != NULL && strcmp(req->values[0].string.text, "all") == 0)
req = NULL;
for (fromattr = from->attrs; fromattr != NULL; fromattr = fromattr->next)
{
if (group != IPP_TAG_ZERO && fromattr->group_tag != group &&
fromattr->group_tag != IPP_TAG_ZERO)
continue;
if (req != NULL && fromattr->name != NULL)
{
for (i = 0; i < req->num_values; i ++)
if (strcmp(fromattr->name, req->values[i].string.text) == 0)
break;
if (i == req->num_values)
continue;
}
copy_attribute(to, fromattr, quickcopy);
}
}
static int
copy_banner(client_t *con,
job_t *job,
const char *name)
{
int i;
int kbytes;
char filename[1024];
banner_t *banner;
cups_file_t *in;
cups_file_t *out;
int ch;
char attrname[255],
*s;
ipp_attribute_t *attr;
LogMessage(L_DEBUG2, "copy_banner(%p[%d], %p[%d], %s)",
con, con->http.fd, job, job->id, name ? name : "(null)");
if (name == NULL ||
strcmp(name, "none") == 0 ||
(banner = FindBanner(name)) == NULL)
return (0);
if (add_file(con, job, banner->filetype, 0))
return (0);
snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
job->num_files);
if ((out = cupsFileOpen(filename, "w")) == NULL)
{
LogMessage(L_ERROR, "copy_banner: Unable to create banner job file %s - %s",
filename, strerror(errno));
job->num_files --;
return (0);
}
fchmod(cupsFileNumber(out), 0640);
fchown(cupsFileNumber(out), getuid(), Group);
if (con->language)
{
snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
con->language->language, name);
if (access(filename, 0) && con->language->language[2])
{
attrname[0] = con->language->language[0];
attrname[1] = con->language->language[1];
attrname[2] = '\0';
snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
attrname, name);
}
if (access(filename, 0))
{
snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
}
}
else
{
snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
}
if ((in = cupsFileOpen(filename, "r")) == NULL)
{
cupsFileClose(out);
unlink(filename);
LogMessage(L_ERROR, "copy_banner: Unable to open banner template file %s - %s",
filename, strerror(errno));
job->num_files --;
return (0);
}
while ((ch = cupsFileGetChar(in)) != EOF)
if (ch == '{')
{
for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
if (!isalpha(ch) && ch != '-' && ch != '?')
break;
else if (s < (attrname + sizeof(attrname) - 1))
*s++ = ch;
else
break;
*s = '\0';
if (ch != '}')
{
cupsFilePrintf(out, "{%s%c", attrname, ch);
continue;
}
if (attrname[0] == '?')
s = attrname + 1;
else
s = attrname;
if (strcmp(s, "printer-name") == 0)
{
cupsFilePuts(out, job->dest);
continue;
}
else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
{
if (attrname[0] != '?')
{
cupsFilePrintf(out, "{%s}", attrname);
}
continue;
}
for (i = 0; i < attr->num_values; i ++)
{
if (i)
cupsFilePutChar(out, ',');
switch (attr->value_tag)
{
case IPP_TAG_INTEGER :
case IPP_TAG_ENUM :
if (strncmp(s, "time-at-", 8) == 0)
cupsFilePuts(out, GetDateTime(attr->values[i].integer));
else
cupsFilePrintf(out, "%d", attr->values[i].integer);
break;
case IPP_TAG_BOOLEAN :
cupsFilePrintf(out, "%d", attr->values[i].boolean);
break;
case IPP_TAG_NOVALUE :
cupsFilePuts(out, "novalue");
break;
case IPP_TAG_RANGE :
cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
attr->values[i].range.upper);
break;
case IPP_TAG_RESOLUTION :
cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
attr->values[i].resolution.yres,
attr->values[i].resolution.units == IPP_RES_PER_INCH ?
"dpi" : "dpc");
break;
case IPP_TAG_URI :
case IPP_TAG_STRING :
case IPP_TAG_TEXT :
case IPP_TAG_NAME :
case IPP_TAG_KEYWORD :
case IPP_TAG_CHARSET :
case IPP_TAG_LANGUAGE :
if (strcasecmp(banner->filetype->type, "postscript") == 0)
{
const char *p;
for (p = attr->values[i].string.text; *p; p ++)
{
if (*p == '(' || *p == ')' || *p == '\\')
{
cupsFilePutChar(out, '\\');
cupsFilePutChar(out, *p);
}
else if (*p < 32 || *p > 126)
cupsFilePrintf(out, "\\%03o", *p);
else
cupsFilePutChar(out, *p);
}
}
else
cupsFilePuts(out, attr->values[i].string.text);
break;
default :
break;
}
}
}
else if (ch == '\\')
{
ch = cupsFileGetChar(in);
if (ch != '{')
cupsFilePutChar(out, '\\');
cupsFilePutChar(out, ch);
}
else
cupsFilePutChar(out, ch);
cupsFileClose(in);
kbytes = (cupsFileTell(out) + 1023) / 1024;
if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
attr->values[0].integer += kbytes;
cupsFileClose(out);
return (kbytes);
}
static int
copy_file(const char *from,
const char *to)
{
cups_file_t *src,
*dst;
int bytes;
char buffer[2048];
LogMessage(L_DEBUG2, "copy_file(\"%s\", \"%s\")\n", from, to);
if ((src = cupsFileOpen(from, "rb")) == NULL)
return (-1);
if ((dst = cupsFileOpen(to, "wb")) == NULL)
{
cupsFileClose(src);
return (-1);
}
while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
if (cupsFileWrite(dst, buffer, bytes) < bytes)
{
cupsFileClose(src);
cupsFileClose(dst);
return (-1);
}
cupsFileClose(src);
return (cupsFileClose(dst));
}
static int
copy_model(const char *from,
const char *to)
{
cups_file_t *src,
*dst;
char buffer[2048];
int i;
char option[PPD_MAX_NAME],
choice[PPD_MAX_NAME];
int num_defaults;
ppd_default_t *defaults;
char cups_protocol[PPD_MAX_LINE];
#ifdef HAVE_LIBPAPER
char *paper_result;
char system_paper[64];
#endif
LogMessage(L_DEBUG2, "copy_model(\"%s\", \"%s\")\n", from, to);
num_defaults = 0;
defaults = NULL;
cups_protocol[0] = '\0';
if ((dst = cupsFileOpen(to, "rb")) != NULL)
{
while (cupsFileGets(dst, buffer, sizeof(buffer)) != NULL)
if (!strncmp(buffer, "*Default", 8))
{
if (!ppd_parse_line(buffer, option, sizeof(option),
choice, sizeof(choice)))
num_defaults = ppd_add_default(option, choice, num_defaults,
&defaults);
}
else if (!strncmp(buffer, "*cupsProtocol:", 14))
strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
cupsFileClose(dst);
}
#ifdef HAVE_LIBPAPER
else if ((paper_result = systempapername()) != NULL)
{
strlcpy(system_paper, paper_result, sizeof(system_paper));
system_paper[0] = toupper(system_paper[0]);
num_defaults = ppd_add_default("PageSize", system_paper,
num_defaults, &defaults);
num_defaults = ppd_add_default("PageRegion", system_paper,
num_defaults, &defaults);
num_defaults = ppd_add_default("PaperDimension", system_paper,
num_defaults, &defaults);
num_defaults = ppd_add_default("ImageableArea", system_paper,
num_defaults, &defaults);
}
#endif
else
{
if (!DefaultLanguage ||
!strcasecmp(DefaultLanguage, "C") ||
!strcasecmp(DefaultLanguage, "POSIX") ||
!strcasecmp(DefaultLanguage, "en") ||
!strncasecmp(DefaultLanguage, "en_US", 5) ||
!strncasecmp(DefaultLanguage, "en_CA", 5) ||
!strncasecmp(DefaultLanguage, "fr_CA", 5))
{
num_defaults = ppd_add_default("PageSize", "Letter", num_defaults,
&defaults);
num_defaults = ppd_add_default("PageRegion", "Letter", num_defaults,
&defaults);
num_defaults = ppd_add_default("PaperDimension", "Letter", num_defaults,
&defaults);
num_defaults = ppd_add_default("ImageableArea", "Letter", num_defaults,
&defaults);
}
else
{
num_defaults = ppd_add_default("PageSize", "A4", num_defaults,
&defaults);
num_defaults = ppd_add_default("PageRegion", "A4", num_defaults,
&defaults);
num_defaults = ppd_add_default("PaperDimension", "A4", num_defaults,
&defaults);
num_defaults = ppd_add_default("ImageableArea", "A4", num_defaults,
&defaults);
}
}
if ((src = cupsFileOpen(from, "rb")) == NULL)
{
if (num_defaults > 0)
free(defaults);
return (-1);
}
if ((dst = cupsFileOpen(to, "wb")) == NULL)
{
if (num_defaults > 0)
free(defaults);
cupsFileClose(src);
return (-1);
}
while (cupsFileGets(src, buffer, sizeof(buffer)) != NULL)
{
if (!strncmp(buffer, "*Default", 8))
{
if (!ppd_parse_line(buffer, option, sizeof(option),
choice, sizeof(choice)))
{
for (i = 0; i < num_defaults; i ++)
if (!strcmp(option, defaults[i].option))
{
snprintf(buffer, sizeof(buffer), "*Default%s: %s", option,
defaults[i].choice);
break;
}
}
}
cupsFilePrintf(dst, "%s\n", buffer);
}
if (cups_protocol[0])
cupsFilePrintf(dst, "%s\n", cups_protocol);
if (num_defaults > 0)
free(defaults);
cupsFileClose(src);
return (cupsFileClose(dst));
}
static void
create_job(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr;
const char *dest;
cups_ptype_t dtype;
int priority;
char *title;
job_t *job;
char job_uri[HTTP_MAX_URI],
printer_uri[HTTP_MAX_URI],
method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
printer_t *printer;
int kbytes;
int i;
int lowerpagerange;
LogMessage(L_DEBUG2, "create_job(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/classes/", 9) != 0 &&
strncmp(con->uri, "/printers/", 10) != 0)
{
LogMessage(L_ERROR, "create_job: cancel request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "create_job: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
{
printer = FindClass(dest);
snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
ServerName, ntohs(con->http.hostaddr.sin_port), dest);
}
else
{
printer = FindPrinter(dest);
snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
ServerName, ntohs(con->http.hostaddr.sin_port), dest);
}
if (!printer->accepting)
{
LogMessage(L_INFO, "create_job: destination \'%s\' is not accepting jobs.",
dest);
send_ipp_error(con, IPP_NOT_ACCEPTING);
return;
}
if ((attr = ippFindAttribute(con->request, "copies", IPP_TAG_INTEGER)) != NULL)
{
#ifdef __APPLE__
if (attr->values[0].integer < MinCopies || attr->values[0].integer > MaxCopies)
#else
if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
#endif
{
LogMessage(L_INFO, "create_job: bad copies value %d.",
attr->values[0].integer);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
}
if ((attr = ippFindAttribute(con->request, "page-ranges", IPP_TAG_RANGE)) != NULL)
{
for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
{
if (attr->values[i].range.lower < lowerpagerange ||
attr->values[i].range.lower > attr->values[i].range.upper)
{
LogMessage(L_ERROR, "create_job: bad page-ranges values %d-%d.",
attr->values[i].range.lower, attr->values[i].range.upper);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
lowerpagerange = attr->values[i].range.upper + 1;
}
}
if (NumJobs >= MaxJobs && MaxJobs)
CleanJobs();
if (NumJobs >= MaxJobs && MaxJobs)
{
LogMessage(L_INFO, "create_job: too many jobs.");
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if (!check_quotas(con, printer))
{
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
priority = attr->values[0].integer;
else
ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
priority = 50);
if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
title = attr->values[0].string.text;
else
ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
title = "Untitled");
if ((job = AddJob(priority, printer->name)) == NULL)
{
LogMessage(L_ERROR, "create_job: unable to add job for destination \'%s\'!",
dest);
send_ipp_error(con, IPP_INTERNAL_ERROR);
return;
}
job->dtype = dtype;
job->attrs = con->request;
con->request = NULL;
attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
if (con->username[0])
SetString(&job->username, con->username);
else if (attr != NULL)
{
LogMessage(L_DEBUG, "create_job: requesting-user-name = \'%s\'",
attr->values[0].string.text);
SetString(&job->username, attr->values[0].string.text);
}
else
SetString(&job->username, "anonymous");
if (attr == NULL)
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
NULL, job->username);
else
{
attr->group_tag = IPP_TAG_JOB;
SetString(&attr->name, "job-originating-user-name");
}
if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
IPP_TAG_ZERO)) != NULL)
{
if (attr->value_tag != IPP_TAG_NAME ||
attr->num_values != 1 ||
strcmp(con->http.hostname, "localhost") != 0)
{
int i;
switch (attr->value_tag)
{
case IPP_TAG_STRING :
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 :
for (i = 0; i < attr->num_values; i ++)
{
free(attr->values[i].string.text);
attr->values[i].string.text = NULL;
if (attr->values[i].string.charset)
{
free(attr->values[i].string.charset);
attr->values[i].string.charset = NULL;
}
}
default :
break;
}
attr->value_tag = IPP_TAG_NAME;
attr->num_values = 1;
attr->values[0].string.text = strdup(con->http.hostname);
}
attr->group_tag = IPP_TAG_JOB;
}
else
{
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
"job-originating-host-name", NULL, con->http.hostname);
}
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
time(NULL));
attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
"time-at-processing", 0);
attr->value_tag = IPP_TAG_NOVALUE;
attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
"time-at-completed", 0);
attr->value_tag = IPP_TAG_NOVALUE;
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
"job-state", IPP_JOB_STOPPED);
job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
"job-media-sheets-completed", 0);
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
printer_uri);
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
title);
if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
attr->values[0].integer = 0;
else
attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
"job-k-octets", 0);
if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
if (attr == NULL)
attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-hold-until", NULL, "no-hold");
if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
!(printer->type & CUPS_PRINTER_REMOTE))
{
SetJobHoldUntil(job->id, attr->values[0].string.text);
}
else
job->hold_until = time(NULL) + 60;
job->state->values[0].integer = IPP_JOB_HELD;
if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
Classification)
{
if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
{
LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
printer->job_sheets[0], printer->job_sheets[1]);
attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
2, NULL, NULL);
attr->values[0].string.text = strdup(printer->job_sheets[0]);
attr->values[1].string.text = strdup(printer->job_sheets[1]);
}
job->job_sheets = attr;
if (Classification)
{
if (ClassifyOverride)
{
if (strcmp(attr->values[0].string.text, "none") == 0 &&
(attr->num_values == 1 ||
strcmp(attr->values[1].string.text, "none") == 0))
{
SetString(&attr->values[0].string.text, Classification);
}
else if (attr->num_values == 2 &&
strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
strcmp(attr->values[0].string.text, "none") != 0 &&
strcmp(attr->values[1].string.text, "none") != 0)
{
SetString(&attr->values[1].string.text, attr->values[0].string.text);
}
}
else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
(attr->num_values == 1 ||
strcmp(attr->values[1].string.text, Classification) != 0))
{
SetString(&attr->values[0].string.text, Classification);
}
}
if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
{
LogMessage(L_INFO, "Adding start banner page \"%s\" to job %d.",
attr->values[0].string.text, job->id);
kbytes = copy_banner(con, job, attr->values[0].string.text);
UpdateQuota(printer, job->username, 0, kbytes);
}
}
else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
job->sheets = attr;
SaveJob(job->id);
LogMessage(L_INFO, "Job %d created on \'%s\' by \'%s\'.", job->id,
job->dest, job->username);
snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
ntohs(con->http.hostaddr.sin_port), job->id);
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
job->state->values[0].integer);
con->response->request.status.status_code = IPP_OK;
}
static void
delete_printer(client_t *con,
ipp_attribute_t *uri)
{
const char *dest;
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
printer_t *printer;
char filename[1024];
LogMessage(L_DEBUG2, "delete_printer(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "delete_printer: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "delete_printer: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
printer = FindClass(dest);
else
printer = FindPrinter(dest);
CancelJobs(dest, NULL, 1);
snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
unlink(filename);
snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
unlink(filename);
if (dtype & CUPS_PRINTER_CLASS)
{
LogMessage(L_INFO, "Class \'%s\' deleted by \'%s\'.", dest,
con->username);
DeletePrinter(printer, 0);
SaveAllClasses();
}
else
{
LogMessage(L_INFO, "Printer \'%s\' deleted by \'%s\'.", dest,
con->username);
DeletePrinter(printer, 0);
SaveAllPrinters();
}
con->response->request.status.status_code = IPP_OK;
}
static void
get_default(client_t *con)
{
LogMessage(L_DEBUG2, "get_default(%p[%d])\n", con, con->http.fd);
if (DefaultPrinter != NULL)
{
copy_attrs(con->response, DefaultPrinter->attrs,
ippFindAttribute(con->request, "requested-attributes",
IPP_TAG_KEYWORD), IPP_TAG_ZERO, 0);
con->response->request.status.status_code = IPP_OK;
}
else
con->response->request.status.status_code = IPP_NOT_FOUND;
}
static void
get_devices(client_t *con)
{
LogMessage(L_DEBUG2, "get_devices(%p[%d])\n", con, con->http.fd);
copy_attrs(con->response, Devices,
ippFindAttribute(con->request, "requested-attributes",
IPP_TAG_KEYWORD), IPP_TAG_ZERO, IPP_TAG_COPY);
con->response->request.status.status_code = IPP_OK;
}
static void
get_jobs(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr,
*requested;
const char *dest;
cups_ptype_t dtype;
cups_ptype_t dmask;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
int completed;
int limit;
int count;
job_t *job;
char job_uri[HTTP_MAX_URI];
LogMessage(L_DEBUG2, "get_jobs(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strcmp(resource, "/") == 0 ||
(strncmp(resource, "/jobs", 5) == 0 && strlen(resource) <= 6))
{
dest = NULL;
dtype = (cups_ptype_t)0;
dmask = (cups_ptype_t)0;
}
else if (strncmp(resource, "/printers", 9) == 0 && strlen(resource) <= 10)
{
dest = NULL;
dtype = (cups_ptype_t)0;
dmask = CUPS_PRINTER_CLASS;
}
else if (strncmp(resource, "/classes", 8) == 0 && strlen(resource) <= 9)
{
dest = NULL;
dtype = CUPS_PRINTER_CLASS;
dmask = CUPS_PRINTER_CLASS;
}
else if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "get_jobs: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
else
dmask = CUPS_PRINTER_CLASS;
if ((attr = ippFindAttribute(con->request, "which-jobs", IPP_TAG_KEYWORD)) != NULL &&
strcmp(attr->values[0].string.text, "completed") == 0)
completed = 1;
else
completed = 0;
if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
limit = attr->values[0].integer;
else
limit = 1000000;
if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
attr->values[0].boolean)
{
if (con->username[0])
strlcpy(username, con->username, sizeof(username));
else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
strlcpy(username, attr->values[0].string.text, sizeof(username));
else
strcpy(username, "anonymous");
}
else
username[0] = '\0';
requested = ippFindAttribute(con->request, "requested-attributes",
IPP_TAG_KEYWORD);
for (count = 0, job = Jobs; count < limit && job != NULL; job = job->next)
{
LogMessage(L_DEBUG2, "get_jobs: job->id = %d", job->id);
if ((dest != NULL && strcmp(job->dest, dest) != 0) &&
(job->printer == NULL || dest == NULL ||
strcmp(job->printer->name, dest) != 0))
continue;
if ((job->dtype & dmask) != dtype &&
(job->printer == NULL || (job->printer->type & dmask) != dtype))
continue;
if (username[0] != '\0' && strcmp(username, job->username) != 0)
continue;
if (completed && job->state->values[0].integer <= IPP_JOB_STOPPED)
continue;
if (!completed && job->state->values[0].integer > IPP_JOB_STOPPED)
continue;
count ++;
LogMessage(L_DEBUG2, "get_jobs: count = %d", count);
snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
ntohs(con->http.hostaddr.sin_port), job->id);
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
"job-more-info", NULL, job_uri);
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
"job-uri", NULL, job_uri);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
"job-printer-up-time", time(NULL));
copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB, 0);
add_job_state_reasons(con, job);
ippAddSeparator(con->response);
}
if (requested != NULL)
con->response->request.status.status_code = IPP_OK_SUBST;
else
con->response->request.status.status_code = IPP_OK;
}
static void
get_job_attrs(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr,
*requested;
int jobid;
job_t *job;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
char job_uri[HTTP_MAX_URI];
LogMessage(L_DEBUG2, "get_job_attrs(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strcmp(uri->name, "printer-uri") == 0)
{
if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
{
LogMessage(L_ERROR, "get_job_attrs: got a printer-uri attribute but no job-id!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = attr->values[0].integer;
}
else
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/jobs/", 6) != 0)
{
LogMessage(L_ERROR, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = atoi(resource + 6);
}
if ((job = FindJob(jobid)) == NULL)
{
LogMessage(L_ERROR, "get_job_attrs: job #%d doesn't exist!", jobid);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d",
ServerName, ntohs(con->http.hostaddr.sin_port),
job->id);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
"job-more-info", NULL, job_uri);
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
"job-uri", NULL, job_uri);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
"job-printer-up-time", time(NULL));
requested = ippFindAttribute(con->request, "requested-attributes",
IPP_TAG_KEYWORD);
copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB, 0);
add_job_state_reasons(con, job);
if (requested != NULL)
con->response->request.status.status_code = IPP_OK_SUBST;
else
con->response->request.status.status_code = IPP_OK;
}
static void
get_ppds(client_t *con)
{
LogMessage(L_DEBUG2, "get_ppds(%p[%d])\n", con, con->http.fd);
copy_attrs(con->response, PPDs,
ippFindAttribute(con->request, "requested-attributes",
IPP_TAG_KEYWORD), IPP_TAG_ZERO, IPP_TAG_COPY);
con->response->request.status.status_code = IPP_OK;
}
static void
get_printer_attrs(client_t *con,
ipp_attribute_t *uri)
{
const char *dest;
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
printer_t *printer;
time_t curtime;
int i;
ipp_attribute_t *requested,
*history;
int need_history;
LogMessage(L_DEBUG2, "get_printer_attrs(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "get_printer_attrs: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
printer = FindClass(dest);
else
printer = FindPrinter(dest);
curtime = time(NULL);
ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
printer->state);
add_printer_state_reasons(con, printer);
ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"printer-state-message", NULL, printer->state_message);
ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
printer->accepting);
ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"printer-up-time", curtime);
ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"printer-state-time", printer->state_time);
ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
ippTimeToDate(curtime));
add_queued_job_count(con, printer);
requested = ippFindAttribute(con->request, "requested-attributes",
IPP_TAG_KEYWORD);
copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO, 0);
copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO, IPP_TAG_COPY);
need_history = 0;
if (MaxPrinterHistory > 0 && printer->num_history > 0 && requested)
{
for (i = 0; i < requested->num_values; i ++)
if (!strcmp(requested->values[i].string.text, "all") ||
!strcmp(requested->values[i].string.text, "printer-state-history"))
{
need_history = 1;
break;
}
}
if (need_history)
{
history = ippAddCollections(con->response, IPP_TAG_PRINTER,
"printer-state-history",
printer->num_history, NULL);
for (i = 0; i < printer->num_history; i ++)
copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
NULL, IPP_TAG_ZERO, 0);
}
con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
}
static void
get_printers(client_t *con,
int type)
{
int i;
ipp_attribute_t *requested,
*history,
*attr;
int need_history;
int limit;
int count;
printer_t *printer;
time_t curtime;
int printer_type,
printer_mask;
char *location;
char name[IPP_MAX_NAME],
*nameptr;
printer_t *iclass;
LogMessage(L_DEBUG2, "get_printers(%p[%d], %x)\n", con, con->http.fd, type);
if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
limit = attr->values[0].integer;
else
limit = 10000000;
if ((attr = ippFindAttribute(con->request, "printer-type", IPP_TAG_ENUM)) != NULL)
printer_type = attr->values[0].integer;
else
printer_type = 0;
if ((attr = ippFindAttribute(con->request, "printer-type-mask", IPP_TAG_ENUM)) != NULL)
printer_mask = attr->values[0].integer;
else
printer_mask = 0;
if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
location = attr->values[0].string.text;
else
location = NULL;
requested = ippFindAttribute(con->request, "requested-attributes",
IPP_TAG_KEYWORD);
need_history = 0;
if (MaxPrinterHistory > 0 && requested)
{
for (i = 0; i < requested->num_values; i ++)
if (!strcmp(requested->values[i].string.text, "all") ||
!strcmp(requested->values[i].string.text, "printer-state-history"))
{
need_history = 1;
break;
}
}
curtime = time(NULL);
for (count = 0, printer = Printers;
count < limit && printer != NULL;
printer = printer->next)
if ((printer->type & CUPS_PRINTER_CLASS) == type &&
(printer->type & printer_mask) == printer_type &&
(location == NULL || printer->location == NULL ||
strcasecmp(printer->location, location) == 0))
{
if (ImplicitClasses && HideImplicitMembers &&
(printer->type & CUPS_PRINTER_REMOTE))
{
strlcpy(name, printer->name, sizeof(name));
if ((nameptr = strchr(name, '@')) != NULL)
{
*nameptr = '\0';
if ((iclass = FindPrinter(name)) != NULL &&
(iclass->type & CUPS_PRINTER_IMPLICIT))
continue;
}
}
if (count > 0)
ippAddSeparator(con->response);
count ++;
ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
"printer-state", printer->state);
add_printer_state_reasons(con, printer);
ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"printer-state-message", NULL, printer->state_message);
ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
printer->accepting);
ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"printer-up-time", curtime);
ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"printer-state-time", printer->state_time);
ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
ippTimeToDate(curtime));
add_queued_job_count(con, printer);
copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO, 0);
copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO,
IPP_TAG_COPY);
if (need_history && printer->num_history > 0)
{
history = ippAddCollections(con->response, IPP_TAG_PRINTER,
"printer-state-history",
printer->num_history, NULL);
for (i = 0; i < printer->num_history; i ++)
copy_attrs(history->values[i].collection = ippNew(),
printer->history[i], NULL, IPP_TAG_ZERO, 0);
}
}
con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
}
static void
hold_job(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr,
*newattr;
int jobid;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
job_t *job;
LogMessage(L_DEBUG2, "hold_job(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/classes/", 9) != 0 &&
strncmp(con->uri, "/jobs/", 5) != 0 &&
strncmp(con->uri, "/printers/", 10) != 0)
{
LogMessage(L_ERROR, "hold_job: hold request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
if (strcmp(uri->name, "printer-uri") == 0)
{
if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
{
LogMessage(L_ERROR, "hold_job: got a printer-uri attribute but no job-id!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = attr->values[0].integer;
}
else
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/jobs/", 6) != 0)
{
LogMessage(L_ERROR, "hold_job: bad job-uri attribute \'%s\'!",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = atoi(resource + 6);
}
if ((job = FindJob(jobid)) == NULL)
{
LogMessage(L_ERROR, "hold_job: job #%d doesn't exist!", jobid);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (!validate_user(con, job->username, username, sizeof(username)))
{
LogMessage(L_ERROR, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
username, jobid, job->username);
send_ipp_error(con, IPP_FORBIDDEN);
return;
}
HoldJob(jobid);
if ((newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
if (attr != NULL)
{
free(attr->values[0].string.text);
if (newattr != NULL)
{
attr->value_tag = newattr->value_tag;
attr->values[0].string.text = strdup(newattr->values[0].string.text);
}
else
{
attr->value_tag = IPP_TAG_KEYWORD;
attr->values[0].string.text = strdup("indefinite");
}
SetJobHoldUntil(job->id, attr->values[0].string.text);
}
LogMessage(L_INFO, "Job %d was held by \'%s\'.", jobid, username);
con->response->request.status.status_code = IPP_OK;
}
static void
move_job(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr;
int jobid;
job_t *job;
const char *dest;
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
LogMessage(L_DEBUG2, "move_job(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strcmp(uri->name, "printer-uri") == 0)
{
if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
{
LogMessage(L_ERROR, "move_job: got a printer-uri attribute but no job-id!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = attr->values[0].integer;
}
else
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/jobs/", 6) != 0)
{
LogMessage(L_ERROR, "move_job: bad job-uri attribute \'%s\'!\n",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = atoi(resource + 6);
}
if ((job = FindJob(jobid)) == NULL)
{
LogMessage(L_ERROR, "move_job: job #%d doesn't exist!", jobid);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (job->state->values[0].integer > IPP_JOB_STOPPED)
{
LogMessage(L_ERROR, "move_job: job #%d is finished and cannot be altered!", jobid);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if (!validate_user(con, job->username, username, sizeof(username)))
{
LogMessage(L_ERROR, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
username, jobid, job->username);
send_ipp_error(con, IPP_FORBIDDEN);
return;
}
if ((attr = ippFindAttribute(con->request, "job-printer-uri", IPP_TAG_URI)) == NULL)
{
LogMessage(L_ERROR, "move_job: job-printer-uri attribute missing!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
httpSeparate(attr->values[0].string.text, method, username, host, &port,
resource);
if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "move_job: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
MoveJob(jobid, dest);
CheckJobs();
con->response->request.status.status_code = IPP_OK;
}
static int
ppd_add_default(const char *option,
const char *choice,
int num_defaults,
ppd_default_t **defaults)
{
int i;
ppd_default_t *temp;
for (i = 0, temp = *defaults; i < num_defaults; i ++)
if (!strcmp(option, temp[i].option))
return (num_defaults);
if (num_defaults == 0)
temp = malloc(sizeof(ppd_default_t));
else
temp = realloc(*defaults, (num_defaults + 1) * sizeof(ppd_default_t));
if (!temp)
{
LogMessage(L_ERROR, "ppd_add_default: Unable to add default value for \"%s\" - %s",
option, strerror(errno));
return (num_defaults);
}
*defaults = temp;
temp += num_defaults;
strlcpy(temp->option, option, sizeof(temp->option));
strlcpy(temp->choice, choice, sizeof(temp->choice));
return (num_defaults + 1);
}
static int
ppd_parse_line(const char *line,
char *option,
int olen,
char *choice,
int clen)
{
if (strncmp(line, "*Default", 8))
return (-1);
for (line += 8, olen --; isalnum(*line); line ++)
if (olen > 0)
{
*option++ = *line;
olen --;
}
*option = '\0';
while (*line && *line != ':')
line ++;
if (!*line)
return (-1);
line ++;
while (isspace(*line))
line ++;
for (clen --; isalnum(*line); line ++)
if (clen > 0)
{
*choice++ = *line;
clen --;
}
*choice = '\0';
return (0);
}
static void
print_job(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr;
ipp_attribute_t *format;
const char *dest;
cups_ptype_t dtype;
int priority;
char *title;
job_t *job;
char job_uri[HTTP_MAX_URI],
printer_uri[HTTP_MAX_URI],
method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI],
filename[1024];
int port;
mime_type_t *filetype;
char super[MIME_MAX_SUPER],
type[MIME_MAX_TYPE],
mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
printer_t *printer;
struct stat fileinfo;
int kbytes;
int i;
int lowerpagerange;
int compression;
LogMessage(L_DEBUG2, "print_job(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/classes/", 9) != 0 &&
strncmp(con->uri, "/printers/", 10) != 0)
{
LogMessage(L_ERROR, "print_job: cancel request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
if ((attr = ippFindAttribute(con->request, "copies", IPP_TAG_INTEGER)) != NULL)
{
#ifdef __APPLE__
if (attr->values[0].integer < MinCopies || attr->values[0].integer > MaxCopies)
#else
if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
#endif
{
LogMessage(L_INFO, "print_job: bad copies value %d.",
attr->values[0].integer);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
}
if ((attr = ippFindAttribute(con->request, "page-ranges", IPP_TAG_RANGE)) != NULL)
{
for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
{
if (attr->values[i].range.lower < lowerpagerange ||
attr->values[i].range.lower > attr->values[i].range.upper)
{
LogMessage(L_ERROR, "print_job: bad page-ranges values %d-%d.",
attr->values[i].range.lower, attr->values[i].range.upper);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
lowerpagerange = attr->values[i].range.upper + 1;
}
}
compression = CUPS_FILE_NONE;
if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
{
if (strcmp(attr->values[0].string.text, "none")
#ifdef HAVE_LIBZ
&& strcmp(attr->values[0].string.text, "gzip")
#endif
)
{
LogMessage(L_ERROR, "print_job: Unsupported compression \"%s\"!",
attr->values[0].string.text);
send_ipp_error(con, IPP_ATTRIBUTES);
ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
"compression", NULL, attr->values[0].string.text);
return;
}
#ifdef HAVE_LIBZ
if (!strcmp(attr->values[0].string.text, "gzip"))
compression = CUPS_FILE_GZIP;
#endif
}
if (!con->filename)
{
LogMessage(L_ERROR, "print_job: No file!?!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
{
if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
{
LogMessage(L_ERROR, "print_job: could not scan type \'%s\'!",
format->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
}
else
{
strcpy(super, "application");
strcpy(type, "octet-stream");
}
if (strcmp(super, "application") == 0 &&
strcmp(type, "octet-stream") == 0)
{
LogMessage(L_DEBUG, "print_job: auto-typing file...");
filetype = mimeFileType(MimeDatabase, con->filename, &compression);
if (filetype != NULL)
{
snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
filetype->type);
if (format != NULL)
{
free(format->values[0].string.text);
format->values[0].string.text = strdup(mimetype);
}
else
ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
"document-format", NULL, mimetype);
}
else
filetype = mimeType(MimeDatabase, super, type);
}
else
filetype = mimeType(MimeDatabase, super, type);
if (filetype == NULL)
{
LogMessage(L_ERROR, "print_job: Unsupported format \'%s/%s\'!",
super, type);
LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
send_ipp_error(con, IPP_DOCUMENT_FORMAT);
if (format)
ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
"document-format", NULL, format->values[0].string.text);
return;
}
LogMessage(L_DEBUG, "print_job: request file type is %s/%s.",
filetype->super, filetype->type);
if (strcasecmp(filetype->super, "application") == 0 &&
strcasecmp(filetype->type, "postscript") == 0)
read_ps_job_ticket(con);
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "print_job: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
{
printer = FindClass(dest);
snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
ServerName, ntohs(con->http.hostaddr.sin_port), dest);
}
else
{
printer = FindPrinter(dest);
snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
ServerName, ntohs(con->http.hostaddr.sin_port), dest);
}
if (!printer->accepting)
{
LogMessage(L_INFO, "print_job: destination \'%s\' is not accepting jobs.",
dest);
send_ipp_error(con, IPP_NOT_ACCEPTING);
return;
}
if (NumJobs >= MaxJobs && MaxJobs)
CleanJobs();
if (NumJobs >= MaxJobs && MaxJobs)
{
LogMessage(L_INFO, "print_job: too many jobs - %d jobs, max jobs is %d.",
NumJobs, MaxJobs);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if (!check_quotas(con, printer))
{
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
priority = attr->values[0].integer;
else
ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
priority = 50);
if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
title = attr->values[0].string.text;
else
ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
title = "Untitled");
if ((job = AddJob(priority, printer->name)) == NULL)
{
LogMessage(L_ERROR, "print_job: unable to add job for destination \'%s\'!",
dest);
send_ipp_error(con, IPP_INTERNAL_ERROR);
return;
}
job->dtype = dtype;
job->attrs = con->request;
con->request = NULL;
attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
if (con->username[0])
SetString(&job->username, con->username);
else if (attr != NULL)
{
LogMessage(L_DEBUG, "print_job: requesting-user-name = \'%s\'",
attr->values[0].string.text);
SetString(&job->username, attr->values[0].string.text);
}
else
SetString(&job->username, "anonymous");
if (attr == NULL)
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
NULL, job->username);
else
{
attr->group_tag = IPP_TAG_JOB;
SetString(&attr->name, "job-originating-user-name");
}
if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
IPP_TAG_ZERO)) != NULL)
{
if (attr->value_tag != IPP_TAG_NAME ||
attr->num_values != 1 ||
strcmp(con->http.hostname, "localhost") != 0)
{
int i;
switch (attr->value_tag)
{
case IPP_TAG_STRING :
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 :
for (i = 0; i < attr->num_values; i ++)
{
free(attr->values[i].string.text);
attr->values[i].string.text = NULL;
if (attr->values[i].string.charset)
{
free(attr->values[i].string.charset);
attr->values[i].string.charset = NULL;
}
}
default :
break;
}
attr->value_tag = IPP_TAG_NAME;
attr->num_values = 1;
attr->values[0].string.text = strdup(con->http.hostname);
}
attr->group_tag = IPP_TAG_JOB;
}
else
{
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
"job-originating-host-name", NULL, con->http.hostname);
}
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
"job-state", IPP_JOB_PENDING);
job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
"job-media-sheets-completed", 0);
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
printer_uri);
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
title);
if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) == NULL)
attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
"job-k-octets", 0);
if (stat(con->filename, &fileinfo))
kbytes = 0;
else
kbytes = (fileinfo.st_size + 1023) / 1024;
UpdateQuota(printer, job->username, 0, kbytes);
attr->values[0].integer += kbytes;
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
time(NULL));
attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
"time-at-processing", 0);
attr->value_tag = IPP_TAG_NOVALUE;
attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
"time-at-completed", 0);
attr->value_tag = IPP_TAG_NOVALUE;
if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
if (attr == NULL)
attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
"job-hold-until", NULL, "no-hold");
if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
!(printer->type & CUPS_PRINTER_REMOTE))
{
job->state->values[0].integer = IPP_JOB_HELD;
SetJobHoldUntil(job->id, attr->values[0].string.text);
}
if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
Classification)
{
if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
{
LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
printer->job_sheets[0], printer->job_sheets[1]);
attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
2, NULL, NULL);
attr->values[0].string.text = strdup(printer->job_sheets[0]);
attr->values[1].string.text = strdup(printer->job_sheets[1]);
}
job->job_sheets = attr;
if (Classification)
{
if (ClassifyOverride)
{
if (strcmp(attr->values[0].string.text, "none") == 0 &&
(attr->num_values == 1 ||
strcmp(attr->values[1].string.text, "none") == 0))
{
SetString(&attr->values[0].string.text, Classification);
}
else if (attr->num_values == 2 &&
strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
strcmp(attr->values[0].string.text, "none") != 0 &&
strcmp(attr->values[1].string.text, "none") != 0)
{
SetString(&attr->values[1].string.text, attr->values[0].string.text);
}
}
else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
(attr->num_values == 1 ||
strcmp(attr->values[1].string.text, Classification) != 0))
{
SetString(&attr->values[0].string.text, Classification);
}
}
if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
{
LogMessage(L_INFO, "Adding start banner page \"%s\" to job %d.",
attr->values[0].string.text, job->id);
kbytes = copy_banner(con, job, attr->values[0].string.text);
UpdateQuota(printer, job->username, 0, kbytes);
}
}
else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
job->sheets = attr;
if (add_file(con, job, filetype, compression))
return;
snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
job->num_files);
rename(con->filename, filename);
ClearString(&con->filename);
if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
attr->num_values > 1)
{
LogMessage(L_INFO, "Adding end banner page \"%s\" to job %d.",
attr->values[1].string.text, job->id);
kbytes = copy_banner(con, job, attr->values[1].string.text);
UpdateQuota(printer, job->username, 0, kbytes);
}
LogMessage(L_INFO, "Job %d queued on \'%s\' by \'%s\'.", job->id,
job->dest, job->username);
LogMessage(L_DEBUG, "Job %d hold_until = %d", job->id, (int)job->hold_until);
SaveJob(job->id);
CheckJobs();
snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
ntohs(con->http.hostaddr.sin_port), job->id);
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
job->state->values[0].integer);
add_job_state_reasons(con, job);
con->response->request.status.status_code = IPP_OK;
}
static void
read_ps_job_ticket(client_t *con)
{
cups_file_t *fp;
char line[256];
int num_options;
cups_option_t *options;
ipp_t *ticket;
ipp_attribute_t *attr,
*attr2,
*prev2;
if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
{
LogMessage(L_ERROR, "read_ps_job_ticket: Unable to open PostScript print file - %s",
strerror(errno));
return;
}
if (cupsFileGets(fp, line, sizeof(line)) == NULL)
{
LogMessage(L_ERROR, "read_ps_job_ticket: Unable to read from PostScript print file - %s",
strerror(errno));
cupsFileClose(fp);
return;
}
if (strncmp(line, "%!PS-Adobe-", 11) != 0)
{
cupsFileClose(fp);
return;
}
num_options = 0;
options = NULL;
while (cupsFileGets(fp, line, sizeof(line)) != NULL)
{
if (strncmp(line, "%cupsJobTicket:", 15) != 0)
break;
num_options = cupsParseOptions(line + 15, num_options, &options);
}
cupsFileClose(fp);
if (num_options == 0)
return;
ticket = ippNew();
cupsEncodeOptions(ticket, num_options, options);
for (attr = ticket->attrs; attr != NULL; attr = attr->next)
{
if (attr->group_tag != IPP_TAG_JOB || !attr->name)
continue;
if (strcmp(attr->name, "job-originating-host-name") == 0 ||
strcmp(attr->name, "job-originating-user-name") == 0 ||
strcmp(attr->name, "job-media-sheets-completed") == 0 ||
strcmp(attr->name, "job-k-octets") == 0 ||
strcmp(attr->name, "job-id") == 0 ||
strncmp(attr->name, "job-state", 9) == 0 ||
strncmp(attr->name, "time-at-", 8) == 0)
continue;
if ((attr2 = ippFindAttribute(con->request, attr->name, IPP_TAG_ZERO)) != NULL)
{
if (con->request->attrs == attr2)
{
con->request->attrs = attr2->next;
prev2 = NULL;
}
else
{
for (prev2 = con->request->attrs; prev2 != NULL; prev2 = prev2->next)
if (prev2->next == attr2)
{
prev2->next = attr2->next;
break;
}
}
if (con->request->last == attr2)
con->request->last = prev2;
_ipp_free_attr(attr2);
}
copy_attribute(con->request, attr, 0);
}
ippDelete(ticket);
cupsFreeOptions(num_options, options);
}
static void
reject_jobs(client_t *con,
ipp_attribute_t *uri)
{
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
const char *name;
printer_t *printer;
ipp_attribute_t *attr;
LogMessage(L_DEBUG2, "reject_jobs(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "reject_jobs: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((name = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "reject_jobs: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
printer = FindClass(name);
else
printer = FindPrinter(name);
printer->accepting = 0;
if ((attr = ippFindAttribute(con->request, "printer-state-message",
IPP_TAG_TEXT)) == NULL)
strcpy(printer->state_message, "Rejecting Jobs");
else
strlcpy(printer->state_message, attr->values[0].string.text,
sizeof(printer->state_message));
AddPrinterHistory(printer);
if (dtype & CUPS_PRINTER_CLASS)
{
SaveAllClasses();
LogMessage(L_INFO, "Class \'%s\' rejecting jobs (\'%s\').", name,
con->username);
}
else
{
SaveAllPrinters();
LogMessage(L_INFO, "Printer \'%s\' rejecting jobs (\'%s\').", name,
con->username);
}
con->response->request.status.status_code = IPP_OK;
}
static void
release_job(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr;
int jobid;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
job_t *job;
LogMessage(L_DEBUG2, "release_job(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/classes/", 9) != 0 &&
strncmp(con->uri, "/jobs/", 5) != 0 &&
strncmp(con->uri, "/printers/", 10) != 0)
{
LogMessage(L_ERROR, "release_job: release request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
if (strcmp(uri->name, "printer-uri") == 0)
{
if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
{
LogMessage(L_ERROR, "release_job: got a printer-uri attribute but no job-id!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = attr->values[0].integer;
}
else
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/jobs/", 6) != 0)
{
LogMessage(L_ERROR, "release_job: bad job-uri attribute \'%s\'!",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = atoi(resource + 6);
}
if ((job = FindJob(jobid)) == NULL)
{
LogMessage(L_ERROR, "release_job: job #%d doesn't exist!", jobid);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (job->state->values[0].integer != IPP_JOB_HELD)
{
LogMessage(L_ERROR, "release_job: job #%d is not held!", jobid);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if (!validate_user(con, job->username, username, sizeof(username)))
{
LogMessage(L_ERROR, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
username, jobid, job->username);
send_ipp_error(con, IPP_FORBIDDEN);
return;
}
if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
if (attr != NULL)
{
free(attr->values[0].string.text);
attr->value_tag = IPP_TAG_KEYWORD;
attr->values[0].string.text = strdup("no-hold");
}
ReleaseJob(jobid);
LogMessage(L_INFO, "Job %d was released by \'%s\'.", jobid, username);
con->response->request.status.status_code = IPP_OK;
}
static void
restart_job(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr;
int jobid;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
job_t *job;
LogMessage(L_DEBUG2, "restart_job(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/classes/", 9) != 0 &&
strncmp(con->uri, "/jobs/", 5) != 0 &&
strncmp(con->uri, "/printers/", 10) != 0)
{
LogMessage(L_ERROR, "restart_job: restart request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
if (strcmp(uri->name, "printer-uri") == 0)
{
if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
{
LogMessage(L_ERROR, "restart_job: got a printer-uri attribute but no job-id!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = attr->values[0].integer;
}
else
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/jobs/", 6) != 0)
{
LogMessage(L_ERROR, "restart_job: bad job-uri attribute \'%s\'!",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = atoi(resource + 6);
}
if ((job = FindJob(jobid)) == NULL)
{
LogMessage(L_ERROR, "restart_job: job #%d doesn't exist!", jobid);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
{
LogMessage(L_ERROR, "restart_job: job #%d is not complete!", jobid);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if (!JobFiles && job->state->values[0].integer > IPP_JOB_STOPPED)
{
LogMessage(L_ERROR, "restart_job: job #%d cannot be restarted - no files!", jobid);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if (!validate_user(con, job->username, username, sizeof(username)))
{
LogMessage(L_ERROR, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
username, jobid, job->username);
send_ipp_error(con, IPP_FORBIDDEN);
return;
}
RestartJob(jobid);
LogMessage(L_INFO, "Job %d was restarted by \'%s\'.", jobid, username);
con->response->request.status.status_code = IPP_OK;
}
static void
send_document(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr;
ipp_attribute_t *format;
int jobid;
job_t *job;
char job_uri[HTTP_MAX_URI],
method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
mime_type_t *filetype;
char super[MIME_MAX_SUPER],
type[MIME_MAX_TYPE],
mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
char filename[1024];
printer_t *printer;
struct stat fileinfo;
int kbytes;
int compression;
LogMessage(L_DEBUG2, "send_document(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/classes/", 9) != 0 &&
strncmp(con->uri, "/jobs/", 6) != 0 &&
strncmp(con->uri, "/printers/", 10) != 0)
{
LogMessage(L_ERROR, "send_document: print request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
if (strcmp(uri->name, "printer-uri") == 0)
{
if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
{
LogMessage(L_ERROR, "send_document: got a printer-uri attribute but no job-id!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = attr->values[0].integer;
}
else
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/jobs/", 6) != 0)
{
LogMessage(L_ERROR, "send_document: bad job-uri attribute \'%s\'!",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = atoi(resource + 6);
}
if ((job = FindJob(jobid)) == NULL)
{
LogMessage(L_ERROR, "send_document: job #%d doesn't exist!", jobid);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (!validate_user(con, job->username, username, sizeof(username)))
{
LogMessage(L_ERROR, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
username, jobid, job->username);
send_ipp_error(con, IPP_FORBIDDEN);
return;
}
compression = CUPS_FILE_NONE;
if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
{
if (strcmp(attr->values[0].string.text, "none")
#ifdef HAVE_LIBZ
&& strcmp(attr->values[0].string.text, "gzip")
#endif
)
{
LogMessage(L_ERROR, "print_job: Unsupported compression \"%s\"!",
attr->values[0].string.text);
send_ipp_error(con, IPP_ATTRIBUTES);
ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
"compression", NULL, attr->values[0].string.text);
return;
}
#ifdef HAVE_LIBZ
if (!strcmp(attr->values[0].string.text, "gzip"))
compression = CUPS_FILE_GZIP;
#endif
}
if (!con->filename)
{
LogMessage(L_ERROR, "send_document: No file!?!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
{
if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
{
LogMessage(L_ERROR, "send_document: could not scan type \'%s\'!",
format->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
}
else
{
strcpy(super, "application");
strcpy(type, "octet-stream");
}
if (strcmp(super, "application") == 0 &&
strcmp(type, "octet-stream") == 0)
{
LogMessage(L_DEBUG, "send_document: auto-typing file...");
filetype = mimeFileType(MimeDatabase, con->filename, &compression);
if (filetype != NULL)
{
snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
filetype->type);
if (format != NULL)
{
free(format->values[0].string.text);
format->values[0].string.text = strdup(mimetype);
}
else
ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
"document-format", NULL, mimetype);
}
else
filetype = mimeType(MimeDatabase, super, type);
}
else
filetype = mimeType(MimeDatabase, super, type);
if (filetype == NULL)
{
LogMessage(L_ERROR, "send_document: Unsupported format \'%s/%s\'!",
super, type);
LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
send_ipp_error(con, IPP_DOCUMENT_FORMAT);
if (format)
ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
"document-format", NULL, format->values[0].string.text);
return;
}
LogMessage(L_DEBUG, "send_document: request file type is %s/%s.",
filetype->super, filetype->type);
if (add_file(con, job, filetype, compression))
return;
if (job->dtype & CUPS_PRINTER_CLASS)
printer = FindClass(job->dest);
else
printer = FindPrinter(job->dest);
if (stat(con->filename, &fileinfo))
kbytes = 0;
else
kbytes = (fileinfo.st_size + 1023) / 1024;
UpdateQuota(printer, job->username, 0, kbytes);
if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
attr->values[0].integer += kbytes;
snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
job->num_files);
rename(con->filename, filename);
ClearString(&con->filename);
LogMessage(L_INFO, "File of type %s/%s queued in job #%d by \'%s\'.",
filetype->super, filetype->type, job->id, job->username);
if ((attr = ippFindAttribute(con->request, "last-document", IPP_TAG_BOOLEAN)) != NULL &&
attr->values[0].boolean)
{
if (printer != NULL &&
!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
(attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL &&
attr->num_values > 1)
{
LogMessage(L_INFO, "Adding end banner page \"%s\" to job %d.",
attr->values[1].string.text, job->id);
kbytes = copy_banner(con, job, attr->values[1].string.text);
UpdateQuota(printer, job->username, 0, kbytes);
}
if (job->state->values[0].integer == IPP_JOB_STOPPED)
job->state->values[0].integer = IPP_JOB_PENDING;
else if (job->state->values[0].integer == IPP_JOB_HELD)
{
if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
job->state->values[0].integer = IPP_JOB_PENDING;
}
SaveJob(job->id);
CheckJobs();
}
else
{
if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
{
job->state->values[0].integer = IPP_JOB_HELD;
job->hold_until = time(NULL) + 60;
SaveJob(job->id);
}
}
snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
ntohs(con->http.hostaddr.sin_port), job->id);
ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
job_uri);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
job->state->values[0].integer);
add_job_state_reasons(con, job);
con->response->request.status.status_code = IPP_OK;
}
static void
send_ipp_error(client_t *con,
ipp_status_t status)
{
LogMessage(L_DEBUG2, "send_ipp_error(%p[%d], %x)\n", con, con->http.fd,
status);
LogMessage(L_DEBUG, "Sending error: %s", ippErrorString(status));
con->response->request.status.status_code = status;
if (ippFindAttribute(con->response, "attributes-charset", IPP_TAG_ZERO) == NULL)
ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, DefaultCharset);
if (ippFindAttribute(con->response, "attributes-natural-language",
IPP_TAG_ZERO) == NULL)
ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL, DefaultLanguage);
}
static void
set_default(client_t *con,
ipp_attribute_t *uri)
{
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
const char *name;
LogMessage(L_DEBUG2, "set_default(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "set_default: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((name = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "set_default: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
DefaultPrinter = FindClass(name);
else
DefaultPrinter = FindPrinter(name);
SaveAllPrinters();
SaveAllClasses();
LogMessage(L_INFO, "Default destination set to \'%s\' by \'%s\'.", name,
con->username);
con->response->request.status.status_code = IPP_OK;
}
static void
set_job_attrs(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr,
*attr2,
*prev2;
int jobid;
job_t *job;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
LogMessage(L_DEBUG2, "set_job_attrs(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
con->response->request.status.status_code = IPP_OK;
if (strcmp(uri->name, "printer-uri") == 0)
{
if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
{
LogMessage(L_ERROR, "set_job_attrs: got a printer-uri attribute but no job-id!");
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = attr->values[0].integer;
}
else
{
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (strncmp(resource, "/jobs/", 6) != 0)
{
LogMessage(L_ERROR, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
uri->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
jobid = atoi(resource + 6);
}
if ((job = FindJob(jobid)) == NULL)
{
LogMessage(L_ERROR, "set_job_attrs: job #%d doesn't exist!", jobid);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (job->state->values[0].integer > IPP_JOB_STOPPED)
{
LogMessage(L_ERROR, "set_job_attrs: job #%d is finished and cannot be altered!", jobid);
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
if (!validate_user(con, job->username, username, sizeof(username)))
{
LogMessage(L_ERROR, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
username, jobid, job->username);
send_ipp_error(con, IPP_FORBIDDEN);
return;
}
for (attr = con->request->attrs; attr != NULL; attr = attr->next)
{
if (attr->group_tag != IPP_TAG_JOB || !attr->name)
continue;
if (!strcmp(attr->name, "attributes-charset") ||
!strcmp(attr->name, "attributes-natural-language") ||
!strcmp(attr->name, "document-compression") ||
!strcmp(attr->name, "document-format") ||
!strcmp(attr->name, "job-detailed-status-messages") ||
!strcmp(attr->name, "job-document-access-errors") ||
!strcmp(attr->name, "job-id") ||
!strcmp(attr->name, "job-k-octets") ||
!strcmp(attr->name, "job-originating-host-name") ||
!strcmp(attr->name, "job-originating-user-name") ||
!strcmp(attr->name, "job-printer-up-time") ||
!strcmp(attr->name, "job-printer-uri") ||
!strcmp(attr->name, "job-sheets") ||
!strcmp(attr->name, "job-state-message") ||
!strcmp(attr->name, "job-state-reasons") ||
!strcmp(attr->name, "job-uri") ||
!strcmp(attr->name, "number-of-documents") ||
!strcmp(attr->name, "number-of-intervening-jobs") ||
!strcmp(attr->name, "output-device-assigned") ||
!strncmp(attr->name, "date-time-at-", 13) ||
!strncmp(attr->name, "job-impressions", 15) ||
!strncmp(attr->name, "job-k-octets", 12) ||
!strncmp(attr->name, "job-media-sheets", 16) ||
!strncmp(attr->name, "time-at-", 8))
{
send_ipp_error(con, IPP_ATTRIBUTES_NOT_SETTABLE);
if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
continue;
}
if (!strcmp(attr->name, "job-priority"))
{
if (attr->value_tag != IPP_TAG_INTEGER)
{
send_ipp_error(con, IPP_REQUEST_VALUE);
if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
}
else if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
{
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
else if (con->response->request.status.status_code == IPP_OK)
SetJobPriority(jobid, attr->values[0].integer);
}
else if (!strcmp(attr->name, "job-state"))
{
if (attr->value_tag != IPP_TAG_ENUM)
{
send_ipp_error(con, IPP_REQUEST_VALUE);
if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
}
else
{
switch (attr->values[0].integer)
{
case IPP_JOB_PENDING :
case IPP_JOB_HELD :
if (job->state->values[0].integer > IPP_JOB_HELD)
{
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
else if (con->response->request.status.status_code == IPP_OK)
job->state->values[0].integer = attr->values[0].integer;
break;
case IPP_JOB_PROCESSING :
case IPP_JOB_STOPPED :
if (job->state->values[0].integer != attr->values[0].integer)
{
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
break;
case IPP_JOB_CANCELLED :
case IPP_JOB_ABORTED :
case IPP_JOB_COMPLETED :
if (job->state->values[0].integer > IPP_JOB_PROCESSING)
{
send_ipp_error(con, IPP_NOT_POSSIBLE);
return;
}
else if (con->response->request.status.status_code == IPP_OK)
{
CancelJob(job->id, 0);
if (JobHistory)
{
job->state->values[0].integer = attr->values[0].integer;
SaveJob(job->id);
}
}
break;
}
}
}
else if (con->response->request.status.status_code != IPP_OK)
continue;
else if ((attr2 = ippFindAttribute(job->attrs, attr->name, IPP_TAG_ZERO)) != NULL)
{
if (job->attrs->attrs == attr2)
{
job->attrs->attrs = attr2->next;
prev2 = NULL;
}
else
{
for (prev2 = job->attrs->attrs; prev2 != NULL; prev2 = prev2->next)
if (prev2->next == attr2)
{
prev2->next = attr2->next;
break;
}
}
if (job->attrs->last == attr2)
job->attrs->last = prev2;
_ipp_free_attr(attr2);
copy_attribute(job->attrs, attr, 0);
if (strcmp(attr->name, "job-hold-until") == 0)
{
SetJobHoldUntil(job->id, attr->values[0].string.text);
if (strcmp(attr->values[0].string.text, "no-hold") == 0)
ReleaseJob(job->id);
else
HoldJob(job->id);
}
}
else if (attr->value_tag == IPP_TAG_DELETEATTR)
{
for (attr2 = job->attrs->attrs, prev2 = NULL;
attr2 != NULL;
prev2 = attr2, attr2 = attr2->next)
if (attr2->name && strcmp(attr2->name, attr->name) == 0)
break;
if (attr2)
{
if (prev2)
prev2->next = attr2->next;
else
job->attrs->attrs = attr2->next;
if (attr2 == job->attrs->last)
job->attrs->last = prev2;
_ipp_free_attr(attr2);
}
}
else
{
copy_attribute(job->attrs, attr, 0);
}
}
SaveJob(job->id);
CheckJobs();
}
static void
start_printer(client_t *con,
ipp_attribute_t *uri)
{
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
const char *name;
printer_t *printer;
LogMessage(L_DEBUG2, "start_printer(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "start_printer: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((name = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "start_printer: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
printer = FindClass(name);
else
printer = FindPrinter(name);
printer->state_message[0] = '\0';
StartPrinter(printer, 1);
if (dtype & CUPS_PRINTER_CLASS)
LogMessage(L_INFO, "Class \'%s\' started by \'%s\'.", name,
con->username);
LogMessage(L_INFO, "Printer \'%s\' started by \'%s\'.", name,
con->username);
CheckJobs();
con->response->request.status.status_code = IPP_OK;
}
static void
stop_printer(client_t *con,
ipp_attribute_t *uri)
{
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
const char *name;
printer_t *printer;
ipp_attribute_t *attr;
LogMessage(L_DEBUG2, "stop_printer(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/admin/", 7) != 0)
{
LogMessage(L_ERROR, "stop_printer: admin request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if ((name = ValidateDest(host, resource, &dtype)) == NULL)
{
LogMessage(L_ERROR, "stop_printer: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
if (dtype & CUPS_PRINTER_CLASS)
printer = FindClass(name);
else
printer = FindPrinter(name);
if ((attr = ippFindAttribute(con->request, "printer-state-message",
IPP_TAG_TEXT)) == NULL)
strcpy(printer->state_message, "Paused");
else
{
strlcpy(printer->state_message, attr->values[0].string.text,
sizeof(printer->state_message));
}
StopPrinter(printer, 1);
if (dtype & CUPS_PRINTER_CLASS)
LogMessage(L_INFO, "Class \'%s\' stopped by \'%s\'.", name,
con->username);
else
LogMessage(L_INFO, "Printer \'%s\' stopped by \'%s\'.", name,
con->username);
con->response->request.status.status_code = IPP_OK;
}
static void
validate_job(client_t *con,
ipp_attribute_t *uri)
{
ipp_attribute_t *attr;
ipp_attribute_t *format;
cups_ptype_t dtype;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
char super[MIME_MAX_SUPER],
type[MIME_MAX_TYPE];
LogMessage(L_DEBUG2, "validate_job(%p[%d], %s)\n", con, con->http.fd,
uri->values[0].string.text);
if (strncmp(con->uri, "/classes/", 9) != 0 &&
strncmp(con->uri, "/printers/", 10) != 0)
{
LogMessage(L_ERROR, "validate_job: request on bad resource \'%s\'!",
con->uri);
send_ipp_error(con, IPP_NOT_AUTHORIZED);
return;
}
if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
strcmp(attr->values[0].string.text, "none") == 0)
{
LogMessage(L_ERROR, "validate_job: Unsupported compression attribute %s!",
attr->values[0].string.text);
send_ipp_error(con, IPP_ATTRIBUTES);
ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
"compression", NULL, attr->values[0].string.text);
return;
}
if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
{
if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
{
LogMessage(L_ERROR, "validate_job: could not scan type \'%s\'!\n",
format->values[0].string.text);
send_ipp_error(con, IPP_BAD_REQUEST);
return;
}
if ((strcmp(super, "application") != 0 ||
strcmp(type, "octet-stream") != 0) &&
mimeType(MimeDatabase, super, type) == NULL)
{
LogMessage(L_ERROR, "validate_job: Unsupported format \'%s\'!\n",
format->values[0].string.text);
LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
send_ipp_error(con, IPP_DOCUMENT_FORMAT);
ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
"document-format", NULL, format->values[0].string.text);
return;
}
}
httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
if (ValidateDest(host, resource, &dtype) == NULL)
{
LogMessage(L_ERROR, "validate_job: resource name \'%s\' no good!", resource);
send_ipp_error(con, IPP_NOT_FOUND);
return;
}
con->response->request.status.status_code = IPP_OK;
}
static int
validate_user(client_t *con,
const char *owner,
char *username,
int userlen)
{
int i, j;
ipp_attribute_t *attr;
struct passwd *user;
struct group *group;
char junk[33];
LogMessage(L_DEBUG2, "validate_user(%p[%d], \"%s\", %p, %d)\n",
con, con->http.fd, owner, username, userlen);
if (con == NULL || owner == NULL || username == NULL || userlen <= 0)
return (0);
if (con->username[0])
strlcpy(username, con->username, userlen);
else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
strlcpy(username, attr->values[0].string.text, userlen);
else
strlcpy(username, "anonymous", userlen);
if (strcasecmp(username, owner) != 0 && strcasecmp(username, "root") != 0)
{
user = getpwnam(username);
endpwent();
for (i = 0, j = 0, group = NULL; i < NumSystemGroups; i ++)
{
group = getgrnam(SystemGroups[i]);
endgrent();
if (group != NULL)
{
for (j = 0; group->gr_mem[j]; j ++)
if (strcasecmp(username, group->gr_mem[j]) == 0)
break;
if (group->gr_mem[j])
break;
}
else
j = 0;
}
if (user == NULL || group == NULL ||
(group->gr_mem[j] == NULL && group->gr_gid != user->pw_gid))
{
for (i = 0; i < NumSystemGroups; i ++)
if (GetMD5Passwd(username, SystemGroups[i], junk) != NULL)
return (1);
return (0);
}
}
return (1);
}