/*
**	$VER: accounts.c 1.2 (07.02.94)
**
**	accounts.library functions
**
**	© Copyright 1994 by Norbert Püschel
**	All Rights Reserved
*/

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/multiuser.h>
#include <clib/accounts_protos.h>
#include <pragmas/accounts_pragmas.h>
#include <clib/alib_protos.h>

#include <exec/memory.h>
#include <exec/alerts.h>
#include <dos/notify.h>
#include <envoy/errors.h>

#include <string.h>

#include <debug.h>

#define NOBODY

#ifdef NOBODY
const UBYTE nobody[] = "nobody";

#define NOBODYNAME (STRPTR)nobody
#endif

#define BUFFLEN 256
#define UNAMELEN 32
#define GNAMELEN 32
#define PWDLEN   16

extern struct Library *AccountsBase;

/* accounts.library guardian process */

extern volatile struct Process *guardian;

struct MinList         UserList;
struct SignalSemaphore UserSemaphore;
struct MinList         GroupList;
struct SignalSemaphore GroupSemaphore;

struct InternalUserInfo {
  struct MinNode  iui_Node;
  struct UserInfo iui_UserInfo;
  struct MinList  iui_SecGroups;
  UBYTE           iui_CryptPasswd[PWDLEN];
};

struct InternalGroupInfo {
  struct MinNode   igi_Node;
  struct GroupInfo igi_GroupInfo;
};

struct SecGroup {
  struct MinNode            sg_Node;
  struct InternalGroupInfo *sg_IGI;
};

void init_users(void)

{
  NewList((struct List *)&UserList);
  memset(&UserSemaphore,0,sizeof(struct SignalSemaphore));
  InitSemaphore(&UserSemaphore);
}

void init_groups(void)

{
  NewList((struct List *)&GroupList);
  memset(&GroupSemaphore,0,sizeof(struct SignalSemaphore));
  InitSemaphore(&GroupSemaphore);
}

void free_users(void)

{
  struct InternalUserInfo *iui;
  struct SecGroup *sg;

  ObtainSemaphore(&UserSemaphore);
  iui = (struct InternalUserInfo *)(UserList.mlh_Head);  
  while(iui->iui_Node.mln_Succ) {
    Remove((struct Node *)iui);
    sg = (struct SecGroup *)(iui->iui_SecGroups.mlh_Head);
    while(sg->sg_Node.mln_Succ) {
      Remove((struct Node *)sg);
      FreeMem(sg,sizeof(struct SecGroup));
      sg = (struct SecGroup *)(iui->iui_SecGroups.mlh_Head);
    }
    FreeMem(iui,sizeof(struct InternalUserInfo));
    iui = (struct InternalUserInfo *)(UserList.mlh_Head);  
  }
  ReleaseSemaphore(&UserSemaphore);  
}

void free_groups(void)

{
  struct InternalGroupInfo *igi;

  ObtainSemaphore(&GroupSemaphore);
  igi = (struct InternalGroupInfo *)(GroupList.mlh_Head);  
  while(igi->igi_Node.mln_Succ) {
    Remove((struct Node *)igi);
    FreeMem(igi,sizeof(struct InternalGroupInfo));
    igi = (struct InternalGroupInfo *)(GroupList.mlh_Head);  
  }
  ReleaseSemaphore(&GroupSemaphore);  
}

BOOL adduser(STRPTR uname,UWORD uid,UWORD gid,ULONG flags,STRPTR pwd)

{
  struct InternalUserInfo *iui;

  iui = (struct InternalUserInfo *)
        AllocMem(sizeof(struct InternalUserInfo),MEMF_PUBLIC);
  if(iui) {
    strncpy(iui->iui_UserInfo.ui_UserName,uname,UNAMELEN);
    iui->iui_UserInfo.ui_UserID = uid;
    iui->iui_UserInfo.ui_PrimaryGroupID = gid;
    iui->iui_UserInfo.ui_Flags = flags;
    strncpy(iui->iui_CryptPasswd,pwd,PWDLEN);
    NewList((struct List *)&(iui->iui_SecGroups));
    AddTail((struct List *)&UserList,(struct Node *)iui);
  }
  return((BOOL)(iui != 0));
}

