/*
 *  MBUSER.C - 6/28/92
 */

#include "mb.h"

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

#ifdef MCH_AMIGA
extern short debug;
extern char tmpstr[];
#endif

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

#ifndef MCH_AMIGA
int ufl;
#else
int ufl = -1;
int uifl = -1;
char *user_file = "T:mbuser.index";
#endif

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

clnuser()
{
  close(ufl);
  ufl = open(usfile, O_RDWR | O_BINARY);
#ifdef MCH_AMIGA
   close(uifl);
   uifl = open(user_file,O_RDWR | O_BINARY);
#endif
}

/*
 *  Close the user file.
 */

clsusr()
{
#ifdef MCH_AMIGA
   if(ufl >= 0)
#endif
  close (ufl);
#ifdef MCH_AMIGA
   ufl = -1;
   if(uifl >= 0)
      close(uifl);
   uifl = -1;
#endif
}

/*
 *  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.
 */
#ifdef MCH_AMIGA
   lock_user();
#endif

  ufl = open(usfile, O_RDWR | O_BINARY);
  if (ufl < 0)
  {
    ufl = open(usfile, O_CREAT | O_RDWR | O_BINARY, pmode);
#ifndef MCH_AMIGA
    if (ufl < 0) { nofile(usfile); exit(1); }
#else
    if(ufl < 0) {
       nofile(usfile);
#ifdef MCH_AMIGA
       unlock_user();
#endif
       done(1);
    }
#endif
    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);
#ifndef MCH_AMIGA
    exit(1);
#else
   set_user((long)ufhs->count);
   unlock_user();
   done(1);
#endif
  }

  maxusers = ufhs->count + uf_inc;
#ifdef MCH_AMIGA
/* size_t and numeric arg to errall*/
#endif
#ifndef MCH_AMIGA
  users = (char *) malloc((size_t)ln_call * maxusers);
  if (users is NULL) errall(2);
#endif

  rdusers();
#ifdef MCH_AMIGA
   unlock_user();
#endif
}

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

readusr()
{
#ifndef MCH_AMIGA
  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();
#else
   lock_user();
   ufl = open(usfile, O_RDWR | O_BINARY);
   read_rec (ufl, 0, (char*)ufhs);
   rdusers();
   unlock_user();
#endif
}

/*
 *  Read each user record, build list of calls.
 */
#ifdef MCH_AMIGA
char first_time = 1;
#endif
rdusers()
{
  register int i;
  register char *up;

#ifndef MCH_AMIGA
  printf("%d users in %s\n", ufhs->count, usfile);
  for (i = 1, up = users; i <= ufhs->count; i++, up += ln_call)
  {
    read_rec(ufl, i, (char *)tuser);
    strncpy(up, tuser->call, ln_call);
  }
#else
   if(first_time) {
      first_time = 0;
      sprintf(tmpstr,"%d users in %s\n", ufhs->count, usfile);
      ttputs(tmpstr);
   }
   /* If the index file exists, then just open it
      otherwise create it from the user.dat file
   */
   if((uifl = open(user_file,O_RDWR | O_BINARY)) >= 0)return;
   uifl = open(user_file,O_RDWR | O_BINARY | O_CREAT);
   for(i = 1; i <= ufhs->count; i++) {
      read_rec(ufl, i, (char *)tuser);
      write(uifl, tuser->call,ln_call);
   }
#endif

}

/*
 *  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;

#ifndef MCH_AMIGA
  for (i = 1, up = users; i <= ufhs->count; i++, up += ln_call)
  if (matchn(call, up, ln_call)) return i;
#else
   check_user();
   unlock_user();
   lseek(uifl,0L,0);
   for(i = 1; i <= ufhs->count; i++) {
      read(uifl,tmpstr,(size_t)ln_call);
      if(matchn(call, tmpstr, ln_call)) return i;
   }
#endif
  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);
#ifdef MCH_AMIGA
  fill(buf->qth,      ' ',ln_qth);
#endif
  fill(buf->unu,        0, userunu);

  return 0;
}

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

upduser(buf)
USER *buf;
{
#ifdef MCH_AMIGA
   check_user();
#endif
  if (s_flag & s_dv) begin_lock();
  read_rec(ufl, 0, (char*)ufhs);
  if (!buf->rn)
  {
#ifndef MCH_AMIGA
    if (ufhs->count >= maxusers)
      {
   if (s_flag & s_dv) end_lock();
   return;
      }
    strncpy (users + (ln_call * ufhs->count), buf->call, ln_call);
#endif
    buf->rn = ++ufhs->count;
#ifdef MCH_AMIGA
      lseek(uifl,0L,2);
      write(uifl,buf->call,ln_call);
      set_user((long)buf->rn);
#endif
  }

  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();
#ifdef MCH_AMIGA
   unlock_user();
#endif
}

/*
 *  Print the address of BBS in STATES.MB
 */

