B_FRPROC.C

24.6 KB 38630979d732856b…
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*                                                                          */
/*      ------------         Bit-Bucket Software, Co.                       */
/*      \ 10001101 /         Writers and Distributors of                    */
/*       \ 011110 /          Freely Available<tm> Software.                 */
/*        \ 1011 /                                                          */
/*         ------                                                           */
/*                                                                          */
/*  (C) Copyright 1987-91, Bit Bucket Software Co., a Delaware Corporation. */
/*                                                                          */
/*                                                                          */
/*                   BinkleyTerm File Request Processor                     */
/*                                                                          */
/*                                                                          */
/*    For complete  details  of the licensing restrictions, please refer    */
/*    to the License  agreement,  which  is published in its entirety in    */
/*    the MAKEFILE and BT.C, and also contained in the file LICENSE.250.    */
/*                                                                          */
/*    USE  OF THIS FILE IS SUBJECT TO THE  RESTRICTIONS CONTAINED IN THE    */
/*    BINKLEYTERM  LICENSING  AGREEMENT.  IF YOU DO NOT FIND THE TEXT OF    */
/*    THIS  AGREEMENT IN ANY OF THE  AFOREMENTIONED FILES,  OR IF YOU DO    */
/*    NOT HAVE THESE FILES,  YOU  SHOULD  IMMEDIATELY CONTACT BIT BUCKET    */
/*    SOFTWARE CO.  AT ONE OF THE  ADDRESSES  LISTED BELOW.  IN NO EVENT    */
/*    SHOULD YOU  PROCEED TO USE THIS FILE  WITHOUT HAVING  ACCEPTED THE    */
/*    TERMS  OF  THE  BINKLEYTERM  LICENSING  AGREEMENT,  OR  SUCH OTHER    */
/*    AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO.      */
/*                                                                          */
/*                                                                          */
/* You can contact Bit Bucket Software Co. at any one of the following      */
/* addresses:                                                               */
/*                                                                          */
/* Bit Bucket Software Co.        FidoNet  1:104/501, 1:343/491             */
/* P.O. Box 460398                AlterNet 7:491/0                          */
/* Aurora, CO 80046               BBS-Net  86:2030/1                        */
/*                                Internet f491.n343.z1.fidonet.org         */
/*                                                                          */
/* Please feel free to contact us at any time to share your comments about  */
/* our software and/or licensing policies.                                  */
/*                                                                          */
/*                                                                          */
/*  This module is based largely on a similar module in OPUS-CBCS V1.03b.   */
/*  The original work is (C) Copyright 1987, Wynn Wagner III. The original  */
/*  author has graciously allowed us to use his code in this work.          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/* Include this file before any other includes or defines! */

#include "includes.h"

static char *their_pwd;                         /* Password in REQ file */
static char required_pwd[10];                   /* Password in OK file  */
static int xfer_seconds;

int prep_match (char *, char *);
int match (char *, char *);
void run_prog (char *);
int check_password (void);
freq_abort (long, int (*)(long));
int what_event (int);
int what_event_sub (int, int, int, int, int, int);

int prep_match (char *template, char *buffer)
{
   register int i, delim;
   register char *sptr;
   int start;

   (void) memset (buffer, 0, 11);

   i = (int) strlen (template);
   sptr = template;


   for (start = i = 0; sptr[i]; i++)
      if ((sptr[i] == '\\') || (sptr[i] == ':'))
         start = i + 1;

   if (start)
      sptr += start;
   delim = 8;                                    /* last column for ? */

   (void) strupr (sptr);

   for (i = 0; *sptr && i < 12; sptr++)
      switch (*sptr)
         {
         case '.':
            if (i > 8)
               return (-1);
            while (i < 8)
               {
               buffer[i++] = ' ';
               }
            buffer[i++] = *sptr;
            delim = 12;
            break;

         case '*':
            while (i < delim)
               {
               buffer[i++] = '?';
               }
            break;

         default:
            buffer[i++] = *sptr;
            break;

         }                                       /* switch */

   while (i < 12)
      {

/* dean suggests:

      buffer[i++] = (i == delim) ? '.' : ((i > delim) ? '?' : ' ');

   to replace the if/else below... 
*/
      if (i == 8)
         buffer[i++] = '.';
      else buffer[i++] = ' ';
      }

   buffer[i] = '\0';

   return 0;
}

