/*
 *  MBMAIL.C - 2/04/89 The message file stuff, except for forwarding.
 */

#include "mb.h"

#define btmax 112   /* Max text in beacon line */

char  orgmsg[6], orgbbs[7], orgtime[5], orgdate[7];

char  *mbfile, *mbbfile, *msgdir, *bidfile;

#ifdef MCH_AMIGA
extern char passchr,vhfstream,hfstream;
extern char tmpstr[];
extern short all_number;
#endif

char  *ufwd;  /* Calls with unread msgs, for BT */
char  *bfwd;  /* Calls with unread msgs, for forwarding */

short ufwdm, ufwdn, bfwdm, bfwdn;
short tstaleb, tstalen, tstaleu;
int bidok, hidok, hasbid, needbid, holdit;

char  *mm[num_mm];
char  wpcall[ln_call];
XBBS  *xbbs;
HOLD  *hold;

MAIL_HDR  *mfhs;
MSG_HDR   *tmmhs;

FILE *bfl;

int mfl, mflb;

msgfile(p, n)
char *p;
word n;
{
  sprintf(p, "%s%u", msgdir, n);
}

/*
 *  Open file to hold BIDs
 */

opnbid()
{
  if ((bfl = fopen(bidfile, "a+")) is NULL) { nofile(bidfile); exit(1); }
  fclose(bfl);
}

/*
 *  Write message header.
 */

wt_mmhs()
{
  if (port->mmhs->rn) write_rec (mfl, port->mmhs->rn, (char *)port->mmhs);
}


/*
 *  Get message title.
 */

get_title()
{
  while(!getdat());
  remnl(port->line);
  strncpy(port->mmhs->title, port->line, mhtitl);
}

/*
 *  Parse the [@ bbs] and [< from] fields of a command.
 */

atfrom(i)
int i;
{
  if (*port->fld[i] is '<') pcall(port->mmhs->from, port->fld[i + 1]);
  else if (*port->fld[i] is '@')
  {
    pcall(port->mmhs->bbs, port->fld[i +1]);
    if ((strchr(port->fld[i + 1], '.')) is NULL) return;
    port->mmhs->ext = 2;
    fill (port->mmhs->call, '\0', 125);
    strncpy(port->mmhs->call[0], port->fld[i + 1], 64);
  }
}

/*
 *  Parse the fields of a send command.
 */

pfld()
{
#ifdef MCH_AMIGA
   char filename[30];
   FILE *fpi;
#endif
  hasbid = false;

  port->mmhs->type = port->opt2;
  pcall(port->mmhs->to, port->fld[1]);
  strncpy(port->mmhs->from, port->user->call, ln_call);
  fill(port->mmhs->bbs, ' ', ln_call);
  fill(port->mmhs->bid, ' ', ln_bid);
  port->mmhs->ext = 0;

  if (port->flds > 3) atfrom(port->flds - 2);
  if (port->flds > 5) atfrom(port->flds - 4);
#ifdef MCH_AMIGA
   /* routine added to mbuser to see if we have the user 'to' in our
      user record file. If so, then use our @bbs rather than the one
      in the message. We presume that if that user's name is on file
      here, then if we also have an @bbs for him then it is likely to
      be just as accurate, or better, than the one in the message.
   */
   replace_bbs(&port->mmhs->to,&port->mmhs->bbs);
#endif

  if (port->flds > 2)
  if(*port->fld[port->flds-1] is '$')
  {
    if (*(port->fld[port->flds - 1] + 1) isnt NULL)
    {
    strncpy(port->mmhs->bid, port->fld[port->flds - 1] + 1, ln_bid);
    hasbid = true;
    }
    if (port->flds > 4) atfrom(port->flds - 3);
    if (port->flds > 6) atfrom(port->flds - 5);
  }

  initmsg(true);  
  if(!bidok) {
    if (port->user->options & (u_bbs | u_expert)) outstr("Sj:\n");
    else prtx(mm[0]);
    get_title();
    return;
  }
  else {  
#ifdef MCH_AMIGA
      /* Check for unwanted bulletins.
         If this is a bulletin or it has a BID then see if we
         want this bulletin.
         If the @bbs has a corresponding
         msgs/bbs.inf then accept it, otherwise No tnx
      */
      if((port->mmhs->type == 'B') || hasbid) {
         if((port->mmhs->bbs[0] != ' ') && !iscall(port->mmhs->bbs)) {
            strcpy(filename,msgdir);
            unbl(tmp->scr,port->mmhs->bbs,ln_call);
            strcat(filename,tmp->scr);
            strcat(filename,".dis");
            if((fpi = fopen(filename,"r")) == NULL) {
               outstr("No - Don't want it tnx\n");
               needbid = 0;
               return;
            }
            fclose(fpi);
         }
      }
#endif
    if (hasbid) {
      checkbid();
      if (!needbid) {
        outstr("No - Already have it\n");
        sprintf(port->mmhs->title, "<<%6.6s - %s>>",
                  port->user->call, port->fld[port->flds-1]);
        wt_mmhs();
        return;
      }
      prtx("OK $C\n");
      get_title();
      return;
    }
    else {
      prtx("OK $C\n");
      get_title();
      needbid = true;     /* will check and may assign one later */
      return;
    }
  }
}

