#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#ifdef WIN32
#include <direct.h>
#endif
#ifdef unix
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <dirent.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>

#include "debug.h"
#ifdef WIN32
#include <io.h>
#include <windows.h>
#endif

#ifdef WIN32
#ifndef _S_IFDIR
#define _S_IFDIR 0040000
#endif
#define S_ISDIR(x) ((_S_IFDIR & x)>0)
#endif

#include "tcplib.h"
#include "ftplib.h"
#include "lprintf.h"

/* Local coppies of lib.c stuff #defined to avoid name conflicts */
#define strcmpnc ftp_strcmpnc
#define cvt_lf_crlf ftp_cvt_lf_crlf
#define cvt_crlf_lf ftp_cvt_crlf_lf
#define lib_file_size ftp_lib_file_size
#define lib_file_osize ftp_lib_file_osize
#define lib_path_split ftp_lib_path_split
#define wild_end ftp_wild_end
#define wild_next ftp_wild_next
#define wild_first ftp_wild_first
#define wild_split ftp_wild_split
#define mystrtok ftp_mystrtok
#define ncat ftp_ncat
#define match_wild ftp_match_wild
#define domatch ftp_domatch

int cvt_lf_crlf(char *bf, int r,  char *tmp);
int cvt_crlf_lf(char *bf, int r);
int lib_file_osize(FILE *f);
int lib_file_size(char *fname);
int lib_path_split(char *in, char *path, char *fname);
int strcmpnc(char *s1, char *s2);
char *mystrtok(char *start, char *toks, char **upto);
char *ncat(char *dst, char *src, int len);
#define ncpy ncpy_ftplib
char *ncpy(char *dst, char *src, int len);
#include "defines.h"
#ifndef FALSE
#define FALSE 0
#endif

int ftp_lib_isdir(char *fname);

typedef struct XWILD_DATA {
	int dev,inode;
	int isdir;
	int islink;
	int isnorm;
	int issub;
	int size;
	int mode;
	int nlinks;
	int gid,uid;
	char symlink[BFSZ];
	char name[BFSZ];
	time_t time_create,time_access,time_write;
	struct XWILD_DATA *next;
} WILD_DATA;

#ifdef WIN32
typedef struct {long h;} WILD_HANDLE;
#else
typedef struct {DIR *h; char *path; char *wild;} WILD_HANDLE;
#endif

#ifdef WIN32
#define snprintf _snprintf
#endif


int wild_first(WILD_HANDLE **hh, char *xwild, int wildBFSZ, WILD_DATA *data);
int wild_next(WILD_HANDLE *h, WILD_DATA *data);
int wild_end(WILD_HANDLE **hh);


#ifdef WIN32
#define vsnprintf _vsnprintf
#endif

#ifndef TRUE
#define TRUE (!FALSE)
#endif

int ftp_accept(Ftplink *l, char *Resp, char *reason);
static int isdebug;
static int isunique;

static int (*real_emsg)(const char * arg_list, ...);
static int (*real_imsg)(const char * arg_list, ...);
static char newer[BFSZ];

#define imsg not_the_real_imsg_for_easy_compile
#define emsg not_the_real_emsg_for_easy_compile
void imsg(char *arg_list, ...){
	char bf[BFSZ];
    va_list arg_ptr;
    char *format;
    int len;

	if (real_imsg == NULL) return;
    va_start(arg_ptr, arg_list);
    format = (char *) arg_list;
    len = vsnprintf(bf,BFSZ-1, format, arg_ptr); bf[BFSZ-1] = 0;
	real_imsg("%s", bf);
}
void emsg(char *arg_list, ...){
	char bf[BFSZ];
    va_list arg_ptr;
    char *format;
    int len;

	if (real_emsg == NULL) return;
    va_start(arg_ptr, arg_list);
    format = (char *) arg_list;
    len = vsnprintf(bf,BFSZ-1, format, arg_ptr); bf[BFSZ-1] = 0;
	real_emsg("%s", bf);
}
int ftp_init(int (*ximsg)(const char * arg_list, ...), int (*xemsg)(const char * arg_list, ...) )
{
	real_imsg = ximsg;
	real_emsg = xemsg;
	tcp_init(ximsg);
	return TRUE;
}
static int isssl;

int ftp_setssl(int x)
{
	isssl = x;
	return TRUE;
}
int ftp_setunique(int x)
{
	isunique = x;
	return TRUE;
}
int ftp_setnewer(char *s, int len)
{
	lprintf(newer,len -10,"%s", s);
	return TRUE;
}
static int isabort;
int ftp_setabort(int t)
{
	isabort = t;
	return TRUE;
}
int ftp_abort_fn(char *type, int sz, int tag)
{
	/*Dummy function for callback*/
	return FALSE;
}

static int doprint;
int ftp_getprint_responses(void)
{
	return doprint;
}

int ftp_setprint_responses(int x)
{
	doprint = x;
	return TRUE;
}

int ftp_setdebug(int x)
{
	isdebug = x;
	return TRUE;
}
static int g_isblock;
static int g_isascii;
static int isappend;
int ftp_setblock(int x)
{
	g_isblock = x;
	return TRUE;
}
int ftp_getascii(void)
{
	return g_isascii;
}
int ftp_setascii(int x)
{
	g_isascii = x;
	return TRUE;
}
int ftp_setappend(int x)
{
	isappend = x;
	return TRUE;
}

#define return_reason(x) {ncpy(reason, x,300);return FALSE;}

/*** Call this to see if ftp cmd channel it is alive, or has closed. ***/
int ftp_isalive(Ftplink *l, char *reason){
//	char buff[BFSZ];
	if (l == NULL)               return_reason("Link is NULL");
	if ((int)(l->cmd) <= 0)             return_reason("Hardware link broken");
	/*if (tcp_read_timed(l->cmd, buff, BFSZ-2, 100)<= 0) return_reason("Ftp link is closed")
	else{
		char *s = strstr(buff, "421");
		if (s != NULL) {
			char *e;
			e = strchr(s, '\r'); if (e != NULL) *e = 0;
			e = strchr(s, '\n'); if (e != NULL) *e = 0;
			return_reason(s);
		}
	}*/
	if (!tcp_isopen(l->cmd))     return_reason("connection lost");
	if (!tcp_check_open(l->cmd)) return_reason("connection lost");
	if (!tcp_isopen(l->cmd))     return_reason("connection lost");
	return TRUE;
}

int ftp_accept_data_chan(Ftplink *l, char *reason)
{
	char tmp2[BFSZ], *s;
	int got_response = FALSE;
	int lcl,rmt;
	strcpy(reason,"");
	l->portok = FALSE;

	if (tcp_peek_line_timed(l->cmd, tmp2, BFSZ-2, 1000) > 0){
		/* we might want to not start data connection */
		s = tmp2;
		if (atoi(s)<=199){
			//lib_msleep(100);
			tcp_peek_line_timed(l->cmd, tmp2, BFSZ-2, 1000);
			s = strchr(tmp2, '\n');
			if (s!= NULL) s++;
			else s = tmp2;
		}
		strcpy(reason, "error before connecting");
		if (atoi(s)>=400) return FALSE;
	}

	if (l->passive) {
		lprintf(reason, BFSZ-1, "Connecting to remote system %s %d\n",l->passive_host,l->passive_port);
		tcp_new(&l->data);
		imsg("ftp: Passive mode, open (host) port %d\n",l->passive_host,l->passive_port);
		if (!tcp_open(l->data,l->passive_host,l->passive_port,NULL)) {
			tcp_error(l->data,tmp2);
			tcp_free(&l->data);                /* Free Data channel */
			lprintf(reason, BFSZ-1, "Couldn't accept data connection, reason %s", tmp2);
			return FALSE;
		}
	} else {
		lprintf(reason, BFSZ-1, "Waiting for incoming connection");
		if (!tcp_accept(l->listen,&l->data,"ftp")) {
			tcp_error(l->listen,tmp2);
			lprintf(reason, BFSZ-1, "Couldn't accept data connection, reason %s", tmp2);
			return FALSE;
		}
	}
	{
		int lcl,rmt;
		tcp_getports(l->data,&lcl,&rmt);
		imsg("Data connection, ports lcl=%d rmt=%d \n",lcl,rmt);
		tcp_getports(l->cmd,&lcl,&rmt);
		imsg("Control          ports lcl=%d rmt=%d \n",lcl,rmt);
	}
	if (!isssl) {
		lprintf(reason, BFSZ-1, "Got data connection");
	}else{
		if (!tcp_ssl_init_real(l->data,FALSE,FALSE,NULL,l->cmd,tmp2,FALSE)) {
			tcp_free(&l->data);                /* Free Data channel */
			lprintf(reason, BFSZ-1, "Got data connection, No SSL, reason %s", tmp2);
			return FALSE;
		}
		tcp_getports(l->data,&lcl,&rmt);
		tcp_getip(l->data,tmp2);
		imsg("SSL started on data connection. lcl=%d rmt=%d (%s)\n",lcl,rmt,tmp2);
	}
	tcp_nodelay(l->data,TRUE);
	return TRUE;
}