int match (char *s1, char *s2)
{
   register char *i, *j;

   i = s1;
   j = s2;

   while (*i)
      {
      if ((*j != '?') && (*i != *j))
         {
         return 1;
         }
      i++;
      j++;
      }

   return 0;
}

/*--------------------------------------------------------------------------*/
/* Process file requests from the remote system. The filespec requested is  */
/* turned into a local filespec if possible, then transferred via the       */
/* caller-supplied routine.                                                 */
/*--------------------------------------------------------------------------*/

int n_frproc (char *request, int nfiles,
              int (*callback)(char *), int (*calltime)(long))
{
   register int i;
   register int j = 0;
   static char s[80];
   static char s1[80];
   static char s2[80];
   char *p;

   FILE *approved;
   struct FILEINFO dta;
   struct stat st;
   char *sptr;

   char *after_pwd;
   long updreq = 0L;
   char updtype = 0;
   int saved_nfiles;

   char our_wildcard[15];
   char their_wildcard[15];
   int mfunc;
   int magic_state = 0;
   int tried_about = 0;

   int failure_reason = 1;                      /* 1 = not available */
                                                /* 2 = no update     */
                                                /* 3 = bad password  */
   if (freq_accum.time == 0L)
      freq_accum.time = (long)time(NULL);

   approved = NULL;
   their_pwd = NULL;
   after_pwd = NULL;
   (void) strcpy (s1, request);

   /*--------------------------------------------------------------------*/
   /* Fix up the file name                                               */
   /*--------------------------------------------------------------------*/
   for (i = 0; request[i]; i++)
      {
      if (request[i] <= ' ')
         {
         request[i++] = '\0';
         j = i;
         break;
         }
      }
   
   if (j)
      {
      /* If we have a '!', find the end of the password, point j
         past it, then truncate and fold if necessary. This leaves
         j properly aligned for other fields.
       */

      if (request[j] == '!')
         {
         their_pwd = request + (++j);
         for (; request[j]; j++)
            {
            if (request[j] <= ' ')
               {
               request[j++] = '\0';
               break;
               }
            }

         if (strlen (their_pwd) > 6)
            their_pwd[6] = '\0';

         (void) fancy_str (their_pwd);
         }

      /* Test for update/backdate request */

      if (request[j] == '+' || request[j] == '-')
         {
         updtype = request[j++];
         updreq = atol (&request[j]);
         }
      }      

   if (!request[0])                             /* Still any filename?  */
      return (nfiles);                          /* If not, return df=0. */

   if (freq_abort(0L, calltime))                /* Any reason to abort? */
      return (-2);                              /* If so, return error. */


   /*--------------------------------------------------------------------*/
   /* Initialization(s)                                                  */
   /*--------------------------------------------------------------------*/
   i = 0;
   sptr = NULL;

   (void) strupr (request);
   status_line ("*%s %s (%s)", (updreq != 0L) ? MSG_TXT(M_UPDATE) : MSG_TXT(M_FILE), MSG_TXT(M_REQUEST), request);

   saved_nfiles = nfiles;

   /*--------------------------------------------------------------------*/
   /* Reserved words                                                     */
   /*--------------------------------------------------------------------*/
   if (!strcmp (request, "FILES"))
      {
      if (CURRENT.rq_FILES)
         (void) strcpy (s, CURRENT.rq_FILES);
      else
         {
         s[0] = '\0';
         sptr = MSG_TXT(M_NO_AVAIL);
         }
      goto avail;
      }

   else if (!strcmp (request, "ABOUT"))
      {
      s[0] = '\0';
      goto avail;
      }

   (void) prep_match (request, their_wildcard);

   /*--------------------------------------------------------------------*/
   /* See if the file is approved for transmission                       */
   /*--------------------------------------------------------------------*/
   if ((approved = share_fopen (CURRENT.rq_OKFile, read_ascii, DENY_WRITE)) == NULL)
      {
      (void) got_error (MSG_TXT(M_OPEN_MSG), CURRENT.rq_OKFile);
      goto err;
      }

   while (!feof (approved))
      {
      /* If we were magic, set flag to cause exit if we don't do it again */
      if (magic_state)
         magic_state = 1;                       /* 1 means done if no @ */

      s[0] = required_pwd[0] = '\0';

      (void) fgets (s, 78, approved);

      for (i = 0; s[i]; i++)
         if (s[i] == 0x09)
            s[i] = ' ';
         else if (s[i] < ' ')
            s[i] = '\0';

      if (!s[0] || s[0] == ';')
         continue;

      /*--------------------------------------------------------------*/
      /* Check for transaction-level password                         */
      /*--------------------------------------------------------------*/
      for (i = 0; s[i]; i++)
         {
         if (s[i] == ' ')
            {
            s[i] = '\0';
            if (s[i + 1] == '!')
               {
               (void) strncpy (required_pwd, s + i + 2, 8);
               if (strlen (required_pwd) > 6)
                  required_pwd[6] = '\0';

               after_pwd = skip_blanks (s + i + 1);
               while (*after_pwd && (!isspace (*after_pwd)))
                  ++after_pwd;

               if (*after_pwd)
                  ++after_pwd;

               for (i = 0; required_pwd[i]; i++)
                  if (required_pwd[i] <= ' ')
                     required_pwd[i] = '\0';

               break;
               }
            else
               {
               after_pwd = skip_blanks (s + i + 1);
               break;
               }
            }
         else if (s[i] < ' ')
            s[i] = '\0';
         }

      if (!s[0])
         continue;

      if (strchr ("@+$",s[0]) != NULL)
         {
         /* Magic name or function */
         if ((s[0] != '>') && stricmp (&s[1], request))
            continue;

         /* Name matches, check password */
         if (!(check_password ()))
            {
            failure_reason = 3;      /* Password doesn't match */
            continue;                /* Go on                  */
            }
         
         mfunc = 0;

         if (s[0] == '$')
            {
            (void) sprintf (s2, after_pwd, remote_addr.Net,
                              remote_addr.Node, remote_addr.Point);
            mfunc = 1;
            }

         if (s[0] == '+')
            {
            (void) sprintf (s, " %d %d %d %d", remote_addr.Zone, remote_addr.Net,
                              remote_addr.Node, remote_addr.Point);
            (void) strcpy (s2, s1);
            (void) strcat (s2, s);
            mfunc = 2;
            }

         if (mfunc)
            {
            run_prog (s2);
            goto check_HLO;
            }
         
         if (s[0] == '@')
            {
            (void) strcpy (s, after_pwd);
            magic_state = 2;                    /* Will be reset up above */
            }
         }

      /*
       * We're past the magic stuff here. So check for whether this is
       * a new iteration of a magic loop that somehow didn't catch.
       * If not, then check out the filespec we have on this line.
       */

      if (magic_state == 1)
         goto check_HLO;

      j = 1;

      if (Netmail_Session == 2)
         SENDBYTE (NUL);

      if (!dfind (&dta, s, 0))
         {
         do
            {
            if (!(--j))                         /* If we've looped a bit  */
               {
               i = freq_abort (0L, calltime);
               if (i)                           /* See if we want to quit */
                  {
                  if (i == 1)                   /* Yes, why? Carrier?     */
                     goto finished;             /* uh-huh -- get out fast */
                  else
                     {
                     failure_reason = i;        /* Not carrier, get reason*/
                     goto make_RSP;             /* Make a .RSP file       */
                     }
                  }
               j = 10;                          /* No, reset bounds       */
               }

            if (!magic_state)                   /* If not "magic",        */
               {
               if (prep_match (dta.name, our_wildcard) < 0)
                  continue;

               if (match (our_wildcard, their_wildcard))
                  continue;

               /* File names match, check password now */

               if (!check_password ())
                  {
                  failure_reason = 3;      /* Password doesn't match */
                  continue;                /* Go on                  */
                  }
               }

            /* Good password, get full path with wildcard from OKFILE */

            (void) strcpy (s2, s);

            /* Subtract the wild card file name, keep path */

            if ((p = strrchr (s2, '\\')) != NULL)
                *++p = '\0';
            else s2[0] = '\0';

            /* Then add in the exact name found */

            (void) strcat (s2, dta.name);

            /* Got full filename, now do file update validation */

            if (updtype && !stat (s2, &st))
               {
               if ((updtype == '+' && (st.st_atime <= updreq))
               ||  (updtype == '-' && (st.st_atime >= updreq)))
                  {
                  failure_reason = 2;      /* No update available    */
                  continue;                /* Go on                  */
                  }
               }

            i = freq_abort (dta.size, calltime); /* Check file size  */
            if (i)                         /* See if we want to quit */
               {
               if (i == 1)                 /* Yes, why? Carrier?     */
                  goto finished;           /* uh-huh -- get out fast */
               else
                  {
                  failure_reason = i;      /* Not carrier, get reason*/
                  goto make_RSP;           /* Make a .RSP file       */
                  }
               }

            /* Everything is OK, send the file if we can */
            CLEAR_INBOUND ();
            if ((*callback) (s2))
               {
               ++nfiles;
               freq_accum.bytes += dta.size;
               freq_accum.CumTime += xfer_seconds;
               ++freq_accum.files;
               }
            j = 1;                               /* Force abort test */
            }
         while (!dfind (&dta, s, 1));
         }                                       /* if dfind */

      else status_line (MSG_TXT(M_OKFILE_ERR), s);  /* if not dfind */

      s[0] = '\0';
      }                                          /* while not eof(approved) */

   if (saved_nfiles != nfiles)
      failure_reason = 9;

make_RSP:

   s[0] = '\0';                                  /* Initialize string     */
   if ((CURRENT.rq_Template != NULL) && (dexists(CURRENT.rq_Template )))
      {
      Make_Response(s1, failure_reason);         /* Build a response      */
      (void) strcpy(s, s1);                             /* Copy it for xmission  */
      }

   if ((!s[0]) && (failure_reason > 3))          /* if no .RSP file,      */
      goto finished;                             /* give it up            */

   /*--------------------------------------------------------------------*/
   /* File requested not found, send the system ABOUT file.              */
   /*--------------------------------------------------------------------*/
avail:

   if (!s[0])
      {
      if (CURRENT.rq_About)
         (void) strcpy (s, CURRENT.rq_About);
      else
         {
         if (tried_about)
            {
            sptr = MSG_TXT(M_NO_ABOUT);
            goto err;
            }
         else
            {
            ++tried_about;
            (void) strcpy (s1, request);
            failure_reason = 1;        /* Didn't find what we wanted */
            goto make_RSP;             /* Make a .RSP file       */
            }
         }
      }

   CLEAR_INBOUND ();
   if ((*callback) (s))
         ++nfiles;

   goto finished;

   /*--------------------------------------------------------------------*/
   /* See if we generated a .QLO file somehow, if so send listed files   */
   /*--------------------------------------------------------------------*/
check_HLO:

   CLEAR_INBOUND ();
   (void) do_FLOfile ("Q", callback);

   /*--------------------------------------------------------------------*/
   /* Maybe the magic request made a conventional .FLO file, try that too*/
   /*--------------------------------------------------------------------*/

   *ext_flags = 'F';
   (void) do_FLOfile (ext_flags, callback);
   goto finished;

   /*--------------------------------------------------------------------*/
   /* Error return                                                       */
   /*--------------------------------------------------------------------*/
err:
   if (sptr)
      status_line ("!%s %s %s: %s",
         (updreq != 0L) ? MSG_TXT(M_UPDATE) : MSG_TXT(M_FILE),
         MSG_TXT(M_REQUEST), &(MSG_TXT(M_ERROR)[1]), sptr);

finished:
   if (approved)
      (void) fclose (approved);

   return (nfiles);
}