/*
 *  Create the header line.
 */

makehdr()
{
  register char st;

  st = 'N';
  if (port->mmhs->stat & m_stale) st = 'O';
  if (port->mmhs->stat & m_fwd)   st = 'F';
  if (port->mmhs->stat & m_read)  st = 'Y';
  if (port->mmhs->stat & m_hold)  st = 'H';
  if (port->mmhs->stat & m_kill)  st = 'K';
  if (port->mmhs->stat & m_busy)  st = 'B';
  sprintf(tmp->scr,"%5u %c%c %5u %6.6s %6.6s %6.6s %4.4s/%4.4s %s\n",
    port->mmhs->number, port->mmhs->type, st, port->mmhs->size,
    port->mmhs->to, port->mmhs->from, port->mmhs->bbs,
    port->mmhs->date + 2, port->mmhs->time, port->mmhs->title);
}

/*
 * Write mail.dat header into text file
 */

makehdr2()
{
  register char st, c;
  int msfl;
  char flags[mmesn];
  fill (flags, ' ',mmesn);
  fill (tmp->scr, '\0', 256);
  tmp->scr[254] = '\r'; tmp->scr[255] = '\n';
  if (port->mmhs->ext is 1)
  {
    for (c=0; c < port->mmhs->count; c++)
    flags[c] = port->mmhs->flag[c] + '0';
  }
  st = 'N';
  if (port->mmhs->stat & m_stale) st = 'O';
  if (port->mmhs->stat & m_fwd)   st = 'F';
  if (port->mmhs->stat & m_hold)  st = 'H';
  if (port->mmhs->stat & m_read)  st = 'Y';
  if (port->mmhs->stat & m_kill)  st = 'K';
  sprintf(tmp->scr,
           "%5u %c%c %5u %6.6s %6.6s %6.6s %6.6s %4.4s %-12.12s %5u\r\n",
    port->mmhs->number, port->mmhs->type, st, port->mmhs->size,
    port->mmhs->to, port->mmhs->from, port->mmhs->bbs,
    port->mmhs->date, port->mmhs->time, port->mmhs->bid, port->mmhs->read);
  if (port->mmhs->ext is 2)
       sprintf(tmp->scr+68, "%c %s\r\n%s\r\n", port->mmhs->ext +'0',
       port->mmhs->call, port->mmhs->title);
  else sprintf(tmp->scr+68, "%c %16.16s\r\n%s\r\n", port->mmhs->ext +'0',
       flags, port->mmhs->title);

  msgfile(port->cmd, port->mmhs->number);
  if ((msfl = open(port->cmd, O_WRONLY | O_BINARY)) < 0) return;
  write_rec(msfl, 0, (char *)tmp->scr);
  close(msfl);
}

/*
 *  Print a message header.
 */

prthdr(tit)
int tit;
{
  char *a, *b;

  register short i;
  register short docc;

  if (tit) prtx(mm[3]);
  makehdr();
  outstr(tmp->scr);
  if((port->opt1 is'L')and((*port->fld[1]isnt';')and(*port->fld[2]isnt';')))
     return;
  if (port->mmhs->ext is 2)
  {
    outstr("    EF:");
    outstr(port->mmhs->call);
    outchar('\n');
  }
  if (port->mmhs->ext is 1) if (port->mode & ops)
  {
    docc = false;
    for (i = 0; i < port->mmhs->count; i++) if (port->mmhs->flag[i])
     docc = true;

    if (docc)
    {
      outstr("    cc:");
      for (i = 0; i < port->mmhs->count; i++)
      {
        if (port->mmhs->flag[i]) outchar(' '); else outstr(" *");
        outnb(port->mmhs->call[i], ln_call);
      }
      outchar('\n');
    }
  }
  if (port->mode & ops) if (*port->mmhs->bid isnt ' ')
    {
    outstr("   BID: ");
    a = port->line;
    b = port->mmhs->bid;
    unbl(a, b, ln_bid);
    outstr(port->line);  outchar('\n');
  }
}
 
