
/*
 *  MBUSER.C - 10/30/88
 */

#include "mb.h"

#define uf_inc 100    /* Headroom in user file */

#ifdef MCH_AMIGA
extern char tmpstr[];
#endif

char *usfile, *usbfile;
USER_HDR *ufhs;
USER     *tuser;
char   *um[num_um];
char   tcall[ln_call];
short  maxusers;
static char *users;

int ufl;

/*
 *  Clean the user file.
 *  Force drain of buffers, update of directory items.
 */

clnuser()
{
  close(ufl);
  ufl = open(usfile, O_RDWR | O_BINARY);
}

/*
 *  Close the user file.
 */

clsusr()
{
  close (ufl);
}

/*
 *  Open the user file.
 *  Allocate space for the user file records.
 */

opnusr()
{
  ufhs = (USER_HDR *) malloc(sizeof(USER_HDR));

  tuser = (USER *) malloc(sizeof(USER));
  tuser->rn = 0;

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

  ufl = open(usfile, O_RDWR | O_BINARY);
  if (ufl < 0)
  {
    ufl = open(usfile, O_CREAT | O_RDWR | O_BINARY, pmode);
    if (ufl < 0) { nofile(usfile); exit(1); }
    inuhdr();
    write_rec(ufl, 0, (char *)ufhs);
  }

/*
 *  Read the user calls.
 */

  read_rec(ufl, 0, (char *)ufhs);

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

  maxusers = ufhs->count + uf_inc;

  users = (char *) malloc(ln_call * maxusers);
  if (users is NULL) errall();

  rdusers();
}

/*
 *  Open the user file and read record zero
 */

readusr()
{
  short prev_cnt;

  prev_cnt = ufhs->count;
  ufl = open(usfile, O_RDWR | O_BINARY);
  read_rec (ufl, 0, (char*)ufhs);
  if (ufhs->count isnt prev_cnt) rdusers();
}

/*
 *  Read each user record, build list of calls.
 */

rdusers()
{
  register int i;
  register char *up;

#ifndef MCH_AMIGA
  printf("%d users in %s\n", ufhs->count, usfile);
#else
  sprintf(tmpstr,"%d users in %s\n", ufhs->count, usfile);
  ttputs(tmpstr);
#endif

  for (i = 1, up = users; i <= ufhs->count; i++, up += ln_call)
  {
    read_rec(ufl, i, (char *)tuser);
    strncpy(up, tuser->call, ln_call);
    if (!tuser->zip) fill(tuser->zip, ' ', ln_zip);
  }

}

/*
 *  initialize the user file header.
 */

inuhdr()
{
  curtim();
  ufhs->count = 0;
  ufhs->version = us_version;
  strncpy(ufhs->date, l_date, ln_date);
  strncpy(ufhs->time, l_time, ln_time);
  ufhs->lmnr = mfhs->next_msg - 1;
  fill (ufhs->unu, '\0', ufhsunu);
}

/*
 *  Do we know this user?
 */

finduser(call)
char *call;
{
  register int i;
  register char *up;

  for (i = 1, up = users; i <= ufhs->count; i++, up += ln_call)
  if (matchn(call, up, ln_call)) return i;

  return 0;
}

/*
 *  Read in a user record.
 */

rduser(call, buf)
char *call;
USER *buf;
{
  register int i;

  if (i = finduser(call))
  {
    read_rec(ufl, i, (char *)buf);
    buf->rn = i;
    return i;
  }

/*
 *  There wasn't one, make one.
 */

  curtim();

  buf->rn = 0;  /* Mark as non current record */

  strncpy(buf->call, call,   ln_call);
  strncpy(buf->date, l_date, ln_date);
  strncpy(buf->time, l_time, ln_time);

  buf->msg_number = 0;
  buf->ssid       = 0;
  buf->state      = 0;
  buf->options    = 0;
  buf->log_count  = 0;
  buf->port       = ' ';

  ljsf(buf->handle, um[0], ln_handle);

  fill(buf->path, ' ', pathl);
  *buf->path = '\0';

  fill(buf->home_bbs, ' ', ln_call);
  fill(buf->zip,      ' ', ln_zip);
  fill(buf->unu,        0, userunu);

  return 0;
}