void run_prog (char *s)
{
   struct baud_str saved_baud;

   status_line ("%s '%s'", MSG_TXT(M_EXECUTING), s);
   if (fullscreen && un_attended)
      {
      screen_clear ();
      }
   scr_printf (&(MSG_TXT(M_EXECUTING)[1]));
   scr_printf (s);
   scr_printf ("\r\n");
   vfossil_cursor (1);
   saved_baud = cur_baud;
   b_spawn (s);
   vfossil_cursor (0);
   if (fullscreen && un_attended)
      {
      screen_clear ();
      sb_dirty ();
      opening_banner ();
      mailer_banner ();
      }

   (void) set_baud (saved_baud.rate_value, 0); /* Restore baud rate */
}

int check_password ()
{
   if (required_pwd[0])
      {
      (void) fancy_str (required_pwd);
      if ((stricmp (required_pwd, their_pwd)) &&
          (stricmp (required_pwd, remote_password)))
         {
         status_line (MSG_TXT(M_FREQ_PW_ERR),
                               required_pwd,
                               their_pwd,
                               remote_password
                     );

         return (0);
         }
      }
   return (1);
}

int freq_abort (long file_size, int (*calltime)(long))
{
   int w_event;
   int xfer_time;

   if (!CARRIER)
      {
      status_line (MSG_TXT(M_CARRIER_REQUEST_ERR));
      return (1);
      }

   if ((CURRENT.rq_Limit != 0) 
   &&  ((freq_accum.files + 1) >= CURRENT.rq_Limit))
      {
      status_line (MSG_TXT(M_FREQ_LIMIT));
      return (4);
      }

   if ((CURRENT.byte_Limit != 0L)
   &&  ((freq_accum.bytes + file_size) > CURRENT.byte_Limit))
      {
      status_line (MSG_TXT(M_BYTE_LIMIT));
      return (6);
      }

   /* Get file time in seconds for comparison with quota */

   if (file_size != 0L)
      xfer_time = calltime (file_size);
   else
      xfer_time = 0;

   xfer_seconds = xfer_time;

   if ((CURRENT.time_Limit != 0)
   &&  (((long)time(NULL) + xfer_time - freq_accum.time) > CURRENT.time_Limit))
      {
      status_line (MSG_TXT(M_TIME_LIMIT));
      return (7);
      }

   xfer_time /= 60;               /* We want minutes for event calculations */

   w_event = what_event (xfer_time);
   if ((w_event >= 0) && (w_event != cur_event))
      {
      if (e_ptrs[w_event].behavior & MAT_NOREQ)
         {
         status_line (MSG_TXT(M_EVENT_OVERRUN));
         return (5);
         }
      }
   else if (w_event == -2)
         {
         status_line (MSG_TXT(M_EVENT_OVERRUN));
         return (5);
         }

   return (0);
}