int ftp_mput_file(Ftplink *l, Chan *c, Chan **xlisten, Chan **cdata, char *fname, char *rmtfile, int isblock, char *reason)
{
	/* Ok, now send the file */
	char resp[BFSZ];
	char tmp[BFSZ];
	int r;
	strcpy(resp,"");
	if (l->fn!=NULL) {
		l->fn("send", 0, l->fntag);
		r = lib_file_size(fname);
		if (r!= -1){
			lprintf(tmp, BFSZ -1, "size %d", r);
			l->fn(tmp, 0, l->fntag);
		}
	}
	if (isdebug)
		imsg("mput %.500s -> %.500s  listen %p, cdata %p\n",fname,rmtfile,*xlisten,*cdata);

	tcp_printf(c,"STOR %.500s\r\n",rmtfile);

	//if (!ftp_response(c,150,199,reason)) goto ex;

	/*if (!ftp_response(c,150, 399,reason)){imsg("DataLink failed (%s) \n",reason,resp); goto ex;}/**/
	if (*cdata==NULL) {
		/* Accept the incoming connection */
		if (!ftp_accept_data_chan(l,reason)) {
			tmp[0] = '\0';
			ftp_response(c,150,299,tmp);
			if (strlen(tmp)>0){
				snprintf(reason, BFSZ-2, "%s", tmp);
			}
			if (strcmpnc(resp, "No response from server")==0)
				ftp_do_abort(l);
			goto ex;
		}
	}
	imsg("Now send the file %d\n",isblock);
	if (isblock)	r = ftp_send_block(l,fname,cdata,reason);
	else			r = ftp_send_stream(l,fname,cdata,reason);
	if (l->fn!=NULL){
		if ( r) l->fn("done_ok",     l->bytes_in, l->fntag);
		if (!r) l->fn("done_failed", l->bytes_in, l->fntag);
	}
	imsg("done transfer\n");
	if (*cdata!=NULL) tcp_free(cdata);
	if (!r || !ftp_response(c,226,299,reason)) goto ex;
	return TRUE;
ex:
	if (strlen(resp)>0) {strcat(reason," "); strcat(reason,resp);}
	if (!isblock) if (*cdata!=NULL) tcp_free(cdata);
	return FALSE;
}
int ftp_response(Chan *c, int expected, int maxval, char *reason)
{
	char bf[BFSZ];
	int code;
	int t;
	strcpy(reason,"No response from server");
	for(;TRUE;){
		t = time(NULL);
		/*if (tcp_peek_line_timed(c,bf, BFSZ-2, 10000) <=0){
			imsg("no response after %d seconds", time(NULL) - t);
			//return FALSE;
		}*/
		if (!tcp_readline(c,bf,BFSZ-1)) {
			lprintf(reason,BFSZ-10,"Connection problem %s",tcp_error(c,bf));
			return FALSE;
		}
		lprintf(reason, BFSZ-10,"%s",bf);
		if (doprint) {
			imsg("<-- %.500s\n",bf);
		}
		if (isdebug) imsg("%s\n",reason);
		if (bf[0]==' ') continue;
		if (bf[0]=='\t') continue;
		if ( !isdigit(bf[0]) ) {lprintf(reason,BFSZ-10,"Did not receive digit response (%s)",bf); return FALSE;}
		if (bf[3]=='-') continue;
		code = atoi(bf);
		if (code != expected) {
			/*imsg("Expected %d, got %.500s\n",expected,bf);*/
		}
		if (code <= 199) {
			/*imsg("got intermediary response, trying again\n"); */
			continue;
		}
		if (code>maxval) {
			/*emsg("Bad response (%.500s) %d %d\n",bf,expected,maxval);*/
			lprintf(reason,BFSZ-10,"%s",bf);
			return FALSE;
		}
		return TRUE;
	}
}
int ftp_get_a_response(Chan *c, int expected, int maxval, char *reason)
{
	char bf[BFSZ];
	int code;
	strcpy(reason,"No response from server");
again:	if (!tcp_readline(c,bf,BFSZ-1)) {emsg("Read %d %d failed %.500s\n",expected,maxval,tcp_error(c,bf)); return FALSE;}
	lprintf(reason,BFSZ-10,"%s",bf);
	if (isdebug) {
		imsg("<-- %.500s\n",bf);
	}
	if (doprint || isdebug) imsg("%s\n",reason);
	if (bf[0]==' ') goto again;
	if (bf[0]=='\t') goto again;
	if ( !isdigit(bf[0]) ) return FALSE;
	if (bf[3]=='-') goto again;
	code = atoi(bf);
	if (code != expected) {
		emsg("Expected %d, got %.500s\n",expected,bf);
	}
	if (code>maxval) {
		emsg("Bad response (%.500s) %d %d\n",bf,expected,maxval);
		lprintf(reason,BFSZ-10,"%s",bf);
		return FALSE;
	}
	return TRUE;
}
int ftp_response_keep(Chan *c, int expected, int maxval, char *bf)
{
	int code;
	if (!tcp_readline(c,bf,BFSZ-1)) {emsg("Read %d %d failed %.500s\n",expected,maxval,tcp_error(c,bf)); return FALSE;}
	if (isdebug) {
		imsg("<-- %.500s\n",bf);
	}
	if ( !isdigit(bf[0]) ) return FALSE;
	code = atoi(bf);
	if (code != expected) {
		emsg("Expected %d, got %.500s\n",expected,bf);
	}
	if (code>maxval) return FALSE;
	return TRUE;
}
char *ftp_portstr(char *bf,char *ip, int port)
{
	char *s;
	ncpy(bf,ip,100);
	strcat(bf,",");
	sprintf(bf,"%.20s,%d,%d",ip,port / 256,port % 256) ;
	for (s=bf;*s!=0;s++) {
		if (*s=='.') *s = ',';
	}
	return bf;
}
static int slow_pass = FALSE;
int ftp_login(Chan *c, char *to, char *user, char *pass, char *reason)
{
	lprintf(reason, BFSZ-1, "Open connection to (%.500s) port %d\n",to,21);
/*	if (!tcp_open(c,to,21,"")) {
		sprintf(reason,"Unable to open port, %.500s\n",tcp_error(c,bf));
		goto ex;
	}
*/
	tcp_nodelay(c,TRUE);


	if (!ftp_response(c,220,299,reason)) goto ex;
	if (isssl) {
		tcp_printf(c,"AUTH TLS-P\r\n");
		if (!ftp_response(c,234,299,reason)) goto ex;
		if (!tcp_ssl_init_real(c,TRUE,FALSE,NULL,NULL,reason,FALSE)) goto ex;
	}
	/*imsg("Send user %s\n",user);*/
	tcp_printf(c,"USER %.100s\r\n",user);
	if (!ftp_response(c,331,399,reason)) goto ex;
	if (slow_pass) {
		int t = time(NULL);
		for (;t==time(NULL);) {
#ifdef WIN32
			Sleep(10);
#endif
		}
	}
	tcp_printf(c,"PASS %.100s\r\n",pass);
	if (!ftp_response(c,230,299,reason)) goto ex;
	/*imsg("Login for %s OK.\n",user);*/
	return TRUE;
ex:
	return FALSE;
}
static int noport = FALSE;
static int change_ports = TRUE;
int ftp_decode_pasv(Ftplink *l, char *reason)
{
	/* 227 Entering Passive Mode (127,0,0,1,4,56) */
	char *s;
	int i;
	char tmp[BFSZ];

	imsg("Decode (%s)\n",reason);
	s = strchr(reason,'(');
	if (s==NULL) {emsg("Unable to find information in pasv response %s\n",reason); return FALSE;}
	lprintf(tmp,BFSZ-10,"%s",s+1);
	for (i=0; i<3; i++) {
		s = strchr(tmp,','); if (s==NULL) break;
		*s = '.';
	}
	s = strchr(tmp,',');
	*s = 0;
	if (l->passive_host!=NULL) free(l->passive_host);
	l->passive_host = strdup(tmp);
	l->passive_port = atoi(s+1)*256;
	s = strchr(s+1,',');
	if (s!=NULL) l->passive_port += atoi(s+1);
	imsg("Decoded (%s) %d\n",l->passive_host,l->passive_port);
	return TRUE;
}
int ftp_reset_port(Ftplink *l, char *reason)
{
	char bf[BFSZ];
	char ip[BFSZ];
	int port;

/*	if (l->data != NULL)
		tcp_free(&(l->data));*/
	if (l->portok) return TRUE;
	if (l->passive) {
try_passive:	tcp_printf(l->cmd,"PASV\r\n");
		if (!ftp_response(l->cmd,200,299,reason)) return FALSE;
		/* 227 Entering Passive Mode (127,0,0,1,4,56) */
		if (!ftp_decode_pasv(l,reason)) {
			snprintf(bf, BFSZ-2, "Decode failed (%.200s)\n",reason);
			snprintf(reason, 200, "%.200s)\n",bf);
			return FALSE;
		}
		l->portok = TRUE;
		return TRUE;
	}
	if (change_ports) { /* To fix bug with multiple transfers*/
		/* Should probably fix by us'ing reuse at both ends or something like that*/
		tcp_free(&l->listen);
		return ftp_setup_port_link(l,reason);
	}
	tcp_getport(l->listen,&port);
	if (!tcp_getmyip(l->cmd,ip))
		return FALSE;
	tcp_printf(l->cmd,"PORT %.500s\r\n",ftp_portstr(bf,ip,port));
	if (!ftp_response(l->cmd,200,299,reason)) {
		if (atoi(reason)==500) {
			l->passive = TRUE;
			goto try_passive;
		}
		return FALSE;
	}
	l->portok = TRUE;
	return TRUE;
}
int ftp_setup_port_link(Ftplink *l,char *reason)
{
	int port=0;
	int send_port=FALSE;
	int lcl,rmt;
	char ip[BFSZ];
	char bf[BFSZ];
	char tmp[BFSZ];

	tcp_getports(l->cmd,&lcl,&rmt);
	if (noport) {
		imsg("Ports %d %d\n",lcl,rmt);
		port = lcl;
	}

try_passive:
	if (l->passive) {
		return ftp_reset_port(l,reason);
	}

	/* Listen on port chosen by os, find the port and our ip number */
	if (!tcp_listen(&l->listen,port,"",TRUE)) {
		sprintf(reason,"Could not listen on port (%d) %.500s, trying any port\n",port,tcp_error(l->listen,tmp));
		tcp_free(&l->listen);
		if (!tcp_listen(&l->listen,0,"",TRUE)) {
			sprintf(reason,"Could not listen %.500s\n",tcp_error(l->listen,tmp));
			return FALSE;
		}
	}
	tcp_getport(l->listen,&port);
	if (port!=lcl) send_port = TRUE;
	if (!tcp_getmyip(l->cmd,ip)){
		sprintf(reason,"Could not get my IP, channel closed? %.500s\n",tcp_error(l->cmd,tmp));
		return FALSE;
	}
	lprintf(reason, BFSZ-2, "Listening on port %d ip addres %.500s\n",port,ip);
	/* Tell the other end where we are listening  */
	if (send_port) {
		tcp_printf(l->cmd,"PORT %.500s\r\n",ftp_portstr(bf,ip,port));
		if (!ftp_response(l->cmd,200,299,reason)) {
			if (atoi(reason)==500) {
				l->passive = TRUE;
				goto try_passive;
			}
			goto ex;
		}
		l->portok = TRUE;
	}
	return TRUE;
ex:
	return FALSE;
}
int ftp_setup_port(Chan *c, Chan **xlisten, char *reason)
{
	int port=0;
	int send_port=FALSE;
	int lcl,rmt;
	char ip[BFSZ];
	char bf[BFSZ];
	char tmp[BFSZ];

	tcp_getports(c,&lcl,&rmt);
	if (noport) {
		imsg("Ports %d %d\n",lcl,rmt);
		port = lcl;
	}

/*	if (l->passive) {
		return ftp_reset_port(l,reason);
	}
*/
	/* Listen on port chosen by os, find the port and our ip number */
	if (!tcp_listen(xlisten,port,"",TRUE)) {
		sprintf(reason,"Could not listen on port (%d) %.500s, trying any port\n",port,tcp_error(*xlisten,tmp));
		tcp_free(xlisten);
		if (!tcp_listen(xlisten,0,"",TRUE)) {
			sprintf(reason,"Could not listen %.500s\n",tcp_error(*xlisten,tmp));
			return FALSE;
		}
	}
	tcp_getport(*xlisten,&port);
	if (port!=lcl) send_port = TRUE;
	if (!tcp_getmyip(c,ip)){
			sprintf(reason,"Could not listen %.500s\n",tcp_error(*xlisten,tmp));
			return FALSE;
	}
	lprintf(reason, BFSZ-1,"Listening on port %d ip addres %.500s\n",port,ip);
	/* Tell the other end where we are listening  */
	if (send_port) {
		tcp_printf(c,"PORT %.500s\r\n",ftp_portstr(bf,ip,port));
		if (!ftp_response(c,200,299,reason)) {
			goto ex;
		}
	}
	return TRUE;
ex:
	return FALSE;
}
int ftp_list_stream(char *fname, Chan *cdata, FILE *f, char *reason)
{
	int x;
	char bf[BFSZ];

	for (;TRUE;) {
		x = tcp_read(cdata,bf,BFSZ-1);
		if (x<=0) {
			if (tcp_wouldblock(cdata)) {
				;
			}
			tcp_close_nice(cdata);
			break;
		}
		bf[x] = 0;
		fwrite(bf,1,x,f);
	}
	return TRUE;
}
int ftp_list_wordlist(Ftplink *l,char *fname, Chan *cdata, Wordlist **w, char *reason)
{
	char *s;
	char bf[BFSZ];
	for (;TRUE;) {
		if (!tcp_readline(cdata,bf,BFSZ-1)) break;
		s = strchr(bf,'\n'); if (s!=NULL) *s = 0;
		s = strchr(bf,'\r'); if (s!=NULL) *s = 0;
		ftp_word_add(w,bf);
		if (l->fn!=NULL) if (!l->fn("read_list", 0, l->fntag)) {
			imsg("Abort requested during list\n");
			sprintf(reason,"User Abort (%.500s) \n",fname);
			ftp_do_abort(l);
			return FALSE;
		}
	}
	return TRUE;
}
int ftp_set_timeout(Ftplink *l, int msec)
{
	l->tout = msec;
	return TRUE;
}
/*
int spy_out_for_abort(Ftplink *l){
	char buff[BFSZ];
	if (tcp_peek_line_timed(l->cmd, buff, BFSZ, 1))
		if (strstr(buff, "ABOR")!= NULL)
			return TRUE;
		return FALSE;
}
*/
int ftp_send_stream(Ftplink *l, char *fname, Chan **cdata, char *reason)
{
	int r,x;
	char bf[BFSZ];
	char bf2[BFSZ*2];
	char *s;
	FILE *f;
	char *mode;
	int rstatus = TRUE;
	if (l->xisascii) mode = "r";
	else mode = "rb";
	imsg("ftp_send_stream");
	f = fopen(fname,mode);
	if (f==NULL) {
		emsg("Could not read %.500s %.500s\n",fname,strerror(errno));
		return FALSE;
	}
	l->bytes_in = 0;
	for (;!feof(f);) {
		r = fread(bf,1,BFSZ-1,f);
		if (r<=0){
		imsg("broke read");
			break;
		}
		s = bf;
		if (l->xisascii) {
			r = cvt_lf_crlf(bf,r,bf2); s = bf2;
		}
		x = tcp_write_timed(*cdata,s,r,l->tout);
		if (x!=r) {
			rstatus = FALSE;
	imsg("broke rstatus");
			break;
		}

		l->bytes_in += r;
		if (l->fn!=NULL) {
			if (!l->fn("write", l->bytes_in, l->fntag)) {
				imsg("Abort requested by user during send\n");
				sprintf(reason,"User Abort (%.500s) \n",fname);
				ftp_do_abort(l);
				rstatus = FALSE;
				goto finished;
			}
		}
		/*if (spy_out_for_abort(l)){
			rstatus = FALSE;
			l->aborted_transfer = TRUE;
			break;
		}*/
		if (feof(f)){
			imsg("end of file %d bytes", ftell(f));
		}
	}
finished:
	fclose(f);
	if (rstatus) sprintf(reason,"Sent (%.500s) successfully\n",fname);
	//tcp_free(*cdata);
	return rstatus;
}
int ftp_send_block(Ftplink *l, char *fname, Chan **cdata, char *reason)
{
	int r,x;
	char bf[BFSZ];
	char *s;
	char bf2[BFSZ];
	FILE *f;
	int rstatus = TRUE;
	f = fopen(fname,"rb");
	if (f==NULL) {
		emsg("Could not read %.500s %.500s\n",fname,strerror(errno));
		return FALSE;
	}

	for (;!feof(f);) {
		r = fread(bf,1,BFSZ-1,f);
		if (r<=0) break;
		s = bf;
		if (l->xisascii) {
			r = cvt_lf_crlf(bf,r,bf2);
			s = bf2;
		}
		x = ftp_write_block(*cdata,s,r,FALSE,reason);
		l->bytes_in += x;
		if (x!=r) {
			emsg("Partial send, socket failure\n");
			rstatus = FALSE;
			goto finished;
		}
		if (l->fn!=NULL) {
			if (!l->fn("write", l->bytes_in, l->fntag)) {
				imsg("Abort requested by user during send\n");
				sprintf(reason,"User Abort (%.500s) \n",fname);
				ftp_do_abort(l);
				rstatus = FALSE;
				goto finished;
			}
		}
		/*if (spy_out_for_abort(l)){
			rstatus = FALSE;
			l->aborted_transfer = TRUE;
			break;
		}*/
	}
	x = ftp_write_block(*cdata,bf,0,TRUE,reason); /* This needed for close block stream */
finished:
	fclose(f);
	if (rstatus) sprintf(reason,"Sent (%.500s) successfully\n",fname);
	tcp_free(cdata);
	return rstatus;
}
int ftp_isopen(Ftplink *l)
{
	if (!tcp_isopen(l->cmd)) return FALSE;
	tcp_check_open(l->cmd);
	if (!tcp_isopen(l->cmd)) return FALSE;
	return TRUE;
}
int ftp_do_abort(Ftplink *l)
{
	char reason[BFSZ];

	tcp_printf(l->cmd,"ABOR\r\n");
	if (!ftp_response(l->cmd,226,299,reason))
		ftp_response(l->cmd,226,299,reason);
	if (l->data!=NULL) tcp_free(&l->data);
	imsg("Abort respons (%s)\n",reason);
	l->aborted_transfer = TRUE;
	return TRUE;
}
static int nostore;
void ftp_set_nostore(int x)
{
	nostore = x;
}
int ftp_get_block(Ftplink *l, FILE *f, Chan *cdata, int isblock, char *reason, long *sz)
{
	int r;
	int nb=0;
	long unsigned int tot=0;
	char bf[66001];
	char tmp[BFSZ];
	int ret=TRUE;
	int ismore=TRUE;
	l->aborted_transfer = FALSE;
	for (;ismore;) {
		if (!isblock) {
			r = tcp_read_timed(cdata,bf,66000,l->tout);
			if (r < 0){
			    tcp_lasterror(cdata);
				sprintf(reason,"Connection severed %d bytes read, err = %s\n",(int) tot, tcp_error(cdata,tmp));

			}
		} else {
			r = ftp_read_block(cdata,bf,66000,&ismore,reason);
			nb++;
		}
		if (r>0) tot += r;
		if (r==0) {
			imsg("Connection closed normally %d\n",r);
			break;
		}
		if (r<0) {
			ret = FALSE;
			sprintf(reason,"Connection Lost, %d bytes read. err = %s.\n", (int) tot, tcp_error(cdata,tmp));
			break;
		}
		if (l->xisascii) r = cvt_crlf_lf(bf,r);
		*sz += r;
		if (l->fn!=NULL) {
			if (!l->fn("read", *sz, l->fntag)) {
				imsg("Abort requested by user\n");
				strcpy(reason, "Abort transfer at users request.");
				ftp_do_abort(l);
				return FALSE;
			}
		}
		if (nostore) continue;
		if (r>0) if ((int) fwrite(bf,1,r,f)!=r) {
			emsg("File write failed %.500s\n",strerror(errno));
			sprintf(reason,"File write failed %.500s\n",strerror(errno));
			ret = FALSE;
			break;
		};
		/*if (spy_out_for_abort(l)){
			ret = FALSE;
			l->aborted_transfer = TRUE;
			break;
		}*/
	}
	imsg("Read %d blocks %d bytes\n",nb,tot);
	return ret;
}
int ftp_read_block(Chan *c, char *bf, int maxlen, int *ismore, char *reason)
{
	unsigned char head[3];
	int r;
	long unsigned int tot=0;
	char tmp[BFSZ];
	int len;
	int upto=0;
	*ismore = TRUE;
	r = tcp_read(c,(char *) head,3);
        tcp_lasterror(c);
	if (r<=0) {
		emsg("Socket closed on read %.500s\n",tcp_error(c,tmp));
		sprintf(reason,"Socket closed on read %.500s\n",tcp_error(c,tmp));
		if (r==0) sprintf(reason,"Socket closed on read by remote end\n");
		*ismore = FALSE;
		if (!tcp_wouldblock(c)) tcp_close_nice(c);

		return -1;
	}
	if (r!=3) {
		emsg("Part of header receieved %d/%d bytes\n",r,3);
		sprintf(reason,"Part of header receieved %d/%d bytes\n",r,3);
		*ismore = FALSE;
		return -1;
	}
	if ((head[0]&64)!=0) *ismore = FALSE;
	len = head[1] * 256 + head[2];
	if (len>=maxlen) {
		emsg("Buffer not big enough %d/%d wanted\n",len,maxlen);
		sprintf(reason,"Buffer not big enough %d/%d wanted\n",len,maxlen);
		*ismore = FALSE;
		return -1;
	}
	for (;len>0;) {
		r = tcp_read(c,bf+upto,len);
		if (r<=0) {
			if (!tcp_check_open(c)) tcp_close_nice(c);
			else if (!tcp_wouldblock(c)) tcp_close_nice(c);
			break;
		}
		len -= r;
		upto += r;
		tot += r;
	}
	if (len>0) {
		emsg("Part of block missing %d bytes lost\n",len);
		*ismore = FALSE;
	}
	return tot;
}
int ftp_write_block(Chan *c, char *bf, int len, int iseof, char *reason)
{
	unsigned char head[3];
	int r;
	head[0] = 0;
	if (iseof) head[0] = 64;
	head[1] = (unsigned char)(len / 256);
	head[2] = (unsigned char)(len % 256);
	r = tcp_write_timed(c,(char *) head,3,1000*60*10);
	tcp_error(c,reason);
	if (r<3) return -1;
	r = tcp_write_timed(c,bf,len,1000*60*10);
	tcp_error(c,reason);
	if (r<len) return -1;
	return len;
}