BOOL addgroup(STRPTR gname,UWORD gid)

{
  struct InternalGroupInfo *igi;

  igi = (struct InternalGroupInfo *)
        AllocMem(sizeof(struct InternalGroupInfo),MEMF_PUBLIC);
  if(igi) {
    strncpy(igi->igi_GroupInfo.gi_GroupName,gname,32);
    igi->igi_GroupInfo.gi_GroupID = gid;
    AddTail((struct List *)&GroupList,(struct Node *)igi);
  }
  return((BOOL)(igi != 0));
}

BOOL addsecgroup(struct InternalUserInfo *iui,struct InternalGroupInfo *igi)

{
  struct SecGroup *sg;

  sg = (struct SecGroup *)AllocMem(sizeof(struct SecGroup),MEMF_PUBLIC);
  if(sg) {
    sg->sg_IGI = igi;
    AddTail((struct List *)&(iui->iui_SecGroups),(struct Node *)sg);
  }
  return((BOOL)(sg != 0));
}

STRPTR nextsep(STRPTR s,UBYTE sep)

{
  while(*s && *s != '\n' && *s != sep) s++;
  return(s);
}

struct InternalUserInfo *finduid(UWORD uid)

{
  struct InternalUserInfo *iui;

  iui = (struct InternalUserInfo *)(UserList.mlh_Head);  
  while(iui->iui_Node.mln_Succ) {
    if(iui->iui_UserInfo.ui_UserID == uid) return(iui);
    iui = (struct InternalUserInfo *)(iui->iui_Node.mln_Succ);  
  }
  return(0);
}

struct InternalGroupInfo *findgid(UWORD gid)

{
  struct InternalGroupInfo *igi;

  igi = (struct InternalGroupInfo *)(GroupList.mlh_Head);  
  while(igi->igi_Node.mln_Succ) {
    if(igi->igi_GroupInfo.gi_GroupID == gid) return(igi);
    igi = (struct InternalGroupInfo *)(igi->igi_Node.mln_Succ);  
  }
  return(0);
}

struct InternalUserInfo *finduname(STRPTR uname)

{
  struct InternalUserInfo *iui;

  iui = (struct InternalUserInfo *)(UserList.mlh_Head);  
  while(iui->iui_Node.mln_Succ) {
    if(strcmp(iui->iui_UserInfo.ui_UserName,uname) == 0) return(iui);
    iui = (struct InternalUserInfo *)(iui->iui_Node.mln_Succ);  
  }
  return(0);
}

struct InternalGroupInfo *findgname(STRPTR gname)

{
  struct InternalGroupInfo *igi;

  igi = (struct InternalGroupInfo *)(GroupList.mlh_Head);  
  while(igi->igi_Node.mln_Succ) {
    if(strcmp(igi->igi_GroupInfo.gi_GroupName,gname) == 0) return(igi);
    igi = (struct InternalGroupInfo *)(igi->igi_Node.mln_Succ);  
  }
  return(0);
}

struct InternalUserInfo *finduser(STRPTR uname,UWORD uid)

{
  struct InternalUserInfo *iui;

  iui = (struct InternalUserInfo *)(UserList.mlh_Head);  
  while(iui->iui_Node.mln_Succ) {
    if(iui->iui_UserInfo.ui_UserID == uid &&
       strcmp(iui->iui_UserInfo.ui_UserName,uname) == 0) return(iui);
    iui = (struct InternalUserInfo *)(iui->iui_Node.mln_Succ);  
  }
  return(0);
}

struct InternalGroupInfo *findgroup(STRPTR gname,UWORD gid)

{
  struct InternalGroupInfo *igi;