int what_event (int delta_time)
{
   time_t long_time;
   struct tm *tm;
   
   int now;
   int tomorrow = 0;
   int our_time;
   int w_event;
   int save_time;

   /* Get the current time into a structure */

   (void) time (&long_time);
   tm = localtime (&long_time);

   /* Calculate minutes since midnight      */

   now = tm->tm_hour * 60 + tm->tm_min;

   /* Then number of minutes past midnight at end of delta_time */

   save_time = our_time = delta_time + now;

   /* Is the end in Tomorrow? (Sounds like a soap opera, eh?) */

   if (1440 < our_time)
      {
      tomorrow = 1;                     /* Remember that we've wrapped        */
      our_time = 1439;                  /* Set event end = 23:59 for now      */
      }
 
   /* Test "today," and if we find an event, get the hell out                 */

   w_event = what_event_sub (tm->tm_mon, tm->tm_wday, tm->tm_mday,
                              now, our_time, 0);

   if (w_event != -1)
      return w_event;

   /*
    * If we need to test tomorrow, figure out all the salient info
    * on what tomorrow is, and test that.
    */

   if (tomorrow)
      {
      long_time += 86400L;              /* Any time tomorrow will do.         */
      tm = localtime (&long_time);
      w_event = what_event_sub (tm->tm_mon, tm->tm_wday, tm->tm_mday,
                                 0, (save_time - 1440), 1);
      }

   return w_event;
}