/*
 *  Update the user record to the file.
 */

upduser(buf)
USER *buf;
{
  if (s_flag & s_dv) begin_lock();
  read_rec(ufl, 0, (char*)ufhs);
  if (!buf->rn)
  {
    if (ufhs->count >= maxusers)
      {
        if (s_flag & s_dv) end_lock();
        return;
      }
    strncpy (users + (ln_call * ufhs->count), buf->call, ln_call);
    buf->rn = ++ufhs->count;
  }

  curtim();
  strncpy(buf->date, l_date, ln_date);
  strncpy(buf->time, l_time, ln_time);
  if (s_flag & s_update)
    {
      buf->msg_number = mfhs->next_msg;
      s_flag clrbit s_update;
    }
  buf->log_count++;
  write_rec(ufl, buf->rn, (char *)buf);
  write_rec(ufl, 0, (char *)ufhs);
  if (s_flag & s_dv) end_lock();
}

/*
 *  Print info about a user.
 */

puser()
{
  register int i;
  register PORTS *p;

  pcall(tcall, port->fld[1]);
  if (!(i = finduser(tcall))) { port->msg = mfind; return; }

  read_rec(ufl, i, (char *)tuser);

  if ((p = findport(tuser->port)) is NULL)
    sprintf( tmp->scr, "Port %c\n", tuser->port);
  else
    sprintf(tmp->scr, "%s\n", p->name);
  if(tuser->port isnt 'L')
  {
    outnb(tuser->call, ln_call);
    outstr(" connected on ");
    outstr(tmp->scr);
  }
  outnb(tuser->call, ln_call);
  if (tuser->port is 'L')
  {
    outstr(" Linked to "); outnb(cport->user->call, ln_call);
    outstr(" Through the GateWay at "); outstr(tuser->path);
  }
  else if (!*tuser->path)
  {
    outstr(" is a direct connect from "); outnb(cport->user->call, ln_call);
  }
  else
  {
    outstr(" connected to "); outnb(cport->user->call, ln_call);
    outstr(" via "); outstr(tuser->path);
  }
  outchar('\n');

  outnb(tuser->call, ln_call);
  sprintf(tmp->scr, " Last connected on %6.6s at %4.4s\n",
    tuser->date, tuser->time);
  outstr(tmp->scr);

  sprintf(tmp->scr, "The Last message number then was %u\n", tuser->msg_number);
  outstr(tmp->scr);

  outnb(tuser->call, ln_call);
  outstr("'s name is ");
  outnb(tuser->handle, ln_handle);
  outchar('\n');

  if (tuser->state & u_home)
  {
    outnb(tuser->call, ln_call);
    outstr("'s home system is ");
    outnb(tuser->home_bbs, ln_call);
    outchar('\n');
  }

  if (tuser->state & u_zip)
  {
    outnb(tuser->call, ln_call);
    outstr("'s zip code is ");
    outnb(tuser->zip, ln_zip);
    outchar('\n');
  }
}

/*
 *  Print a user record.
 */

prtuser(p)
USER *p;
{
  fill(port->cmd, ' ', 6);
  if (p->options & u_local)   port->cmd[0] = 'L';
  if (p->options & u_bbs)     port->cmd[1] = 'B';
  if (p->options & u_expert)  port->cmd[2] = 'E';
  if (p->options & u_delete)  port->cmd[3] = 'D';
  if (p->options & u_sysop)   port->cmd[4] = 'S';
  if (p->options & u_exclude) port->cmd[5] = 'K';
  sprintf(tmp->scr,
  "%6.6s %6.6s %4.4s %4d %5u %6.6s %2.2d%c%6.6s %-12.12s %6.6s\n  Path: %s\n",
    p->call, p->date, p->time, p->log_count,
    p->msg_number, p->home_bbs, p->ssid,
    p->port, port->cmd, p->handle, p->zip, p->path);

  outstr(tmp->scr);
}