  igi = (struct InternalGroupInfo *)(GroupList.mlh_Head);  
  while(igi->igi_Node.mln_Succ) {
    if(igi->igi_GroupInfo.gi_GroupID == gid &&
      strcmp(igi->igi_GroupInfo.gi_GroupName,gname) == 0) return(igi);
    igi = (struct InternalGroupInfo *)(igi->igi_Node.mln_Succ);  
  }
  return(0);
}

BOOL issecmem(struct InternalUserInfo *iui,struct InternalGroupInfo *igi)

{
  struct SecGroup *sg;

  sg = (struct SecGroup *)(iui->iui_SecGroups.mlh_Head);
  while(sg->sg_Node.mln_Succ) {
    if(sg->sg_IGI == igi) return(TRUE);
    sg = (struct SecGroup *)(sg->sg_Node.mln_Succ);
  }
  return(FALSE);
}

void read_users(void)

{
  BPTR passwd;
  UBYTE buff[BUFFLEN];
  STRPTR s,pwd,uidstr,gidstr;
  ULONG flags = 0;
  LONG uid,gid;

  ObtainSemaphore(&UserSemaphore);

#ifdef NOBODY
  if(!adduser(NOBODYNAME,0,0,0,"")) return;
#endif

  passwd = Open(muPasswd_FileName,MODE_OLDFILE);
  if(passwd) {
    while(FGets(passwd,buff,BUFFLEN-1)) {
      s = nextsep(buff,'|');
      if(*s != '|') break;
      *(s++) = '\0';
      pwd = s;
      s = nextsep(pwd,'|');
      if(*s != '|') break;
      if(s != pwd) flags = UFLAGF_NeedsPassword;
      *(s++) = '\0';
      uidstr = s;
      s = nextsep(uidstr,'|');
      if(*s != '|') break;
      *(s++) = '\0';
      gidstr = s;
      s = nextsep(gidstr,'|');
      if(*s != '|') break;
      *s = '\0';

      if(StrToLong(uidstr,&uid) <= 0) break;
      if(StrToLong(gidstr,&gid) <= 0) break;

      flags |= UFLAGF_AdminPassword; /* should evaluate config !!! */
      if(uid == muROOT_UID) flags |= (UFLAGF_AdminGroups|UFLAGF_AdminAll);

      if(!adduser(buff,uid,gid,flags,pwd)) break;
    }
    Close(passwd);
  }
  ReleaseSemaphore(&UserSemaphore);
}

void read_groups(void)

{
  BPTR group;
  UBYTE buff[BUFFLEN];
  STRPTR s,uidstr,gidstr;
  LONG uid,gid;
  struct InternalGroupInfo *igi;
  struct InternalUserInfo *iui;
  BOOL done;

  ObtainSemaphore(&UserSemaphore);
  ObtainSemaphore(&GroupSemaphore);

  group = Open(muGroup_FileName,MODE_OLDFILE);
  if(group) {
    while(FGets(group,buff,BUFFLEN-1) && *buff != ' ' && *buff != '\n') {
      s = nextsep(buff,'|');
      if(*s != '|') break;
      *(s++) = '\0';
      gidstr = s;
      s = nextsep(gidstr,'|');
      if(*s != '|') break;
      *s = '\0';

      if(StrToLong(gidstr,&gid) <= 0) break;

      if(!addgroup(buff,gid)) break;
    }
    while(FGets(group,buff,BUFFLEN-1)) { /* secondary groups */
      s = nextsep(buff,':');
      if(*s != ':') break;
      *(s++) = '\0';
      if(StrToLong(buff,&gid) <= 0) break;
      igi = findgid(gid);
      if(gid) {
        do {
          uidstr = s;
          s = nextsep(uidstr,',');
          done = (*s != ',');
          *(s++) = '\0';
          if(StrToLong(uidstr,&uid) <= 0) break;
          iui = finduid(uid);
          if(iui) {
            if(!addsecgroup(iui,igi)) break;
          }
        } while(!done);
      }
    }
    Close(group);
  }
  ReleaseSemaphore(&GroupSemaphore);
  ReleaseSemaphore(&UserSemaphore);
}