int what_event_sub (int cur_mon, int cur_day, int cur_mday,
                    int now, int our_time, int tomorrow)
{
   int i;

   cur_day = 1 << cur_day;             /* Make cur_day a bit mask             */

   for (i = 0; i < num_events; i++ )
      {

      /* If this is an event we need to think about (i.e., it started
       * before the end of the transfer, and ends after "now") ...
       */

      if ((our_time >= e_ptrs[i].minute)
      &&  (now <= ( e_ptrs[i].minute + e_ptrs[i].length)))
         {
         /* And if it's on a day we care about ... */

         if ((cur_day & e_ptrs[i].days)
         &&  ((!e_ptrs[i].day) || (e_ptrs[i].day == (char)cur_mday))
         &&  ((!e_ptrs[i].month) || (e_ptrs[i].month == (char)cur_mon)))
            {
            /* Don't do events that have been exited already unless
             * the tomorrow flag is set (in which case, no events have
             * occurred, have they?)
             */

            if (!tomorrow
            &&  (e_ptrs[i].last_ran == (char) cur_mday)
            &&  (e_ptrs[i].behavior & MAT_SKIP))
               continue;

            /* If the event we are considering does not allow requests,
             * then this is a bugger we are looking for. Return its index.
             */

            if ((e_ptrs[i].behavior & MAT_NOREQ))
               return i;
            }                          /* end of if ((cur_day & ... ))        */
         }                             /* end of if ( our_time >= ... )       */
      }                                /* end of for ( ... )                  */
   return -1;
}