int ftp_new(Ftplink **ff)
{
	Ftplink *f=*ff;
	if (f!=NULL) {emsg("ftp_new failed not null\n"); imsg("ftp_new failed not null\n"); abort();}
	f = calloc(1,sizeof(Ftplink));
	*ff = f;
	if (f->tout==0) f->tout = 120000;
	return TRUE;
}
int ftp_free(Ftplink **ff)
{
	Ftplink *l=*ff;
	if (*ff==NULL) return TRUE;
	if (l->cmd!=NULL) tcp_free(&l->cmd);
	if (l->data!=NULL) tcp_free(&l->data);
	if (l->listen!=NULL) tcp_free(&l->listen);
	if (l->user!=NULL) free(l->user); l->user = NULL;
	if (l->pass!=NULL) free(l->pass); l->pass = NULL;
	if (l->host!=NULL) free(l->host); l->host = NULL;
	if (l->passive_host!=NULL) free(l->passive_host); l->passive_host = NULL;
	free(*ff);
	*ff = NULL;
	return TRUE;
}


int ftp_link_set(Ftplink *l, char *host, char *user, char *pass)
{
	l->host = strdup(host);
	l->user = strdup(user);
	l->pass = strdup(pass);
	return TRUE;
}
int ftp_link_setascii(Ftplink *l, int t)
{
	l->xisascii = t;
	return TRUE;
}
extern void lib_msleep(int x);
int ftp_link_build(Ftplink **l, char *host, char *user, char *pass, char *reason)
{
    int i;
	Ftplink *ll;
	ftp_new(l);
	ll = *l;
	ftp_link_set(*l,host,user,pass);
	/* Chrisp added, tries for up to 1 second as it may be a very transitory problem */
	for (i=0; i<10; i++) {
		if (ftp_open_link(*l,reason)) goto isok;
		if (ll->cmd != NULL)
			tcp_free(&(ll->cmd)); /* if it fails we need to free this channel so
								we can open it again without errors */
	    lib_msleep(100);
	}
	goto failed;
isok:	if (!ftp_login_link(*l,reason)) goto failed;
	return TRUE;
failed:
	return FALSE;
}
char *ftp_xfer_name(char *fname,char *tmp, int len)
{
	lprintf(tmp,len-10,"%s_ftp_xfer",fname);
	return tmp;
}
static int restart_at;
void ftp_setrestart(int x)
{
	restart_at = x;

}
int ftp_get(char *fname, char *lclname, char *host, char *user, char *pass, int *sz, char *reason)
{
	int r;
	char bf[BFSZ];
	int start=0;
	char xfer[BFSZ];
	FILE *f;
	imsg("Get fname=(%.100s) lclname=(%.100s)\n",fname,lclname);
	strcpy(bf,"");
	ftp_xfer_name(lclname,xfer, BFSZ);

	f = fopen(xfer,"rb");
	if (f!=NULL) {
		start = lib_file_osize(f);
		sprintf(reason, "Resuming download of %.500s at %d\n",xfer,start);
		fclose(f);
	}
	start = restart_at;
	r = ftp_get_raw(fname,xfer,host,user,pass,start,sz,reason);
	if (r) {
		if (remove(lclname)!=0) {
			if (errno!=ENOENT) {
				sprintf(reason,"Remove (%.500s) failed %.500s",lclname,strerror(errno));
				return FALSE;
			}
		}
		if (rename(xfer,lclname)!=0) {
			sprintf(reason,"Rename (%.500s->%.500s) failed %.500s",xfer,lclname,strerror(errno));
			return FALSE;
		}
		sprintf(reason,"Stored (%.500s) %d bytes",lclname,lib_file_size(lclname));
	}
	return r;
}
int ftp_get_raw(char *fname, char *lclname, char *host, char *user, char *pass, int start, int *sz, char *reason)
{
	int r = FALSE;
	char tmp[BFSZ];
	Ftplink *l=NULL;

	*sz = 0;
	strcpy(reason,"");
	imsg("ftp_get fname=%.500s host=%.500s user=%.500s pass=%.500s\n",fname,host,user,pass);
	if (!ftp_link_build(&l,host,user,pass,reason)) {
		sprintf(reason,"Could not build control link %.500s\n",tcp_error(l->cmd,tmp));
		goto failed;
	}
	l->xisascii = g_isascii;
	l->isblock = g_isblock;
	if (isabort) ftp_set_callback(l,0,ftp_abort_fn);

	r = ftp_get_link(l,fname,lclname,start,reason);
	*sz = l->bytes_in;
failed:
	ftp_free(&l);
	if (!r) {
		char bf[BFSZ];
		snprintf(bf, BFSZ-2, "Get failed (%.500s) (%.500s)\n",fname,reason);
		snprintf(reason, BFSZ-2, "%s", bf);
	}
	return r;
}
int ftp_open_link(Ftplink *l, char *reason)
{
	int r;
	char *s;
	char host[BFSZ];
	int port=21;
	char tmp[BFSZ];
	if (strlen(l->host)==0) {
		sprintf(reason,"tcp_open: host name to connect to blank");
		return FALSE;
	}
	tcp_new(&l->cmd);
	lprintf(host,BFSZ-10,"%s",l->host);
	s = strchr(host,':');
	if (s!=NULL) {
		*s = 0;
		port = atoi(s+1);
		if (port==0) port = 21;
	}
	r = tcp_open_reuse(l->cmd,host,port,"");
	if (!r) sprintf(reason,"tcp_open(%.500s:%d) failed %.500s",host,port,tcp_error(l->cmd,tmp));
	return r;
}
int ftp_login_link(Ftplink *l,char *reason)
{
	if (!ftp_login(l->cmd,l->host,l->user,l->pass,reason)) return FALSE;
	if (!ftp_setup_port_link(l,reason)) return FALSE;
	return TRUE;
}

