home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************
- * *
- * LPD.c *
- * *
- * Line Printer Daemon using TCP/IP printer protocol *
- * *
- * -------------- The lpd routines -------------- *
- * *
- * Written by Casper Boon, August, 1992. *
- * *
- * © 1992 Casper Boon. *
- * *
- * Before really examining this code I would recommend looking at the *
- * BackGrounder code (and comments) and reading RFC 1179. *
- * *
- * This code maintains 4 connections on the LPD port. It was found *
- * that one was not enough. When a file is being sent to the mac the *
- * user on the unix system might want to know the state of the queue so *
- * the user issues an lpq request which tries to open another connection*
- * which fails because the only connection was in use for the file *
- * transfer. *
- * *
- * *
- ************************************************************************/
-
- #include "LPD.H"
- #include "PAP.H"
- #include "TCPStream.H"
- #include "BackGrounder.h"
- #include "lpdProtos.h"
-
- #include <Packages.H>
-
-
- #define ACK() Acknowledge(pass->crefnum, NIL)
- #define NACK(s) Acknowledge(pass->crefnum, s)
-
- #define isadigit(c) ( ( (c) >= '0' ) && ( (c) <= '9' ) )
-
- #define CMND_MAX 512
- #define DATA_MAX 10240 /* give it a 10K buffer for file transfers */
-
- #define CMND_TYPE 1
- #define SCMD_TYPE 2
- #define DATA_TYPE 3
-
-
- typedef struct _pass_block_
- {
- integer type;
- integer crefnum;
- integer state;
- integer eof;
- LongWord remoteHost;
- Word remotePort;
- char printer[128];
-
- Byte data[DATA_MAX+2];
- integer length;
-
- LongInt expecting;
- LongInt collected;
- LongInt wanted;
- } PassBlk, *PassPtr;
-
-
- LongWord localHost;
-
- extern Boolean printing;
- Boolean tcp_closing = FALSE;
-
- #define MAX_CONNS 20
- #define N_CONNS 2
-
- static PassPtr cons[MAX_CONNS] = {
- NIL, NIL, NIL, NIL, NIL,
- NIL, NIL, NIL, NIL, NIL,
- NIL, NIL, NIL, NIL, NIL,
- NIL, NIL, NIL, NIL, NIL
- };
-
-
- integer log_dir;
- integer n_connections = 0;
-
- void TimeStamp(void);
- void TimeStamp2(void);
- Boolean StartListening(void);
- void MadeConnection(integer flag, Ptr param);
- void GotSomeData(integer flag, Ptr param);
- Boolean BadResult(integer err);
- void ResetConnection(PassPtr pass);
- void ResetDone(integer flag, Ptr param);
- void GotCommand(PassPtr pass, Word length);
- void SpoolCommands(PassPtr pass, Word length);
- void GetSpoolFile(PassPtr pass, char *name, char *printer,
- LongInt expecting, Boolean ctl_file);
- void GotData(PassPtr pass, Word length);
- void doClose(void);
- OSErr OpenTCPDriver(void);
-
- void Acknowledge(integer crefnum, StringPtr s);
- void SendString(integer crefnum, StringPtr s);
-
- void Fatal(StringPtr message);
-
-
- /************************************************************************
- * *
- * send the acknowlegde if s is NIL or a NAK with message otherwise *
- * *
- ************************************************************************/
- void Acknowledge(integer crefnum, StringPtr s)
- {
- Word slen;
- integer state;
-
- if (!s)
- TCPSWrite(crefnum, "\0", 1, &state);
- else
- {
- slen = *s + 1; *s = 1;
- TCPSWrite(crefnum, (Ptr)s, slen, &state);
- }
- WaitForState(&state);
- }
-
- /************************************************************************
- * *
- * Sends a string to the remote host. *
- * *
- ************************************************************************/
- void SendString(integer crefnum, StringPtr s)
- {
- Word slen;
- integer state;
-
- slen = *s++;
- TCPSWrite(crefnum, (Ptr)s, slen, &state);
-
- WaitForState(&state);
- }
-
- /************************************************************************
- * *
- * Initialisation for LPD. *
- * *
- ************************************************************************/
- Boolean InitLPD()
- {
- SetUpSpoolDir(); /* set up the spool folder */
- log_dir = GetLogDir(); /* get the logging folder */
-
- InitLogFile(LogFileName, log_dir); /* initialise the log file */
-
- log_printf("Printer daemon started at ");
-
- TimeStamp();
-
- if (InitTCPS()!=noErr) /* open MacTCP */
- {
- SysBeep(5);
- Fatal("\pCannot initialise TCP");
- return FALSE;
- }
-
- ReadConfig();
- UnloadSeg(ReadConfig);
-
- if ( PAPLoad() == NIL )
- {
- SysBeep(5);
- Fatal("\pCannot Load PAP");
- return FALSE;
- }
-
- return StartListening();
- }
-
- /************************************************************************
- * *
- * Clear the log file. *
- * *
- ************************************************************************/
- void ClearLPDLogFile()
- {
- integer lf;
-
- if ( gotAdmin && mailing && (FSOpenRO(LogFileName, log_dir, &lf) == noErr) )
- {
- SendMail(administrator, adminsHost, NIL, lf);
-
- FSClose(lf);
- }
-
- ClearLogFile();
-
- log_printf("Log file cleared at "); TimeStamp();
- }
-
- /************************************************************************
- * *
- * Put the date and time in long format into the log file. *
- * *
- ************************************************************************/
- void TimeStamp()
- {
- LongWord dateTime;
- Str255 stamp;
-
- GetDateTime(&dateTime);
- IUTimeString(dateTime, TRUE, stamp);
- log_printf("%p ", stamp);
- IUDateString(dateTime, longDate, stamp);
- log_printf("%p\n", stamp);
- }
-
- /************************************************************************
- * *
- * Put the date and time in short format into the log file. *
- * *
- ************************************************************************/
- void TimeStamp2()
- {
- LongWord dateTime;
- Str255 stamp;
-
- GetDateTime(&dateTime);
- IUTimeString(dateTime, TRUE, stamp);
- log_printf("%p ", stamp);
- IUDateString(dateTime, shortDate, stamp);
- log_printf("%p\n", stamp);
- }
-
- /************************************************************************
- * *
- * Resetting a connection is really just closing and reopenning. *
- * *
- ************************************************************************/
- void ResetConnection(PassPtr pass)
- {
- if (tcp_closing) return;
- TCPSClose(pass->crefnum, &pass->state);
- Background(&pass->state, ResetDone, (Ptr)pass);
- CloseSpoolFile(); LPD_Banner_Down();
- if (pass->collected) Poke_Spooler();
- pass->collected = 0;
- }
-
- /************************************************************************
- * *
- * When the connection closes we reopen the new connection. *
- * *
- ************************************************************************/
- void ResetDone(integer flag, Ptr param)
- {
- PassPtr pass = (PassPtr)param;
-
- if (n_connections > 0) n_connections--;
- Quitable = (n_connections == 0);
-
- TCPSOpen(&pass->crefnum, NIL, LPD_PORT, 0, TRUE, &pass->state);
- Background(&pass->state, MadeConnection, (Ptr)pass);
- }
-
- /************************************************************************
- * *
- * StartListening. This is part of the initialisation phase. Five *
- * connection buffers are allocated and connections openned. *
- * *
- ************************************************************************/
- Boolean StartListening()
- {
- integer n_c = 0;
- PassPtr pass;
- integer i;
-
- Quitable = TRUE;
- Poke_Spooler();
-
- /* create N_CONNS connections */
- for (i = 0; i < N_CONNS; i++)
- {
- cons[i] = (pass = (PassPtr)NewPtrClear(sizeof(PassBlk)));
-
- if (!pass)
- {
- log_printf("Error allocating buffer %d\n", i);
- continue;
- }
-
- n_c++;
-
- if (TCPSOpen(&pass->crefnum, NIL, LPD_PORT, 0,
- TRUE, &pass->state) == noErr)
- Background(&pass->state, MadeConnection, (Ptr)pass);
- else
- {
- DisposPtr((Ptr)pass);
- cons[i] = NIL;
- n_c--;
- }
- }
-
- return (n_c > 0);
- }
-
-
- /************************************************************************
- * *
- * when a connection is made (an external host connects to LPD) or *
- * there is an error opening the connection, we come here. *
- * *
- ************************************************************************/
- void MadeConnection(integer flag, Ptr param)
- {
- PassPtr pass = (PassPtr)param;
- Byte *res;
- Word localPort;
-
- if (flag != noErr)
- { /* a failed connection..., try again */
- if DEBUGGING log_printf("Bad open status %d\n", flag);
-
- if (tcp_closing) return;
-
- TCPSOpen(&pass->crefnum, NIL, LPD_PORT, 0, TRUE, &pass->state);
-
- Background(&pass->state, MadeConnection, (Ptr)pass);
- return;
- }
-
- Quitable = FALSE;
- n_connections++;
-
- /* find out who connected */
- TCPSConnInfo(pass->crefnum,
- &localHost, &pass->remoteHost,
- &localPort, &pass->remotePort);
-
-
- if DEBUGGING
- {
- res = (Byte*)(&pass->remoteHost);
-
- log_printf("[%d] request from %d.%d.%d.%d on port %d ",
- pass->crefnum,
- res[0], res[1], res[2], res[3], pass->remotePort);
- TimeStamp2();
- }
-
- /* check that this host is allowed to connect */
- if (!ValidHost(pass->remoteHost))
- { /* don't know this host */
- SendString(pass->crefnum, "\pSorry, You don't have privileges to print.\012");
-
- /* disconnect and wait again */
- ResetConnection(pass);
-
- return;
- }
-
- /* clear the data buffer */
- ClearBytes((Ptr)pass->data, CMND_MAX);
-
- /* and wait for some data */
- pass->type = CMND_TYPE;
- pass->length = CMND_MAX;
- *pass->data = 0;
- TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
- &pass->eof, &pass->state);
-
- Background(&pass->state, GotSomeData, (Ptr)pass);
- }
-
- /************************************************************************
- * *
- * GotSomeData. Determine what type of data we expected and do what *
- * is appropriate. *
- * *
- ************************************************************************/
- void GotSomeData(integer flag, Ptr param)
- {
- PassPtr pass = (PassPtr)param;
-
- if ( !BadResult(flag) || (flag == commandTimeout) )
- { /* here comes the data */
- if DEBUGGING
- log_printf("[%d] GotSomeData: length %d\n", pass->crefnum, pass->length);
-
- /* timed out, that's OK but need to try again */
- if (flag == commandTimeout && pass->length == 0)
- {
- pass->type = CMND_TYPE;
- pass->length = CMND_MAX;
- *pass->data = 0;
- TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
- &pass->eof, &pass->state);
- Background(&pass->state, GotSomeData, (Ptr)pass);
- }
- else if (pass->length != 0)
- {
- pass->data[pass->length] = 0;
- switch (pass->type)
- {
- case CMND_TYPE: GotCommand(pass, pass->length); break;
- case SCMD_TYPE: SpoolCommands(pass, pass->length); break;
- case DATA_TYPE: GotData(pass, pass->length); break;
- }
- }
- else
- ResetConnection(pass);
- }
- else
- {
- if DEBUGGING
- log_printf("[%d] GotSomeData: bad result %d\n",
- pass->crefnum, flag);
- ResetConnection(pass);
- }
- }
-
- /************************************************************************
- * *
- * a bad result is failure on a non-terminated connection *
- * *
- ************************************************************************/
- Boolean BadResult(integer err)
- {
- if ( err == noErr ||
- err == connectionClosing ||
- err == connectionTerminated )
- return FALSE;
-
- if (err != commandTimeout) /* most timeouts will be retried anyway */
- log_printf("Bad result %d\n", err);
- return TRUE;
- }
-
-
- /************************************************************************
- * *
- * *
- ************************************************************************/
- void GotCommand(PassPtr pass, Word length)
- {
- register char *a, *b;
-
- if DEBUGGING
- log_printf("[%d] Got a command length %d \"(%d)%s\"\n",
- pass->crefnum, length, pass->data[0], &pass->data[1]);
-
- switch (pass->data[0])
- {
- case 1: /* Print any waiting jobs */
- /* one operand : print queue */
- ACK();
- Poke_Spooler();
- break;
- case 2: /* new job */
- /* one operand : print queue */
-
- ACK();
-
- a = (char*)&pass->data[1];
- b = pass->printer;
- while (*a && *a != '\012') *b++ = *a++;
- *b = 0;
-
- /* look for a sub command */
- pass->type = SCMD_TYPE;
- pass->length = CMND_MAX;
- TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
- &pass->eof, &pass->state);
- Background(&pass->state, GotSomeData, (Ptr)pass);
-
- /* now return rather than break because everything else resets
- * the connection */
-
- return;
- case 3: /* Send Queue State short */
- case 4: /* Send Queue State long */
- /* two operands : print queue and list */
- ACK();
- SendJobs(pass->crefnum);
- break;
- case 5: /* remove print jobs */
- /* three operands : print queue ; agent and list */
- /* agent is the user making the request. */
- ACK();
- if DEBUGGING log_printf("Command 5 %s\n", &pass->data[1]);
- break;
- default:
- NACK("\pBad Command\012");
- log_printf("[%d] Bad command \'%d\'\n", pass->crefnum, pass->data[0]);
- break;
- }
-
- ResetConnection(pass);
- }
-
- /************************************************************************
- * *
- * *
- ************************************************************************/
- void SpoolCommands(PassPtr pass, Word length)
- {
- integer i;
-
- if DEBUGGING
- log_printf("[%d] Got a sub-command length %d \"(%d)%s\"\n",
- pass->crefnum, length, pass->data[0], &pass->data[1]);
-
- switch (pass->data[0])
- {
- case '\0': /* end of sub-requests */
- Poke_Spooler(); /* seems like a good time */
- break;
- case '\1': /* abort job */
- /* no operands. delete any files created during this job */
- ACK();
- break;
- case '\2': /* receive control file */
- case '\3': /* receive data file */
- /* two operands, count and file-name */
- ACK();
- /* how much do we want */
- i = 1; pass->expecting = 0;
- while (isadigit(pass->data[i]))
- {
- pass->expecting *= 10;
- pass->expecting += (pass->data[i++] - '0');
- }
- GetSpoolFile(pass, (char*)&pass->data[i], pass->printer, pass->expecting, pass->data[0] == '\2');
-
- /* return rather than break because all else resets connection */
- return;
- case '\4': /* receive data file, pass on to printer */
- /* two operands, count and file-name */
- ACK();
- return;
- default:
- NACK("\pIllegal sub request\n");
- log_printf("[%d] Bad sub command (%d)\n", pass->crefnum, pass->data[0]);
- break;
- }
-
- ResetConnection(pass);
- }
-
-
-
- /************************************************************************
- * *
- * *
- ************************************************************************/
- void GetSpoolFile(PassPtr pass, char *name, char *printer,
- LongInt expecting, Boolean ctl_file)
- {
- pass->wanted = expecting;
-
- LPD_Banner_Up();
-
- CreateSpoolFile(name, printer);
-
- if DEBUGGING
- log_printf("[%d] Expecting %ld Bytes\n", pass->crefnum, expecting);
-
- pass->collected = 0;
-
- pass->type = DATA_TYPE;
- pass->length = DATA_MAX;
-
- *pass->data = 0;
- TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
- &pass->eof, &pass->state);
- Background(&pass->state, GotSomeData, (Ptr)pass);
- }
-
- /************************************************************************
- * *
- * *
- ************************************************************************/
- void GotData(PassPtr pass, Word length)
- {
- if (length > pass->expecting)
- length = pass->expecting;
-
- pass->expecting -= length;
- pass->collected += length;
-
- if (length)
- { /* we must have finished */
- WriteSpoolFile((char*)pass->data, length);
- LPD_Banner_PCent(pass->collected, pass->wanted);
- }
-
- if (!pass->eof /*&& (pass->data[length-1])*/ && (pass->expecting > 0) )
- {
- *pass->data = 0;
- TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
- &pass->eof, &pass->state);
- Background(&pass->state, GotSomeData, (Ptr)pass);
- return;
- }
-
- if DEBUGGING
- log_printf("[%d] Received %ld Bytes\n", pass->crefnum, pass->collected);
-
- /* and wait for a new sub-command if the connection still exists */
- CloseSpoolFile();
-
- LPD_Banner_Down();
-
- if (pass->expecting > 0) NACK("\pDidn't get it all\n");
- else ACK(); /* ACK receipt of file */
-
- if (pass->eof)
- {
- ResetConnection(pass);
- return;
- }
-
- /* now wait for another sub command */
- pass->type = SCMD_TYPE;
- pass->length = CMND_MAX;
-
- *pass->data = 0;
- TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
- &pass->eof, &pass->state);
- Background(&pass->state, GotSomeData, (Ptr)pass);
- }
-
- /************************************************************************
- * *
- * *
- ************************************************************************/
- void doClose()
- {
- integer i;
-
- tcp_closing = TRUE;
-
- log_printf("Closing LPD at "); TimeStamp();
-
- PAPUnload();
-
- ExitTCPS();
- }
-
-
- /************************************************************************
- * *
- * *
- ************************************************************************/
- void Fatal(StringPtr message)
- {
- log_printf("%p\n", message);
-
- ParamText(message, NIL, NIL, NIL);
-
- InternalAlert();
- }
-