/* System-dependent definitions of various files, spool directories, etc */
#include <stdio.h>
#include <tos.h>
#include <string.h>
#include <transprt.h>
#include <ctype.h>
#include <stdlib.h>
#include "files.h"
#include "md5.h"

#define	ATARI		1	
#define	READ_TEXT	"r"
#define	WRITE_TEXT	"w"

char	Whitespace[] = " \t\r\n";
extern	int 	def_drv, showbanner, banlines;
extern	char	*wmsg[];

#ifdef	ATARI
char System[] = "UNIX";			/* Was MSDOS $@#!	*/
char *Userfile = "ftpusers";	/* Authorized FTP users and passwords */
char *Logfile = "ftp_serv.log";	/* FTP Server Log file		*/
char *Welcome= "welcome.ftp";	/* Welcome file		*/
char *Tmpdir = "tmp";
char Eol[] = "\r\n";
#define	SEPARATOR	"\\"
#endif

/*	========== Match resolve_KA9 to STinG resolve =============== */
/*	done elsewhere (22 Oct 97)	*/

/* Convert single hex-ascii character to binary */
int 	htob(char c)
{
	if('0' <= c && c <= '9')
		return c - '0';
	else if('a' <= c && c <= 'f')
		return c - 'a' + 10;
	else if('A' <= c && c <= 'F')
		return c - 'A' + 10;
	else
		return -1;
}

/* Read an ascii-encoded hex string, convert to binary and store in
 * output buffer. Return number of bytes converted
 */
int 	readhex (uint8 *out, char *in, int size)
{
	int c, count;

	if (in == NULL)
		return 0;
	for (count=0; count < size; count++)
	{
		while (*in == ' ' || *in == '\t')
			in++;	/* Skip white space */
		if ((c = htob(*in++)) == -1)
			break;	/* Hit non-hex character */
		out[count] = c << 4;	/* First nybble */
		while (*in == ' ' || *in == '\t')
			in++;	/* Skip white space */
		if ((c = htob (*in++)) == -1)
			break;	/* Hit non-hex character */
		out[count] |= c;	/* Second nybble */
	}
	return count;
}

/* ------- Remove \r\n from end of line --------- */

void	rip (register char *s)
{
	register char *cp;

	if((cp = strchr(s,'\n')) != NULL)
		*cp = '\0';
	if((cp = strchr(s,'\r')) != NULL)
		*cp = '\0';
}

/* ==================================================== */
static char *rootdir = "";

/* Establish a root directory other than the default. Can only be called
 * once, at startup time
 */
void	initroot (char *root)
{
	rootdir = strdup( root );

	Startup = rootdircat (Startup);
	Userfile = rootdircat(Userfile);
	Logfile = rootdircat (Logfile);
	Welcome= rootdircat (Welcome);
}

/* Concatenate root, separator and arg strings into a malloc'ed output
 * buffer, then remove repeated occurrences of the separator char
 */
char *rootdircat (char *filename)
{
	char *out = filename;

	if ( strlen(rootdir) > 0 )
	{
		char *separator = SEPARATOR;

		out = malloc( strlen(rootdir)		/* no mallocw */
				+ strlen(separator)
				+ strlen(filename) + 3);

		out[0] = def_drv + 'A';		out[1]= ':';	out[2] = '\0';
		strcat (out,rootdir);
		strcat (out,separator);
		strcat (out,filename);
		if (*separator != '\0')
		{
			char *p1, *p2;

			/* Remove any repeated occurrences */
			p1 = p2 = out;
			while(*p2 != '\0'){
				*p1++ = *p2++;
				while(p2[0] == p2[-1] && p2[0] == *separator)
					p2++;
			}
			*p1 = '\0';
		}
	}
	return out;
}

/* Read through FTPUSERS looking for user record
 * Returns line which matches username, or NULL when no match.
 * Each of the other variables must be copied before freeing the line.
 */
char	*userlookup (username, password, directory, permission, ip_address)
char	*username;
char	**password;
char	**directory;
int 	*permission;
int32	*ip_address;
{
	FILE	*fp;
	uint32	ip_addr;
	char	*buf;
	char	*cp;

	if((fp = fopen (Userfile, READ_TEXT)) == NULL)
		/* Userfile doesn't exist */
		return NULL;

	buf = malloc (BUFSIZ);
	while ( fgets (buf, BUFSIZ, fp) != NULL )
	{
		if(*buf == '#')
			continue;	/* Comment */

		if((cp = strchr (buf, ' ')) == NULL)
			/* Bogus entry */
			continue;
		*cp++ = '\0';		/* Now points to password */

		if( stricmp (username, buf) == 0 )
			break;		/* Found user */
	}
	if (feof (fp) )
	{
		/* username not found in file */
		fclose(fp);
		free(buf);
		return NULL;
	}
	fclose(fp);

	if ( password != NULL )
		*password = cp;

	/* Look for space after password field in file */
	if ((cp = strchr (cp, ' ')) == NULL)
	{
		/* Invalid file entry */
		free (buf);
		return NULL;
	}
	*cp++ = '\0';	/* Now points to directory field */

	if ( directory != NULL )
		*directory = cp;

	if((cp = strchr (cp, ' ')) == NULL)
	{
		/* Permission field missing */
		free (buf);
		return NULL;
	}

	*cp++ = '\0';	/* now points to permission field */

	if ( permission != NULL )
		*permission = (int)strtol( cp, NULL, 0 );

	if ((cp = strchr (cp, ' ')) == NULL)
	{
		/* IP address missing */
		if ( ip_address != NULL )
			*ip_address = 0L;
	} else {
		*cp++ = '\0';	/* now points at IP address field */
		if ( ip_address != NULL )
			resolve ( cp , (char **)NULL, &ip_addr, 1);
	}

	return buf;
}

