#include <cups/cups.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <cups/string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#ifdef WIN32
# include <winsock.h>
#else
# include <unistd.h>
# include <fcntl.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
#endif
static int printFile(int fdin, int fileOut, int socket, int waitEOF);
static void getSocketOptions(const char *uri, int *bidiP, int *waitEOFP);
static int sendUrgentReset(int socket);
static void sighup_handler(int sig);
static int gSocketOut = -1;
#ifndef HAVE_HSTRERROR
# define hstrerror cups_hstrerror
const char *
cups_hstrerror(int error)
{
static const char * const errors[] =
{
"OK",
"Host not found.",
"Try again.",
"Unrecoverable lookup error.",
"No data associated with name."
};
if (error < 0 || error > 4)
return ("Unknown hostname lookup error.");
else
return (errors[error]);
}
#endif
void print_backchannel(const unsigned char *buffer, size_t nbytes);
int
main(int argc,
char *argv[])
{
char method[255],
hostname[1024],
username[255],
resource[1024];
int fp;
int copies;
int port;
int delay;
int fd;
int error;
struct sockaddr_in addr;
struct hostent *hostaddr;
int bidi;
int waitEOF;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
setbuf(stderr, NULL);
#ifdef HAVE_SIGSET
sigset(SIGPIPE, SIG_IGN);
sigset(SIGHUP, sighup_handler);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
action.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &action, NULL);
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGHUP);
action.sa_handler = sighup_handler;
sigaction(SIGHUP, &action, NULL);
#else
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, sighup_handler);
#endif
if (argc == 1)
{
puts("network socket \"Unknown\" \"AppSocket/HP JetDirect\"");
return (0);
}
else if (argc < 6 || argc > 7)
{
fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
argv[0]);
return (1);
}
if (argc == 6)
{
fp = 0;
copies = 1;
}
else
{
if ((fp = open(argv[6], O_RDONLY)) < 0)
{
perror("ERROR: unable to open print file");
return (1);
}
copies = atoi(argv[4]);
}
httpSeparate(argv[0], method, username, hostname, &port, resource);
if (port == 0)
port = 9100;
if ((hostaddr = httpGetHostByName(hostname)) == NULL)
{
fprintf(stderr, "ERROR: Unable to locate printer \'%s\' - %s\n",
hostname, hstrerror(h_errno));
return (1);
}
fprintf(stderr, "INFO: Attempting to connect to host %s on port %d\n",
hostname, port);
memset(&addr, 0, sizeof(addr));
memcpy(&(addr.sin_addr), hostaddr->h_addr, hostaddr->h_length);
addr.sin_family = hostaddr->h_addrtype;
addr.sin_port = htons(port);
while (copies > 0)
{
for (delay = 5;;)
{
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("ERROR: Unable to create socket");
return (1);
}
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
error = errno;
close(fd);
fd = -1;
if (error == ECONNREFUSED || error == EHOSTDOWN ||
error == EHOSTUNREACH)
{
fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in %d seconds...\n",
hostname, delay);
sleep(delay);
if (delay < 30)
delay += 5;
}
else
{
perror("ERROR: Unable to connect to printer (retrying in 30 seconds)");
sleep(30);
}
}
else
break;
}
if (argc < 7)
{
#ifdef HAVE_SIGSET
sigset(SIGTERM, SIG_IGN);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_handler = SIG_IGN;
sigaction(SIGTERM, &action, NULL);
#else
signal(SIGTERM, SIG_IGN);
#endif
}
copies --;
if (fp != 0)
{
fputs("PAGE: 1 1\n", stderr);
lseek(fp, 0, SEEK_SET);
}
fputs("INFO: Connected to host, sending print job...\n", stderr);
gSocketOut = fd;
getSocketOptions(argv[0], &bidi, &waitEOF);
if (bidi) fprintf(stderr, "DEBUG: socket bidirectional enabled.\n");
(void) printFile(fp, bidi ? STDOUT_FILENO : -1, fd, waitEOF);
gSocketOut = -1;
close(fd);
}
if (fp != 0)
close(fp);
fputs("INFO: Ready to print.\n", stderr);
return (0);
}
static int printFile(int fdin, int fileOut, int socket, int waitEOF)
{
char fileBuffer[16 * 1024];
char socketReadBuffer[1 * 1024];
char *inFileBuffer = NULL;
fd_set readSet;
fd_set writeSet;
ssize_t bytesToSend = 0;
int val;
int fileEOFRead = false;
int maxfdp1 = 0;
struct timeval timeout90;
struct timeval *timeout = NULL;
int numReady = 0;
int jobFinished = false;
int err = 0;
maxfdp1 = MAX(fdin, fileOut);
maxfdp1 = MAX(maxfdp1, socket);
++maxfdp1;
val = fcntl(fdin, F_GETFL, 0);
fcntl(fdin, F_SETFL, val | O_NONBLOCK);
val = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, val | O_NONBLOCK);
while (jobFinished == false && err == 0) {
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_SET(socket, &readSet);
if (bytesToSend > 0)
{
FD_SET(socket, &writeSet);
timeout = NULL;
}
else if (!fileEOFRead)
{
FD_SET(fdin, &readSet);
timeout = NULL;
}
else
{
timeout90.tv_sec = 90;
timeout90.tv_usec = 0;
timeout = &timeout90;
}
#ifdef __hpux
numReady = select(maxfdp1, (int *) &readSet, &writeSet, 0, timeout);
#else
numReady = select(maxfdp1, &readSet, &writeSet, 0, timeout);
#endif
if (numReady > 0)
{
if (FD_ISSET(fdin, &readSet))
{
bytesToSend = read(fdin, fileBuffer, sizeof(fileBuffer));
if (bytesToSend > 0) {
inFileBuffer = fileBuffer;
} else if (bytesToSend == 0) {
fileEOFRead = true;
fputs("INFO: Print file sent, waiting for printer to finish...\n", stderr);
if (!waitEOF)
shutdown(socket, 1);
} else {
perror("ERROR: socket reading from input steam");
err = errno;
break;
}
}
else if (FD_ISSET(socket, &writeSet))
{
ssize_t bytesSent = write(socket, inFileBuffer, (size_t) bytesToSend);
if (bytesSent >= 0) {
bytesToSend -= bytesSent;
inFileBuffer += bytesSent;
} else if (bytesSent == EAGAIN) {
} else {
perror("ERROR: socket failed socket write");
err = errno;
break;
}
}
if (FD_ISSET(socket, &readSet))
{
ssize_t receivedBytes = read(socket, socketReadBuffer, sizeof(socketReadBuffer));
if (receivedBytes > 0)
{
if (fileOut >= 0) {
write(fileOut, socketReadBuffer, (size_t) receivedBytes);
} else {
fprintf(stderr, "DEBUG: Received (and ignored) %lu bytes of back-channel data!\n", (unsigned long) receivedBytes);
}
}
else if (receivedBytes == 0)
{
jobFinished = true;
if (!fileEOFRead) err = -1;
}
else
{
perror("DEBUG: failed to read socket back-channel");
}
}
}
else if (numReady == 0)
{
jobFinished = true;
}
else
{
if (errno != EINTR)
{
perror("ERROR: select");
err = errno;
}
}
}
if (waitEOF)
shutdown(socket, 1);
fprintf(stderr, "DEBUG: socket finished sending file, err = %d.\n", err);
return err;
}
static void getSocketOptions(const char *uri, int *bidiP, int *waitEOFP)
{
char method[255];
char hostname[1024];
char username[255];
char resource[1024];
int port = 0;
char *resourcePtr = NULL;
char *options = NULL;
char optionName[255];
char value[255];
char *ptr = NULL;
*bidiP = *waitEOFP = false;
method[0] = username[0] = hostname[0] = resource[0] = '\0';
httpSeparate(uri, method, username, hostname, &port, resource);
if ((options = strchr(resource, '?')) != NULL)
{
*options++ = '\0';
while (*options != '\0')
{
for (ptr = optionName; *options && *options != '=' && *options != '+'; )
*ptr++ = *options++;
*ptr = '\0';
*value = '\0';
if (*options == '=')
{
options ++;
for (ptr = value; *options && *options != '+';)
*ptr++ = *options++;
*ptr = '\0';
if (*options == '+')
options ++;
}
else if (*options == '+')
{
options ++;
}
if (strcasecmp(optionName, "bidi") == 0)
{
*bidiP = *value == '\0' ||
strcasecmp(value, "on") == 0 ||
strcasecmp(value, "yes") == 0 ||
strcasecmp(value, "true") == 0;
}
else if (strcasecmp(optionName, "waiteof") == 0)
{
*waitEOFP = *value == '\0' ||
strcasecmp(value, "on") == 0 ||
strcasecmp(value, "yes") == 0 ||
strcasecmp(value, "true") == 0;
}
}
}
}
static int sendUrgentReset(int socket)
{
char resetByte = 0;
int err = 0;
if (send(socket, &resetByte, sizeof(resetByte), MSG_OOB) < 0) {
err = errno;
perror("Failed to send socket reset");
}
return err;
}
static void sighup_handler(int sig)
{
(void)sig;
if (gSocketOut >= 0)
{
sendUrgentReset(gSocketOut);
}
#ifdef HAVE_SIGSET
sigset(SIGHUP, sighup_handler);
#elif !defined(HAVE_SIGACTION)
signal(SIGHUP, sighup_handler);
#endif
}
void
print_backchannel(const unsigned char *buffer,
size_t nbytes)
{
char line[255],
*lineptr;
for (lineptr = line; nbytes > 0; buffer ++, nbytes --)
{
if (*buffer < 0x20 || *buffer >= 0x7f)
{
snprintf(lineptr, sizeof(line) - (lineptr - line), "<%02X>", *buffer);
lineptr += strlen(lineptr);
}
else
*lineptr++ = *buffer;
if ((lineptr - line) > 72)
{
*lineptr = '\0';
fprintf(stderr, "DEBUG: DATA: %s\n", line);
lineptr = line;
}
}
if (lineptr > line)
{
*lineptr = '\0';
fprintf(stderr, "DEBUG: DATA: %s\n", line);
}
}