void __saveds guardian_func(void)

{
  struct muBase *muBase;
  struct NotifyRequest *passwd_req;
  struct NotifyRequest *group_req;
  BYTE passwd_sig,group_sig;
  BPTR passwd_dir,olddir;  

  BOOL sok = FALSE,pok,gok;
  struct Message *msg;
  struct DosPacket *dp;
  struct MsgPort *port;

  ULONG mask,rmask,pmask,gmask;

  port = &(((struct Process *)FindTask(0))->pr_MsgPort);
  WaitPort(port);
  msg = GetMsg(port);

  debug("Startup Message received !\n");

  if(msg == 0 || (dp = (struct DosPacket *)(msg->mn_Node.ln_Name)) == 0) {
    Alert(AN_Unknown);
    Wait(0); /* not much that I can do here */
  }

  init_users();
  init_groups();

  muBase = (struct muBase *)OpenLibrary(MULTIUSERNAME,MULTIUSERVERSION);

  if(muBase) {
    passwd_dir = muGetPasswdDirLock();
    CloseLibrary((struct Library *)muBase);
  }
  else {
    UBYTE buff[256];

    if(GetVar("PASSWDDIR",buff,256,0) != -1) {
      passwd_dir = Lock(buff,SHARED_LOCK);
    }
    else {
      passwd_dir = Lock("S:",SHARED_LOCK);
    }
  }

  if(passwd_dir) {
    debug("Passwd dir found !\n");
    olddir = CurrentDir(passwd_dir);
    passwd_sig = AllocSignal(-1);
    if(passwd_sig != -1) {
      group_sig = AllocSignal(-1);
      if(group_sig != -1) {
        debug("Signals allocated !\n");
        passwd_req = (struct NotifyRequest *)
          AllocMem(sizeof(struct NotifyRequest),MEMF_PUBLIC|MEMF_CLEAR);        
        if(passwd_req) {
          group_req = (struct NotifyRequest *)
            AllocMem(sizeof(struct NotifyRequest),MEMF_PUBLIC|MEMF_CLEAR);        
          if(group_req) {
            debug("Notifiers allocated !\n");
            passwd_req->nr_Name = muPasswd_FileName;
            passwd_req->nr_Flags = NRF_SEND_SIGNAL;
            passwd_req->nr_stuff.nr_Signal.nr_Task = FindTask(0);
            passwd_req->nr_stuff.nr_Signal.nr_SignalNum = passwd_sig;

            group_req->nr_Name = muGroup_FileName;
            group_req->nr_Flags = NRF_SEND_SIGNAL;
            group_req->nr_stuff.nr_Signal.nr_Task = FindTask(0);
            group_req->nr_stuff.nr_Signal.nr_SignalNum = group_sig;

            pok = StartNotify(passwd_req);
            gok = StartNotify(group_req);

            read_users();
            read_groups();

            sok = TRUE;
            dp->dp_Res1 = DOSTRUE;
            PutMsg(dp->dp_Port,msg); /* reply startup */

            pmask = 1L << passwd_sig;
            gmask = 1L << group_sig;
            mask = pmask | gmask | SIGBREAKF_CTRL_C;

            debug("Starting Loop !\n");

            for(;;) {
              rmask = Wait(mask);

              if(rmask & SIGBREAKF_CTRL_C) break;
              if(rmask & pmask) {
                debug("New Users !\n");
                ObtainSemaphore(&UserSemaphore);
                free_users();
                read_users();
                ReleaseSemaphore(&UserSemaphore);
              }
              if(rmask & gmask) {
                debug("New Groups !\n");
                ObtainSemaphore(&GroupSemaphore);
                free_groups();
                read_groups();
                ReleaseSemaphore(&GroupSemaphore);
              }
            }

            debug("Ending Loop !\n");

            if(gok) EndNotify(group_req);
            if(pok) EndNotify(passwd_req);

            FreeMem(group_req,sizeof(struct NotifyRequest));
          }
          FreeMem(passwd_req,sizeof(struct NotifyRequest));
        }
        FreeSignal(group_sig);
      }
      FreeSignal(passwd_sig);
    }
    UnLock(CurrentDir(olddir));
  }
 
  debug("Freeing Userinfo ...\n");
  free_users();
  debug("Freeing Groupinfo ...\n");
  free_groups();

  if(!sok) {
    dp->dp_Res1 = DOSFALSE;
    dp->dp_Res2 = ERROR_NO_FREE_STORE;
    PutMsg(dp->dp_Port,msg);
  }

  debug("Ending Guardian !\n");

  Forbid();
  guardian = 0;
}