/*
 *  The "display users" command.
 */

duser()
{
  register int i;
  register short ok;

  if (port->flds is 1) pgst(um[2]); else
  {
    if ((port->fl = fopen(port->fld[1], "r")) isnt NULL)
    { fclose(port->fl); port->msg = mexst; return; }
    if ((port->fl = fopen(port->fld[1], "w")) is NULL)
    { port->msg = mcant; return; }
  }

  for (i = ufhs->count; i; i--)
  {
    read_rec(ufl, i, (char *)tuser);
    ok = false;
    switch(port->opt2)
    {
      case 'L' : ok = (tuser->options & u_local);  break;
      case 'M' : ok = (tuser->options & u_bbs);    break;
      case 'S' : ok = (tuser->options & u_sysop);  break;
      case 'U' : ok = true; break;
      case 'X' : ok = (tuser->options & u_exclude); break;
    }

    if (ok)
    {
      if (port->flds is 1)
      {
        pghd();
        prtuser(tuser);
        if (pgck() is 'Q') return;
        if (pgck() is 'Q') return;
      }
      else
      {
        prtuser(tuser);
        fprintf(port->fl, "%s", tmp->scr);
      }
    }
  }
  if (port->flds is 1) pgdn(); else fclose(port->fl);
}

/*
 *  The N, NE, NH, NZ (update my user info) commands.
 */

chguser()
{
  switch(port->opt2)
  {
    case ' ' : ljsf(port->user->handle, port->line + 2, ln_handle);
               port->user->state setbit u_name;
               break;

    case 'E' : port->user->options flipbit u_expert;
               break;

    case 'H' : pcall(port->user->home_bbs, port->fld[1]);

               if (matchn(port->user->home_bbs, cport->user->call, ln_call))
                 port->user->options setbit u_local;
               else port->user->options clrbit u_local;

               port->user->state setbit u_home;
               port->user->state clrbit u_sent;
               break;

    case 'Z' : ljsf(port->user->zip, port->fld[1], ln_zip);
               port->user->state setbit u_zip;
               port->user->state clrbit u_sent;
               break;
  }
  port->msg = mdone;
}

/*
 *  EU command with no argument.
 *  Loop through all users, ask delete for each one.
 */

edusera()
{
  register word i;

  for (i = ufhs->count; i; i--)
  {
    read_rec(ufl, i, (char *)tuser);
    if (tuser->rn isnt i)
    {
      sprintf(tmp->scr, "User record %u has record # %u\n", i, tuser->rn);
      tuser->rn = i;
    }

    prtx(um[2]);
    prtuser(tuser);

    prtx(um[3]); getcmd();
    if (port->mode & gone) return;
    if (port->opt1 is 'Q') return;
    if (port->opt1 is 'Y')
    {
      tuser->options |= u_delete;
      write_rec(ufl, tuser->rn, (char *)tuser);
    }
  }
}

/*
 *  Edit a user record.
 */

