/* Routines common to both the FTP client and server
 * Copyright 1991 Phil Karn, KA9Q
 */
 /* Mods by G1EMM */
 /* Mods by KO4KS */
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "socket.h"
#include "proc.h"
#include "ftp.h"
#include "ftpcli.h"
#include "usock.h"
#ifdef LZW
#include "lzw.h"
#endif
#include "md5.h"


#define	MD5BLOCK	64	/* Preferred MD5 block size */

static void hashit __ARGS((int hash,long total, long *hmark));

static void
hashit (hash, total, hmark)
int hash;
long total, *hmark;
{
	while(hash && total >= (*hmark)+1000)	{
		if (hash == V_HASH)
			tputc('#');
		if(hash == V_BYTE)
			tprintf("Bytes sent : %ld\r", total);
		*hmark += 1000;
	}

}


/* Send a file (opened by caller) on a network socket.
 * Normal return: count of bytes sent
 * Error return: -1
 * re-written by KO4KS to (1) reduce code size (2) all binary compression
 */
long
sendfile(fp,s,mode,hash)
register FILE *fp;	/* File to be sent */
register int s;		/* Socket to be sent on */
int mode;	/* Transfer mode */
int hash;	/* Print hash marks every BLKSIZE bytes */
{
register int c;
register long total = 0;
long hmark = 0;
int oldf = 0;
struct usock *up;
register struct mbuf *bp;

	if (mode == ASCII_TYPE)		{
		oldf = setflush(s,-1);
		/* Let the newline mapping code in usputc() do the work */
		sockmode(s,SOCK_ASCII);
	} else	
		sockmode(s,SOCK_BINARY);
	up = itop(s);
	if (mode != ASCII_TYPE && up->zout == NULLLZW)	{	/* also, not lzw compressed */
		for(;;){
			pwait (0);	/* be a good neighbor and share! */
			bp = ambufw(BLKSIZE);
			if((bp->cnt = fread(bp->data,1,BLKSIZE,fp)) == 0){
				free_p(bp);
				break;
			}
			pwait (0);	/* be a good neighbor and share! */
			total += bp->cnt;
			if(send_mbuf(s,bp,0,NULLCHAR,0) == -1){
				total = -1;
				break;
			}
			hashit (hash, total, &hmark);
		}
	} else	{

	    	/* At this point, it is either ASCII_TYPE, or LZW compressed */
		while((c = getc(fp)) != EOF)	{
			if (mode == ASCII_TYPE)		{
#ifndef TNOS_68K
#if !defined(UNIX) && !defined(__TURBOC__)
				if(c == '\r'){
					/* Needed only if the OS uses a CR/LF
					 * convention and getc doesn't do
					 * an automatic translation
					 */
					continue;
				}
#endif
#else
				if(c == '\l')
					continue;
#endif
				if (!c)
					continue;
			}
			if(usputc(s,(char)c) == -1){
				total = -1;
				break;
			}
			total++;
			hashit (hash, total, &hmark);
			if (!(total % 256))
				pwait (0);	/* be a good neighbor and share! */
		}
	}
	if (mode == ASCII_TYPE)	{
		usflush(s);
		setflush(s,oldf);
	}
	if(hash)
		tputc('\n');
	return total;
}
/* Receive a file (opened by caller) from a network socket.
 * Normal return: count of bytes received
 * Error return: -1
 * re-written by KO4KS to (1) reduce code size (2) all binary compression
 */