/* allocation functions */

struct UserInfo * __saveds LIBAllocUserInfo(void)

{
  debug("AllocUserInfo\n");

  return((struct UserInfo *)AllocMem(sizeof(struct UserInfo),MEMF_CLEAR));
}

struct GroupInfo * __saveds LIBAllocGroupInfo(void)

{
  debug("AllocGroupInfo\n");

  return((struct GroupInfo *)AllocMem(sizeof(struct GroupInfo),MEMF_CLEAR));
}

void __saveds __asm LIBFreeUserInfo(register __a0 struct UserInfo *user)

{
  debug3("FreeUserInfo: Name: %s, UID: %ld\n",
         user->ui_UserName,user->ui_UserID);

  FreeMem(user,sizeof(struct UserInfo));
}


void __saveds __asm LIBFreeGroupInfo(register __a0 struct GroupInfo *group)

{
  debug3("FreeGroupInfo: Name: %s, GID: %ld\n",
         group->gi_GroupName,group->gi_GroupID);

  FreeMem(group,sizeof(struct GroupInfo));
}

/* xxxToyyy functions */

ULONG __saveds __asm LIBNameToUser(register __a0 STRPTR uname,
                                   register __a1 struct UserInfo *user)

{
  ULONG retval;
  struct InternalUserInfo *iui;

  debug("NameToUser\n");

  ObtainSemaphoreShared(&UserSemaphore);

#ifdef NOBODY
  if(stricmp(NOBODYNAME,uname) == 0) uname = NOBODYNAME; /* normalize */
#endif

  iui = finduname(uname);

  if(iui) {
    *user = iui->iui_UserInfo;
    retval = 0;
  }
  else retval = ENVOYERR_UNKNOWNUSER;

  ReleaseSemaphore(&UserSemaphore);

  return(retval);
}

ULONG __saveds __asm LIBNameToGroup(register __a0 STRPTR gname,
                                    register __a1 struct GroupInfo *group)

{
  ULONG retval;
  struct InternalGroupInfo *igi;

  debug("NameToGroup\n");

  ObtainSemaphoreShared(&GroupSemaphore);

  igi = findgname(gname);

  if(igi) {
    *group = igi->igi_GroupInfo;
    retval = 0;
  }
  else retval = ENVOYERR_UNKNOWNGROUP;

  ReleaseSemaphore(&GroupSemaphore);

  return(retval);
}

ULONG __saveds __asm LIBIDToUser(register __d0 UWORD uid, 
                                 register __a0 struct UserInfo *user)

{
  ULONG retval;
  struct InternalUserInfo *iui;

  debug("IDToUser\n");

  ObtainSemaphoreShared(&UserSemaphore);

  iui = finduid(uid);

  if(iui) {
    *user = iui->iui_UserInfo;
    retval = 0;
  }
  else retval = ENVOYERR_UNKNOWNUSER;

  ReleaseSemaphore(&UserSemaphore);

  return(retval);
}

ULONG __saveds __asm LIBIDToGroup(register __d0 UWORD gid,
                                  register __a0 struct GroupInfo *group)