/*
int ftp_accept(Ftplink *l, char *resp, char *reason)
{
	//char bf[BFSZ];
	char tmp2[BFSZ];
	int got_response = FALSE;
	int lcl,rmt;
	strcpy(resp,"");
	strcpy(reason,"");
	l->portok = FALSE;
	if (l->passive) {
		imsg("Connecting to remote system %s %d\n",l->passive_host,l->passive_port);
		tcp_new(&l->data);
		if (!tcp_open(l->data,l->passive_host,l->passive_port,NULL)) {
			tcp_error(l->data,reason);
			ftp_response(l->cmd,150,199,resp); /* Read in the error response CMD channel * /
			tcp_free(&l->data);                /* Free Data channel * /
			emsg("%s", resp);
			strcat(reason, resp);
			return FALSE;
		}
	} else {
		/ * First check for errors on command channel, should do both at once * /
		for (;TRUE;) {
			if (tcp_waitfor(l->listen,100)) break;
			if (tcp_waitfor(l->cmd,100)) {
				if (!ftp_response(l->cmd,150,199,resp)) return FALSE;
				got_response = TRUE;
			}
		}
		imsg("Waiting for incoming connection\n");
		if (!tcp_accept(l->listen,&l->data,"ftp")) {
			tcp_error(l->listen,reason);
			if (!ftp_response(l->cmd,150,199,resp)) return FALSE;
			return FALSE;
		}
		imsg("Got data connection\n");
	}

	if (isssl) {
		imsg("ftplib: Turning on data encryption\n");
		if (!tcp_ssl_init_real(l->data,require_cert,FALSE,l->cmd,reason)) {
			if (!ftp_response(l->cmd,150,199,resp)) return FALSE;
			return FALSE;
		}
		tcp_getports(l->data,&lcl,&rmt);
		tcp_getip(l->data,tmp2);
		imsg("ftplib: Got ssl mode lcl=%d rmt=%d (%s)\n",lcl,rmt,tmp2);
	}
	tcp_nodelay(l->data,TRUE);
	if (!got_response) {
		if (!ftp_response_keep(l->cmd,150,199,resp)) return FALSE;
	}
	imsg("ftp_accept done ok");
	return TRUE;
}*/


