#include <ctype.h>
#include <errno.h>
#include <libc.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include "../scheduler/mime.h"
#define HAVE_TM_GMTOFF 1
#define ErrorFile stderr
#define L_PAGE -1
#define L_NONE 0
#define L_EMERG 1
#define L_ALERT 2
#define L_CRIT 3
#define L_ERROR 4
#define L_WARN 5
#define L_NOTICE 6
#define L_INFO 7
#define L_DEBUG 8
#define L_DEBUG2 9
static void ConvertJob(const char *filename, mime_type_t *inMimeType, const char *options, const char *ppdPath, mime_type_t *outMimeType, mime_t *mimeDatabase, const char *out, const char *userName, const char *jobName, const char *numCopies);
int LogMessage(int level, const char *message, ...);
char *getDateTime(time_t t);
static int start_process(const char *command, const char *const argv[], const char *const envp[], int infd, int outfd, int errfd, int root);
static mime_type_t *getMimeType(mime_t *mimeDatabase, const char *mimeStr);
char ServerBin[1024] = "/usr/libexec/cups";
static int FilterLevel = 0;
static const char *RIPCache = "8m";
static const char *ServerRoot = "/etc/cups";
static const char *TempDir = "/tmp";
static const char *DataDir = "/usr/share/cups";
static const char *FontPath = "/usr/share/cups/fonts";
static const int LogLevel = L_INFO;
static int MaxFDs = 0;
int main (int argc, char *argv[])
{
mime_t *mimeDatabase = NULL;
mime_type_t *inMimeType;
mime_type_t *outMimeType;
const char *options = "";
const char *ppd = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/Resources/English.lproj/Generic.ppd";
const char *outMimeStr = "application/pdf";
const char *inMimeStr = NULL; const char *filename = NULL; const char *outFilename = NULL;
const char *userName = "unknown"; const char *jobName = "unknown"; const char *numCopies = "1"; int unlinkPPD = FALSE;
int unlinkInputFile = FALSE;
char c = 0;
int status = 0;
int waitStatus = 0;
int waitErr = 0;
while ((c = getopt(argc, argv, "f:o:i:j:P:a:uU:J:c:D")) != -1) {
switch(c) {
case 'f':
filename = optarg;
break;
case 'o':
outFilename = optarg;
break;
case 'i':
inMimeStr = optarg;
break;
case 'j':
outMimeStr = optarg;
break;
case 'P':
ppd = optarg;
break;
case 'a':
options = optarg;
break;
case 'u':
unlinkPPD = TRUE;
break;
case 'U':
userName = optarg;
break;
case 'J':
jobName = optarg;
break;
case 'c':
numCopies = optarg;
break;
case 'D':
unlinkInputFile = TRUE;
break;
case '?':
default:
fprintf(stderr, "Ignoring unexpected parameter: %s\n", argv[optind - 1]);
}
}
if (argc != optind) {
fprintf(stderr, "Usage: %s [-f <input filename>] [-o <output filename>] [-i <input mimetype>] [-j <output mimetype>] [-P <PPD filename>] [-u] [-a <attribute string>] [-U <username>] [-J <jobname] [-c <copies>] [-D]\n", argv[0]);
status = 1;
} else {
char directory[1024];
snprintf(directory, sizeof(directory), "%s/filter", ServerBin);
mimeDatabase = mimeNew();
mimeMerge(mimeDatabase, ServerRoot, directory);
if (inMimeStr == NULL) {
inMimeType = mimeFileType(mimeDatabase, filename);
} else {
inMimeType = getMimeType(mimeDatabase, inMimeStr);
}
outMimeType = getMimeType(mimeDatabase, outMimeStr);
ConvertJob(filename, inMimeType, options, ppd, outMimeType, mimeDatabase, outFilename, userName, jobName, numCopies);
do {
waitErr = wait(&waitStatus);
} while (waitErr != -1);
}
if (unlinkInputFile && filename && filename[0]){
unlink(filename);
}
if (unlinkPPD && ppd != NULL) {
unlink(ppd);
}
return status;
}
static void ConvertJob(const char *filename, mime_type_t *inMimeType, const char *options, const char *ppdPath, mime_type_t *outMimeType, mime_t *mimeDatabase, const char *out, const char *userName, const char *jobName, const char *numCopies)
{
int i;
int slot;
int num_filters;
mime_filter_t *filters;
char method[255];
int pid;
int statusfds[2],
filterfds[2][2];
const char *argv[8];
const char *envp[20];
char command[1024],
path[1024],
language[255],
charset[255],
classification[1024],
content_type[255],
out_url[1024],
device_uri[1024],
ppd[1024],
printer_name[255],
root[1024],
cache[255],
tmpdir[1024],
ldpath[1024],
datadir[1024],
fontpath[1050];
int currentCost = 0;
int id = 1;
static const char *outputURL = "file://dev/stdout";
struct rlimit limit;
static const int filterLimit = 0;
if (out != NULL) {
snprintf(out_url, sizeof(out_url), "file:/%s", out);
outputURL = out_url;
}
getrlimit(RLIMIT_NOFILE, &limit);
if (limit.rlim_max > FD_SETSIZE)
MaxFDs = FD_SETSIZE;
else
MaxFDs = limit.rlim_max;
limit.rlim_cur = MaxFDs;
setrlimit(RLIMIT_NOFILE, &limit);
num_filters = 0;
currentCost = 0;
{
filters = mimeFilter(mimeDatabase, inMimeType, outMimeType, &num_filters);
if (num_filters == 0)
{
LogMessage(L_ERROR, "Unable to convert file!");
return;
}
for (i = 0; i < num_filters;)
if (strcmp(filters[i].filter, "-") == 0)
{
num_filters --;
if (i < num_filters)
memcpy(filters + i, filters + i + 1,
(num_filters - i) * sizeof(mime_filter_t));
}
else
i ++;
if (num_filters == 0)
{
free(filters);
filters = NULL;
}
else
{
for (i = 0; i < num_filters; i ++)
currentCost += filters[i].cost;
}
}
if ((FilterLevel + currentCost) > filterLimit && FilterLevel > 0 &&
filterLimit > 0)
{
if (filters != NULL)
free(filters);
LogMessage(L_INFO, "Holding job %d because filter limit has been reached.", id);
return;
}
FilterLevel += currentCost;
argv[0] = "tofile";
argv[1] = "1";
argv[2] = userName;
argv[3] = jobName;
argv[4] = numCopies;
argv[5] = options;
argv[6] = filename;
argv[7] = NULL;
LogMessage(L_DEBUG, "StartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
strcpy(language, "LANG=C");
strcpy(charset, "utf-8");
snprintf(path, sizeof(path), "PATH=%s/filter:/bin:/usr/bin", ServerBin);
snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
inMimeType->super,
inMimeType->type);
snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s", outputURL);
snprintf(ppd, sizeof(ppd), "PPD=%s", ppdPath);
snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", ".tofile");
snprintf(cache, sizeof(cache), "RIP_MAX_CACHE=%s", RIPCache);
snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot);
snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir);
snprintf(fontpath, sizeof(fontpath), "CUPS_FONTPATH=%s", FontPath);
classification[0] = '\0';
if (getenv("LD_LIBRARY_PATH") != NULL)
snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
else
ldpath[0] = '\0';
envp[0] = path;
envp[1] = "SOFTWARE=CUPS/1.1";
envp[2] = "USER=root";
envp[3] = charset;
envp[4] = language;
envp[5] = "GMT"; envp[6] = ppd;
envp[7] = root;
envp[8] = cache;
envp[9] = tmpdir;
envp[10] = content_type;
envp[11] = device_uri;
envp[12] = printer_name;
envp[13] = datadir;
envp[14] = fontpath;
envp[15] = ldpath;
envp[16] = classification;
envp[17] = NULL;
LogMessage(L_DEBUG, "StartJob: envp = \"%s\",\"%s\",\"%s\",\"%s\","
"\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\","
"\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
envp[0], envp[1], envp[2], envp[3], envp[4],
envp[5], envp[6], envp[7], envp[8], envp[9],
envp[10], envp[11], envp[12], envp[13], envp[14],
envp[15], envp[16]);
#if 0
if (pipe(statusfds))
{
LogMessage(L_ERROR, "Unable to create job status pipes - %s.",
strerror(errno));
return;
}
LogMessage(L_DEBUG, "StartJob: statusfds = %d, %d",
statusfds[0], statusfds[1]);
*statusFD = statusfds[0];
#else
statusfds[0] = -1;
statusfds[1] = STDERR_FILENO;
#endif
filterfds[1][0] = open("/dev/null", O_RDONLY);
filterfds[1][1] = -1;
LogMessage(L_DEBUG, "StartJob: filterfds[%d] = %d, %d", 1, filterfds[1][0],
filterfds[1][1]);
for (i = 0, slot = 0; i < num_filters; i ++)
{
if (filters[i].filter[0] != '/')
snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
filters[i].filter);
else
{
strncpy(command, filters[i].filter, sizeof(command) - 1);
command[sizeof(command) - 1] = '\0';
}
if (i < (num_filters - 1) ||
strncmp(outputURL, "file:", 5) != 0)
pipe(filterfds[slot]);
else
{
filterfds[slot][0] = -1;
if (strncmp(outputURL, "file:/dev/", 10) == 0)
filterfds[slot][1] = open(outputURL + 5,
O_WRONLY | O_EXCL);
else
filterfds[slot][1] = open(outputURL + 5,
O_WRONLY | O_CREAT | O_TRUNC, 0600);
}
LogMessage(L_DEBUG, "StartJob: filter = \"%s\"", command);
LogMessage(L_DEBUG, "StartJob: filterfds[%d] = %d, %d",
slot, filterfds[slot][0], filterfds[slot][1]);
pid = start_process(command, argv, envp, filterfds[!slot][0],
filterfds[slot][1], statusfds[1], 0);
close(filterfds[!slot][0]);
close(filterfds[!slot][1]);
if (pid == 0)
{
LogMessage(L_ERROR, "Unable to start filter \"%s\" - %s.",
filters[i].filter, strerror(errno));
return;
}
LogMessage(L_INFO, "Started filter %s (PID %d).",
command, pid);
argv[6] = NULL;
slot = !slot;
}
if (filters != NULL)
free(filters);
if (strncmp(outputURL, "file:", 5) != 0)
{
sscanf(outputURL, "%254[^:]", method);
snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, method);
argv[0] = outputURL;
filterfds[slot][0] = -1;
filterfds[slot][1] = open("/dev/null", O_WRONLY);
LogMessage(L_DEBUG, "StartJob: backend = \"%s\"", command);
LogMessage(L_DEBUG, "StartJob: filterfds[%d] = %d, %d",
slot, filterfds[slot][0], filterfds[slot][1]);
pid = start_process(command, argv, envp, filterfds[!slot][0],
filterfds[slot][1], statusfds[1], 1);
close(filterfds[!slot][0]);
close(filterfds[!slot][1]);
if (pid == 0)
{
LogMessage(L_ERROR, "Unable to start backend \"%s\" - %s.",
method, strerror(errno));
return;
}
else
{
LogMessage(L_INFO, "Started backend %s (PID %d).", command, pid);
}
}
else
{
filterfds[slot][0] = -1;
filterfds[slot][1] = -1;
close(filterfds[!slot][0]);
close(filterfds[!slot][1]);
}
close(filterfds[slot][0]);
close(filterfds[slot][1]);
close(statusfds[1]);
}
int
LogMessage(int level,
const char *message,
...)
{
int len;
char line[1024];
va_list ap;
static char levels[] =
{
' ',
'X',
'A',
'C',
'E',
'W',
'N',
'I',
'D',
'd'
};
#ifdef HAVE_VSYSLOG
static int syslevels[] =
{
0,
LOG_EMERG,
LOG_ALERT,
LOG_CRIT,
LOG_ERR,
LOG_WARNING,
LOG_NOTICE,
LOG_INFO,
LOG_DEBUG,
LOG_DEBUG
};
#endif
if (level > LogLevel)
return (1);
fprintf(ErrorFile, "%c %s ", levels[level], getDateTime(time(NULL)));
va_start(ap, message);
len = vsnprintf(line, sizeof(line), message, ap);
va_end(ap);
fputs(line, ErrorFile);
if (len > 0 && line[len - 1] != '\n')
putc('\n', ErrorFile);
fflush(ErrorFile);
return (1);
}
char *
getDateTime(time_t t)
{
struct tm *date;
static char s[1024];
static const char *months[12] =
{
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
date = localtime(&t);
snprintf(s, sizeof(s), "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
date->tm_mday, months[date->tm_mon], 1900 + date->tm_year,
date->tm_hour, date->tm_min, date->tm_sec,
#ifdef HAVE_TM_GMTOFF
date->tm_gmtoff / 3600, (date->tm_gmtoff / 60) % 60);
#else
timezone / 3600, (timezone / 60) % 60);
#endif
return (s);
}
static int
start_process(const char *command,
const char *const argv[],
const char *const envp[],
int infd,
int outfd,
int errfd,
int root)
{
int fd;
int pid;
LogMessage(L_DEBUG, "start_process(\"%s\", %08x, %08x, %d, %d, %d)",
command, argv, envp, infd, outfd, errfd);
if ((pid = fork()) == 0)
{
close(0);
dup(infd);
close(1);
dup(outfd);
if (errfd > 2)
{
close(2);
dup(errfd);
}
for (fd = 3; fd < MaxFDs; fd ++)
close(fd);
#if 0
if (!root && getuid() == 0)
{
if (setgid(Group))
exit(errno);
if (setuid(User))
exit(errno);
}
#endif
#if CHANGE_PERM
setgroups(0, NULL);
umask(077);
#endif
execve(command, (char *const*) argv, (char *const*) envp);
perror(command);
exit(errno);
}
else if (pid < 0)
{
LogMessage(L_ERROR, "Unable to fork %s - %s.", command, strerror(errno));
return (0);
}
return (pid);
}
static mime_type_t *getMimeType(mime_t *mimeDatabase, const char *mimeStr)
{
char super[MIME_MAX_SUPER];
char type[MIME_MAX_TYPE];
const char *inStr = NULL;
char *inSuper = super;
char *inType = type;
inStr = mimeStr;
while (*inStr != '/' && *inStr != '\0' && (inSuper - super + 1) < sizeof(super)) {
*inSuper++ = tolower(*inStr++);
}
*inSuper = '\0';
if (*inStr != '/') {
fprintf(stderr, "Invalid format for mime type: %s\n", mimeStr);
} else {
++inStr;
while(*inStr != '\0' && (type - type + 1) < sizeof(type)) {
*inType++ = tolower(*inStr++);
}
*inType++ = '\0';
}
return mimeType(mimeDatabase, super, type);
}