/*
 *  Read the message, look for the last bbs header.
 *  Set the @ field of the current message to the "at bbs" in that header.
 */

findat()
{
  register short i;

  fill(port->mmhs->bbs, ' ', ln_call);
  msgfile(port->line, port->mmhs->number);
  if ((port->fl = fopen(port->line, "r")) is NULL) { nofile(port->line); return; }
  fseek(port->fl, (long)RECSIZE, 0);
  while(fgets(port->line, linelen, port->fl) isnt NULL)
  {
    if (!parsehd(port->line)) break;
    pcall(port->mmhs->bbs, orgbbs);
  }
  fclose (port->fl);
}

/*
 *  Parse a message header line.
 */

parsehd(in)
char *in;
{
  register char *p;
  register int count, n;

  if (!ishead(in)) return false;

  if ((p = strchr(in, 'R')) is NULL) return false;
  {
    p++;

    if (*p is ':') p++;
    if (*p is ' ') p++;
    count = 6;
    n = 0;
    while (*p and count--)
    {
      if (!isalnum(*p)) break;
      orgdate[n++] = *p++;
    }
    orgdate[n] = '\0';

    if (*p is '/') p++;

    count = 4;
    n = 0;
    while (*p and count--)
    {
      if (!isalnum(*p)) break;
      orgtime[n++] = *p++;
    }
    orgtime[n] = '\0';
  }

  if ((p = strchr(in, '@')) is NULL) return false;
  {
    p++;

/* skip colon or a space in @:call  OR @ call */

    if (*p is ':') p++;
    if (*p is ' ') p++;
    count = ln_call;
    n = 0;
    while (*p and count--)
    {
      if (!isalnum(*p)) break;
      toupper(*p);
      orgbbs[n++] = *p++;
    }
    orgbbs[n] = '\0';
  }

  if ((p = strchr(in, '#')) isnt NULL) 
  {
    p++;

    if (*p is ':') p++;
    if (*p is ' ') p++;
    count = 5;
    n = 0;
    while (*p and count--)
    {
      if (!isalnum(*p)) break;
      orgmsg[n++] = *p++;
    }
    orgmsg[n] = '\0';
  }
  else
  {
    p = strchr(in, '@');
    p--; count = 5;
    while(count-- and *p isnt ' ') p--;
    p++;
    if (*p is '<') p++;
    count = 5;
    n = 0;
    while (*p and count--)
    {
      if (*p is '@') break;
      orgmsg[n++] = *p++;
    }
    orgmsg[n] = '\0';
  }
  return true;
}


/*
 *  Find a message given it's number.
 */

findmsg(nr)
word nr;
{
  register word i;

  for (i = mfhs->last; i; i--)
  {
    read_rec(mfl, i, (char *)port->mmhs);
    if (port->mmhs->number is nr) return true;
    if (port->mmhs->number < nr)  return false;
  }
  return false;
}

/*
 *  Create the distribution list, if required.
 */

dodis()
{
  register FILE *dfl;
  if (port->mmhs->ext is 2) return;
  port->mmhs->ext = 0;
  if (*port->mmhs->bbs is ' ') return;

  unbl(tmp->scr, port->mmhs->bbs, ln_call);

  strcpy(port->line, msgdir);
  strcat(port->line, tmp->scr);
  strcat(port->line, ".DIS");

  if ((dfl = fopen(port->line, "r")) is NULL) return;

  port->mmhs->ext = 1 ;

  for (port->mmhs->count = 0;
    (port->mmhs->count < mmesn) and (fgets(tmp->scr, scrmax, dfl) isnt NULL);
    port->mmhs->count++)
  {
    strupr(tmp->scr);
    pcall(port->mmhs->call[port->mmhs->count], tmp->scr);
    if(matchn(port->mmhs->call[port->mmhs->count], port->user->call, ln_call))
         port->mmhs->flag[port->mmhs->count] = false;
    else port->mmhs->flag[port->mmhs->count] = true;
  }
  fclose(dfl);
}

/*
 *  Do we hold this message?
 */

ishold(c)
char *c;
{
  register HOLD *hp;

  for (hp = hold; hp isnt NULL; hp = hp->next)
  if (matchn(hp->call, c, ln_call)) return true;

  return false;
}

/*
 *  Create message utilities - initmsg and cremsg.
 *  Used by "send message", "make message from file",
 *  "kill msg" (service messages), "copy message".
 */

