#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <libraries/dosextens.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define UID_NAME_ARRAY

#include "/lib/users.h"
#include "/lib/iomodes.h"
#include "/lib/misc.h"

#define CUID	  ((struct Process *)(SysBase->ThisTask))->pr_Pad

/* passwd-program for AXsh. Programmed by Pasi Ojala. */

char *vers="\0$VER: passwd 1.33 (1.1.95)\n";

extern struct ExecBase *SysBase;

struct User user;
struct FileInfoBlock fib;
int		Rows=0;
char	Mailhome[100]="",
		passwdfile[80]="AXsh:etc/passwd",
		Host[32]="pasbox"; /* %thismachine */
long	defq=50;
UWORD	defl=UID_VISITOR,olduid=0;

FILE *fopen();

void __regargs __chkabort(void);
void __regargs __chkabort(void){}
void chkabort(void) {}
void read_config(void);
int  getline(UBYTE *, int, UBYTE);
void convert_datestamp(char *a,struct DateStamp v);

int p_listusers(int rows);
int p_chpasswd(char *loginname);
int p_chfn(char *loginname);
int p_chquota(char *loginname);
int p_chlevel(char *loginname);
int p_chtime(char *loginname);
int p_remuser(char *loginname);
int p_finger(char *loginname);


int main(int argc,char *argv[])
{
	int i,error=0;
	UBYTE in1[100];

	read_config();	/* setting up.. */

	if(who(COM_GET,NULL,"rows",in1))
		Rows=24; /* default value */
	else
		Rows=atoi(in1);

	if(Rows<5)
		Rows=5;
	if(Rows>100)
		Rows=100;

	setmode(MODE_RAW);

	if(argc==6 && !strcmp(argv[1],"-a"))	/* add user for superuser */
	{
		if(!(((struct Process *)SysBase->ThisTask)->pr_Pad & UID_SUPERUSER))
		{
			printf("No privileges for adding user\n");
			error=20;
		}
		else
		{
			if(strlen(argv[2])>16)
			{
				printf("Too long loginname, max 16 chars!\n");
				error = 20;
			}
			else
			if(strlen(argv[4])>45)
			{
				printf("Too long real name, max 45 chars!\n");
				error = 20;
			}
			else
			if(strlen(argv[5])>99)
			{
				printf("Too long home directory name, max 99 chars!\n");
				error = 20;
			}
			else
			{
				crypt(in1,argv[3]);
				adduser(passwdfile,argv[2],in1,argv[4],argv[5],defq,defl);
			}
		}
	}
	else
	if(argc==3)
	{
		if(!strcmp(argv[1],"-d"))	/* finger for all users */
			error=p_finger(argv[2]);
		else
		if(!strcmp(argv[1],"-f"))	/* change full name (user himself or superuser) */
			error=p_chfn(argv[2]);
		else
		if(!strcmp(argv[1],"-q"))	/* change quota (superuser) */
			error=p_chquota(argv[2]);
		else
		if(!strcmp(argv[1],"-p"))	/* change accesslevel (superuser) */
			error=p_chlevel(argv[2]);
		else
		if(!strcmp(argv[1],"-t"))	/* change time limit (superuser) */
			error=p_chtime(argv[2]);
		else
		if(!strcmp(argv[1],"-r"))	/* remove user (superuser) */
		{
			error=p_remuser(argv[2]);
		}
	}
	else
	if(argc==2 && argv[1][0]!='-')	/* password change for superuser & user himself */
	{
		error=p_chpasswd(argv[1]);
	}
	else
	if(argc==2 && !strcmp(argv[1],"-l"))	/* list users for all users */
	{
		error=p_listusers(Rows);
	}
	else
	if(argc==2 && !strcmp(argv[1],"-ll"))	/* list level names */
	{
		i=0;
		while(UID_Name[i])
		{
			if(UID_Name[i][0])
				printf("%s\n",UID_Name[i]);
			i++;
		}
	}
	else
	{
usage:
		printf("Usage: passwd -l	   List users\n");
		printf("       passwd -ll	  List level names\n");
		printf("       passwd user	 Change password\n");
		printf("       passwd -f user  Change full name\n");
		printf("       passwd -d user  Finger user\n");
		printf("       passwd -q user  Change quota (superuser only)\n");
		printf("       passwd -p user  Change privileges (superuser only)\n");
		printf("       passwd -t user  Change/Set time limit (superuser only)\n");
		printf("       passwd -a usr passwd fullname home\n");
		printf("                       Add user (superuser only)\n");
		printf("       passwd -r user  Remove user (superuser only)\n");
		error=5;
	}
leave:
	setmode(MODE_CONSOLE);
	exit(error);
}