int ftp_set_callback(Ftplink *l, int cbtag, int fn(char *type, int sz, int tag))
{
	l->fntag = cbtag;
	l->fn = fn;
	return TRUE;
}

/*
command_action(v,w,x,y,z){
	strcpy(reason, x);
	tcp_printf(l->cmd,"%s",v);
	if(!ftp_response(l->cmd,y,z,reason)){
		if (l->fn!=NULL){
			sprintf(failbuffer, "done_failed - %s", x);
			l->fn(failbuffer, 0, l->fntag);
		}else if (w!=NULL)l->fn(w,0,l->fntag);
		if (l->data!=NULL) tcp_free(&l->data);
		if(f!=NULL)
			if(fclose(f)!=0){
				strcat(reason," fclose - ");strcat(reason,strerror(errno));
			}
		return FALSE;
	}
}*/



#define do_fn_fail_message(x) {ncpy(reason, x,300); sprintf(failbuffer, "done_failed - %s", x);if (l->fn!=NULL) l->fn(failbuffer, 0, l->fntag);}
#define command_action(v,w,x,y,z) {ncpy(reason, x,300); tcp_printf(l->cmd,"%s",v);if(!ftp_response(l->cmd,y,z,reason)){if (l->fn!=NULL){sprintf(failbuffer, "done_failed - %s", x);l->fn(failbuffer, 0, l->fntag);}else if (w!=NULL)l->fn(w,0,l->fntag); if (l->data!=NULL) tcp_free(&l->data);if(f!=NULL)if(fclose(f)!=0){strcat(reason," fclose - ");strcat(reason,strerror(errno));}return FALSE;}}
#define do_fn_fail_message1(x,y) {char bf[BFSZ];sprintf(bf,x,y);ncpy(reason, bf,300); sprintf(failbuffer, "done_failed - %s", bf);if (l->fn!=NULL) l->fn(failbuffer, 0, l->fntag);}
int ftp_get_link(Ftplink *l, char *rmtname, char *lclname, int start, char *reason)
{
	int r;
	char mode[BFSZ];
	char failbuffer[BFSZ];
	char tmp[BFSZ];
	FILE *f=NULL;

	l->bytes_in = 0;
	strcpy(reason,"Get Link");
	imsg("rmtname=(%.100s) lclname=(%.100s)\n",rmtname,lclname);

	if (l->fn!=NULL) {
		l->fn("get",0, l->fntag);
		snprintf(tmp, BFSZ-2,"SIZE %.200s\r\n",rmtname);
		tcp_printf(l->cmd,"%s", tmp);
		if (ftp_response(l->cmd,213,299,tmp)){
			r = atoi(tmp+4);
			if (r > 0){
				lprintf(tmp, BFSZ -1, "size %d", r);
				l->fn(tmp, 0, l->fntag);
			}
		}
	}

	if (!ftp_reset_port(l,reason)) { do_fn_fail_message("can't reset port") goto ex_done;}

	strcpy(mode,"w");

	if (l->isblock)  command_action("MODE B\r\n", NULL, "can't set mode B", 200, 299)

	if (l->xisascii) command_action("TYPE A\r\n", NULL, "can't set type A", 200, 299)
	else			 command_action("TYPE I\r\n", NULL, "can't set type I", 200, 299)

	if (isssl) 		 command_action("PROT P\r\n", NULL, "can't negotiate SSL", 200, 299)

	if (!(l->xisascii)) strcat(mode,"b");

	if (start>0) {
		snprintf(tmp, BFSZ-2, "REST %d\r\n",start);
		command_action(tmp, "rest_ok", "rest_failed", 350, 399)
		if (!(l->xisascii)) strcpy(mode,"a+b");
		else strcpy(mode,"a+");
	}


	snprintf(tmp, BFSZ-2,"RETR %.200s\r\n",rmtname);
	tcp_printf(l->cmd,"%s", tmp);
	/**command_action(tmp, NULL, reason, 150, 199) dont worry about 150 response here **/

	if (l->fn!=NULL) l->fn("accept",0, l->fntag);
	if (!ftp_accept_data_chan(l,reason)) {
		tmp[0] = '\0';
		if (!ftp_response(l->cmd,226,299,tmp)){
			if (strlen(tmp)>0)
				snprintf(reason, BFSZ-2, "%s", tmp);
			if (strcmpnc(tmp, "No response from server")==0)
				ftp_do_abort(l);
		}
		do_fn_fail_message1("accept failed, %s", reason)
		goto ex_done;
	}

	imsg("Open %.500s (%.500s)\n",lclname,mode);
	f = fopen(lclname,mode);
	setvbuf(f,NULL,_IOFBF,32000);
	if (f==NULL) {
		sprintf(reason,"Unable to write to (%.500s)(%s) %.500s",lclname,mode,strerror(errno));
		do_fn_fail_message("cant open local file")
		goto ex_done;
	}

	r = ftp_get_block(l,f,l->data,l->isblock,reason,&(l->bytes_in));
	if (!r) {
		if (l->aborted_transfer){
			do_fn_fail_message("User Aborted")
			tcp_free(&(l->data));
			if (f!=NULL) if (fclose(f)!=0) {strcat(reason," fclose - ");strcat(reason,strerror(errno));}
			return TRUE;
		}else{
			do_fn_fail_message("failure receiveing file, disk full?")
		}
		goto ex_done;
	}
	if (fclose(f)!=0) {f = NULL; sprintf(reason,"fclose %.500s",strerror(errno));
		do_fn_fail_message("cant close local file, disk full? disk error?")
		goto ex_done;
	}
	f = NULL;
	tcp_free(&(l->data));

	if (r) {
		sprintf(reason,"Stored (%.500s) successfully %d bytes",lclname,lib_file_size(lclname));
		if (l->fn!=NULL) l->fn("done_ok",l->bytes_in, l->fntag);
	}
	if (!ftp_response(l->cmd,226,299,reason)){
		do_fn_fail_message1("got file, but server did not agree, file may be corrupted. (%s)", reason)
		goto ex_done;
	}
	return r;
ex_done:
	tcp_free(&(l->data));
	if (f!=NULL) if (fclose(f)!=0) {strcat(reason," fclose - ");strcat(reason,strerror(errno));}
	return FALSE;
}



int ftp_list(char *fname, char *host, char *user, char *pass, FILE *fout, char *reason)
{
	int r = FALSE;
	Ftplink *l=NULL;

	if (!ftp_link_build(&l,host,user,pass,reason)) goto ex;
	/* Ok, now get the listing  */
	r = ftp_link_list(l,"LIST",fname,fout,reason);
ex:
	ftp_free(&l);
	return r;
}
int ftp_mlsd(char *fname, char *host, char *user, char *pass, FILE *fout, char *reason)
{
	int r = FALSE;
	Ftplink *l=NULL;

	if (!ftp_link_build(&l,host,user,pass,reason)) goto ex;
	/* Ok, now get the listing  */
	r = ftp_link_list(l,"MLSD",fname,fout,reason);
ex:
	ftp_free(&l);
	return r;
}
int ftp_mlst(char *fname, char *host, char *user, char *pass, FILE *fout, char *reason)
{
	int r = FALSE;
	Ftplink *l=NULL;

	if (!ftp_link_build(&l,host,user,pass,reason)) goto ex;
	/* Ok, now get the listing  */

	tcp_printf(l->cmd,"PWD\r\n");
	if (!ftp_response(l->cmd,200,599,reason)) goto ex;
	tcp_printf(l->cmd,"FEAT\r\n");
	if (!ftp_response(l->cmd,200,599,reason)) goto ex;
	tcp_printf(l->cmd,"HELP SITE\r\n");
	if (!ftp_response(l->cmd,200,599,reason)) goto ex;
	tcp_printf(l->cmd,"CLNT NcFTPGet 3.0.0\r\n");
	if (!ftp_response(l->cmd,200,599,reason)) goto ex;
	tcp_printf(l->cmd,"CWD pub\r\n");
	if (!ftp_response(l->cmd,200,599,reason)) goto ex;
	tcp_printf(l->cmd,"CWD dmail\r\n");
	if (!ftp_response(l->cmd,200,599,reason)) goto ex;
	tcp_printf(l->cmd,"CWD beta\r\n");
	if (!ftp_response(l->cmd,200,599,reason)) goto ex;
	tcp_printf(l->cmd,"TYPE I\r\n");
	if (!ftp_response(l->cmd,200,299,reason)) goto ex;
	tcp_printf(l->cmd,"MLST %.200s\r\n",fname);
	if (!ftp_response(l->cmd,200,299,reason)) goto ex;
ex:
	ftp_free(&l);
	return r;
}
int ftp_link_list(Ftplink *l, char *cmd, char *rmtname, FILE *fout, char *reason)
{
	int r = FALSE;
	int lcl,rmt;
	char resp[BFSZ];
	strcpy(resp,"");
	/* Ok, now get the listing  */
	if (!ftp_reset_port(l,reason)) goto ex;
	/*imsg("ftp_list, send list command\n");*/
	if (strlen(rmtname)==0) rmtname = ".";
	/*imsg("ftp_list, -->(%.500s %.300s)\n",cmd,rmtname);*/
	/*if (isssl) 		 tcp_printf(l->cmd,"PROT P\r\n");
	if (!ftp_response(l->cmd,200,299,reason)){goto ex;}*/

	tcp_printf(l->cmd,"%.500s %.300s\r\n",cmd,rmtname);

	/* Accept the incoming connection */
	/**printf("getting response\n");**/

/*	if (!ftp_accept_data_chan(l,reason)) {imsg("DataLink failed (%s %s)\n",reason,resp); goto ex;}*/

	if (!ftp_accept_data_chan(l,reason)) {
		resp[0] = '\0';
		if (!ftp_response(l->cmd,226,299,resp)){
			if (strlen(resp)>0){
				snprintf(reason, BFSZ-2, "%s", resp);
				resp[0] = '\0';
			}
			if (strcmpnc(resp, "No response from server")==0)
				ftp_do_abort(l);
		}
		goto ex;
	}

	tcp_getports(l->data,&lcl,&rmt);
	/*msg("ftp_list, read data\n");*/
	if (!ftp_list_stream(rmtname,l->data,fout,reason)) goto ex;
	/*imsg("ftp_list, Done\n");*/
	tcp_free(&l->data);
	if (!ftp_response(l->cmd,226,299,reason)) goto ex;
	r = TRUE;
ex:
	if (strlen(resp)>0) {strcat(reason," "); strcat(reason,resp);}
	return r;
}