eduser()
{
  register USER *u;

  pcall(tcall, port->fld[1]);
  if (matchn(tcall, port->user->call, ln_call)) u = port->user;
  else { u = tuser; rduser(tcall, u); }
  while(*port->fld[0] isnt '\0')
  {
    prtuser(u);
    outstr("PRIVILEGE: (D)elete, (E)xpert, (B)bs, (S)ysop, e(X)clude\n");
    outstr("DATA: (C)all, ss(I)d, (N)ame, por(T), (P)ath, (H)ome, (Z)ip\n");
    getcmd();
    if (port->mode & gone) return;
    switch(*port->fld[0])
    {
      case 'D' : u->options flipbit u_delete; *port->fld[0] = '\0'; break;
      case 'E' : u->options flipbit u_expert; break;
      case 'B' : u->options flipbit u_bbs; break;
      case 'S' : u->options flipbit u_sysop; break;
      case 'X' : u->options flipbit u_exclude;break;
      case 'C' : if (*port->fld[1]) pcall(u->call, port->fld[1]); break;
      case 'I' : u->ssid = atoi(port->fld[1]); break;
      case 'N' : if (*port->fld[1])
                 {
                   u->state setbit u_name;
                   ljsf(u->handle, port->line+2, ln_handle);
                 }
                 break;
      case 'T' : if (*port->fld[1]) u->port = *port->fld[1]; break;
      case 'P' : if (port->fld[1]) strncpy(u->path, port->fld[1], pathl);
                 else *u->path = '\0'; break;
      case 'H' : if (*port->fld[1])
                 {
                   u->state setbit u_home;
                   u->state clrbit u_sent;
                   pcall(u->home_bbs, port->fld[1]);
                   if (matchn(u->home_bbs, cport->user->call, ln_call))
                   u->options setbit u_local; else u->options clrbit u_local;
                 }
                 else
                 {
                   fill(u->home_bbs, ' ', ln_call);
                   u->options clrbit u_local;
                   u->state clrbit u_home;
                   u->state setbit u_sent;
                 }
                 break;
      case 'Z' : if (*port->fld[1])
                 {
                   u->state setbit u_zip;
                   u->state clrbit u_sent;
                   ljsf(u->zip, port->fld[1], ln_zip);
                 }
                 else
                 {
                   fill(u->zip, ' ', ln_zip);
                   u->state clrbit u_zip;
                   u->state setbit u_sent;
                 }
                 break;
    }
  }

/*
 *  If this is a new user, add to the list.
 */

  upduser(u);
}

/*
 *  Backup the user file.
 */

untuser()
{
  register char *rp, *wp, *lp;
  register int uflb;
  register int incnt, inrec;

  if (sure()) return;
  prtx(um[1]);
  lp = tmp->scr + (RECSIZE * (scrmax / RECSIZE));
  upduser(port->user);
  close(ufl);
  unlink(usbfile);

  if (samedir(usfile, usbfile)) rename(usfile, usbfile);
  else copy(usfile, usbfile, false);

  unlink(usfile);

  ufl  = open(usfile,  O_CREAT  | O_RDWR | O_BINARY, pmode);
  uflb = open(usbfile, O_RDONLY | O_BINARY);

  incnt = ufhs->count;
  inuhdr();

  inrec = 1;
  while(incnt)
  {
    for (rp = tmp->scr; (rp < lp) and incnt;)
    {
      incnt--;
      read_rec(uflb, inrec++, rp);
      if (!(((USER *)rp)->options & u_delete)) rp += RECSIZE;
    }
    for (wp = tmp->scr; wp < rp; wp += RECSIZE)
      write_rec(ufl, ++ufhs->count, wp);
  }

  write_rec(ufl, 0, (char *)ufhs);
  close (uflb);
  clnuser();
  rdusers();
  rduser(port->user->call, port->user);
}

/*
 *  If all ports are free, Issue the command for all ports to lockup.
 *  wait for confirmation and then do the command.
 */

setunt()
{
  byte pflg;
  long l;

  if (!(s_flag & s_dv))
  {
    switch(port->opt2)
    {
      case 'A' :
      case 'R' :
      case 'M' : untmsg(); break;
      case 'U' : untuser(); break;
      default  : ;
    }
    return;
  }

/*
 *  If all windows are free issue the lock command
 */

  getc_flag();
  if (b_flag is window)
  {
    putcomd ('A', 'H');
    pflg = getp_flag();
    putc_flag (pflg clrbit window);
  }
  else
  {
    outstr("Other windows BUSY\n");
    return;
  }

/*
 *  Wait for windows to confirm the lock by looking for a
 *  cleared c_flag. Do the command if cleared, otherwise return.
 */

  settmr( &l, 20);
  while (true)
  {
    if (!(chktmr(l)))
    {
      putc_flag(0); break;
    }
    getc_flag();
    if (c_flag  is 0)
    {
      switch(port->opt2)
        {
          case 'A' :
          case 'R' :
          case 'M' : untmsg(); break;
          case 'U' : untuser(); break;
          default  : ;
        }
      break;
    }
    switchw();
  }
  putcomd('\0','\0');
}