int getline(UBYTE *a,int lenght,UBYTE echo)
{
	int i=0,c,done=0;

	while(!done)
	{
		if(WaitForChar(Input(),1000000))
		{
			c = Read(Input(),&a[i],1);
			if(c<1)
				return -1;	/* EOF */

			if(a[i]==0x0a || a[i]==0x0d)
			{
				done = 1;
   				a[i] = 0;
   			}
   			else
   			{
				if(a[i]==0x7f || a[i]==0x08)
				{
					if(i>0)
					{
						if(echo) printf("\010 \010");
						i--;
					}
				}
				else
				{
					if(i<lenght-2 && a[i]>0x1f && a[i]<0x80)
					{
						if(echo) Write(Output(),&a[i],1);
						i++;
					}
					else
						if(a[i]==0x03)
						{
							return -1;
						}
				}
			}
			fflush(stdout);
		}
		else
		{
			if(SetSignal(SIGBREAKF_CTRL_C,0) & SIGBREAKF_CTRL_C)
			{
				printf("\n");
				return -1;
			}
		}
	}
	printf("\n");
	return 0;
}


void read_config()
{
	FILE *handle;
	char line[80];
	int i,found=0;

	if(handle=fopen("AXsh:etc/rc","rb"))	/* luetaan config */
	{
		while(!feof(handle))
		{
			if(fgets(line,80,handle)==0)
				break;

			if(i=strlen(line))
				line[i-1]=0;

			if(!found)
			{
				if(!strncmp(line,"%mail:",6))
					found=1;
				else if(!strncmp(line,"%thismachine:",13))
					found=2;
				else if(!strncmp(line,"%defuserlevel:",14))
					found=3;
				else if(!strncmp(line,"%defquota:",10))
					found=4;
				else if(!strncmp(line,"%passwdfile:",12))
					found=5;
			}
			else
			{
				switch(found)
				{
				case 1:
					strcpy(Mailhome,line);
					break;
				case 2:
					strcpy(Host,line);
					break;
				case 3:
					i=0;
					while(UID_Name[i] && strcmp(UID_Name[i],line))
						i++;
					if(UID_Name[i])
						defl=(i<<UID_ACCESSSHIFT);
					break;
				case 4:
					defq=atol(line);
					break;
				case 5:
					strncpy(passwdfile,line,80);
					break;
				}
				found=0;
			}
		}
		fclose(handle);
	}
	else
		printf("AXsh:etc/rc not found!\n");

	if(handle=fopen("AXsh:etc/rc.mail","rb"))	/* luetaan config */
	{
		while(!feof(handle))
		{
			if(fgets(line,80,handle)==0)
				break;

			if(i=strlen(line))
				line[i-1]=0;

			if(!found)
			{
				if(!strncmp(line,"%mailhome:",10))
					found=1;
			}
			else
			{
				switch(found)
				{
				case 1:
					strcpy(Mailhome,line);
					break;
				}
				found=0;
			}
		}
		fclose(handle);
	}
	else
		printf("AXsh:etc/rc.mail not found!\n");
}