int ftp_link_list_wordlist(Ftplink *l, char *cmd, char *rmtname, Wordlist **w, char *reason)
{
	int r = FALSE;
	int lcl,rmt;
	char resp[BFSZ];
	strcpy(resp,"");
	/* Ok, now get the listing  */
	if (!ftp_reset_port(l,reason)) goto ex;
	/*imsg("ftp_list, send list command\n");*/
	/*imsg("ftp_list, -->(%.500s %.300s)\n",cmd,rmtname);*/
	if (rmtname[0] != 0)
		tcp_printf(l->cmd,"%.500s %.300s\r\n",cmd,rmtname);
	else
		tcp_printf(l->cmd,"%.500s\r\n",cmd);

	/* */
	/* Accept the incoming connection */
	/*if (!ftp_accept_data_chan(l,reason)) {imsg("DataLink failed (%s)\n",reason); goto ex;}*/
	if (!ftp_accept_data_chan(l,reason)) {
		resp[0] = '\0';
		if (!ftp_response(l->cmd,226,299,resp)){
			snprintf(reason, BFSZ-2, "%s", resp);
			if (strcmpnc(resp, "No response from server")==0)
				ftp_do_abort(l);
		}
		goto ex;
	}

	tcp_getports(l->data,&lcl,&rmt);
	/*imsg("Connection came on ports %d %d\n",lcl,rmt);*/
	/*imsg("ftp_list, read data\n");*/
	ftp_list_wordlist(l,rmtname,l->data,w,reason);
	/*imsg("ftp_list, Done\n");*/
	tcp_free(&l->data);
	if (!ftp_response(l->cmd,226,299,reason)) goto ex;
	r = TRUE;
ex:
	if (strlen(resp)>0) {strcat(reason," "); strcat(reason,resp);}

	ftp_word_reverse(w);
	return r;
}


int ftp_send(char *fname, char *rmtname, char *host, char *user, char *pass, char *reason)
{
	int r = FALSE;
	Ftplink *l=NULL;

	strcpy(reason,"ftp_link_build failed");
	if (!ftp_link_build(&l,host,user,pass,reason)) goto failed;
	if (isabort) ftp_set_callback(l,0,ftp_abort_fn);
	l->isblock = g_isblock;
	l->xisascii = g_isascii;
	strcpy(reason,"ftp_send_link failed");
	r = ftp_send_link(l,fname,rmtname,reason);
failed:
	ftp_free(&l);
	return r;
}

/*macro do_fn_fail_message(x) {strcpy(reason, x); sprintf(failbuffer, "done_failed - %s", x);if (l->fn!=NULL) l->fn(failbuffer, 0, l->fntag);}*/
int ftp_send_link(Ftplink *l, char *fname, char *rmtname, char *reason)
{
	char bf[BFSZ];
	char unique[BFSZ];
	char failbuffer[BFSZ];
	char *s;
	int r;
	int (*ftp_send_file)(Ftplink *l, char *fname, Chan **data, char *reason);


	if (l->fn!=NULL) {
		l->fn("send", 0, l->fntag);
		r = lib_file_size(fname);
		if (r!= -1){
			lprintf(bf, BFSZ -1, "size %d", r);
			l->fn(bf, 0, l->fntag);
		}
	}
	l->bytes_in = 0;

	if (l->isblock) {
		/* Send in 'block' mode */
		tcp_printf(l->cmd,"MODE B\r\n");
		if (!ftp_response(l->cmd,200,299,reason)){
			do_fn_fail_message("can't set MODE B")
			goto ex_done;
		}
		ftp_send_file = ftp_send_block;
	} else {
		ftp_send_file = ftp_send_stream;
	}
	if (l->xisascii) 	tcp_printf(l->cmd,"TYPE A\r\n");
	else 				tcp_printf(l->cmd,"TYPE I\r\n");

	//if (isssl) 		 command_action("PROT P\r\n", NULL, "can't negotiate SSL", 200, 299)

	if (!ftp_response(l->cmd,200,299,reason)){
		if (l->xisascii) do_fn_fail_message("can't set Type A")
		else			 do_fn_fail_message("can't set Type I")
		goto ex_done;
	}
	if (!ftp_reset_port(l,reason)) {do_fn_fail_message("can't reset port") goto ex_done;}

	/* Ok, now send the file */
	if (isunique) {
		tcp_printf(l->cmd,"STOU %.300s\r\n",rmtname);
	} else {
		if (isappend) {
			tcp_printf(l->cmd,"APPE %.300s\r\n",rmtname);
		} else {
			tcp_printf(l->cmd,"STOR %.300s\r\n",rmtname);
		}
	}
/*	if (!ftp_response(l->cmd,150,199,reason)){
		sprintf(failbuffer, "done_failed - %s", reason);
		if (l->fn!=NULL) l->fn(failbuffer, 0, l->fntag);
		goto ex_done;
	}*/

	/* Accept the incoming connection */

	if (!ftp_accept_data_chan(l,reason)) {
		char resp[BFSZ];
		if (!ftp_response(l->cmd,226,299,resp)){
			if (strlen(resp)>0){
				snprintf(reason, BFSZ-2, "%s", resp);
				resp[0] = '\0';
			}
			do_fn_fail_message1("accept failed: %s", reason);
			if (strcmpnc(resp, "No response from server")==0)
				ftp_do_abort(l);
		}
		goto ex_done;
	}else{
		//imsg("DataLink accepted (%s)\n",reason);
		if (l->fn!=NULL) l->fn("accept",0, l->fntag);
	}

	r = ftp_send_file(l,fname,&(l->data),reason);

	if (r && l->fn!=NULL) l->fn("done_ok",l->bytes_in, l->fntag);
	if (!r && l->fn!=NULL) l->fn("done_failed",l->bytes_in, l->fntag);
	if (l->data!=NULL) tcp_free(&(l->data));
	if (!r || !ftp_response(l->cmd,226,299,reason)) {
		sprintf(failbuffer, "done_failed - %s", reason);
		if (l->fn!=NULL) l->fn(failbuffer, 0, l->fntag);
		goto ex_done;
	}
	if (isunique) {
		ncpy(bf,reason,BFSZ-1);
		s = strstr(bf,"name:");
		if (s==NULL) {
			imsg("unique: (%s)\n",bf);
			do_fn_fail_message("Filename not in response");
			goto ex_done;
		}
		s+= 5;
		lprintf(unique,BFSZ-10,"%s",s);
		unique[strlen(unique)-2] = 0;
		sprintf(reason, "Unique file name (%.500s) \n",unique);
	}


	return TRUE;
ex_done:
	if (l->data!=NULL) tcp_free(&l->data);
	return FALSE;
}
int ftp_mget(char *wild, char *dst, char *host, char *user, char *pass, char *reason)
{
	int r = FALSE;
	Ftplink *l=NULL;

	if (!ftp_link_build(&l,host,user,pass,reason)) goto failed;
	l->xisascii = g_isascii;
	r = ftp_mget_link(l,wild,dst,reason);

	l->isblock = g_isblock;

failed:
	ftp_free(&l);
	return r;
}
/*
-rwxrwxrwx   1 user     group             788 Sep  5 19:25 bemsg.h
-rwxrwxrwx   1 user     group             788 Sep  5 19:29 emsg.h
*/
int ftp_mget_link(Ftplink *l,char *wild,char *dst,char *reason)
{
	char bf[BFSZ];
	char tmp[BFSZ];
	char tmp2[BFSZ];
	char fname[BFSZ];
	char wpath[BFSZ];
	int r = FALSE;
	char *s;
	FILE *f=NULL;
	FILE *fout=NULL;
	fout = fopen("ftp_list.tmp","w");
	if (fout==NULL) {
		return FALSE;
	}
	r = ftp_link_list(l,"NLST",wild,fout,reason);
	fclose(fout); fout=NULL;
	if (!r) goto ex;
	f = fopen("ftp_list.tmp","r");
	if (f==NULL) goto ex;
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		s = strchr(bf,'\n'); if (s!=NULL) *s = 0;
		s = strchr(bf,'\r'); if (s!=NULL) *s = 0;
		lib_path_split(bf,tmp2,fname);

		lib_path_split(wild,wpath,tmp);
		strcat(wpath,fname);
		r = ftp_get_link(l,wpath,ftp_related(bf,dst,tmp),0,reason);
		if (!r) {
			char bf[BFSZ];
			snprintf(bf, BFSZ-2, "mgets fails : %s", reason);
			sprintf(reason, "%s", bf);
			goto ex;
		}
	}
