/* Stuff common to both the FTP server and client */
#include <stdio.h>
#include "config.h"
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "tcp.h"
#include "ftp.h"
#include "telnet.h"
#include "iface.h"
#include "finger.h"
#include "session.h"

#include <sys/types.h>
#include <sys/stat.h>

#ifndef COH42
#include <sys/inode.h>
#endif



#ifndef TRUE	/* k28 */
#define TRUE 1
#define FALSE 0
#endif

void free();

/* FTP Data channel Receive upcall handler */
void
ftpdr(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	register struct ftp *ftp;
	struct mbuf *bp;
	int wr;
	wr = 0; 
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif
	ftp = (struct ftp *)tcb->user;
	if(ftp->state != RECEIVING_STATE){
		close_tcp(tcb);
		return;
	}
	/* This will likely also generate an ACK with window rotation */
	recv_tcp(tcb,&bp,cnt);


	/* RLW - this new strategy for ascii conversion involves
	 * getting a malloc'd buffer, and copying byte by
	 * byte, skipping any '\r' chars. Then writing out
	 * the buffer in a single shot. The previous method
	 * pulled up a byte, checked it, then wrote the byte
	 * and repeated. The profiling revealed that an ftp
	 * was spending 83% of its time in this algorithm.
	 *
	 */
	if(ftp->type == ASCII_TYPE) {
			char *wbuf, 	/* base of malloc'd buffer */
			     *inbuf,
			     *dptr, 	/* destination - current pos in wbuf */
			     *sptr; 	/* source in bp->data */
			int wr, i, newcnt; 	/* byte counters */

			if(cnt != 0 )
				{
				if( (wbuf=malloc( cnt )) == NULL )
					{
					bp = free_mbuf(bp);
					return;
					}

				if( (inbuf=malloc( cnt )) == NULL )
					{
					free( wbuf );
					bp = free_mbuf(bp);
					return;
					}

				}
			else
				{ /* no data to do */
				bp = free_mbuf(bp);
				return;
				}

			if(pullup(&bp,inbuf,cnt) != cnt)
				{

				bp = free_mbuf(bp);
				free(inbuf);
				free(wbuf);
				return;
				}
				/* scan the source, and copy each char that
				 * is not '\r' to the destination buffer..
				 * keeping track of number of chars so we
				 * don't page fault and so we can make
				 * write of the correct length. RLW
				 */
			for( i = 0, sptr = inbuf, dptr = wbuf, newcnt = 0;
			     i < cnt; i++, sptr++, dptr++, newcnt++ )
				{
					/* skip any number of '\r'
					 * - there _may_ be times when this
					 * is not the desired behavior.
					 */
				while( *sptr == '\r' && i < cnt ) 
					{sptr++; i++;}

					/* move non-car. ret chars */
				*dptr = *sptr;
				}

				/* write it out to disk */
			wr = fwrite(wbuf,1,newcnt,ftp->fp);

			if( ftp->hashbell & FTPHASH ) {
				int val, i;
				ftp->databytes += wr;
				val = (ftp->databytes >> 10 ) - ftp->lasthash;
				ftp->lasthash = (ftp->databytes >> 10 );
				for( i = 0; i < val; i++) printf( "#" );
				fflush(stdout);
			}
				/* free resources */
			bp = free_mbuf(bp);
			free(wbuf);
			free(inbuf);
				/* done */
			return;
			} /* ftp->type = ASCII */
#ifdef DEAD
		/* the old ascii conversion algorithm */
		while(pullup(&bp,&c,1) == 1){



			if(c != '\r')

				putc(c,ftp->fp);
		}
		return;
	}

