#include <cups/cups.h>
#include <cups/string-private.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
static int do_command(int outfd, int infd, const char *command);
static int print_job(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
static int print_waiting(int outfd, int infd, char *dest);
static int remove_job(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
static int status_long(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
static int status_short(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
static void usage(void) __attribute__((noreturn));
int
main(int argc,
char *argv[])
{
int i;
int status;
char *op,
**opargs,
*dest;
int cupslpd_argc;
char *cupslpd_argv[1000];
int cupslpd_stdin[2],
cupslpd_stdout[2],
cupslpd_pid,
cupslpd_status;
op = NULL;
opargs = argv + argc;
dest = NULL;
cupslpd_argc = 1;
cupslpd_argv[0] = (char *)"cups-lpd";
for (i = 1; i < argc; i ++)
if (!strncmp(argv[i], "-o", 2))
{
cupslpd_argv[cupslpd_argc++] = argv[i];
if (!argv[i][2])
{
i ++;
if (i >= argc)
usage();
cupslpd_argv[cupslpd_argc++] = argv[i];
}
}
else if (argv[i][0] == '-')
usage();
else if (!op)
op = argv[i];
else if (!dest)
dest = argv[i];
else
{
opargs = argv + i;
break;
}
if (!op ||
(!strcmp(op, "print-job") && (!dest || !opargs)) ||
(!strcmp(op, "remove-job") && (!dest || !opargs)) ||
(strcmp(op, "print-job") && strcmp(op, "print-waiting") &&
strcmp(op, "remove-job") && strcmp(op, "status-long") &&
strcmp(op, "status-short")))
{
printf("op=\"%s\", dest=\"%s\", opargs=%p\n", op, dest, opargs);
usage();
}
cupslpd_argv[cupslpd_argc] = NULL;
pipe(cupslpd_stdin);
pipe(cupslpd_stdout);
if ((cupslpd_pid = fork()) < 0)
{
perror("testlpd: Unable to fork");
return (1);
}
else if (cupslpd_pid == 0)
{
dup2(cupslpd_stdin[0], 0);
close(cupslpd_stdin[0]);
close(cupslpd_stdin[1]);
dup2(cupslpd_stdout[1], 1);
close(cupslpd_stdout[0]);
close(cupslpd_stdout[1]);
execv("./cups-lpd", cupslpd_argv);
perror("testlpd: Unable to exec ./cups-lpd");
exit(errno);
}
else
{
close(cupslpd_stdin[0]);
close(cupslpd_stdout[1]);
}
if (!strcmp(op, "print-job"))
status = print_job(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
else if (!strcmp(op, "print-waiting"))
status = print_waiting(cupslpd_stdin[1], cupslpd_stdout[0], dest);
else if (!strcmp(op, "remove-job"))
status = remove_job(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
else if (!strcmp(op, "status-long"))
status = status_long(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
else if (!strcmp(op, "status-short"))
status = status_short(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
else
{
printf("Unknown operation \"%s\"!\n", op);
status = 1;
}
close(cupslpd_stdin[1]);
close(cupslpd_stdout[0]);
while (wait(&cupslpd_status) != cupslpd_pid);
printf("cups-lpd exit status was %d...\n", cupslpd_status);
return (status);
}
static int
do_command(int outfd,
int infd,
const char *command)
{
size_t len;
char status;
printf("COMMAND: %02X %s", command[0], command + 1);
len = strlen(command);
if ((size_t)write(outfd, command, len) < len)
{
puts(" Write failed!");
return (-1);
}
if (read(infd, &status, 1) < 1)
puts("STATUS: ERROR");
else
printf("STATUS: %d\n", status);
return (status);
}
static int
print_job(int outfd,
int infd,
char *dest,
char **args)
{
int fd;
char command[1024],
control[1024],
buffer[8192];
int status;
struct stat fileinfo;
char *jobname;
int sequence;
ssize_t bytes;
if (stat(args[0], &fileinfo))
{
perror(args[0]);
return (-1);
}
if ((fd = open(args[0], O_RDONLY)) < 0)
{
perror(args[0]);
return (-1);
}
snprintf(command, sizeof(command), "\002%s\n", dest);
if ((status = do_command(outfd, infd, command)) != 0)
{
close(fd);
return (status);
}
if ((jobname = strrchr(args[0], '/')) != NULL)
jobname ++;
else
jobname = args[0];
sequence = (int)getpid() % 1000;
snprintf(control, sizeof(control),
"Hlocalhost\n"
"P%s\n"
"J%s\n"
"ldfA%03dlocalhost\n"
"UdfA%03dlocalhost\n"
"N%s\n",
cupsUser(), jobname, sequence, sequence, jobname);
bytes = (ssize_t)strlen(control);
snprintf(command, sizeof(command), "\002%d cfA%03dlocalhost\n",
(int)bytes, sequence);
if ((status = do_command(outfd, infd, command)) != 0)
{
close(fd);
return (status);
}
bytes ++;
if (write(outfd, control, (size_t)bytes) < bytes)
{
printf("CONTROL: Unable to write %d bytes!\n", (int)bytes);
close(fd);
return (-1);
}
printf("CONTROL: Wrote %d bytes.\n", (int)bytes);
if (read(infd, command, 1) < 1)
{
puts("STATUS: ERROR");
close(fd);
return (-1);
}
else
{
status = command[0];
printf("STATUS: %d\n", status);
}
snprintf(command, sizeof(command), "\003%d dfA%03dlocalhost\n",
(int)fileinfo.st_size, sequence);
if ((status = do_command(outfd, infd, command)) != 0)
{
close(fd);
return (status);
}
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
{
if (write(outfd, buffer, (size_t)bytes) < bytes)
{
printf("DATA: Unable to write %d bytes!\n", (int)bytes);
close(fd);
return (-1);
}
}
write(outfd, "", 1);
close(fd);
printf("DATA: Wrote %d bytes.\n", (int)fileinfo.st_size);
if (read(infd, command, 1) < 1)
{
puts("STATUS: ERROR");
close(fd);
return (-1);
}
else
{
status = command[0];
printf("STATUS: %d\n", status);
}
return (status);
}
static int
print_waiting(int outfd,
int infd,
char *dest)
{
char command[1024];
snprintf(command, sizeof(command), "\001%s\n", dest);
return (do_command(outfd, infd, command));
}
static int
remove_job(int outfd,
int infd,
char *dest,
char **args)
{
int i;
char command[1024];
snprintf(command, sizeof(command), "\005%s", dest);
for (i = 0; args[i]; i ++)
{
strlcat(command, " ", sizeof(command));
strlcat(command, args[i], sizeof(command));
}
strlcat(command, "\n", sizeof(command));
return (do_command(outfd, infd, command));
}
static int
status_long(int outfd,
int infd,
char *dest,
char **args)
{
char command[1024],
buffer[8192];
ssize_t bytes;
if (args[0])
snprintf(command, sizeof(command), "\004%s %s\n", dest, args[0]);
else
snprintf(command, sizeof(command), "\004%s\n", dest);
bytes = (ssize_t)strlen(command);
if (write(outfd, command, (size_t)bytes) < bytes)
return (-1);
while ((bytes = read(infd, buffer, sizeof(buffer))) > 0)
{
fwrite(buffer, 1, (size_t)bytes, stdout);
fflush(stdout);
}
return (0);
}
static int
status_short(int outfd,
int infd,
char *dest,
char **args)
{
char command[1024],
buffer[8192];
ssize_t bytes;
if (args[0])
snprintf(command, sizeof(command), "\003%s %s\n", dest, args[0]);
else
snprintf(command, sizeof(command), "\003%s\n", dest);
bytes = (ssize_t)strlen(command);
if (write(outfd, command, (size_t)bytes) < bytes)
return (-1);
while ((bytes = read(infd, buffer, sizeof(buffer))) > 0)
{
fwrite(buffer, 1, (size_t)bytes, stdout);
fflush(stdout);
}
return (0);
}
static void
usage(void)
{
puts("Usage: testlpd [options] print-job printer filename [... filename]");
puts(" testlpd [options] print-waiting [printer or user]");
puts(" testlpd [options] remove-job printer [user [job-id]]");
puts(" testlpd [options] status-long [printer or user]");
puts(" testlpd [options] status-short [printer or user]");
puts("");
puts("Options:");
puts(" -o name=value");
exit(0);
}