ex:
	if (f!=NULL) fclose(f); f = NULL;
	return r;
}
int ftp_mput(char *wild, int wild_len, char *dst, char *host, char *user, char *pass, char *reason){
	return ftp_mput_options(wild, wild_len, dst, host, user, pass, reason, FALSE, NULL);
}

int ftp_mput_options(char *wild, int wild_len, char *dst, char *host, char *user, char *pass, char *reason, int use_passive, int (*progress_indicator_callback)(char *, int i, int j))
{
	int r = FALSE;
	Ftplink *l=NULL;

	if (!ftp_link_build(&l,host,user,pass,reason)) goto failed;
	l->isblock = g_isblock;
	l->xisascii = g_isascii;
	l->passive = use_passive;

	if (progress_indicator_callback != NULL)
		ftp_set_callback(l, 012345, progress_indicator_callback);

	r = ftp_mput_link(l,wild,wild_len,dst,reason);
failed:
	ftp_free(&l);
	return r;
}
char *ftp_related(char *fname,char *dst, char *tmp)
{
	char path[BFSZ],name[BFSZ];

	/* remove path from fname, and add path to dst */

	lprintf(tmp,BFSZ-10,"%s",dst);
	if (strlen(tmp)>0)
		if (tmp[strlen(tmp)-1]!='/') strcat(tmp,"/");
	lib_path_split(fname,path,name);
	strcat(tmp,name);
	return tmp;
}
int ftp_mput_link(Ftplink *l, char *wild, int wild_len, char *dst, char *reason)
{
	WILD_HANDLE *h=NULL;
	WILD_DATA d;
	int r;
	int k;
	char tmp[BFSZ];
	if (l->isblock) {
		/* Send in 'block' mode */
		tcp_printf(l->cmd,"MODE B\r\n");
		if (!ftp_response(l->cmd,200,299,reason)) goto ex;
	}

	r = wild_first(&h,wild, wild_len,&d);
	for (;r;) {

		if (d.isnorm) {
			if (!ftp_reset_port(l,reason)) {wild_end(&h);goto ex;}
			if (l->fn != NULL){
				snprintf(tmp, BFSZ-2, "mput file %s", d.name);
				l->fn(tmp, 0, l->fntag);
			}
			k = ftp_mput_file(l,l->cmd,&l->listen,&l->data,d.name,ftp_related(d.name,dst,tmp),l->isblock,reason);
			if (!k) break;
		}
		r = wild_next(h,&d);
	}
	wild_end(&h);
	imsg("No more files to send, closing connection now\n");
	if (l->data!=NULL) tcp_free(&l->data);
	return TRUE;
ex:
	if (l->data!=NULL) tcp_free(&l->data);
	return FALSE;
}

int ftp_printf(Ftplink *l, char * arg_list, ...)
{
        va_list arg_ptr;
	int r;
        char *format;
        int len;
	char output[5000];
        va_start(arg_ptr, arg_list);
        format = arg_list;
	output[0] = 0;
        len = vsnprintf(output,4000, format, arg_ptr);
	r = tcp_printf(l->cmd,"%.200s",output);
	if (isdebug) imsg("send> %.200s",output);
	return r;
}
int ftp_result(Ftplink *l, int expected, int maxval, char *reason)
{
	return ftp_response_keep(l->cmd,expected,maxval,reason);
}



int cvt_lf_crlf(char *bf, int r,  char *tmp)
{
	char *s=bf;
	char *o=tmp;
	if (r<0) return r;
	for (;r>0;s++,r--) {
		if (*s=='\n') *o++ = '\r';
		*o++ = *s;
	}
	*o = 0;
	return o-tmp;
}
int cvt_crlf_lf(char *bf, int r)
{
	char *s=bf;
	char *o=bf;

	if (r<0) return r;
	/* bf[r] = 0; */
	for (;r>0;s++,r--) {
		if (*s=='\r') {
			if (r==1) continue; /*Strip if next char might be lf */
			if (s[1] == '\n') continue;
		}
		*o++ = *s;
	}
	*o = 0;
	return o-bf;
}
int lib_file_osize(FILE *f)
{
        struct stat bf;
		fflush(f);
        if (fstat(fileno(f),&bf)!=0) {return 0;}
        return bf.st_size;
}
int lib_file_size(char *fname)
{
	FILE *f;
	int r;
	f = fopen(fname,"r");
	if (f==NULL) return -1;
	r = lib_file_osize(f);
	fclose(f);
	return r;
}
int lib_path_split(char *in, char *path, char *fname)
{
	char bf[BFSZ];
	char *s;
	char *last=NULL;
	lprintf(bf,BFSZ-10,"%s",in); in = bf;


	for (s=bf;*s!=0;s++) if (*s=='/') last = s;
	if (last==NULL) for (s=bf;*s!=0;s++) if (*s=='\\') last = s;
	if (last==NULL) {
		strcpy(path,"");
		ncpy(fname,bf,BFSZ-1);
		return TRUE;
	}
	*last = 0;
	ncpy(path,bf,BFSZ-1);
	strcat(path,"/");
	ncpy(fname,last+1,BFSZ-1);
	return TRUE;
}

#ifdef WIN32
int wild_first(WILD_HANDLE **hh, char *xwild, int wildBFSZ, WILD_DATA *data)
{
	WILD_HANDLE *h;
	struct _finddata_t c_file;
	char wild[BFSZ];
	lprintf(wild,wildBFSZ-10,"%s",xwild);
	if (strlen(wild)==2) if (wild[1]==':') strcat(wild,"\\*");
	if (ftp_lib_isdir(wild)) {
		strcat(wild,"\\*");
	}
	h = *hh  = malloc(sizeof(WILD_HANDLE));
	memset(data,0,sizeof(WILD_DATA));
/*	imsg("wild_first (%.400s)\n",wild);*/
	h->h = _findfirst(wild,&c_file);
	if (h->h== -1L) {
/*		if (lib_exists(wild)) {

		} else {

		}*/
		free(h);
		*hh = NULL;
		return FALSE;
	}
	data->isdir = (c_file.attrib & _A_SUBDIR);
	data->isnorm = TRUE;
	if (data->isdir) data->isnorm = FALSE;
	if (c_file.attrib & _A_HIDDEN) data->isnorm = FALSE;
	lprintf(data->name,BFSZ-10,"%s",c_file.name);
	data->time_create = c_file.time_create;
	data->time_access = c_file.time_access;
	data->time_write = c_file.time_write;
	data->size = c_file.size;
	return TRUE;
}
int wild_next(WILD_HANDLE *h, WILD_DATA *data)
{
	struct _finddata_t c_file;
	int r  = _findnext(h->h,&c_file);
	if (r!=0) return FALSE;
	memset(data,0,sizeof(WILD_DATA));
	data->isdir = (c_file.attrib & _A_SUBDIR);
	data->isnorm = TRUE;
	if (data->isdir) data->isnorm = FALSE;
	if (c_file.attrib & _A_HIDDEN) data->isnorm = FALSE;
	lprintf(data->name,BFSZ-10,"%s",c_file.name);
	data->time_create = c_file.time_create;
	data->time_access = c_file.time_access;
	data->time_write = c_file.time_write;
	data->size = c_file.size;

	data->gid = 0;
	data->uid = 0;
	data->mode = 0;
	data->islink = FALSE;
	data->nlinks = 1;


	strcpy(data->symlink,"");

	return TRUE;
}
int wild_end(WILD_HANDLE **hh)
{
	WILD_HANDLE *h = *hh;
	if (h!=NULL) _findclose(h->h);
	free(h);
	*hh = NULL;
	return TRUE;
}
#else
/*
Examples
	/a/b/c*.*	/a/b  c*.*
	/a/b/c/*.*	/a/b/c *.*
	/a/b/c		/a/b/c *.*  If (!isdir()) then return 'c'
*/
int wild_split(char *in, char *path, char *wild)
{
	char bf[BFSZ];
	char *s;
	char *last=NULL;
	lprintf(bf,BFSZ-10,"%s",in); in = bf;
	if (strlen(bf)>1) if (bf[strlen(bf)-1]=='/') bf[strlen(bf)-1] = 0;
	if (strlen(bf)>1) if (bf[strlen(bf)-1]=='\\') bf[strlen(bf)-1] = 0;
	for (s=in; *s!=0; s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last==NULL) {
		strcpy(path,"");
		ncpy(wild,bf,BFSZ-1);
		return TRUE;
	}
	/*
	Added back in, but without wild card, this fixes directory listing of
	/home
	*/
	if (ftp_lib_isdir(bf)) {
		lprintf(path,BFSZ-10,"%s",bf);
		lprintf(wild,BFSZ-10,"%s","*");
		return TRUE;
	}
	*last = 0;
	lprintf(path,BFSZ-10,"%s",bf);
	lprintf(wild,BFSZ-10,"%s",last+1);
	return TRUE;
}
int wild_first(WILD_HANDLE **hh, char *xwild, int wildBFSZ, WILD_DATA *data)
{
	WILD_HANDLE *h;
	char w[BFSZ];
	char path[BFSZ];
	char wild[BFSZ];
	lprintf(wild,wildBFSZ-10,"%s",xwild);
	*hh = h = malloc(sizeof(WILD_HANDLE));
	memset(data,0,sizeof(WILD_DATA));


	wild_split(wild,path,w);
	/* printf("wild_first (%s) (%s) (%s) \n",wild,path,w);*/
	h->h = opendir(path);
	if (h->h== NULL) {
		emsg("opendir failed (%s) (%s)\n",path,strerror(errno));
		free(h);
		*hh = NULL;
		return FALSE;
	}
	h->path = strdup(path);
	h->wild = strdup(w);
	return wild_next(h,data);
}
int wild_next(WILD_HANDLE *h, WILD_DATA *data)
{
	struct dirent *dval;
	char tmp[BFSZ];
	struct stat st;
	char dresult[3000 + sizeof(struct dirent)];
	int r;
	memset(data,0,sizeof(WILD_DATA));
again:
	safe_readdir(h->h,dresult,&dval);
	if (dval==NULL) return FALSE;

	/* Now do a wild match on the file name, then the path */
	if (!match_wild(h->wild,dval->d_name)) goto again;

	/* Now stat the file so we can fill in the details */
	sprintf(tmp,"%.400s/%.400s",h->path,dval->d_name);
	sprintf(data->name,"%.400s",dval->d_name);
	if (lstat(tmp,&st)!=0) {
		/* imsg("Could not stat (%.500s) (%.500s)\n",data->name,strerror(errno)); */
		goto again;
	}


	data->gid = st.st_gid;
	data->uid = st.st_uid;
	data->mode = st.st_mode;
	data->nlinks = st.st_nlink;
	data->dev = st.st_dev;
	data->inode = st.st_ino;
	data->islink = S_ISLNK(st.st_mode);
	if (data->islink) {
		lprintf(data->symlink,BFSZ-10,"%s","readlink_failed");
		r = readlink(tmp,data->symlink,BFSZ-1);
		if (r>=0) data->symlink[r] = 0;
	}

	data->isdir = S_ISDIR(st.st_mode);
	data->isnorm = S_ISREG(st.st_mode);
	if (data->isdir) data->isnorm = FALSE;
	data->time_create = st.st_ctime;
	data->time_access = st.st_atime;
	data->time_write = st.st_mtime;
	data->size = st.st_size;
	return TRUE;
}
int wild_end(WILD_HANDLE **hh)
{
	WILD_HANDLE *h = *hh;
	if (h==NULL) return TRUE;
	if (h!=NULL) closedir(h->h);
	free(h->path);
	free(h->wild);
	free(h);
	*hh = NULL;
	return TRUE;
}