initmsg(h)
int h;
{
  if (s_flag & s_dv) begin_lock();
  read_rec(mfl, 0, (char *)mfhs);

  port->mmhs->rn = mfhs->next++;
  port->mmhs->number = mfhs->next_msg++;
  port->mmhs->stat = m_busy;
  strncpy(port->mmhs->date, l_date, ln_date);
  strncpy(port->mmhs->time, l_time, ln_time);
  port->mmhs->read = 0;
  port->mmhs->size = 0;
  if(port->mmhs->ext isnt 2) fill(port->mmhs->call, '\0', 125);
  if(h)sprintf(port->mmhs->title, "<<%6.6s -- disconnected>>"
                                ,port->user->call);


/*
 *  Clean up the file header.
 */

  if (!mfhs->first) mfhs->first = port->mmhs->rn;
  mfhs->last = port->mmhs->rn;
  mfhs->count++;
  write_rec(mfl, 0, (char *)mfhs);
  wt_mmhs();
  mfhs->next_msg--;
  if (s_flag & s_dv) end_lock();
#ifdef MCH_AMIGA
   titles();
#endif
}

cremsg()
{
  char *a, *b;

  register short i;
  register XBBS *xp;
  register FILE *out;

  port->mmhs->stat   = 0;
  strncpy(port->mmhs->date, l_date, ln_date);
  strncpy(port->mmhs->time, l_time, ln_time);
  port->mmhs->read = 0;
  port->mmhs->size = filesize;

  for (xp = xbbs; xp isnt NULL; xp = xp->next)
  if (wcm(xp->from, port->mmhs->bbs))
  {
    strncpy(port->mmhs->bbs, xp->to, ln_call);
    break;
  }

  if (ishold(port->mmhs->to))   port->mmhs->stat = m_hold;
  if (ishold(port->mmhs->from)) port->mmhs->stat = m_hold;
  if (ishold(port->mmhs->bbs))  port->mmhs->stat = m_hold;

  if (holdit) port->mmhs->stat = m_hold;

  if (*port->mmhs->bid isnt ' ') {
    checkbid();
    if (hasbid) if (!holdit) if (needbid) {
      if ((out = fopen(bidfile,"a")) is NULL) {
        port->msg = mcant; return;
      }

      a = port->cmd;
      b = port->mmhs->bid;
      unbl(a, b, ln_bid);

      sprintf(port->line, "%4.4s%s\n", port->mmhs->date + 2, port->cmd);
      if (s_flag & s_dv) begin_lock();
      fputs(port->line, out);
      if (s_flag & s_dv) end_lock();
      fclose(out);
    }
  }
  dodis();

  if ((port->mmhs->ext is 1) and (*port->mmhs->bid is ' '))
  port->mmhs->stat = m_hold;

  wt_mmhs();
  makehdr2();

  if (*port->fld[port->flds-1] isnt '$') port->fld[port->flds-1] = nullstr;

  sprintf(port->line, "%u %-.6s@%-.6s %s %s", port->mmhs->number,
   port->mmhs->to, port->mmhs->bbs, port->fld[port->flds-1], port->mmhs->title);

  log('M', port->opt1, port->opt2, port->line);
  mfhs->next_msg++;
#ifdef MCH_AMIGA
   titles();
#endif
}

/*
 *  Set up the beacon line.
 */

setfwd()
{
  register char *cp, *lp;
  register short i;
#ifdef MCH_AMIGA
   all_number = 0;
#endif

  bldfwd();
  if (ufwdm > 1)
  {
    strcpy(port->line, "BT Mail for:");
    cp = ufwd;
    lp = port->line + strlen(port->line);
    while ((cp < ufwd + ln_call * ufwdn) and (lp < port->line + btmax))
    {
      *lp++ = ' ';
#ifdef MCH_AMIGA
      if(all_number && matchn(cp,"ALL        ",ln_call)) {
         sprintf(tmpstr,"ALL(%d)",all_number);
         strcpy(lp,tmpstr);
         lp += strlen(tmpstr);
         cp += ln_call;
      }
      else   /* BE CAREFUL HERE ... NEXT line MUST be ONE STATEMENT */
#endif
      for (i = 0; i < ln_call; i++, cp++) if (*cp isnt ' ') *lp++ = *cp;
    }
    *lp++ = '\n';
    *lp = '\0';
    alltnc(port->line);
  }
}

/*
 *  General permission check: sysop or TO or FROM.
 */

permit()
{
  if (port->mmhs->stat & (m_kill | m_busy)) return false;
  if (matchn(port->user->call, port->mmhs->to,   ln_call)) return true;
  if (matchn(port->user->call, port->mmhs->from, ln_call)) return true;
  return false;
}

/*
 *  Return true if user has kill permission for current message.
 */