void convert_datestamp(char *a,struct DateStamp v)
{
	int year=1978,day,month=1;
	char wdays[7][10]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"},
		 months[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
	unsigned int hours,minutes,seconds,wday,pos1,pos2,
				 dpm[12]={31,28,31,30,31,30,31,31,30,31,30,31};

	wday=(day=v.ds_Days)%7;
	while(day>=(365+((year%4)==0 && (year%100))))
	{
		day-=365+((year%4)==0 && (year%100));
		year++;
	}
	while(day>=(dpm[month-1]+((year%4)==0 && (year%100) && month==2)))
	{
		day-=dpm[month-1]+((year%4)==0 && (year%100) && month==2);
		month++;
	}
	day++;
	year=year%100; /* use only tens and ones */
	hours=v.ds_Minute/60;
	minutes=v.ds_Minute%60;
	seconds=v.ds_Tick/TICKS_PER_SECOND;

	sprintf(a,"%s xx-%s-xx xx:xx.xx",wdays[wday],months[month-1]);
	pos1=strlen(wdays[wday])+1;
	pos2=pos1+strlen(months[month-1])+4;
	a[pos1]=day/10+48;
	a[pos1+1]=day%10+48;
	a[pos2]=year/10+48;
	a[pos2+1]=year%10+48;
	a[pos2+3]=hours/10+48;
	a[pos2+4]=hours%10+48;
	a[pos2+6]=minutes/10+48;
	a[pos2+7]=minutes%10+48;
	a[pos2+9]=seconds/10+48;
	a[pos2+10]=seconds%10+48;
}


int p_listusers(int rows)
{
	int pos,count,more=IsInteractive(Output());
	UBYTE in1[100];

	count=pos=0;
	olduid=CUID;
	CUID=UID_SUPERUSER | olduid;
	while(read_userdata(passwdfile,&user,pos++)!=-1)
	{
		printf("%-16s %d (%-10s) %s",user.Loginname,
				user.UID & ~UID_ACCESSMASK,
				UID_Name[(user.UID & UID_ACCESSMASK)>>UID_ACCESSSHIFT],
				user.Fullname);
		if(user.TimeLimit)	printf(" [%ld min]\n",user.TimeLimit);
		else				printf("\n");
		if(more && (++count>rows-3))
		{
			printf("\033[7mMore?\033[m");
			fflush(stdout);
			if(WaitForChar(Input(),120000000))
				Read(Input(),in1,1);

			if(in1[0]==0x03 || in1[0]=='q' || in1[0]=='Q')
			{
				printf("**Break!\n");
				break;
			}
			printf("\b\b\b\b\b\033[K");
			fflush(stdout);
			count=0;
		}
		if(SetSignal(0,0) & SIGBREAKF_CTRL_C)
		{
			printf("**Break!\n");
			break;
		}
	}
	printf("\n");
	CUID=olduid;
	return 0;
}


int p_chpasswd(char *loginname)
{
	int pos,error=0;
	UBYTE in1[100],in2[100];

	if((pos=search_userdata(passwdfile,&user,0,loginname))!=-1)
	{
		if((user.UID&~UID_ACCESSMASK)!=((CUID)&~UID_ACCESSMASK) && !(CUID & UID_SUPERUSER))
		{
			printf("No privileges for attempted operation\n");
			error=20;
		}
		else
		{
			if(strcmp(user.Password,"********") &&
				((user.UID&UID_SUPERUSER) ||
				 !(CUID & UID_SUPERUSER) ||
				 (user.UID&~UID_ACCESSMASK)==((CUID)&~UID_ACCESSMASK)))
			{
				printf("Type in the old password: ");
				fflush(stdout);
				if(getline(in2,16,0))
					return 20;
			}

			if((CUID & UID_SUPERUSER) ||
				!strcmp(user.Password,crypt(in1,in2)) ||
				!strcmp(user.Password,"********"))
			{
				printf("\nType in the new password: ");
				fflush(stdout);
				if(getline(in1,16,0))
					return 20;
				printf("Retype  the new password: ");
				fflush(stdout);
				if(getline(in2,16,0))
					return 20;
				if(!strcmp(in1,in2))
				{
					if((CUID & UID_SUPERUSER) && !strcmp(in1,"*"))
					{
						strcpy(user.Password,"********");
						write_userdata(passwdfile,&user,pos);
						printf("Password removed.\n");
					}
					else
					if(strlen(in1)>4)
					{
						strcpy(user.Password,crypt(in1,in2));
						write_userdata(passwdfile,&user,pos);
						printf("Password changed.\n");
					}
					else
						printf("Try a longer password.\nPassword not changed\n");
				}
				else
				{
					printf("Password not changed!\n");
					error=5;
				}
			}
			else
			{
				error=10;
				printf("Sorry!\n");
			}
		}
	}
	else
	{
		printf("No such user: %s\n",loginname);
		error=5;
	}
	return error;
}


int p_chfn(char *loginname)
{
	int pos,error=0;
	UBYTE in1[100];

	if((pos=search_userdata(passwdfile,&user,0,loginname))!=-1)
	{
		if(user.UID!=CUID && !(CUID & UID_SUPERUSER))
		{
			printf("No privileges for attempted operation\n");
			error=20;
		}
		else
		{
			printf("Current full name: \"%s\"\nNew full name (or 'none' for empty) : ",user.Fullname);
			fflush(stdout);
			if(getline(in1,44,1))
				return 20;
			if(!strcmp(in1,"none"))
			{
				user.Fullname[0]=in1[0]=0;
				write_userdata(passwdfile,&user,pos);
				printf("Full name cleared\n");
			}
			if(in1[0])
			{
				strcpy(user.Fullname,in1);
				write_userdata(passwdfile,&user,pos);
				printf("Full name changed\n");
			}
		}
	}
	else
	{
		printf("No such user: %s\n",loginname);
		error=5;
	}
	return error;
}


int p_chquota(char *loginname)
{
	int pos,error=0;
	UBYTE in1[100];

	if((pos=search_userdata(passwdfile,&user,0,loginname))!=-1)
	{
		if(!(CUID & UID_SUPERUSER))
		{
			printf("No privileges for changing quota\n");
			error=20;
		}
		else
		{
			printf("Current quota: ");
			if(user.Quota)	printf("%ld",user.Quota);
			else			printf("Unset");
			printf("\nNew quota (or 'unset' for none): ");
			fflush(stdout);
			if(getline(in1,16,1))
				return 20;
			if(!strcmp(in1,"unset"))
			{
				user.Quota=0;
				in1[0]=0;
				write_userdata(passwdfile,&user,pos);
				printf("Quota removed\n");
			}
			if(in1[0])
			{
				user.Quota=atol(in1);
				write_userdata(passwdfile,&user,pos);
				printf("Quota changed\n");
			}
		}
	}
	else
	{
		printf("No such user: %s\n",loginname);
		error=5;
	}
	return error;
}


int p_chlevel(char *loginname)
{
	int i,pos,error=0;
	UBYTE in1[100];

	if((pos=search_userdata(passwdfile,&user,0,loginname))!=-1)
	{
		if(!(CUID & UID_SUPERUSER))
		{
			printf("No privileges for changing accesslevel\n");
			error=20;
		}
		else
		{
			printf("Current accesslevel of %s is %s\n",user.Loginname,
					UID_Name[(user.UID & UID_ACCESSMASK)>>UID_ACCESSSHIFT]);

			printf("New accesslevel : ");
			fflush(stdout);
			if(getline(in1,15,1))
				return 20;
			i=0;
			while(UID_Name[i] && strcmp(UID_Name[i],in1))
				++i;
			if(in1[0] && UID_Name[i])
			{
				user.UID=(user.UID & ~UID_ACCESSMASK) | (i<<UID_ACCESSSHIFT);
			}
			else
			{
				printf("Accesslevel not changed!\n");
				error=5;
			}
			if(!error)
			{
				write_userdata(passwdfile,&user,pos);
				printf("Accesslevel changed\n");
			}
		}
	}
	else
	{
		printf("No such user: %s\n",loginname);
		error=5;
	}
	return error;
}


int p_chtime(char *loginname)
{
	int i,pos,error=0;
	UBYTE in1[100];

	if((pos=search_userdata(passwdfile,&user,0,loginname))!=-1)
	{
		if(!(CUID & UID_SUPERUSER))
		{
			printf("No privileges for changing time limit\n");
			error=20;
		}
		else
		{
			printf("Current limit of %s is %d\n", user.Loginname, user.TimeLimit);
			printf("New time limit : ");
			fflush(stdout);
			if(getline(in1,15,1))
				return 20;
			i=atoi(in1);
			if(i==0 || (i>5 && i<65535))
			{
				user.TimeLimit = i;
			}
			else
			{
				printf("Time limit not changed!\n");
				error=5;
			}
			if(!error)
			{
				write_userdata(passwdfile,&user,pos);
				printf("Time limit changed\n");
			}
		}
	}
	else
	{
		printf("No such user: %s\n",loginname);
		error=5;
	}
	return error;
}


int p_remuser(char *loginname)
{
	int pos,error=0;

	if((pos=search_userdata(passwdfile,&user,0,loginname))!=-1)
	{
		if(!(CUID & UID_SUPERUSER))
		{
			printf("No privileges for removing user\n");
			error=20;
		}
		else
		{
			removeuser(passwdfile,pos);
		}
	}
	else
	{
		printf("No such user: %s\n",loginname);
		error=5;
	}
	return error;
}


int p_finger(char *loginname)
{
	int pos,error=0;
	long lock;
	UBYTE in1[100],in2[100];
	FILE *handle;

	if((pos=search_userdata(passwdfile,&user,0,loginname))!=-1)
	{
		sprintf(in2,"%s@%s",user.Loginname,Host);
		printf("Login name: %-28s In real life: %s\nDirectory : %-28s User ID     : %d (%s)\n",
				in2,
				user.Fullname,
				user.Home,
				user.UID & ~UID_ACCESSMASK,
				UID_Name[(user.UID & UID_ACCESSMASK)>>UID_ACCESSSHIFT]);
		sprintf(in1,"%s.lastlogin",user.Home);
		if(handle=fopen(in1,"rb"))
		{
			fgets(in1,80,handle);
			fclose(handle);
			if(strlen(in1))
				in1[strlen(in1)-1]=0;
		}
		else
		{
			strcpy(in1,"Unknown");
		}
		printf("Last login: %-28s Quota	   : ",in1);
		if(user.Quota)	printf("%ld",user.Quota);
		else 			printf("Unset");

		if(user.TimeLimit)	printf("	Limit: %ld min\n",user.TimeLimit);
		else				printf("\n");

		if(Mailhome[0])
		{
			sprintf(in1,"%s%s",Mailhome,user.Loginname);
			if(lock=Lock(in1,ACCESS_READ))
			{
				Examine(lock,&fib);
				UnLock(lock);
				if(fib.fib_Size)
				{
					convert_datestamp(in1,fib.fib_Date);
					printf("Mail, last arrived at %s\n",in1);
				}
				else
				{
					printf("No unread mail\n");
				}
			}
			else
			{
				printf("No unread mail\n");
			}
		}

		sprintf(in1,"%s.project",user.Home);
		printf("Project: ");
		if(handle=fopen(in1,"rb"))
		{
			fgets(in1,80,handle);
			printf(in1);	/* projectista tulostetaan vain eka rivi */
			fclose(handle);
		}
		else
			printf("\n");
		sprintf(in1,"%s.plan",user.Home);
		if(handle=fopen(in1,"rb"))
		{
			printf("Plan:\n");
			while(!feof(handle))
			{
				if(fgets(in1,80,handle)==0)
					break;

				fputs(in1,stdout);
			}
			fclose(handle);
			printf("\n");
		}
		else
			printf("No plan.\n");
	}
	else
	{
		printf("No such user: %s\n",loginname);
		error=5;
	}
	return error;
}