long
recvfile(fp,s,mode,hash)
register FILE *fp;
register int s;
int mode;
int hash;
{
register int c;
register long total = 0;
long hmark = 0;
int cnt;
struct usock *up;
struct mbuf *bp;

	if (mode == ASCII_TYPE)
		sockmode(s,SOCK_ASCII);
	else
		sockmode(s,SOCK_BINARY);
	up = itop(s);
	if (mode != ASCII_TYPE && up->zout == NULLLZW)	{	/* also, not lzw compressed */
		while((cnt = recv_mbuf(s,&bp,0,NULLCHAR,0)) != 0){
			pwait (0);	/* be a good neighbor and share! */
			if(cnt == -1){
				total = -1;
				break;
			}
			total += cnt;
			hashit (hash, total, &hmark);
			pwait (0);	/* be a good neighbor and share! */
			if(fp != NULLFILE){
				if(write_p(fp,bp) == -1){
					free_p(bp);
					total = -1;
					break;
				}
				free_p(bp);
			} else
				send_mbuf(Curproc->output, bp, 0, NULLCHAR, 0);
			pwait (0);	/* be a good neighbor and share! */
		}
	} else	{
		while((c = recvchar(s)) != EOF){
			if(fp != NULLFILE){
#if !defined(UNIX) && !defined(__TURBOC__) && !defined(AMIGA)
				if (mode == ASCII_TYPE)		{
					if(c == '\n'){
						/* Needed only if the OS uses a CR/LF
						 * convention and putc doesn't do
						 * an automatic translation
						 */
						putc('\r',fp);
					}
				}
#endif
				if(putc(c,fp) == EOF){
					total = -1;
					break;
				}
			} else
				tputc((char)c);
			total++;
			hashit (hash, total, &hmark);
			if (!(total % 256))
				pwait (0);	/* be a good neighbor and share! */
		}
	}
	/* Detect an abnormal close */
	if(socklen(s,0) == -1)
		total = -1;
	if(hash)
		tputc('\n');
	return total;
}
/* Determine if a file appears to be binary (i.e., non-text).
 * Return 1 if binary, 0 if ascii text after rewinding the file pointer.
 *
 * Used by FTP to warn users when transferring a binary file in text mode.
 */
int
isbinary(fp)
FILE *fp;
{
	int c,i;
	int rval;

	rval = 0;
	for(i=0;i<512;i++){
		if((c = getc(fp)) == EOF)
			break;
		if(c & 0x80){
			/* High bit is set, probably not text */
			rval = 1;
			break;
		}
	}
	/* Assume it was at beginning */
	fseek(fp,0L,SEEK_SET);
	return rval;
}

unsigned long
getsize(fp)
FILE *fp;
{
	fseek(fp,0L,SEEK_END);	/* Go to end of file */
	return (unsigned long) ftell(fp);	/* Return file pointer position */
}

/* Compute checksum of the first n bytes */
unsigned long
checksum(fp,n)
FILE *fp;
long n;
{
	unsigned long sum;
	long i;
	int c;

	rewind(fp);
	sum = 0;
	for(i=1;i<=n;i++){
		if((c = fgetc(fp)) == EOF)
			break;
		sum += (unsigned long)c;
	}
	return sum;
}

/* Compute MD5 hash of local file */
int
md5hash(fp,hash,ascii)
FILE *fp;
char hash[16];
int ascii;
{
	MD5_CTX md;
	char *buf;
	int len;

	MD5Init(&md);

	if(ascii){
		/* Text file, hash canonical form (newlines = crlf) */
		char *cp;
		int c;

		cp = buf = malloc(MD5BLOCK);
		len = 0;
		while((c = fgetc(fp)) != EOF){
			if(c == '\n'){
				*cp++ = '\r';
				if(++len == MD5BLOCK){
					MD5Update(&md,buf,len);
					pwait(NULL);
					cp = buf;
					len = 0;
				}
			}
			*cp++ = c;
			if(++len == MD5BLOCK){
				MD5Update(&md,buf,len);
				pwait(NULL);
				cp = buf;
				len = 0;
			}
		}
		if(len != 0)
			MD5Update(&md,buf,len);
		free(buf);
	} else {
		buf = malloc(BUFSIZ);
		while((len = fread(buf,1,BUFSIZ,fp)) != 0){
			MD5Update(&md,buf,len);
			pwait(NULL);
		}
		free(buf);
	}
	MD5Final(&md);
	memcpy(hash,md.digest,16);
	return 0;
}