/* Subroutine for logging in the user whose name is name and password is pass.
 * The buffer path should be long enough to keep a line from the userfile.
 * If pwdignore is true, the password check will be overridden.
 * The return value is the permissions field or -1 if the login failed.
 * Path is set to point at the path field, and pwdignore will be true if no
 * particular password was needed for this user.
 */
int 	userlogin (name, pass, path, len, pwdignore)
char	*name;
char	*pass;
char	**path;
int 	len;			/* Length of buffer pointed at by *path */
int 	*pwdignore;
{
	char	*buf;
	char	*password;
	char	*directory;
	int 	permission;
	int 	anonymous;
	char	*cp;
	uint8	hashpass[16],digest[16];
	MD5_CTX md;

	if ( (buf = userlookup( name, &password, &directory,
			&permission, NULL )) == NULL )
		return -1;

	anonymous = *pwdignore;
	if (strcmp (password, "*") == 0)
	{
		anonymous = TRUE;	/* User ID is password-free */
	}
	else
	{
		if (readhex (hashpass, password, sizeof(hashpass)) != sizeof (hashpass))
		{
			/* Invalid hashed password in file */
			free(buf);
			return -1;
		}
		MD5Init (&md);
		MD5Update ( &md, (unsigned char *)name, strlen (name) );
		MD5Update ( &md, (unsigned char *)pass, strlen (pass) );
		MD5Final ( digest, &md );
		if (memcmp (digest, hashpass, sizeof(hashpass)) != 0)
		{
			/* Incorrect password given */
			free(buf);
			return (-1);
		}
	}
	if ( strlen( directory ) + 1 > len )
	{
		/* not enough room for path */
		free (buf);
		return (-1);
	}

	strcpy (*path, directory);
	/* Convert any backslashes to forward slashes, for backward
	 * compatibility with the old NET
	 */
	while((cp = strchr(*path,'\\')) != NULL)
		*cp = '/';

	free (buf);
	*pwdignore = anonymous;
	/* Finally return the permission bits */
	return (permission);
}

/* MD5 hash plaintext passwords in user file */
void	usercvt (void)
{
	FILE 	*fp, *fptmp;
	char	*buf;
	uint8	hexbuf[16], digest[16];
	int 	needsit = 0;
	int 	len, nlen, plen, i;
	char	*pass;
	MD5_CTX md;

	if ((fp = fopen (Userfile, READ_TEXT)) == NULL)
		return;		/* Userfile doesn't exist */

	buf = malloc (BUFSIZ);
	while (fgets (buf, BUFSIZ, fp) != NULL)
	{
		rip (buf);
		len = strlen (buf);
		if (len == 0 || *buf == '#')
			continue;	/* Blank or comment line */

		if ((nlen = strcspn (buf, Whitespace)) == len)
			continue;	/* No end to the name! */

		/* Skip whitespace between name and pass */
		for (pass = &buf[nlen]; isspace(*pass); pass++)
			;
		if (*pass != '\0' && *pass != '*'
		 && readhex (hexbuf, pass, sizeof(hexbuf)) != 16)
		 {
			needsit = 1;
			break;
		}
	}
	if (!needsit)
	{
		/* Everything is in order */
		fclose (fp);
		free (buf);
		return;
	}
	/* At least one entry needs its password hashed */
	rewind (fp);
	fptmp = tmpfile();
	while (fgets (buf, BUFSIZ, fp) != NULL)
	{
		rip (buf);
		if ((len = strlen(buf)) == 0 || *buf == '#'
		 || (nlen = strcspn (buf, Whitespace)) == len)
		 {
			/* Line is blank, a comment or unparseable;
			 * copy unchanged
			 */
			fputs (buf, fptmp);
			fputc ( '\n', fptmp );
			continue;
		}
		/* Skip whitespace between name and pass */
		for (pass = &buf[nlen]; isspace(*pass); pass++)
			;

		if(*pass == '\0' || *pass == '*'
		 || (plen = strcspn (pass, Whitespace)) == strlen(pass)
		 || readhex (hexbuf, pass, sizeof(hexbuf)) == sizeof (hexbuf))
		 {
			/* Other fields are missing, no password is required,
			 * or password is already hashed; copy unchanged
			 */
			fputs (buf, fptmp);
			fputc ('\n',fptmp);
			continue;
		}
		MD5Init (&md);
		MD5Update (&md,(unsigned char *)buf,  nlen);	/* Hash name */
		MD5Update (&md,(unsigned char *)pass, plen);	/* Hash password */
		MD5Final (digest, &md);
		fwrite (buf, 1, nlen, fptmp);	/* Write name */
		fputc (' ', fptmp);		/* and space */
		for(i=0; i<16; i++)		/* Write hashed password */
			fprintf (fptmp, "%02x", digest[i]);
		fputs (&pass[plen], fptmp);		/* Write remainder of line */
		fputc ('\n', fptmp);
	}
	/* Now copy the temp file back into the userfile */
	fclose (fp);
	rewind (fptmp);
	if ((fp = fopen (Userfile, WRITE_TEXT)) == NULL)
	{
		printf ("Can't rewrite %s\n", Userfile);
		free (buf);
		return;
	}
	while (fgets (buf, BUFSIZ, fptmp) != NULL)
		fputs (buf, fp);
	fclose (fp);
	fclose (fptmp);
	free (buf);
}
