home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / System / lpDaemon SRC / lpd Sources / LPD.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-02-19  |  16.8 KB  |  664 lines  |  [TEXT/KAHL]

  1. /************************************************************************
  2.  *                                                                        *
  3.  *    LPD.c                                                                *
  4.  *                                                                        *
  5.  *  Line Printer Daemon using TCP/IP printer protocol                    *
  6.  *                                                                        *
  7.  *              -------------- The lpd routines --------------            *
  8.  *                                                                        *
  9.  *  Written by Casper Boon, August, 1992.                                *
  10.  *                                                                        *
  11.  *    © 1992 Casper Boon.                                                    *
  12.  *                                                                        *
  13.  * Before really examining this code I would recommend looking at the     *
  14.  * BackGrounder code (and comments) and reading RFC 1179.                *
  15.  *                                                                        *
  16.  * This code maintains 4 connections on the LPD port.  It was found     *
  17.  * that one was not enough.  When a file is being sent to the mac the    *
  18.  * user on the unix system might want to know the state of the queue so    *
  19.  * the user issues an lpq request which tries to open another connection*
  20.  * which fails because the only connection was in use for the file        *
  21.  * transfer.                                                            *
  22.  *                                                                        *
  23.  *                                                                        *
  24.  ************************************************************************/
  25.  
  26. #include "LPD.H"
  27. #include "PAP.H"
  28. #include "TCPStream.H"
  29. #include "BackGrounder.h"
  30. #include "lpdProtos.h"
  31.  
  32. #include <Packages.H>
  33.  
  34.  
  35. #define ACK()    Acknowledge(pass->crefnum, NIL)
  36. #define NACK(s)    Acknowledge(pass->crefnum, s)
  37.  
  38. #define isadigit(c)        ( ( (c) >= '0' ) && ( (c) <= '9' ) )
  39.  
  40. #define CMND_MAX      512
  41. #define DATA_MAX    10240    /* give it a 10K buffer for file transfers */
  42.  
  43. #define CMND_TYPE    1
  44. #define    SCMD_TYPE    2
  45. #define DATA_TYPE    3
  46.  
  47.  
  48. typedef struct _pass_block_
  49.     {
  50.     integer        type;
  51.     integer        crefnum;
  52.     integer        state;
  53.     integer        eof;
  54.     LongWord    remoteHost;
  55.     Word         remotePort;
  56.     char        printer[128];
  57.  
  58.     Byte        data[DATA_MAX+2];
  59.     integer         length;
  60.  
  61.     LongInt        expecting;
  62.     LongInt        collected;
  63.     LongInt        wanted;
  64.     } PassBlk, *PassPtr;
  65.  
  66.  
  67. LongWord localHost;
  68.  
  69. extern Boolean printing;
  70. Boolean tcp_closing = FALSE;
  71.  
  72. #define MAX_CONNS    20
  73. #define N_CONNS        2
  74.  
  75. static PassPtr     cons[MAX_CONNS] = {
  76.             NIL, NIL, NIL, NIL, NIL,
  77.             NIL, NIL, NIL, NIL, NIL,
  78.             NIL, NIL, NIL, NIL, NIL,
  79.             NIL, NIL, NIL, NIL, NIL
  80.             };
  81.  
  82.  
  83. integer log_dir;
  84. integer n_connections = 0;
  85.  
  86. void TimeStamp(void);
  87. void TimeStamp2(void);
  88. Boolean StartListening(void);
  89. void MadeConnection(integer flag, Ptr param);
  90. void GotSomeData(integer flag, Ptr param);
  91. Boolean BadResult(integer err);
  92. void ResetConnection(PassPtr pass);
  93. void ResetDone(integer flag, Ptr param);
  94. void GotCommand(PassPtr pass, Word length);
  95. void SpoolCommands(PassPtr pass, Word length);
  96. void GetSpoolFile(PassPtr pass, char *name, char *printer,
  97.                         LongInt expecting, Boolean ctl_file);
  98. void GotData(PassPtr pass, Word length);
  99. void doClose(void);
  100. OSErr OpenTCPDriver(void);
  101.  
  102. void Acknowledge(integer crefnum, StringPtr s);
  103. void SendString(integer crefnum, StringPtr s);
  104.  
  105. void Fatal(StringPtr message);
  106.  
  107.  
  108. /************************************************************************
  109.  *                                                                        *
  110.  * send the acknowlegde if s is NIL or a NAK with message otherwise        *
  111.  *                                                                        *
  112.  ************************************************************************/
  113. void Acknowledge(integer crefnum, StringPtr s)
  114. {
  115.     Word    slen;
  116.     integer state;
  117.  
  118.     if (!s)
  119.         TCPSWrite(crefnum, "\0", 1, &state);
  120.     else
  121.         {
  122.         slen = *s + 1; *s = 1;
  123.         TCPSWrite(crefnum, (Ptr)s, slen, &state);
  124.         }
  125.     WaitForState(&state);
  126. }
  127.  
  128. /************************************************************************
  129.  *                                                                        *
  130.  *                 Sends a string to the remote host.                        *
  131.  *                                                                        *
  132.  ************************************************************************/
  133. void SendString(integer crefnum, StringPtr s)
  134. {
  135.     Word    slen;
  136.     integer state;
  137.  
  138.     slen = *s++;
  139.     TCPSWrite(crefnum, (Ptr)s, slen, &state);
  140.  
  141.     WaitForState(&state);
  142. }
  143.  
  144. /************************************************************************
  145.  *                                                                        *
  146.  *                     Initialisation for LPD.                            *
  147.  *                                                                        *
  148.  ************************************************************************/
  149. Boolean InitLPD()
  150. {
  151.     SetUpSpoolDir();                     /* set up the spool folder    */
  152.     log_dir = GetLogDir();                  /* get the logging folder    */
  153.  
  154.     InitLogFile(LogFileName, log_dir);     /* initialise the log file    */
  155.  
  156.     log_printf("Printer daemon started at ");
  157.  
  158.     TimeStamp();
  159.  
  160.     if (InitTCPS()!=noErr)                             /* open MacTCP    */
  161.         {
  162.         SysBeep(5);
  163.         Fatal("\pCannot initialise TCP");
  164.         return FALSE;
  165.         }
  166.  
  167.     ReadConfig();
  168.     UnloadSeg(ReadConfig);
  169.  
  170.     if ( PAPLoad() == NIL )
  171.         {
  172.         SysBeep(5);
  173.         Fatal("\pCannot Load PAP");
  174.         return FALSE;
  175.         }
  176.  
  177.     return StartListening();
  178. }
  179.  
  180. /************************************************************************
  181.  *                                                                        *
  182.  *                         Clear the log file.                            *
  183.  *                                                                        *
  184.  ************************************************************************/
  185. void ClearLPDLogFile()
  186. {
  187.     integer    lf;
  188.  
  189.     if ( gotAdmin && mailing && (FSOpenRO(LogFileName, log_dir, &lf) == noErr) )
  190.         {
  191.         SendMail(administrator, adminsHost, NIL, lf);
  192.  
  193.         FSClose(lf);
  194.         }
  195.  
  196.     ClearLogFile();
  197.  
  198.     log_printf("Log file cleared at "); TimeStamp();
  199. }
  200.  
  201. /************************************************************************
  202.  *                                                                        *
  203.  *       Put the date and time in long format into the log file.            *
  204.  *                                                                        *
  205.  ************************************************************************/
  206. void TimeStamp()
  207. {
  208.     LongWord    dateTime;
  209.     Str255        stamp;
  210.  
  211.     GetDateTime(&dateTime);
  212.     IUTimeString(dateTime, TRUE, stamp);
  213.     log_printf("%p ", stamp);
  214.     IUDateString(dateTime, longDate, stamp);
  215.     log_printf("%p\n", stamp);
  216. }
  217.  
  218. /************************************************************************
  219.  *                                                                        *
  220.  *        Put the date and time in short format into the log file.        *
  221.  *                                                                        *
  222.  ************************************************************************/
  223. void TimeStamp2()
  224. {
  225.     LongWord    dateTime;
  226.     Str255        stamp;
  227.  
  228.     GetDateTime(&dateTime);
  229.     IUTimeString(dateTime, TRUE, stamp);
  230.     log_printf("%p ", stamp);
  231.     IUDateString(dateTime, shortDate, stamp);
  232.     log_printf("%p\n", stamp);
  233. }
  234.  
  235. /************************************************************************
  236.  *                                                                        *
  237.  *    Resetting a connection is really just closing and reopenning.        *
  238.  *                                                                        *
  239.  ************************************************************************/
  240. void ResetConnection(PassPtr pass)
  241. {
  242.     if (tcp_closing) return;
  243.     TCPSClose(pass->crefnum, &pass->state);
  244.     Background(&pass->state, ResetDone, (Ptr)pass);
  245.     CloseSpoolFile(); LPD_Banner_Down();
  246.     if (pass->collected) Poke_Spooler();
  247.     pass->collected = 0;
  248. }
  249.  
  250. /************************************************************************
  251.  *                                                                        *
  252.  *    When the connection closes we reopen the new connection.            *
  253.  *                                                                        *
  254.  ************************************************************************/
  255. void ResetDone(integer flag, Ptr param)
  256. {
  257.     PassPtr pass = (PassPtr)param;
  258.  
  259.     if (n_connections > 0) n_connections--;
  260.     Quitable = (n_connections == 0);
  261.  
  262.     TCPSOpen(&pass->crefnum, NIL, LPD_PORT, 0, TRUE, &pass->state);
  263.     Background(&pass->state, MadeConnection, (Ptr)pass);
  264. }
  265.  
  266. /************************************************************************
  267.  *                                                                        *
  268.  * StartListening.  This is part of the initialisation phase. Five        *
  269.  *  connection buffers are allocated and connections openned.            *
  270.  *                                                                        *
  271.  ************************************************************************/
  272. Boolean StartListening()
  273. {
  274.     integer    n_c = 0;
  275.     PassPtr    pass;
  276.     integer    i;
  277.  
  278.     Quitable = TRUE;
  279.     Poke_Spooler();
  280.  
  281.     /* create N_CONNS connections */
  282.     for (i = 0; i < N_CONNS; i++)
  283.         {
  284.         cons[i] = (pass = (PassPtr)NewPtrClear(sizeof(PassBlk)));
  285.  
  286.         if (!pass)
  287.             {
  288.             log_printf("Error allocating buffer %d\n", i);
  289.             continue;
  290.             }
  291.  
  292.         n_c++;
  293.  
  294.         if (TCPSOpen(&pass->crefnum, NIL, LPD_PORT, 0,
  295.                                             TRUE, &pass->state) == noErr)
  296.             Background(&pass->state, MadeConnection, (Ptr)pass);
  297.         else
  298.             {
  299.             DisposPtr((Ptr)pass);
  300.             cons[i] = NIL;
  301.             n_c--;
  302.             }
  303.         }
  304.  
  305.     return (n_c > 0);
  306. }
  307.  
  308.  
  309. /************************************************************************
  310.  *                                                                        *
  311.  * when a connection is made (an external host connects to LPD) or        *
  312.  * there is an error opening the connection, we    come here.                *
  313.  *                                                                        *
  314.  ************************************************************************/
  315. void MadeConnection(integer flag, Ptr param)
  316. {
  317.     PassPtr pass = (PassPtr)param;
  318.     Byte    *res;
  319.     Word    localPort;
  320.  
  321.     if (flag != noErr)
  322.         {                    /* a failed connection..., try again */
  323.         if DEBUGGING log_printf("Bad open status %d\n", flag);
  324.  
  325.         if (tcp_closing) return;
  326.  
  327.         TCPSOpen(&pass->crefnum, NIL, LPD_PORT, 0, TRUE, &pass->state);
  328.  
  329.         Background(&pass->state, MadeConnection, (Ptr)pass);
  330.         return;
  331.         }
  332.  
  333.     Quitable = FALSE;
  334.     n_connections++;
  335.  
  336.     /* find out who connected */
  337.     TCPSConnInfo(pass->crefnum,
  338.                     &localHost, &pass->remoteHost,
  339.                     &localPort, &pass->remotePort);
  340.  
  341.  
  342.     if DEBUGGING
  343.         {
  344.         res = (Byte*)(&pass->remoteHost);
  345.  
  346.         log_printf("[%d] request from %d.%d.%d.%d on port %d ",
  347.                         pass->crefnum,
  348.                         res[0], res[1], res[2], res[3], pass->remotePort);
  349.         TimeStamp2();
  350.         }
  351.  
  352.                     /* check that this host is allowed to connect */
  353.     if (!ValidHost(pass->remoteHost))
  354.         {    /* don't know this host */
  355.         SendString(pass->crefnum, "\pSorry, You don't have privileges to print.\012");
  356.  
  357.         /* disconnect and wait again */
  358.         ResetConnection(pass);
  359.  
  360.         return;
  361.         }
  362.  
  363.                                         /* clear the data buffer */
  364.     ClearBytes((Ptr)pass->data, CMND_MAX);
  365.  
  366.     /* and wait for some data */
  367.     pass->type = CMND_TYPE;
  368.     pass->length = CMND_MAX;
  369.     *pass->data = 0;
  370.     TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
  371.                                         &pass->eof, &pass->state);
  372.  
  373.     Background(&pass->state, GotSomeData, (Ptr)pass);
  374. }
  375.  
  376. /************************************************************************
  377.  *                                                                        *
  378.  * GotSomeData.  Determine what type of data we expected and do what    *
  379.  *   is appropriate.                                                    *
  380.  *                                                                        *
  381.  ************************************************************************/
  382. void GotSomeData(integer flag, Ptr param)
  383. {
  384.     PassPtr        pass = (PassPtr)param;
  385.  
  386.     if ( !BadResult(flag) || (flag == commandTimeout) )
  387.         { /* here comes the data */
  388.         if DEBUGGING
  389.             log_printf("[%d] GotSomeData: length %d\n", pass->crefnum, pass->length);
  390.  
  391.         /* timed out, that's OK but need to try again */
  392.         if (flag == commandTimeout && pass->length == 0)
  393.             {
  394.             pass->type = CMND_TYPE;
  395.             pass->length = CMND_MAX;
  396.             *pass->data = 0;
  397.             TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
  398.                                         &pass->eof, &pass->state);
  399.             Background(&pass->state, GotSomeData, (Ptr)pass);
  400.             }
  401.         else if (pass->length != 0)
  402.             {
  403.             pass->data[pass->length] = 0;
  404.             switch (pass->type)
  405.                 {
  406.                 case CMND_TYPE: GotCommand(pass, pass->length);        break;
  407.                 case SCMD_TYPE: SpoolCommands(pass, pass->length);    break;
  408.                 case DATA_TYPE: GotData(pass, pass->length);        break;
  409.                 }
  410.             }
  411.         else
  412.             ResetConnection(pass);
  413.         }
  414.     else
  415.         {
  416.         if DEBUGGING
  417.             log_printf("[%d] GotSomeData: bad result %d\n",
  418.                                                 pass->crefnum, flag);
  419.         ResetConnection(pass);
  420.         }
  421. }
  422.  
  423. /************************************************************************
  424.  *                                                                        *
  425.  * a bad result is failure on a non-terminated connection                *
  426.  *                                                                        *
  427.  ************************************************************************/
  428. Boolean BadResult(integer err)
  429. {
  430.     if ( err == noErr ||
  431.             err == connectionClosing ||
  432.                 err == connectionTerminated )
  433.         return FALSE;
  434.  
  435.     if (err != commandTimeout)    /* most timeouts will be retried anyway */
  436.         log_printf("Bad result %d\n", err);
  437.     return TRUE;
  438. }
  439.  
  440.  
  441. /************************************************************************
  442.  *                                                                        *
  443.  *                                                                        *
  444.  ************************************************************************/
  445. void GotCommand(PassPtr pass, Word length)
  446. {
  447.     register char *a, *b;
  448.  
  449.     if DEBUGGING
  450.         log_printf("[%d] Got a command length %d \"(%d)%s\"\n",
  451.                         pass->crefnum, length, pass->data[0], &pass->data[1]);
  452.  
  453.     switch (pass->data[0])
  454.         {
  455.         case 1:    /* Print any waiting jobs */
  456.             /* one operand : print queue */
  457.             ACK();
  458.             Poke_Spooler();
  459.             break;
  460.         case 2:    /* new job */
  461.             /* one operand : print queue */
  462.  
  463.             ACK();
  464.  
  465.             a = (char*)&pass->data[1];
  466.             b = pass->printer;
  467.             while (*a && *a != '\012') *b++ = *a++;
  468.             *b = 0;
  469.  
  470.             /* look for a sub command */
  471.             pass->type = SCMD_TYPE;
  472.             pass->length = CMND_MAX;
  473.             TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
  474.                                                     &pass->eof, &pass->state);
  475.             Background(&pass->state, GotSomeData, (Ptr)pass);
  476.  
  477.             /* now return rather than break because everything else resets
  478.              *  the connection */
  479.  
  480.             return;
  481.         case 3:    /* Send Queue State short */
  482.         case 4:    /* Send Queue State long */
  483.             /* two operands : print queue and list */
  484.             ACK();
  485.             SendJobs(pass->crefnum);
  486.             break;
  487.         case 5:    /* remove print jobs */
  488.             /* three operands : print queue ; agent and list */
  489.             /* agent is the user making the request. */
  490.             ACK();
  491.             if DEBUGGING log_printf("Command 5 %s\n", &pass->data[1]);
  492.             break;
  493.         default:
  494.             NACK("\pBad Command\012");
  495.             log_printf("[%d] Bad command \'%d\'\n", pass->crefnum, pass->data[0]);
  496.             break;
  497.         }
  498.  
  499.     ResetConnection(pass);
  500. }
  501.  
  502. /************************************************************************
  503.  *                                                                        *
  504.  *                                                                        *
  505.  ************************************************************************/
  506. void SpoolCommands(PassPtr pass, Word length)
  507. {
  508.     integer    i;
  509.  
  510.     if DEBUGGING
  511.         log_printf("[%d] Got a sub-command length %d \"(%d)%s\"\n",
  512.                     pass->crefnum, length, pass->data[0], &pass->data[1]);
  513.  
  514.     switch (pass->data[0])
  515.         {
  516.         case '\0':    /* end of sub-requests */
  517.             Poke_Spooler();    /* seems like a good time */
  518.             break;
  519.         case '\1':    /* abort job */
  520.             /* no operands. delete any files created during this job */
  521.             ACK();
  522.             break;
  523.         case '\2':    /* receive control file */
  524.         case '\3':    /* receive data file */
  525.             /* two operands, count and file-name */
  526.             ACK();
  527.             /* how much do we want */
  528.             i = 1; pass->expecting = 0;
  529.             while (isadigit(pass->data[i]))
  530.                 {
  531.                 pass->expecting *= 10;
  532.                 pass->expecting += (pass->data[i++] - '0');
  533.                 }
  534.             GetSpoolFile(pass, (char*)&pass->data[i], pass->printer, pass->expecting, pass->data[0] == '\2');
  535.  
  536.             /* return rather than break because all else resets connection */
  537.             return;
  538.         case '\4':    /* receive data file, pass on to printer */
  539.             /* two operands, count and file-name */
  540.             ACK();
  541.             return;
  542.         default:
  543.             NACK("\pIllegal sub request\n");
  544.             log_printf("[%d] Bad sub command (%d)\n", pass->crefnum, pass->data[0]);
  545.             break;
  546.         }
  547.  
  548.     ResetConnection(pass);
  549. }
  550.  
  551.  
  552.  
  553. /************************************************************************
  554.  *                                                                        *
  555.  *                                                                        *
  556.  ************************************************************************/
  557. void GetSpoolFile(PassPtr pass, char *name, char *printer,
  558.                                     LongInt expecting, Boolean ctl_file)
  559. {
  560.     pass->wanted = expecting;
  561.  
  562.     LPD_Banner_Up();
  563.  
  564.     CreateSpoolFile(name, printer);
  565.  
  566.     if DEBUGGING
  567.         log_printf("[%d] Expecting %ld Bytes\n", pass->crefnum, expecting);
  568.  
  569.     pass->collected = 0;
  570.  
  571.     pass->type = DATA_TYPE;
  572.     pass->length = DATA_MAX;
  573.  
  574.     *pass->data = 0;
  575.     TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
  576.                                         &pass->eof, &pass->state);
  577.     Background(&pass->state, GotSomeData, (Ptr)pass);
  578. }
  579.  
  580. /************************************************************************
  581.  *                                                                        *
  582.  *                                                                        *
  583.  ************************************************************************/
  584. void GotData(PassPtr pass, Word length)
  585. {
  586.     if (length > pass->expecting)
  587.         length = pass->expecting;
  588.  
  589.     pass->expecting -= length;
  590.     pass->collected += length;
  591.  
  592.     if (length)
  593.         {    /* we must have finished */
  594.         WriteSpoolFile((char*)pass->data, length);
  595.         LPD_Banner_PCent(pass->collected, pass->wanted);
  596.         }
  597.  
  598.     if (!pass->eof /*&& (pass->data[length-1])*/ && (pass->expecting > 0) )
  599.         {
  600.         *pass->data = 0;
  601.         TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
  602.                                         &pass->eof, &pass->state);
  603.         Background(&pass->state, GotSomeData, (Ptr)pass);
  604.         return;
  605.         }
  606.  
  607.     if DEBUGGING
  608.         log_printf("[%d] Received %ld Bytes\n", pass->crefnum, pass->collected);
  609.  
  610.     /* and wait for a new sub-command if the connection still exists */
  611.     CloseSpoolFile();
  612.  
  613.     LPD_Banner_Down();
  614.  
  615.     if (pass->expecting > 0)    NACK("\pDidn't get it all\n");
  616.     else                        ACK();        /* ACK receipt of file */
  617.  
  618.     if (pass->eof)
  619.         {
  620.         ResetConnection(pass);
  621.         return;
  622.         }
  623.  
  624.     /* now wait for another sub command */
  625.     pass->type = SCMD_TYPE;
  626.     pass->length = CMND_MAX;
  627.  
  628.     *pass->data = 0;
  629.     TCPSRead(pass->crefnum, (Ptr)pass->data, &pass->length,
  630.                                         &pass->eof, &pass->state);
  631.     Background(&pass->state, GotSomeData, (Ptr)pass);
  632. }
  633.  
  634. /************************************************************************
  635.  *                                                                        *
  636.  *                                                                        *
  637.  ************************************************************************/
  638. void doClose()
  639. {
  640.     integer    i;
  641.  
  642.     tcp_closing = TRUE;
  643.  
  644.     log_printf("Closing LPD at "); TimeStamp();
  645.  
  646.     PAPUnload();
  647.  
  648.     ExitTCPS();
  649. }
  650.  
  651.  
  652. /************************************************************************
  653.  *                                                                        *
  654.  *                                                                        *
  655.  ************************************************************************/
  656. void Fatal(StringPtr message)
  657. {
  658.     log_printf("%p\n", message);
  659.  
  660.     ParamText(message, NIL, NIL, NIL);
  661.  
  662.     InternalAlert();
  663. }
  664.