/*
 *  PASSWD.C
 *
 * $Header: Welmat:src/RCS/passwd.c,v 1.9 93/01/23 16:08:31 rwm Exp Locker: rwm $
 *
 */


#include <exec/types.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include "pwd.h"
#include <proto/all.h>
#include <dos/dostags.h>
#include <clib/dos_protos.h>
#include <exec/libraries.h>
#include <fcntl.h>
#include <time.h>
#include <dos.h>
#include <exec/lists.h>

extern struct DosLibrary *DOSBase;

#define BTOCP(bp,type)  ((type)((long)bp << 2))

extern long NullFH;
extern char *LoginBuf;
extern char PasswdBuf[];
extern char *DeviceName;
extern long DeviceUnit;
extern void SerHangup();
extern int NoCheckBad;
extern int UnLockedb;
extern void xexit();
struct passwd *getpwnam(const char *);
char *Breakout(char **pptr);
char *PasswdFile;
static struct passwd *Pas;
void typeit();
void argmacro();
void ulog();

int	LogLevel = -1;
char	*LogHost = "-";
char	*LogWho = "-";
char	*LogFile;
char	LogBuf[512];

typedef struct List LIST;
typedef struct Node NODE;

typedef struct {
    NODE    Node;
    FILE    *Fi;
    short   Refs;
} LNode;

void LockFile(const char *);
void UnLockFile(const char *);
void FreeLockNode(LNode *);


LIST LockList = { (NODE *)&LockList.lh_Tail, NULL, (NODE *)&LockList.lh_Head };


int CheckLoginAndPassword()
{
    Pas = getpwnam(LoginBuf);

    if(Pas == NULL) {
      if (NoCheckBad) 
        return(3);
      else      
        return(NULL);
    }
    if(strcmp(Pas->pw_passwd, "*") == 0) {
      return(1);
    }
    if(strcmp(Pas->pw_passwd, "**") == 0) {
      return(2);
    }
    if (strcmp(PasswdBuf, Pas->pw_passwd) == 0) {
      return(1); 
    }
    return(0);
}

int RunPasswdEntry(baudrate,deviceflags)
int baudrate;
int deviceflags;
{
    BPTR outfile,infile;
    struct TagItem systag[4];
    int dohangup,typef,typeu,redirb,loop;
    static char buf[256];
    char redirin[100],redirout[100],argn[256];
    char *arg0 = Pas->pw_shell_arg0;
    struct Process *proc = (struct Process *)FindTask(NULL);
    APTR oldConsoleTask = proc->pr_ConsoleTask;
    BPTR oldDir;
    BPTR DirLock;
    int  code;

    dohangup=TRUE;
    redirb=FALSE;
    loop=TRUE;
    typef=FALSE;
    typeu=TRUE;

    infile=outfile=NULL;

    while (loop) {
       if (*arg0 == '$') {
         ++arg0;
         dohangup=FALSE;
       } else if (*arg0 == '*') {
  	 ++arg0;
         redirb=TRUE;
       } else if (*arg0 == '<') {
  	 ++arg0;
         typef=TRUE;
       } else if (*arg0 == '#') {
        ++arg0;
        typeu=FALSE;
       } else loop = FALSE;
    }

    proc->pr_ConsoleTask = (APTR)BTOCP(NullFH,
                                struct FileHandle *)->fh_Port;

    DirLock = (BPTR)Lock(Pas->pw_dir, SHARED_LOCK);
    if (DirLock) {
	oldDir = (BPTR)CurrentDir(DirLock);
    }

    if (typef) {
 
    ulog(-1, "Type %s", arg0);
    typeit(arg0);

    } else { /* typef */
       argmacro(argn,Pas->pw_shell_argn,DeviceName,DeviceUnit,
          deviceflags,baudrate);
       if (!typeu) {
          sprintf(buf,"%s %s\n",arg0,argn);
          systag[0].ti_Tag = TAG_END;
       } else {  /* typeu */
          strcpy(redirin,"null:");
          strcpy(redirout,"null:");

          if (redirb) {
              sprintf(redirin, "UUSER:%s/%d/R300000G1"
                  , DeviceName, DeviceUnit);
              strcpy(redirout,redirin);
          }
      
          if (!(outfile = Open(redirout,MODE_NEWFILE))) {
             printf("error opening outfile\n");
             xexit(9);
          }

          if (!(infile = Open(redirin,MODE_OLDFILE))) {
             printf("error opening infile\n");
             xexit(9);
          }
            
          systag[0].ti_Tag = SYS_Input;
          systag[0].ti_Data = infile;
          systag[1].ti_Tag = SYS_Output;
          systag[1].ti_Data = outfile;
          systag[2].ti_Tag = TAG_END;

          if ((DOSBase->dl_lib.lib_Version) >= 36) {
             sprintf(buf, "%s %s -Getty -DEVICE %s -UNIT %d\n",
                 arg0, argn, DeviceName, DeviceUnit); 
          } else {  
             sprintf(buf, "%s <%s >%s %s -Getty -DEVICE %s -UNIT %d\n",
                 arg0, redirin, redirout, argn, DeviceName, DeviceUnit);
          }
       }
       if ((DOSBase->dl_lib.lib_Version) >= 36) {

/* 2.0 method of executing */
          ulog(-1, "System(%s)", buf);
          code = System(buf,systag);
        } else { 

/* 1.3 method of executing */
          ulog(-1, "Execute(%s)", buf);
/*          code = system(buf);   SAS */
          code = Execute(buf,NULL,NULL);
        }

        ulog(-1, "Return %d",code);
        if (infile) {
           Close(infile);
           Close(outfile);
        } 
    }
    proc->pr_ConsoleTask = oldConsoleTask;

    if (DirLock)
	UnLock(CurrentDir(oldDir));

    if (dohangup == TRUE) SerHangup();
    return(dohangup ? 0 : 1);
}