#endif


int ftp_word_add(Wordlist **w, char *name)
{
	Wordlist *g;
	g = calloc(1,sizeof(Wordlist));
	g->word = strdup(name);
	g->next = *w;
	*w = g;
	return TRUE;
}
int ftp_word_find(Wordlist *w, char *s)
{
	for (;w!=NULL;w=w->next) {
		if (strcmp(w->word,s)==0) return TRUE;
	}
	return FALSE;
}
int ftp_word_findnc(Wordlist *w, char *s)
{
	for (;w!=NULL;w=w->next) {
		if (strcmpnc(w->word,s)==0) return TRUE;
	}
	return FALSE;
}
int ftp_word_free(Wordlist **w)
{
	Wordlist *g,*gnext;
	for (g=*w;g!=NULL;g=gnext) {
		gnext = g->next;
		free(g->word);
		free(g);
	}
	*w = NULL;
	return TRUE;
}
int ftp_word_reverse(Wordlist **wlist)
{
	Wordlist *w,*start=NULL;

	if (*wlist==NULL) return TRUE;
	for (w=*wlist;w!=NULL;w=w->next) {
		ftp_word_add(&start,w->word);
	}
	ftp_word_free(wlist);
	*wlist = start;
	return TRUE;
}


int ftp_getpwd(Ftplink *f, char *buffer, unsigned int bfsz, char *reason)
{
	buffer[0] = '\0';
	ftp_printf(f, "PWD\r\n");
	if ( ftp_result(f, 257, 400, reason) ) {
		char *ptr = strchr(reason, '"');
		if ( ptr==NULL) return FALSE;
		lprintf(buffer,bfsz-10,"%s",ptr+1);
		ptr = strchr(buffer,'"'); if (ptr!=NULL) *ptr = 0;
		return TRUE;
	}
	return FALSE;
}

/*
 * This array is designed for mapping upper and lower case letter
 * together for a case independent comparison.  The mappings are
 * based upon ascii character sequences.
 */
static const unsigned char charmap[] = {
        0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
        0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
        0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
        0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
        0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
        0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
        0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
        0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
        0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
        0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
        0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
        0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
        0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
        0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
        0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
        0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
        0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
        0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
        0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
        0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
        0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
        0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
        0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
        0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
        0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
        0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};

int strcmpnc(char *s1, char *s2)
{
        const unsigned char *cm = charmap,
             *us1 = (const unsigned char *)s1,
             *us2 = (const unsigned char *)s2;

        while (cm[*us1] == cm[*us2++])
                if (*us1++ == '\0')
                        return (0);
        return (cm[*us1] - cm[*--us2]);
}


int ftp_decode(char *bf, char *fname, char *xdate, int *sz, int *isdir, int *islink)
{
	char *s;
	char *p;
	char tmp[BFSZ];
	*isdir = *islink = FALSE;
	if (bf[0]=='d') *isdir = TRUE;
	if (bf[0]=='l') *islink = TRUE;
	if (bf[0]=='L') {
		*isdir = TRUE;
		*islink = TRUE;
	}

	*sz = 0;
	lprintf(tmp,BFSZ-10,"%s",bf);
	/*-rwxrwxrwx   1 user     group            3165 Dec 21  1999 dfilter.h*/

	s = mystrtok(tmp," ",&p); if (s==NULL) return FALSE;
	if (strlen(s)==8) { /* MSDOS format
		/* 09-27-00  04:50AM                  245 webio.h */
		lprintf(xdate,30,"%s",s);
		s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
		ncat(xdate,s,30); /* Add time on */
		s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
		if (strcmp(s,"<DIR>")==0) {
  			*isdir = TRUE;
			s = mystrtok(NULL,"\r\n",&p); if (s==NULL) return FALSE;
  			lprintf(fname,130,"%s",s+9);
		} else {
			*sz = atoi(s);
			s = mystrtok(NULL,"\r\n",&p); if (s==NULL) return FALSE;
			lprintf(fname,130,"%s",s);
		}
		return TRUE;
	}

	if (strlen(bf)<47) return FALSE;
	//  lrwxr-xr-x   1 9088     system        10 Oct 20  1998 .index.html -> index.html
	s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
	s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
	s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
	s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
	*sz = atoi(s);
	s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
	lprintf(xdate,30,"%s",s);
	s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
	ncat(xdate," ",30);
	ncat(xdate,s,30);
	s = mystrtok(NULL," \r\n",&p); if (s==NULL) return FALSE;
	ncat(xdate," ",30);
	ncat(xdate,s,30);
	s = mystrtok(NULL,"\r\n",&p); if (s==NULL) return FALSE;
	lprintf(fname,200,"%s",s);
	s = strchr(fname,'\n'); if (s!=NULL) *s = 0;
	s = strchr(fname,'\r'); if (s!=NULL) *s = 0;
	return TRUE;
}
char *mystrtok(char *start, char *toks, char **upto)
{
	char *s=start;
	if (start!=NULL) *upto = NULL;
	if (s==NULL) s = *upto;
	if (s==NULL) return NULL;
	for (;*s!=0;s++) {
		if (strchr(toks,*s)==NULL) break;
	}
	if (*s==0) return NULL;
	start = s;
	for (;*s!=0;s++) {
		if (strchr(toks,*s)!=NULL) break;
	}
	if (*s==0) {
		*upto = NULL;
		return start;
	}
	*s = 0;
	*upto = s+1;
	return start;
}
char *ncat(char *dst, char *src, int len)
{
        char *xdst = dst;
        int xlen = len;
	int r = strlen(dst);

	len -= r;
	dst += r;
        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        xdst[xlen] = 0;
        return xdst;
}
int ftp_lib_isdir(char *fname)
{
	struct stat st;

	if (stat( fname,&st)!=0) return FALSE;
	if (S_ISDIR(st.st_mode)) return TRUE;
	return FALSE;
}
int domatch(char *text, char *p);
int match_wild(char *p,char *text)
{
    if (p[0] == '*' && p[1] == '\0')
	return TRUE;
    return domatch(text, p) == TRUE;
}
#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif

#define TRUE			1
#define FALSE			0
#define ABORT			-1
#define NEGATE_CLASS		'^'

int domatch(char *text, char *p)
{
    register int	last;
    register int	matched;
    register int	reverse;

    for ( ; *p; text++, p++) {
	if (*text == '\0' && *p != '*')
	    return ABORT;
	switch (*p) {
	case '\\':
	    /* Literal match with following character. */
	    p++;
	    /* FALLTHROUGH */
	default:
	    if (*text != *p)
		return FALSE;
	    continue;
	case '?':
	    /* Match anything. */
	    continue;
	case '*':
	    while (*++p == '*')
		/* Consecutive stars act just like one. */
		continue;
	    if (*p == '\0')
		/* Trailing star matches everything. */
		return TRUE;
	    while (*text)
		if ((matched = domatch(text++, p)) != FALSE)
		    return matched;
	    return ABORT;
	case '[':
	    reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
	    if (reverse)
		/* Inverted character class. */
		p++;
	    matched = FALSE;
	    if (p[1] == ']' || p[1] == '-')
		if (*++p == *text)
		    matched = TRUE;
	    for (last = *p; *++p && *p != ']'; last = *p)
		if (*p == '-' && p[1] != ']'
		    ? *text <= *++p && *text >= last : *text == *p)
		    matched = TRUE;
	    if (matched == reverse)
		return FALSE;
	    continue;
	}
    }
    return *text == '\0';
}

char *ncpy(char *dst, char *src, int len)
{
        char *xdst = dst;
        int xlen = len;

        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        xdst[xlen] = 0;
        return xdst;
}