pstates()
{
  FILE *hier;
  char hiercall[ln_call];

  port->msg = mfind;
  if ((hier = fopen( stfile, "r")) is NULL) return;
  while (fgets(tmp->scr, linelen, hier) isnt NULL)
    {
      pcall(hiercall, tmp->scr);
      if (wcm(hiercall, tcall))
   {
     outstr("BBS Address -> ");
     outstr(tmp->scr);
     outchar('\n');
     port->msg = NULL;
     break;
   }
    }
  fclose(hier);
  return;
}

/*
 *  Print info about a user.
 */

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

  pcall(tcall, port->fld[1]);
  if (!(i = finduser(tcall))) { pstates(); 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 'Z')
  {
    outnb(tuser->call, ln_call);
    outstr(" connected on ");
    outstr(tmp->scr);
  }
  outnb(tuser->call, ln_call);
  if (tuser->port is 'Z')
  {
    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);

  outstr("Name - ");
  outnb(tuser->handle, ln_handle);

  if (tuser->state & u_home)
  {
    outstr(", Mail bbs - ");
    outnb(tuser->home_bbs, ln_call);
  }

  if (tuser->state & u_qth)
  {
    sprintf(tmp->scr, ", QTH - %s", tuser->qth);
    outstr(tmp->scr);
  }

  if (tuser->state & u_zip)
  {
    outstr(", Zip - ");
    outnb(tuser->zip, ln_zip);
  }
    outchar('\n');
}

/*
 *  Print a user record.
 */

prtuser(p)
USER *p;
{
  fill(port->cmd, ' ', 7);
  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] = 'X';
  if (p->options & u_hold)    port->cmd[6] = 'H';
  sprintf(tmp->scr,
#ifndef MCH_AMIGA
/* The log_count should be %5d */
  "%6.6s %6.6s %4.4s %4d %5u %6.6s %2.2d%c%7.7s %-12.12s %6.6s\n%-20.20sPath: %s\n",
#else
  "%6.6s %6.6s %4.4s %5d %5u %6.6s %2.2d%c%7.7s %-12.12s %6.6s\n%-20.20sPath: %s\n",
#endif
    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->qth, 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; }
  }
#ifdef MCH_AMIGA
   check_user();
   unlock_user();
#endif

  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, NQ, NZ (update my user info) commands.
 */

chguser()
{
  port->msg = mdone;
  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' : if (!iscall(port->fld[1]))
          {
       port->msg = mwhat;
       break;
          }
      if (matchn(port->user->call, port->fld[1], strlen(port->fld[1])))
          {
       port->msg = um[8];
       break;
          }
          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 'P' : port->user->state flipbit u_pause;
          if (port->user->state & u_pause) outstr("pause off ");
#ifndef MCH_AMIGA
          else outstr("on ");
#else
          else outstr("pause on ");
#endif
          break;

    case 'Q' : remnl(port->line);
          strncpy(port->user->qth, port->line+3, ln_qth);
          port->user->state setbit u_qth;
          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;
  }
}

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

edusera()
{
  register word i;
#ifdef MCH_AMIGA
   check_user();
   unlock_user();
#endif
  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 h(O)ld\n");
    outstr("DATA: (C)all ss(I)d (N)ame por(T) (P)ath (H)ome (Q)th (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 'O' : u->options flipbit u_hold; 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 'Q' : if (*port->fld[1])
       {
         u->state setbit u_qth;
         remnl(port->line);
         strncpy(u->qth, port->line+2, ln_qth);
       }
       else
       {
         fill(u->qth, '\0', ln_qth);
         u->state clrbit u_qth;
       }
       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();
#ifdef MCH_AMIGA
/* clobber the index file
   rdusers will recreate it and leave it open
*/
   close(uifl);
   unlink(user_file);
#endif
  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()
{
  word pflg;
  long l;

#ifndef MCH_AMIGA
  if (!(s_flag & s_dv))
  {
    do_op();
    return;
  }

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

  getbusy();
  if (b_flag is p_window)
  {
    putcomd ('A', 'H');
    pflg = getp_flag();
    putc_flag (pflg clrbit p_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)
    {
      do_op();
      break;
    }
    switchw();
  }
  putcomd('\0','\0');
#else
   if(getbusy()) {
      outstr("Other windows BUSY\n");
      return;
   }
   do_op();
   unbusy();
#endif
}
do_op()
{
  switch(port->opt2)
  {
    case 'A' :
    case 'R' :
    case 'M' : if (untmsg()) cprs_bid(); break;
    case 'U' : untuser(); break;
    default  : ;
  }
}
#ifdef MCH_AMIGA
check_user()
{
   extern long get_user();
   lock_user();
   if(ufhs->count != get_user())clnuser();
}
#endif