void
argmacro(nargn,oargn,serialn,serialu,serialf,serialb)
char *nargn,*oargn,*serialn;
int serialu,serialf,serialb;
{
   while ((*nargn = *oargn) != '\0') {
      if (*nargn =='%') {
         *nargn='\0';
         switch(*(++oargn)) {
           case 's':
             sprintf(nargn,"%s%s",nargn,serialn);
             break;
           case 'l':
             sprintf(nargn,"%s%s",nargn,LoginBuf);
             break;
           case 'p':
             sprintf(nargn,"%s%s",nargn,PasswdBuf);
             break;
           case 'u':
             sprintf(nargn,"%s%d",nargn,serialu);
             break;
           case 'f':
             sprintf(nargn,"%s%d",nargn,serialf);
             break;
           case 'b':
             sprintf(nargn,"%s%d",nargn,serialb);
             break;
           case 'B':
             sprintf(nargn,"%s%d",nargn,UnLockedb);
             break;
           default:
             sprintf(nargn,"%s%%",nargn);
             break;
         }         
         while (*nargn != '\0') nargn++;
         nargn--;
      }
      nargn++;
      oargn++;
   }
}


void
typeit(tname)
char *tname;
{

    FILE *fi;
    char c;

    if (fi = fopen(tname, "r")) {
        char buf[128];
        while (fgets(buf, 127 , fi)) {
            SerPuts(buf);
            SerPuts("\r");
        }
        fclose(fi);
        SetUpTimeout(40);
        while (((c=GetChar())!=EOF) && c!=0x0d);
    }
}
 