#endif /* DEAD */


	while(bp != NULLBUF){
		if(bp->cnt != 0)
			wr = fwrite(bp->data,1,(unsigned)bp->cnt,ftp->fp);
			if( ftp->hashbell & FTPHASH ) {
				int val, i;
				ftp->databytes += wr;
				val = (ftp->databytes >> 10 ) - ftp->lasthash;
				ftp->lasthash = (ftp->databytes >> 10 );
				for( i = 0; i < val; i++) printf( "#" );
				fflush(stdout);
			}
		bp = free_mbuf(bp);
	}

 	if(ftp->fp != stdout && ferror(ftp->fp)){ /* write error (dsk full?) */
 		fclose(ftp->fp);
 		ftp->fp = NULLFILE;
 		close_self(tcb,RESET);
 	}
}
/* FTP Data channel Transmit upcall handler */
void
ftpdt(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	struct ftp *ftp;
	struct mbuf *bp;
	register char *cp;
	register int c;
#ifdef	SYS5
	struct stat ss_buf;
#endif


	static int savedch = FALSE;	/* added k28 - this mod costs 80 bytes when
			applied to the Unix version */

#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	ftp = (struct ftp *)tcb->user;
	if(ftp->state != SENDING_STATE){
		close_tcp(tcb);
		return;
	}
	if((bp = alloc_mbuf(cnt)) == NULLBUF){
		/* Hard to know what to do here - You bet!  And this could be a real
		 * memory leak if memory is in yellow zone - K5JB */
		return;
	}
	c = 0;	/* double duty as character and flag */
	if(ftp->type == IMAGE_TYPE){
		bp->cnt = fread(bp->data,1,cnt,ftp->fp);
			if( ftp->hashbell & FTPHASH ) {
				int val, i;
				ftp->databytes += bp->cnt;
				val = (ftp->databytes >> 10 ) - ftp->lasthash;
				ftp->lasthash = (ftp->databytes >> 10 );
				for( i = 0; i < val; i++) printf( "#" );
				fflush(stdout);
			}
		if(bp->cnt != cnt)
			c = EOF;	/* flag to get outa here */
	} else { 	/* is ASCII type */
		cp = bp->data;
		/* lifted this out routine I wrote for finger so we wouldn't
		 * have a chronic queue of (window - 1).  If necessary to handle
		 * ill formed MS-DOS text files, just remove the #ifs -- K5JB k28
		 */

		if(savedch){	/* unlikely case */
			*cp++ = '\r';
			*cp++ = '\n';
			bp->cnt += 2;
			cnt -= 2;
			savedch = FALSE;
		}

		while(cnt > 0 && (c = getc(ftp->fp)) != EOF){
			if( ftp->hashbell & FTPHASH ) {
				int val, i;
				ftp->databytes++;
				val = (ftp->databytes >> 10 ) - ftp->lasthash;
				ftp->lasthash = (ftp->databytes >> 10 );
				for( i = 0; i < val; i++) printf( "#" );
				fflush(stdout);
			}
			switch((char)c) {
				case '\032':	/* NO ^Z's! - they clear screens */
					break;


				case '\r':	/* Unix and the others use \n as */
								/*	EOL character */
					break;
				case '\n':	/* convert LF to CR/LF */

					if(cnt == 1){ /* unlikely case, no room for 2 chars */
						savedch = TRUE;
						cnt = 0;	/* make it a short one - drop out */
						break;
					}
					*cp++ = '\r';
					bp->cnt++;
					cnt--;
						/* and fall through */

				default:
					*cp++ = (char)c;
					bp->cnt++;
					cnt--;
					break;
			}
		} /* while !feof() && cnt != 0 */
	} /* else not image type */
	if(bp->cnt != 0)
		send_tcp(tcb,bp);
	else
		free_p(bp);
	if(c == EOF){	/* EOF seen in either ASCII or IMAGE types */
#ifdef	UNIX
#ifdef	SYS5
#ifndef	hp9000s500
/* If ftp->fp points to an open pipe (from dir()) it must be closed with */
/* pclose().  System V fstat() can tell us if this was a pipe or not. */
		if (fstat(fileno(ftp->fp), &ss_buf) < 0)
			perror("ftpdt: fstat");
#ifdef COH386
		if ((ss_buf.st_mode & S_IFPIP) == S_IFPIP)
#else
		if ((ss_buf.st_mode & IFIFO) == IFIFO)
#endif
			pclose(ftp->fp);	/* close pipe from dir */
		else
			fclose(ftp->fp);
#else	/* hp9000s500 */
/* HP-UX 5.21 on the 500 doesn't understand IFIFO, since we probably don't *.
/ * care anyway, treat it like BSD is treated... */
		if (pclose(ftp->fp) < 0)
			fclose(ftp->fp);
#endif	/* hp9000s500 */
#else	/* SYS5 */
/* Berkeley Unix can't tell if this was a pipe or not.  Try a pclose() */
/* first.  If this fails, it must have been an open file. */
		if (pclose(ftp->fp) < 0)
			fclose(ftp->fp);
#endif	/* SYS5 */
#else	/* UNIX */
/* Anything other than Unix */
		fclose(ftp->fp);
#endif	/* UNIX */
		ftp->fp = NULLFILE;
		close_tcp(tcb);
	}	/* if EOF */
}
/* Allocate an FTP control block */
struct ftp *
ftp_create(bufsize)
unsigned bufsize;
{
	void ftp_delete();
	register struct ftp *ftp;

#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif
	if((ftp = (struct ftp *)calloc(1,sizeof (struct ftp))) == NULLFTP)
		return NULLFTP;
	if(bufsize != 0 && (ftp->buf = malloc(bufsize)) == NULLCHAR){
#ifdef DEBUG
		printf("called by ftp_create\n");fflush(stdout);
#endif
		ftp_delete(ftp);
#ifdef DEBUG
		printf("called by ftp_create\n");fflush(stdout);
#endif
		return NULLFTP;
	}
	ftp->state = COMMAND_STATE;
	ftp->type = ASCII_TYPE;	/* Default transfer type */
	return ftp;
}
/* Free resources, delete control block */
void
ftp_delete(ftp)
register struct ftp *ftp;
{
	int i;
	void freesession();
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(ftp->fp != NULLFILE && ftp->fp != stdout)
		fclose(ftp->fp);
	if(ftp->data != NULLTCB)
		del_tcp(ftp->data);
	if(ftp->username != NULLCHAR)
		free(ftp->username);
        for (i = 0; i < MAXPATH; i++)
	  if(ftp->path[i] != NULLCHAR)
	    free(ftp->path[i]);
	if(ftp->buf != NULLCHAR)
		free(ftp->buf);
	if(ftp->cd != NULLCHAR)
		free(ftp->cd);
	if(ftp->session != NULLSESSION)
		freesession(ftp->session);
	free((char *)ftp);
}