{
  ULONG retval;
  struct InternalGroupInfo *igi;

  debug("IDToGroup\n");

  ObtainSemaphoreShared(&GroupSemaphore);

  igi = findgid(gid);

  if(igi) {
    *group = igi->igi_GroupInfo;
    retval = 0;
  }
  else retval = ENVOYERR_UNKNOWNGROUP;

  ReleaseSemaphore(&GroupSemaphore);

  return(retval);
}

/* Nextxxx functions */

ULONG __saveds __asm LIBNextUser(register __a0 struct UserInfo *user)

{
  ULONG retval = ENVOYERR_LASTUSER;
  struct InternalUserInfo *iui;

  debug("NextUser\n");

  ObtainSemaphoreShared(&UserSemaphore);

  if(user->ui_UserID != 0
#ifdef NOBODY
     || stricmp(user->ui_UserName,NOBODYNAME) == 0
#endif
    ) { /* take next */
    iui = finduser(user->ui_UserName,user->ui_UserID);

    if(iui) iui = (struct InternalUserInfo *)(iui->iui_Node.mln_Succ);  
    else retval = ENVOYERR_UNKNOWNUSER;
  }
  else { /* take first */   
    iui = (struct InternalUserInfo *)(UserList.mlh_Head);  
  }

  if(iui->iui_Node.mln_Succ) {
    *user = iui->iui_UserInfo;
    retval = 0;
  }

  ReleaseSemaphore(&UserSemaphore);

  return(retval);
}

ULONG __saveds __asm LIBNextGroup(register __a0 struct GroupInfo *group)

{
  ULONG retval = ENVOYERR_LASTGROUP;
  struct InternalGroupInfo *igi = 0;

  debug("NextGroup\n");

  ObtainSemaphoreShared(&GroupSemaphore);

  if(group->gi_GroupID != 0 || ((igi = findgid(0)) != 0 && 
     strcmp(igi->igi_GroupInfo.gi_GroupName,group->gi_GroupName) == 0)) {

    if(igi == 0) igi = findgroup(group->gi_GroupName,group->gi_GroupID);

    if(igi) igi = (struct InternalGroupInfo *)(igi->igi_Node.mln_Succ);  
    else retval = ENVOYERR_UNKNOWNGROUP;
  }
  else { /* take first */   
    igi = (struct InternalGroupInfo *)(GroupList.mlh_Head);  
  }

  if(igi->igi_Node.mln_Succ) {
    *group = igi->igi_GroupInfo;
    retval = 0;
  }

  ReleaseSemaphore(&GroupSemaphore);

  return(retval);
}

ULONG __saveds __asm LIBNextMember(register __a0 struct GroupInfo *group,
                                   register __a1 struct UserInfo *user)

{
  ULONG retval = ENVOYERR_LASTMEMBER;
  struct InternalUserInfo *iui;
  struct InternalGroupInfo *igi;

  debug("NextMember\n");

  ObtainSemaphoreShared(&UserSemaphore);
  ObtainSemaphoreShared(&GroupSemaphore);

  if(group->gi_GroupID) {
    igi = findgid(group->gi_GroupID);
  }
  else {
    igi = findgname(group->gi_GroupName);
    if(igi == 0) igi = findgid(0);
  }
  if(igi) {
    *group = igi->igi_GroupInfo;

    if(user->ui_UserID != 0
#ifdef NOBODY
       || stricmp(user->ui_UserName,NOBODYNAME) == 0
#endif
      ) { /* takenext */

      iui = finduser(user->ui_UserName,user->ui_UserID);

      if(iui) iui = (struct InternalUserInfo *)(iui->iui_Node.mln_Succ);  
      else retval = ENVOYERR_UNKNOWNUSER;
    }
    else { /* take first */   
      iui = (struct InternalUserInfo *)(UserList.mlh_Head);  
    }
    
    while(iui->iui_Node.mln_Succ) {
      if(iui->iui_UserInfo.ui_PrimaryGroupID == group->gi_GroupID ||
         issecmem(iui,igi)) break;
      iui = (struct InternalUserInfo *)(iui->iui_Node.mln_Succ);  
    }

    if(iui->iui_Node.mln_Succ) {
      *user = iui->iui_UserInfo;
      retval = 0;
    }
  }
  else retval = ENVOYERR_UNKNOWNGROUP;
    
  ReleaseSemaphore(&GroupSemaphore);
  ReleaseSemaphore(&UserSemaphore);

  return(retval);
}