struct passwd *
getpwnam(name)
char *name;
{
    FILE *fi;
    char *buf = (char *)malloc(256);
    static char User[32];
    static char Passwd[32];
    static char Dir[128];
    static char Shell[256];
    struct passwd Pas = { User, Passwd, 0, 0, 0, "", "", Dir, Shell };

    fi = fopen(PasswdFile, "r");
    if (fi == NULL)
	return(NULL);

    while (fgets(buf, 256, fi)) {
	char *ptr = buf;
	char *arg;

	arg = Breakout(&ptr);

        if ((DOSBase->dl_lib.lib_Version) >= 36) {
            char pat[256];

            ParsePattern(arg,pat,255);
            if (!MatchPattern(pat, name))
                continue;
        } else {
	    if (strcmp(name, arg) != 0) 
	        continue;
        }
	strcpy(Pas.pw_name, arg);
	strcpy(Pas.pw_passwd, Breakout(&ptr));
	Pas.pw_uid = atoi(Breakout(&ptr));
	Pas.pw_gid = atoi(Breakout(&ptr));
	Breakout(&ptr);     /*  finger info */
	strcpy(Pas.pw_dir, Breakout(&ptr));
	strcpy(Pas.pw_shell, Breakout(&ptr));

	{
	    short i = strlen(Pas.pw_dir) - 1;
	    if (i >= 0 && Pas.pw_dir[i] != ':' && Pas.pw_dir[i] != '/') {
		Pas.pw_dir[i++] = '/';
		Pas.pw_dir[i] = 0;
	    }
	}

	{
	    char *str;

	    Pas.pw_shell_arg0 = Pas.pw_shell;
	    for (str = Pas.pw_shell; *str && *str != ' ' && *str != 9; ++str);
	    if (*str) {
		*str = 0;
		++str;
		while (*str == ' ' || *str == 9)
		    ++str;
		Pas.pw_shell_argn = str;
	    } else {
		Pas.pw_shell_argn = str;
	    }
	}
	fclose(fi);
	return(&Pas);
    }
    fclose(fi);
    return(NULL);
}


char *Breakout(char **pptr)
{
    char *base;
    char *ptr;

    base = *pptr;
    if (base == NULL)
	return("");
    for (ptr = base; *ptr && *ptr != '\n' && *ptr != ','; ++ptr);
    if (*ptr == ',') {
	*pptr = ptr + 1;
	*ptr = 0;
    } else {
	*pptr = NULL;
	*ptr = 0;
    }
    return(base);
}

void
ulog(level, ctl)
int level;
char *ctl;
{
    long clock;
    struct tm *ut;
    int logfd;
    int len;
    long *args = (long *)&ctl + 1;  /*  should use varargs  */

    if (level > LogLevel)
	return;

    (void)time(&clock);
    ut = localtime(&clock);

    sprintf(LogBuf, "(%02d/%02d-%02d:%02d:%02d) Login,%s,%s ",
	ut->tm_mon+1, ut->tm_mday, ut->tm_hour, ut->tm_min, ut->tm_sec,
	LogHost,
	LogWho
    );
    sprintf(LogBuf + strlen(LogBuf), ctl,
	args[0], args[1], args[2],
	args[3], args[4], args[5]
    );

    len = strlen(LogBuf);
    LogBuf[len++] = '\n';
    LogBuf[len] = 0;
    LockFile("LOG-UPDATE"); 
    logfd = open(LogFile, O_CREAT|O_WRONLY|O_APPEND, 0644);
    if (logfd >= 0) {
	write(logfd, LogBuf, len);
	close(logfd);
    }
    UnLockFile("LOG-UPDATE");
}

void
LockFile(file)
const char *file;
{

    LNode *node;
    LNode *n;
   
    if (node = (void *)malloc(sizeof(LNode) + strlen(file) + 16)) {
	node->Node.ln_Name = (char *)(node + 1);
	sprintf(node->Node.ln_Name, "T:%s.LOCK", file);

	for (n = (LNode *)LockList.lh_Head; n != (LNode *)&LockList.lh_Tail;
                         n = (LNode *)n->Node.ln_Succ) {
	    if (strcmp(node->Node.ln_Name, n->Node.ln_Name) == 0) {
		++n->Refs;
		free(node);
		return;
	    }
	}

	while ((node->Fi = fopen(node->Node.ln_Name, "w")) == NULL) {
	    Delay(100);
	    chkabort();
	}
	node->Refs = 1;
	AddTail(&LockList, &node->Node);
    }
}

void
UnLockFile(file)
const char *file;
{
    LNode *node;
    short len;

    len = strlen(file);

    for (node = (LNode *)LockList.lh_Head; node != (LNode *)&LockList.lh_Tail; node = (LNode *)node->Node.ln_Succ) {
	if (strncmp(file, node->Node.ln_Name + 2, len) == 0 && strlen(node->Node.ln_Name) == len + 7) {
	    if (--node->Refs == 0) {
                Remove(node);
                fclose(node->Fi);
                unlink(node->Node.ln_Name);
                free(node);
	        return;
            }
 	}
    }
}
