#include <cups/cups.h>
#include <cups/string.h>
#include <cups/language.h>
#include <stdlib.h>
#include <errno.h>
#include <syslog.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
int print_file(const char *name, const char *file,
const char *title, const char *docname,
const char *user, int num_options,
cups_option_t *options);
int recv_print_job(const char *dest, int num_defaults, cups_option_t *defaults);
int remove_jobs(const char *dest, const char *agent, const char *list);
int send_state(const char *dest, const char *list, int longstatus);
char *smart_gets(char *s, int len, FILE *fp);
int
main(int argc,
char *argv[])
{
int i;
int num_defaults;
cups_option_t *defaults;
char line[256],
command,
*dest,
*list,
*agent,
status;
int hostlen;
unsigned hostip;
struct sockaddr_in hostaddr;
struct hostent *hostent;
char hostname[256];
setbuf(stdout, NULL);
openlog("cups-lpd", LOG_PID, LOG_LPR);
hostlen = sizeof(hostaddr);
if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
{
syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
strcpy(hostname, "unknown");
}
else
{
hostip = ntohl(hostaddr.sin_addr.s_addr);
hostent = gethostbyaddr((void *)&(hostaddr.sin_addr), hostlen, AF_INET);
if (hostent)
strlcpy(hostname, hostent->h_name, sizeof(hostname));
else
{
snprintf(hostname, sizeof(hostname), "%d.%d.%d.%d",
(hostip >> 24) & 255, (hostip >> 16) & 255,
(hostip >> 8) & 255, hostip & 255);
}
syslog(LOG_INFO, "Connection from %s (%d.%d.%d.%d)",
hostname, (hostip >> 24) & 255, (hostip >> 16) & 255,
(hostip >> 8) & 255, hostip & 255);
}
num_defaults = 0;
defaults = NULL;
num_defaults = cupsAddOption("job-originating-host-name", hostname,
num_defaults, &defaults);
for (i = 1; i < argc; i ++)
if (argv[i][0] == '-')
{
switch (argv[i][1])
{
case 'o' :
if (argv[i][2])
num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
&defaults);
else
{
i ++;
if (i < argc)
num_defaults = cupsParseOptions(argv[i], num_defaults, &defaults);
else
syslog(LOG_WARNING, "Expected option string after -o option!");
}
break;
default :
syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
break;
}
}
else
syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!", argv[i]);
if (smart_gets(line, sizeof(line), stdin) == NULL)
{
syslog(LOG_ERR, "Unable to get command line from client!");
putchar(1);
return (1);
}
command = line[0];
dest = line + 1;
for (list = dest + 1; *list && !isspace(*list); list ++);
while (isspace(*list))
*list++ = '\0';
switch (command)
{
default :
syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
syslog(LOG_ERR, "Command line = %s", line + 1);
putchar(1);
status = 1;
break;
case 0x01 :
syslog(LOG_INFO, "Print waiting jobs (no-op)");
putchar(0);
status = 0;
break;
case 0x02 :
syslog(LOG_INFO, "Receive print job for %s", dest);
status = recv_print_job(dest, num_defaults, defaults);
break;
case 0x03 :
syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
status = send_state(dest, list, 0);
break;
case 0x04 :
syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
status = send_state(dest, list, 1);
break;
case 0x05 :
agent = list;
for (; *list && !isspace(*list); list ++);
while (isspace(*list))
*list++ = '\0';
syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
status = remove_jobs(dest, agent, list);
putchar(status);
break;
}
syslog(LOG_INFO, "Closing connection");
closelog();
return (status);
}
int
print_file(const char *name,
const char *file,
const char *title,
const char *docname,
const char *user,
int num_options,
cups_option_t *options)
{
http_t *http;
ipp_t *request;
ipp_t *response;
ipp_attribute_t *attr;
char uri[HTTP_MAX_URI];
cups_lang_t *language;
int jobid;
if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
cupsEncryption())) == NULL)
{
syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
return (0);
}
language = cupsLangDefault();
if ((request = ippNew()) == NULL)
{
syslog(LOG_ERR, "Unable to create request: %s", strerror(errno));
return (0);
}
request->request.op.operation_id = IPP_PRINT_JOB;
request->request.op.request_id = 1;
snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", name);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, cupsLangEncoding(language));
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL,
language != NULL ? language->language : "C");
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, user);
if (title)
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title);
if (docname)
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", NULL, docname);
cupsEncodeOptions(request, num_options, options);
snprintf(uri, sizeof(uri), "/printers/%s", name);
response = cupsDoFileRequest(http, request, uri, file);
if (response == NULL)
jobid = 0;
else if (response->request.status.status_code > IPP_OK_CONFLICT)
jobid = 0;
else if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
jobid = 0;
else
jobid = attr->values[0].integer;
if (jobid)
syslog(LOG_INFO, "Print file - job ID = %d", jobid);
else if (response)
syslog(LOG_ERR, "Unable to print file - %s",
ippErrorString(response->request.status.status_code));
else
syslog(LOG_ERR, "Unable to print file - %s",
ippErrorString(cupsLastError()));
if (response != NULL)
ippDelete(response);
httpClose(http);
cupsLangFree(language);
return (jobid);
}
int
recv_print_job(const char *dest,
int num_defaults,
cups_option_t *defaults)
{
int i;
int status;
int fd;
FILE *fp;
char filename[1024];
int bytes;
char line[256],
command,
*count,
*name;
int num_data;
char control[1024],
data[32][256],
temp[32][1024];
char user[1024],
title[1024],
docname[1024],
queue[256],
*instance;
int num_dests;
cups_dest_t *dests,
*destptr;
int num_options;
cups_option_t *options;
int banner;
status = 0;
num_data = 0;
fd = -1;
control[0] = '\0';
strlcpy(queue, dest, sizeof(queue));
if ((instance = strrchr(queue, '/')) != NULL)
*instance++ = '\0';
num_dests = cupsGetDests(&dests);
if ((destptr = cupsGetDest(queue, instance, num_dests, dests)) == NULL)
{
if (!queue[0] || !strcmp(queue, "lp"))
if ((destptr = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
strlcpy(queue, destptr->name, sizeof(queue));
if (destptr == NULL)
{
if (instance)
syslog(LOG_ERR, "Unknown destination %s/%s!", queue, instance);
else
syslog(LOG_ERR, "Unknown destination %s!", queue);
cupsFreeDests(num_dests, dests);
putchar(1);
return (1);
}
}
putchar(0);
while (smart_gets(line, sizeof(line), stdin) != NULL)
{
if (strlen(line) < 2)
{
status = 1;
break;
}
command = line[0];
count = line + 1;
for (name = count + 1; *name && !isspace(*name); name ++);
while (isspace(*name))
*name++ = '\0';
switch (command)
{
default :
case 0x01 :
status = 1;
break;
case 0x02 :
if (strlen(name) < 2)
{
syslog(LOG_ERR, "Bad control file name \"%s\"", name);
putchar(1);
status = 1;
break;
}
if (control[0])
{
if ((fd = open(control, O_WRONLY)) < 0)
{
syslog(LOG_ERR, "Unable to append to temporary control file - %s",
strerror(errno));
putchar(1);
status = 1;
break;
}
lseek(fd, 0, SEEK_END);
}
else
{
if ((fd = cupsTempFd(control, sizeof(control))) < 0)
{
syslog(LOG_ERR, "Unable to open temporary control file - %s",
strerror(errno));
putchar(1);
status = 1;
break;
}
strcpy(filename, control);
}
break;
case 0x03 :
if (strlen(name) < 2)
{
syslog(LOG_ERR, "Bad data file name \"%s\"", name);
putchar(1);
status = 1;
break;
}
if (num_data >= (sizeof(data) / sizeof(data[0])))
{
syslog(LOG_ERR, "Too many data files (%d)", num_data);
putchar(1);
status = 1;
break;
}
strlcpy(data[num_data], name, sizeof(data[0]));
if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
{
syslog(LOG_ERR, "Unable to open temporary data file - %s",
strerror(errno));
putchar(1);
status = 1;
break;
}
strcpy(filename, temp[num_data]);
num_data ++;
break;
}
putchar(status);
if (status)
break;
for (i = atoi(count); i > 0; i -= bytes)
{
if (i > sizeof(line))
bytes = sizeof(line);
else
bytes = i;
if ((bytes = fread(line, 1, bytes, stdin)) > 0)
bytes = write(fd, line, bytes);
if (bytes < 1)
{
syslog(LOG_ERR, "Error while reading file - %s",
strerror(errno));
status = 1;
break;
}
}
if (!status)
{
if (fread(line, 1, 1, stdin) < 1)
{
status = 1;
syslog(LOG_ERR, "Error while reading trailing nul - %s",
strerror(errno));
}
else if (line[0])
{
status = 1;
syslog(LOG_ERR, "Trailing character after file is not nul (%02X)!",
line[0]);
}
}
close(fd);
putchar(status);
if (status)
break;
}
if (!status)
{
if ((fp = fopen(control, "rb")) == NULL)
status = 1;
else
{
title[0] = '\0';
user[0] = '\0';
docname[0] = '\0';
banner = 0;
while (smart_gets(line, sizeof(line), fp) != NULL)
{
switch (line[0])
{
case 'J' :
strlcpy(title, line + 1, sizeof(title));
break;
case 'N' :
strlcpy(docname, line + 1, sizeof(docname));
break;
case 'P' :
strlcpy(user, line + 1, sizeof(user));
break;
case 'L' :
banner = 1;
break;
}
if (status)
break;
}
rewind(fp);
while (smart_gets(line, sizeof(line), fp) != NULL)
{
switch (line[0])
{
case 'c' :
case 'd' :
case 'f' :
case 'g' :
case 'l' :
case 'n' :
case 'o' :
case 'p' :
case 'r' :
case 't' :
case 'v' :
if (!user[0])
{
syslog(LOG_WARNING, "No username specified by client! "
"Using \"anonymous\"...");
strcpy(user, "anonymous");
}
num_options = 0;
options = NULL;
for (i = 0; i < num_defaults; i ++)
num_options = cupsAddOption(defaults[i].name,
defaults[i].value,
num_options, &options);
for (i = 0; i < destptr->num_options; i ++)
num_options = cupsAddOption(destptr->options[i].name,
destptr->options[i].value,
num_options, &options);
if (!banner)
num_options = cupsAddOption("job-sheets", "none",
num_options, &options);
if (line[0] == 'l')
num_options = cupsAddOption("raw", "", num_options, &options);
if (line[0] == 'p')
num_options = cupsAddOption("prettyprint", "", num_options,
&options);
for (i = 0; i < num_data; i ++)
if (strcmp(data[i], line + 1) == 0)
break;
if (i >= num_data)
{
status = 1;
break;
}
if (print_file(queue, temp[i], title, docname, user, num_options,
options) == 0)
status = 1;
else
status = 0;
cupsFreeOptions(num_options, options);
break;
}
if (status)
break;
}
fclose(fp);
}
}
unlink(control);
for (i = 0; i < num_data; i ++)
unlink(temp[i]);
cupsFreeDests(num_dests, dests);
return (status);
}
int
remove_jobs(const char *dest,
const char *agent,
const char *list)
{
int id;
http_t *http;
ipp_t *request,
*response;
cups_lang_t *language;
char uri[HTTP_MAX_URI];
(void)dest;
if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
cupsEncryption())) == NULL)
{
syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
return (1);
}
language = cupsLangDefault();
while ((id = atoi(list)) > 0)
{
while (isdigit(*list))
list ++;
while (isspace(*list))
list ++;
request = ippNew();
request->request.op.operation_id = IPP_CANCEL_JOB;
request->request.op.request_id = 1;
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, cupsLangEncoding(language));
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL, language->language);
sprintf(uri, "ipp://localhost/jobs/%d", id);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, agent);
if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
{
if (response->request.status.status_code > IPP_OK_CONFLICT)
{
syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
ippErrorString(response->request.status.status_code));
ippDelete(response);
cupsLangFree(language);
httpClose(http);
return (1);
}
else
syslog(LOG_INFO, "Job ID %d cancelled", id);
ippDelete(response);
}
else
{
syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
ippErrorString(cupsLastError()));
cupsLangFree(language);
httpClose(http);
return (1);
}
}
cupsLangFree(language);
httpClose(http);
return (0);
}
int
send_state(const char *dest,
const char *list,
int longstatus)
{
int id;
http_t *http;
ipp_t *request,
*response;
ipp_attribute_t *attr;
cups_lang_t *language;
ipp_pstate_t state;
const char *jobdest,
*jobuser,
*jobname;
ipp_jstate_t jobstate;
int jobid,
jobsize,
jobcount,
jobcopies,
rank;
char rankstr[255];
char namestr[1024];
char uri[HTTP_MAX_URI];
char queue[256],
*instance;
static const char * const ranks[10] =
{
"th",
"st",
"nd",
"rd",
"th",
"th",
"th",
"th",
"th",
"th"
};
static const char * const requested[] =
{
"job-id",
"job-k-octets",
"job-state",
"job-printer-uri",
"job-originating-user-name",
"job-name",
"copies"
};
strlcpy(queue, dest, sizeof(queue));
if ((instance = strrchr(queue, '/')) != NULL)
*instance++ = '\0';
if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
cupsEncryption())) == NULL)
{
syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
printf("Unable to connect to server: %s", strerror(errno));
return (1);
}
request = ippNew();
request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
request->request.op.request_id = 1;
language = cupsLangDefault();
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, cupsLangEncoding(language));
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL, language->language);
snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", queue);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", NULL, "printer-state");
if ((response = cupsDoRequest(http, request, "/")) != NULL)
{
if (response->request.status.status_code > IPP_OK_CONFLICT)
{
syslog(LOG_WARNING, "Unable to get printer list: %s\n",
ippErrorString(response->request.status.status_code));
printf("Unable to get printer list: %s\n",
ippErrorString(response->request.status.status_code));
ippDelete(response);
return (1);
}
if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
state = (ipp_pstate_t)attr->values[0].integer;
else
state = IPP_PRINTER_STOPPED;
switch (state)
{
case IPP_PRINTER_IDLE :
printf("%s is ready\n", dest);
break;
case IPP_PRINTER_PROCESSING :
printf("%s is ready and printing\n", dest);
break;
case IPP_PRINTER_STOPPED :
printf("%s is not ready\n", dest);
break;
}
ippDelete(response);
}
else
{
syslog(LOG_WARNING, "Unable to get printer list: %s\n",
ippErrorString(cupsLastError()));
printf("Unable to get printer list: %s\n",
ippErrorString(cupsLastError()));
return (1);
}
id = atoi(list);
request = ippNew();
request->request.op.operation_id = id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
request->request.op.request_id = 1;
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
"attributes-charset", NULL, cupsLangEncoding(language));
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
"attributes-natural-language", NULL, language->language);
snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", queue);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
NULL, uri);
if (id)
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
else
{
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, list);
ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
}
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", sizeof(requested) / sizeof(requested[0]),
NULL, requested);
jobcount = 0;
if ((response = cupsDoRequest(http, request, "/")) != NULL)
{
if (response->request.status.status_code > IPP_OK_CONFLICT)
{
printf("get-jobs failed: %s\n",
ippErrorString(response->request.status.status_code));
ippDelete(response);
return (1);
}
rank = 1;
for (attr = response->attrs; attr != NULL; attr = attr->next)
{
while (attr != NULL &&
(attr->group_tag != IPP_TAG_JOB || attr->name == NULL))
attr = attr->next;
if (attr == NULL)
break;
jobid = 0;
jobsize = 0;
jobstate = IPP_JOB_PENDING;
jobname = "untitled";
jobuser = NULL;
jobdest = NULL;
jobcopies = 1;
while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
{
if (strcmp(attr->name, "job-id") == 0 &&
attr->value_tag == IPP_TAG_INTEGER)
jobid = attr->values[0].integer;
if (strcmp(attr->name, "job-k-octets") == 0 &&
attr->value_tag == IPP_TAG_INTEGER)
jobsize = attr->values[0].integer * 1024;
if (strcmp(attr->name, "job-state") == 0 &&
attr->value_tag == IPP_TAG_ENUM)
jobstate = (ipp_jstate_t)attr->values[0].integer;
if (strcmp(attr->name, "job-printer-uri") == 0 &&
attr->value_tag == IPP_TAG_URI)
if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
jobdest ++;
if (strcmp(attr->name, "job-originating-user-name") == 0 &&
attr->value_tag == IPP_TAG_NAME)
jobuser = attr->values[0].string.text;
if (strcmp(attr->name, "job-name") == 0 &&
attr->value_tag == IPP_TAG_NAME)
jobname = attr->values[0].string.text;
if (strcmp(attr->name, "copies") == 0 &&
attr->value_tag == IPP_TAG_INTEGER)
jobcopies = attr->values[0].integer;
attr = attr->next;
}
if (jobdest == NULL || jobid == 0)
{
if (attr == NULL)
break;
else
continue;
}
if (!longstatus && jobcount == 0)
puts("Rank Owner Job File(s) Total Size");
jobcount ++;
if (jobstate == IPP_JOB_PROCESSING)
strcpy(rankstr, "active");
else
{
snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
rank ++;
}
if (longstatus)
{
puts("");
if (jobcopies > 1)
snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
jobname);
else
strlcpy(namestr, jobname, sizeof(namestr));
printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
printf(" %-39.39s %d bytes\n", namestr, jobsize);
}
else
printf("%-7s %-7.7s %-7d %-31.31s %d bytes\n", rankstr, jobuser,
jobid, jobname, jobsize);
if (attr == NULL)
break;
}
ippDelete(response);
}
else
{
printf("get-jobs failed: %s\n", ippErrorString(cupsLastError()));
return (1);
}
if (jobcount == 0)
puts("no entries");
cupsLangFree(language);
httpClose(http);
return (0);
}
char *
smart_gets(char *s,
int len,
FILE *fp)
{
char *ptr,
*end;
int ch;
ptr = s;
end = s + len - 1;
while ((ch = getc(fp)) != EOF)
{
if (ch == '\n')
break;
else if (ch == '\r')
{
ch = getc(fp);
if (ch != '\n')
ungetc(ch, fp);
break;
}
else if (ptr < end)
*ptr++ = ch;
}
*ptr = '\0';
if (ch == EOF && ptr == s)
return (NULL);
else
return (s);
}