kpermit()
{
  if (port->mmhs->stat & m_kill) return false;
  if (port->mode & ops) return true;
  if (permit()) return true;
  return ((port->mmhs->type is 'T') and (port->opt2 is 'T'));
}

/*
 *  Return true if user has list permission for current message.
 */

lpermit()
{
  if (port->user->options & u_sysop) return true;
  if (permit()) return true;
  return ((port->mmhs->type isnt 'P')
      and !(port->mmhs->stat & (m_fwd | m_kill | m_busy)));
}

/*
 *  Return true if user has read permission for current message.
 */

rpermit()
{
  if ( port->mode & ops) return true;
  if (permit()) return true;
  return (port->mmhs->type isnt 'P');
}

/*
 *  Clean the mail file.
 *  Force drain of buffers, update of directory item.
 */

clnmsg()
{
  close(mfl);
  mfl = open(mbfile, O_RDWR | O_BINARY);
}

/*
 *  Close the mail file.
 */

clsmsg()
{
  close (mfl);
}

/*
 *  Open the mail file.
 */

opnmsg()
{
  register char *tp;

/*
 *  Allocate space for the mail file records.
 */

  mfhs  =  (MAIL_HDR *) malloc(sizeof(MAIL_HDR));
  tmmhs =  (MSG_HDR *)  malloc(sizeof(MSG_HDR));

/*
 *  Allocate the "who has mail" lists.
 */

  ufwd = (char *) malloc (ln_call * ufwdm);
  bfwd = (char *) malloc (ln_call * bfwdm);
  if (bfwd is NULL) errall();

/*
 *  Open the file. If it does not exist, make one.
 */

  if ((mfl = open(mbfile, O_RDWR | O_BINARY)) < 0)
  {
    if ((mfl = open(mbfile, O_CREAT | O_RDWR | O_BINARY, pmode)) < 0)
      { nofile(mbfile); exit(1); }
    inithdr();
    mfhs->next_msg = 1;
#ifdef MCH_AMIGA
   titles();
#endif
    write_rec(mfl, 0, (char *)mfhs);
  }

/*
 *  Read the mail file header.
 */

  read_rec(mfl, 0, (char *)mfhs);

/*
 *  If wrong version mail file, quit now.
 */

  if (mfhs->version isnt mb_version)
  {
#ifndef MCH_AMIGA
    printf("Expected version %d, got version %d\n",
      mb_version, mfhs->version);
#else
    sprintf(tmpstr,"Expected version %d, got version %d\n",
      mb_version, mfhs->version);
    ttputs(tmpstr);
#endif
    nofile(mbfile);
    exit(1);
  }

}

/*
 *  Initialize the mail file header.
 */

inithdr()
{
  mfhs->next    = 1;
  mfhs->first   = 0;
  mfhs->last    = 0;
  mfhs->version = mb_version;
  mfhs->free    = 0;
  mfhs->count   = 0;
  mfhs->unt_msg = 1;

#ifdef MCH_AMIGA
   titles();
#endif
  curtim();
  strncpy(mfhs->date, l_date, ln_date);
  strncpy(mfhs->time, l_time, ln_time);
  fill(mfhs->unu, '\0', mfhsunu);
}

/*
 *  Open mail file and read first record.
 */

readmsg()
{
  mfl = open(mbfile, O_RDWR | O_BINARY);
  read_rec(mfl, 0, (char *)mfhs);
}

/*
 *  Does this station want us to send BIDs ?
 */

isbid()
{
  register char *f;

/*  Is this a sid? It must have a '-' and end with a ']'  */

  if ((port->line[strlen(port->line) - 2] isnt ']' ) or
     ((f =  strrchr(port->line, '-')) is NULL))
     return;

/*  Look for specal characters in the last field.  */

  f++;
  for (; *f isnt ']';f++)
  switch(*f)
  {
     case 'H' : hidok = true; break;
     case '$' : if(*(f+1) is ']') bidok = true; break;
  }
}

/*
 *  Check to see whether we already have this BID.
 */

checkbid()
{
  FILE *out;
  char *a, *b;

  if (*port->mmhs->bid is ' ') { needbid = true; return; }
  if ((out = fopen(bidfile, "r")) is NULL) {
    nofile(bidfile); return; 
  }
  while (fgets(port->line, linelen, out) isnt NULL) {

    /* the first 4 characters are the date */

    strcpy(port->cmd, port->line + 4);
    a = port->cmd;
    remnl(a);

    a = port->line;
    b = port->mmhs->bid;
    unbl(a, b, ln_bid);

    if (match(port->cmd, port->line)) {
      needbid = false; 
      fclose(out);
      return;
    }
  }
  needbid = true;
  fclose(out);
}