/* memberof */

ULONG __saveds __asm LIBMemberOf(register __a0 struct GroupInfo *group,
                                 register __a1 struct UserInfo *user)

{
  ULONG retval = ENVOYERR_UNKNOWNMEMBER;
  struct InternalUserInfo *iui;
  struct InternalGroupInfo *igi;

  debug("MemberOf\n");

  ObtainSemaphoreShared(&UserSemaphore);
  ObtainSemaphoreShared(&GroupSemaphore);

  if(user->ui_UserID) {
    iui = finduid(user->ui_UserID);
  }
  else {
    iui = finduname(user->ui_UserName);
#ifdef NOBODY
    if(iui == 0) iui = finduid(0);
#endif
  }
  if(iui) {
    *user = iui->iui_UserInfo;

    if(group->gi_GroupID) {
      igi = findgid(group->gi_GroupID);
    }
    else {
      igi = findgname(group->gi_GroupName);
      if(igi == 0) igi = findgid(0);
    }
    if(igi) {
      *group = igi->igi_GroupInfo;
      if(user->ui_PrimaryGroupID == group->gi_GroupID || issecmem(iui,igi)) {
        retval = 0;
      }
    }
    else retval = ENVOYERR_UNKNOWNGROUP;
  }
  else retval = ENVOYERR_UNKNOWNUSER;

  ReleaseSemaphore(&GroupSemaphore);
  ReleaseSemaphore(&UserSemaphore);

  debug2("MemberOf-result: %ld\n",retval);

  return(retval);
}

/* encryption & verification */

STRPTR __saveds __asm LIBECrypt(register __a0 STRPTR buff,
                                register __a1 STRPTR passwd,
                                register __a2 STRPTR uname)

{
  debug3("ECrypt: Name: %s, Pwd: %s\n",uname,passwd); 

  return(ACrypt(buff,passwd,uname));
}

ULONG __saveds __asm LIBVerifyUser(register __a0 STRPTR uname,
                                   register __a1 STRPTR passwd,
                                   register __a2 struct UserInfo *user)

{
  UBYTE buff[16];

  debug3("VerifyUser: Name: %s, Pwd: %s\n",uname,passwd);

  if(ACrypt(buff,passwd,uname)) {
    return(VerifyUserCrypt(uname,buff,user));
  }
  else {
    return(ENVOYERR_NORESOURCES);
  }
}

ULONG __saveds __asm LIBVerifyUserCrypt(register __a0 STRPTR uname,
                                        register __a1 STRPTR passwd,
                                        register __a2 struct UserInfo *user)

{
  ULONG retval = ENVOYERR_UNKNOWNUSER;
  struct InternalUserInfo *iui;

  debug3("VerifyUserCrypt: Name: %s, Pwd: %s\n",uname,passwd);

  ObtainSemaphoreShared(&UserSemaphore);

#ifdef NOBODY
  if(stricmp(NOBODYNAME,uname) == 0) uname = NOBODYNAME; /* normalize */
#endif

  iui = finduname(uname);
  if(iui && (iui->iui_CryptPasswd[0] == '\0' ||
     strcmp(iui->iui_CryptPasswd,passwd) == 0)) {
    *user = iui->iui_UserInfo;
    retval = 0;
  }

  ReleaseSemaphore(&UserSemaphore);

  debug2("VerifyUserCrypt: %ld\n",retval);

  return(retval);
}
