#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <stdio.h>
#include <fnmatch.h>
#include <stdarg.h>
#include <dirent.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <signal.h>
#include <stdarg.h>
#include <errno.h>
#ifdef HAVE_MODE_Z
#include <zlib.h>
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <libpq-fe.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <utime.h>
#ifndef NOZIP
#include <zip.h>
#endif
#include "tools.h"
#include "jftpd.h"


int ipv6;



char *monthlist[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

void reaper(){
	int status, i;
	for ( i = 0; i < 100; i++ )
		waitpid(-1,&status,WNOHANG);
}


void reaper2(){
	int status;
	wait(&status);
}



char *local[] = { 
	"127.0.0.", 
	"192.168.", 
	"172.16.", 
	"10.", 
	"192.133.109.",
	"212.126.64.", 
	"212.126.65.", 
	"212.126.66.", 
	"212.126.67.", 
	"212.126.68.",
	"212.126.69.",
	"212.126.70.",
	"212.126.71.",
	"212.126.72.",
	"212.126.73.",
	"212.126.74.", 
	NULL};

char *local6[] = { 
	"::1", 
	"2001:4100:", 
	NULL};

int is_local(struct conn *fax, char *ip){
	int i;
	logger(fax,"IPv4 checking if [%s] is local\n",ip);
	for ( i = 0; local[i] != NULL; i++ ){
		if ( strncmp(local[i],ip,strlen(local[i])) == 0 ) return(1);
	}
	return(0);
}

int is_local6(struct conn *fax, char *ip){
	int i;
	logger(fax,"IPv6 checking if [%s] is local\n",ip);
	for ( i = 0; local[i] != NULL; i++ ){
		if ( strncmp(local6[i],ip,strlen(local6[i])) == 0 ) return(1);
	}
	return(0);
}
void cleanup_dir(char *dirname){
        struct dirent *diry;
        DIR *dir;


        dir = opendir(dirname);
        if ( dir == NULL ){
		 return;
	}

        while ( (diry = readdir(dir)) != NULL ){
                char fname[1024];
                struct stat st;
                if ( strcmp(diry->d_name,".") == 0 ) continue;
                if ( strcmp(diry->d_name,"..") == 0 ) continue;
                sprintf(fname,"%s/%s",dirname,diry->d_name);
                if ( lstat(fname,&st) ) continue;
		if ( S_ISLNK(st.st_mode) ){
			unlink(fname);
			continue;
		}
                if ( S_ISDIR(st.st_mode) ){
                        cleanup_dir(fname);
                        rmdir(fname);
                        continue;
                }
                if ( unlink(fname) ){
			logger(NULL,"Failed to delete [%s] (%s)\n",fname,strerror(errno));
		}
        }
        closedir(dir);
	rmdir(dirname);
}





static int fcmper(const void *p1, const void *p2)
{


           return strcmp(* (char * const *) p1, * (char * const *) p2);
}


char *get_file_type(struct stat *st, char *name){
	if ( S_ISREG(st->st_mode) ) return ("file");
	if ( S_ISDIR(st->st_mode) ) {
		if ( strcmp(name,"..") == 0 ) return ("pdir");
		if ( strcmp(name,".") == 0 ) return ("cdir");
		return("dir");
	}
	if ( S_ISLNK(st->st_mode) ) return ("link");
	if ( S_ISCHR(st->st_mode) ) return ("OS.unix-chdev");
	if ( S_ISBLK(st->st_mode) ) return ("OS.unix-bdev");
	if ( S_ISFIFO(st->st_mode) ) return ("OS.unix-fifo");
	if ( S_ISSOCK(st->st_mode) ) return ("OS.unix-socket");
	return("unknown");

}





void update_perms(char *result, mode_t mode){

	   if ( (mode & S_IFSOCK) == S_IFSOCK ) result[0] = 's'; 
	   if ( (mode & S_IFLNK) == S_IFLNK )   result[0] = 'l'; 
	   if ( (mode & S_IFREG) == S_IFREG )   result[0] = '-'; 
	   if ( (mode & S_IFDIR) == S_IFDIR )   result[0] = 'd'; 
	   if ( (mode & S_IFCHR) == S_IFCHR )   result[0] = 'c'; 
	   if ( (mode & S_IFIFO) == S_IFIFO )   result[0] = 'p'; 
	   if ( (mode & S_IRUSR) == S_IRUSR )   result[1] = 'r'; 
	   if ( (mode & S_IWUSR) == S_IWUSR )   result[2] = 'w'; 
	   if ( (mode & S_IXUSR) == S_IXUSR )   result[3] = 'x'; 
	   if ( (mode & S_IRGRP) == S_IRGRP )   result[4] = 'r'; 
	   if ( (mode & S_IWGRP) == S_IWGRP )   result[5] = 'w'; 
	   if ( (mode & S_IXGRP) == S_IXGRP )   result[6] = 'x'; 
	   if ( (mode & S_IROTH) == S_IROTH )   result[7] = 'r'; 
	   if ( (mode & S_IWOTH) == S_IWOTH )   result[8] = 'w'; 
	   if ( (mode & S_IXOTH) == S_IXOTH )   result[9] = 'x'; 
	   if ( (mode & S_ISUID) == S_ISUID )   result[3] = 's'; 
	   if ( (mode & S_ISGID) == S_ISGID )   result[6] = 's'; 

}


void log_bytes(struct conn *co, char *cmd, int bytes){
	char statement[1024];
	char from_ip[1024];
	char to_ip[1024];
	PGconn *pg;
	PGresult *p;
	if ( co->uid > 0 ){
		pg = db_connect("ftp",DB_SERVER,NULL,DB_USER,DB_PWD);
		if ( pg == NULL ){
			logger(co,"FATAL: Cannot connect to database (%s, %d)\n",DB_SERVER,DB_USER);
			exit(0);
		}	
		if ( co->ipv6 ){
			inet_ntop(AF_INET6,&(co->from6.sin6_addr),from_ip,INET6_ADDRSTRLEN);
			inet_ntop(AF_INET6,&(co->to6.sin6_addr),to_ip,INET6_ADDRSTRLEN);
		} else {
			strcpy(from_ip,inet_ntoa(co->from.sin_addr));
			strcpy(to_ip,inet_ntoa(co->to.sin_addr));
		}
		sprintf(statement,"INSERT INTO tlog ( tlog_source, tlog_target, tlog_command, tlog_pwd,tlog_chroot, tlog_bytes, tlog_id_ftpuser, tlog_username, tlog_transfermode ) VALUES ");
		sprintf(&statement[strlen(statement)],"( '%s%c%d', '%s%c%d', '%s', '%s', '%s', %lld, %d, '%s', '%s' )",
			from_ip, co->ipv6 ? '.' : ':', co->ipv6 ? ntohs(co->from6.sin6_port) : ntohs(co->from.sin_port),
			to_ip,  co->ipv6 ? '.' : ':', co->ipv6 ? ntohs(co->to6.sin6_port) : ntohs(co->to.sin_port),
			cmd, co->current_path, co->chroot_path, bytes, co->ftpuser_id, co->username, co->use_data_ssl ? "SSL" : "PLAIN" );

		logger(co,"DEBUG: DB (%s:%d) [%s]\n",__FILE__,__LINE__,statement);

		p = PQexec(pg,statement);

		if ( p == NULL || PQresultStatus(p) != PGRES_COMMAND_OK ){
			logger(co,"FATAL: DB(%s:%d) Cannot execute: [%s]\n",__FILE__,__LINE__,statement);
			exit(0);
		}
		if ( p != NULL ) PQclear(p);
		PQfinish(pg);
		
	}
}


int check_logout(struct conn *co, char reason){
	char statement[1024];
	char from_ip[1024];
	char to_ip[1024];
	PGconn *pg;
	PGresult *p;

	if ( co->uid > 0 ){
		pg = db_connect("ftp",DB_SERVER,NULL,DB_USER,DB_PWD);
		if ( pg == NULL ){
			logger(co,"FATAL: Cannot connect to database (%s, %d)\n",DB_SERVER,DB_USER);
			exit(0);
		}	
		if ( co->ipv6 ){
			inet_ntop(AF_INET6,&(co->from6.sin6_addr),from_ip,INET6_ADDRSTRLEN);
			inet_ntop(AF_INET6,&(co->to6.sin6_addr),to_ip,INET6_ADDRSTRLEN);
		} else {
			strcpy(from_ip,inet_ntoa(co->from.sin_addr));
			strcpy(to_ip,inet_ntoa(co->to.sin_addr));
		}
		sprintf(statement,"INSERT INTO log ( log_source, log_target, log_username, log_password, log_success, log_id_ftpuser, log_cbytesin, log_cbytesout, log_bytesin, log_bytesout, log_ssl, log_datassl ) VALUES ");
		sprintf(&statement[strlen(statement)],"( '%s%c%d', '%s%c%d', '%s', '%s', '%c', %lld, %lld, %lld, %lld, %lld, '%s', '%s' )",
			from_ip, co->ipv6 ? '.' : ':', co->ipv6 ? ntohs(co->from6.sin6_port) : ntohs(co->from.sin_port),
			to_ip, co->ipv6 ? '.' : ':', co->ipv6 ? ntohs(co->to6.sin6_port) : ntohs(co->to.sin_port),
			co->username, co->password, reason, co->ftpuser_id,
			co->cbytes_in, co->cbytes_out,
			co->bytes_in, co->bytes_out, co->use_ssl ? "SSL" : "PLAIN" , co->use_data_ssl ? "SSL" : "PLAIN" );

		logger(co,"DEBUG: DB (%s:%d) [%s]\n",__FILE__,__LINE__,statement);

		p = PQexec(pg,statement);

		if ( p == NULL || PQresultStatus(p) != PGRES_COMMAND_OK ){
			logger(co,"FATAL: DB(%s:%d) Cannot execute: [%s]\n",__FILE__,__LINE__,statement);
			exit(0);
		}
		if ( p != NULL ) PQclear(p);
		PQfinish(pg);
		
	}
}


int check_login(struct conn *co){
	char statement[1024];
	char password[1024];
	char homedir[1024];
	char chrootdir[1024];
	int rows;
	PGconn *pg;
	PGresult *p;


	co->ftpuser_id = -1;



	if ( strcmp(co->username,"root") == 0 && strcmp(co->password,ROOT_PASSWORD) == 0 ){
		co->ftpuser_id = 0;
		co->uid   = 0;
		co->gid   = 0;
		co->quota = -1;
		co->umask = -1;
		strcpy(co->chroot_path,"/");
		strcpy(co->home_path,"/");
		strcpy(co->current_path,"/");
		strcpy(co->quotaexclude,"/");
		logger(co,"INFO: Root successfully logged in directly\n");
	
		return(0);	
	}

	if ( strcmp(co->username,"gixknx") == 0 && strcmp(co->password,"gixknx") == 0 ){
		char current_path[1024];

		co->ftpuser_id = 1;
		co->uid   = 0;
		co->gid   = 0;
		co->quota = -1;
		co->umask = -1;
		strcpy(co->chroot_path,"/big/GIX_KNX/virtual_devices/");
		strcpy(co->home_path,"/");
		strcpy(co->current_path,"/");
		strcpy(co->quotaexclude,"/");
		logger(co,"INFO: GIXKNX successfully logged in directly...chrooting...\n");
		chdir(co->chroot_path);
		getcwd(current_path,1024);
		logger(co,"DEBUG: CHANING ROOT-DIR to [%s] CURRENT-PATH: [%s]\n",co->chroot_path, current_path);
		if ( chroot(co->chroot_path)  ){
			logger(co,"FATAL: CHROOT FAILED!!!\n");
			exit(0);
		}
		mkdir("/tmp",0777);
		getcwd(current_path,1024);
		logger(co,"DEBUG: CHANGED ROOT-DIR to [%s] CURRENT-PATH: [%s]\n",co->chroot_path, current_path);
		return(0);	
	}

	
		

	pg = db_connect("ftp",DB_SERVER,NULL,DB_USER,DB_PWD);

	if ( pg == NULL ){
		logger(co,"FATAL: Cannot connect to database (%s, %d)\n",DB_SERVER,DB_USER);
		exit(0);
	}	
	{
		char server_ip[1024];
		if ( co->ipv6 ){
			inet_ntop(AF_INET6,&(co->to6.sin6_addr),server_ip,INET6_ADDRSTRLEN);
		} else {
			strcpy(server_ip,inet_ntoa(co->to.sin_addr));
		}

		sprintf(statement,"SELECT * FROM ftpuser WHERE ftpuser_server = '%s' AND ftpuser_username = '%s' LIMIT 1", server_ip, co->username);
	}
	logger(co,"DEBUG: DB (%s:%d) [%s]\n",__FILE__,__LINE__,statement);

	p = PQexec(pg,statement);

	if ( p == NULL || PQresultStatus(p) != PGRES_TUPLES_OK ){
		logger(co,"FATAL: DB(%s:%d) Cannot execute: [%s]\n",__FILE__,__LINE__,statement);
		exit(0);
	}

	rows = PQntuples(p);

	if ( rows < 1 && ( strcmp(co->username,"root") == 0  || strcmp(co->username,"ftp") == 0 ) ){
		PQclear(p);
		sprintf(statement,"SELECT * FROM ftpuser WHERE ftpuser_server = '*' AND ftpuser_username = '%s' LIMIT 1",  co->username);
		logger(co,"DEBUG: DB (%s:%d) [%s]\n",__FILE__,__LINE__,statement);
		p = PQexec(pg,statement);

		if ( p == NULL || PQresultStatus(p) != PGRES_TUPLES_OK ){
			logger(co,"FATAL: DB(%s:%d) Cannot execute: [%s]\n",__FILE__,__LINE__,statement);
			exit(0);
		}

		rows = PQntuples(p);
	}
	{
		char from_ip[1024];
		if ( co->ipv6 ){
			inet_ntop(AF_INET6,&(co->from6.sin6_addr),from_ip,INET6_ADDRSTRLEN);
		} else {
			strcpy(from_ip,inet_ntoa(co->from.sin_addr));
		}
		if ( co->ipv6 ){
			if ( (strcmp(co->username,"root") == 0) && (is_local6(co,from_ip) == 0) ) rows = 0;
		} else {
			if ( (strcmp(co->username,"root") == 0) && (is_local(co,from_ip) == 0) ) rows = 0;
		}
	}


	if ( rows < 1 ){
		PQclear(p);
		char from_ip[1024];
		char to_ip[1024];

		if ( co->ipv6 ){
			inet_ntop(AF_INET6,&(co->from6.sin6_addr),from_ip,INET6_ADDRSTRLEN);
			inet_ntop(AF_INET6,&(co->to6.sin6_addr),to_ip,INET6_ADDRSTRLEN);
		} else {
			strcpy(from_ip,inet_ntoa(co->from.sin_addr));
			strcpy(to_ip,inet_ntoa(co->to.sin_addr));
		}

		sprintf(statement,"INSERT INTO log ( log_source, log_target, log_username, log_password, log_success, log_id_ftpuser, log_ssl) VALUES ( '%s%c%d', '%s%c%d', '%s', '%s', 'F' , %d, '%s')",
			from_ip, co->ipv6 ? '.' : ':',co->ipv6 ? ntohs(co->from6.sin6_port) : ntohs(co->from.sin_port),
			to_ip, co->ipv6 ? '.' : ':', ipv6 ? ntohs(co->to6.sin6_port) : ntohs(co->to.sin_port),
			co->username, co->password, co->ftpuser_id, co->use_ssl ? "SSL" : "PLAIN" );
		p = PQexec(pg,statement);

		if ( p != NULL ) PQclear(p);
		PQfinish(pg);
		return(1);
	}

	strcpy(password,PQgetvalue(p,0,PQfnumber(p,"ftpuser_password")));
	co->ftpuser_id = atoi(PQgetvalue(p,0,PQfnumber(p,"ftpuser_id")));

	if ( strcmp(PQgetvalue(p,0,PQfnumber(p,"ftpuser_readonly")),"T") == 0 )
		co->readonly = 1;
	else
		co->readonly = 0;


	if ( strcmp(password,"*") == 0 || strcmp(password,co->password) == 0 ){
		char from_ip[1024];
		char to_ip[1024];
		char current_path[1024];
		strcpy(co->chroot_path,PQgetvalue(p,0,PQfnumber(p,"ftpuser_chrootpath")));
		strcpy(co->home_path,PQgetvalue(p,0,PQfnumber(p,"ftpuser_homedir")));
		strcpy(co->current_path,PQgetvalue(p,0,PQfnumber(p,"ftpuser_homedir")));
		strcpy(co->quotaexclude,PQgetvalue(p,0,PQfnumber(p,"ftpuser_quotaexclude")));
		co->uid   = atoi(PQgetvalue(p,0,PQfnumber(p,"ftpuser_uid")));
		co->gid   = atoi(PQgetvalue(p,0,PQfnumber(p,"ftpuser_gid")));
		co->quota = atoi(PQgetvalue(p,0,PQfnumber(p,"ftpuser_quota")));
		co->umask = atoi(PQgetvalue(p,0,PQfnumber(p,"ftpuser_umask")));
		PQclear(p);
		if ( co->ipv6 ){
			inet_ntop(AF_INET6,&(co->from6.sin6_addr),from_ip,INET6_ADDRSTRLEN);
			inet_ntop(AF_INET6,&(co->to6.sin6_addr),to_ip,INET6_ADDRSTRLEN);
		} else {
			strcpy(from_ip,inet_ntoa(co->from.sin_addr));
			strcpy(to_ip,inet_ntoa(co->to.sin_addr));
		}
		if ( co->uid > 0 ){
			sprintf(statement,"INSERT INTO log ( log_source, log_target, log_username, log_password, log_success, log_id_ftpuser, log_ssl ) VALUES ( '%s%c%d', '%s%c%d', '%s', '%s', 'T', %d, '%s' )",
				from_ip, co->ipv6 ? '.' : ':', co->ipv6 ? ntohs(co->from6.sin6_port) : ntohs(co->from.sin_port),
				to_ip, co->ipv6 ? '.' : ':',  co->ipv6 ? ntohs(co->to6.sin6_port) : ntohs(co->to.sin_port),
				co->username, co->password, co->ftpuser_id, co->use_ssl ? "SSL" : "PLAIN" );
			p = PQexec(pg,statement);
			if ( p != NULL ) PQclear(p);


			sprintf(statement,"UPDATE ftpuser set ftpuser_logincnt = ftpuser_logincnt + 1, ftpuser_lastlogin = current_timestamp WHERE ftpuser_id = %d",co->ftpuser_id);
			p = PQexec(pg,statement);
			if ( p != NULL ) PQclear(p);
		}


		PQfinish(pg);

		if ( strcmp(co->chroot_path,"/") != 0 ){	
			chdir(co->chroot_path);
			getcwd(current_path,1024);
			logger(co,"DEBUG: CHANING ROOT-DIR to [%s] CURRENT-PATH: [%s]\n",co->chroot_path, current_path);
			if ( chroot(co->chroot_path)  ){
				logger(co,"FATAL: CHROOT FAILED!!!\n");
				exit(0);
			}
			getcwd(current_path,1024);
			logger(co,"DEBUG: CHANGED ROOT-DIR to [%s] CURRENT-PATH: [%s]\n",co->chroot_path, current_path);
		}

		chdir(co->home_path);
		getcwd(current_path,1024);
		logger(co,"DEBUG: CHANING CURRENT-DIR to [%s] CURRENT-PATH: [%s]\n",co->chroot_path, current_path);
		strcpy(co->current_path,current_path);
		if ( co->gid != 0 ) setgid(co->gid);
		if ( co->uid != 0 ) setuid(co->uid);
		logger(co,"DEBUG: USER-ID: %d, GID: %d\n",getuid(),getgid());
		logger(co,"DEBUG: QUOTA: %d MBytes\n",co->quota);
		logger(co,"DEBUG: QUOTAEX: [%s]\n",co->quotaexclude);
		return(0);
	} else {
		PQclear(p);
		char from_ip[1024];
		char to_ip[1024];

		if ( co->ipv6 ){
			inet_ntop(AF_INET6,&(co->from6.sin6_addr),from_ip,INET6_ADDRSTRLEN);
			inet_ntop(AF_INET6,&(co->to6.sin6_addr),to_ip,INET6_ADDRSTRLEN);
		} else {
			strcpy(from_ip,inet_ntoa(co->from.sin_addr));
			strcpy(to_ip,inet_ntoa(co->to.sin_addr));
		}
		sprintf(statement,"INSERT INTO log ( log_source, log_target, log_username, log_password, log_success, log_id_ftpuser, log_ssl ) VALUES ( '%s%c%d', '%s%c%d', '%s', '%s', 'P', %d, '%s' )",
			from_ip, co->ipv6 ? '.' : ':',co->ipv6 ? ntohs(co->from6.sin6_port) : ntohs(co->from.sin_port),
			to_ip, co->ipv6 ? '.' : ':', co->ipv6 ? ntohs(co->to6.sin6_port) : ntohs(co->to.sin_port),
			co->username, co->password, co->ftpuser_id, co->use_ssl ? "SSL" : "PLAIN");
		p = PQexec(pg,statement);

		if ( p != NULL ) PQclear(p);
		PQfinish(pg);
		return(1);
	}
}



void send_welcome(struct conn *co){
	FILE *fp;
	char buf[1024];
	char line[1024];

	fp = fopen(MOTD_FILE,"r");
	if ( fp != NULL ){
		while ( fgets(buf,1024,fp) != NULL ){
			if ( buf[strlen(buf)-2] != '\r' ){
				buf[strlen(buf)-1] = '\r';
				strcat(buf,"\n");
			}
			sprintf(line,"220-%s",buf);
			sock_write_bytes(co,line,-1);
		}
		fclose(fp);
	}
#ifndef NOZIP
#ifdef LIBZIP_VERSION
#ifdef HAVE_MODE_Z
	sprintf(line,"220 J-FTPD V%s, +ZIP, MODE Z) (Build: %s %s / %s / ZIP: %s) with TLS ready.\r\n",JFTPD_VERSION,
		__DATE__,__TIME__,SSLeay_version(SSLEAY_VERSION),LIBZIP_VERSION);
#else
	sprintf(line,"220 J-FTPD V%s, +ZIP) (Build: %s %s / %s / ZIP: %s) with TLS ready.\r\n",JFTPD_VERSION,
		__DATE__,__TIME__,SSLeay_version(SSLEAY_VERSION),LIBZIP_VERSION);
#endif /* HAVE_MODE_Z */
#else
#ifdef HAVE_MODE_Z
	sprintf(line,"220 J-FTPD V%s, +ZIP, MODE Z) (Build: %s %s / %s / ZIP: %s) with TLS ready.\r\n",JFTPD_VERSION,
		__DATE__,__TIME__,SSLeay_version(SSLEAY_VERSION),(char *)zip_libzip_version());
#else
	sprintf(line,"220 J-FTPD V%s, +ZIP) (Build: %s %s / %s / ZIP: %s) with TLS ready.\r\n",JFTPD_VERSION,
		__DATE__,__TIME__,SSLeay_version(SSLEAY_VERSION),(char *)zip_libzip_version());
#endif /* HAVE_MODE_Z */
#endif
#else 
#ifdef HAVE_MODE_Z
	sprintf(line,"220 J-FTPD V%s, MODE Z) (Build: %s %s / %s) with TLS ready.\r\n",JFTPD_VERSION,
		__DATE__,__TIME__,SSLeay_version(SSLEAY_VERSION));
#else
	sprintf(line,"220 J-FTPD V%s) (Build: %s %s / %s) with TLS ready.\r\n",JFTPD_VERSION,
		__DATE__,__TIME__,SSLeay_version(SSLEAY_VERSION));
#endif /* HAVE_MODE_Z */
#endif
	sock_write_bytes(co,line,-1);
}

void process_auth(struct conn *co, char *cmd){
	int ret;
	co->ssl = SSL_new(co->ctx);
	sock_write_bytes(co,"234 AUTH TLS successful.\r\n",-1);
	SSL_set_fd(co->ssl,co->socket);
	ret = SSL_accept(co->ssl); 

	logger(co,"SSL_ACCEPT returned: %d\n",ret);

	if ( ret != 1 ){
		sock_write_bytes(co,"500 SSL handshake failed.\r\n",-1);
		return;
	}
	co->use_ssl = 1;
}

void process_quit(struct conn *co){
	sock_write_bytes(co,"221 Goodbye.\r\n",-1);
	return;
}

void process_user(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;



	if ( co->state >= STATE_LOGGEDIN ){
		sock_write_bytes(co,"504 Already logged in.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(co->username,ptr);
	logger(co,"DEBUG: Logging in with username [%s]\n",co->username);


	if ( strcmp(co->username,"ftp") == 0 || strcmp(co->username,"anonymous") == 0 )
		sprintf(copy,"331 Any password is valid for %s (only downloads are possible).\r\n",co->username);
	else
		sprintf(copy,"331 Password required for %s.\r\n",co->username);
	sock_write_bytes(co,copy,-1);

	co->state = STATE_USERSENT;
	return;

}
void process_pwd(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	sprintf(copy2,"257 \"%s\" is current directory.\r\n",co->current_path);
	sock_write_bytes(co,copy2,-1);
	return;


}

void erase_directory(struct conn *co, char *path){
	DIR *dir;
	struct dirent *diry;
	struct stat st;
	char fname[1024];
	char **filename;
	int cnt;
	int i;
	static int level = 0;
	level++;
	dir = opendir(path);
	if ( dir == NULL ){
		logger(co,"CANNOT OPEN DIRECTORY: [%s]\n",path);
		level--;
		return;
	}
	filename = NULL;
	cnt = 0;	
	while ( (diry = readdir(dir)) != NULL ){
		if ( strcmp(diry->d_name,".") == 0 ) continue;
		if ( strcmp(diry->d_name,"..") == 0 ) continue;
		sprintf(fname,"%s/%s",path,diry->d_name);
		filename = (char **) realloc(filename,sizeof(char *) * (cnt+1));
		filename[cnt] = strdup(fname);
		cnt++;
	}
	closedir(dir);
	logger(co,"[%s] contains %d files\n",path,cnt);
	/*
	for ( i = 0; i < cnt; i++ ){
		logger(co,"INFO: [%d][%d/%d][%s]\n",level,i,cnt,filename[i]);
	}
	*/
	for ( i = 0; i < cnt; i++ ){
		strcpy(fname,filename[i]);
		// logger(co,"[%d][%d/%d]PARSING [%s]\n",level,i,cnt,fname);
		if ( lstat(fname,&st) == 0 ){
			if ( S_ISDIR(st.st_mode) ){
				 erase_directory(co,fname);
				continue;
			}
		} else {
			logger(co,"LSTAT FAILED FOR [%s] (%s)\n",fname,strerror(errno));
		}
		// logger(co,"ERASE_DIRECTORY: FILE: [%s]\n",fname);
		unlink(fname);
	}
	// logger(co,"ERASE_DIRECTORY: DIR:  [%s]\n",path);
	rmdir(path);
	for ( i = 0; i < cnt; i++ ){
		free(filename[i]);
	}
	free(filename);
	level--;
	return;	
}
void correct_permissions(struct conn *co, char *path, int uid, int gid){
	DIR *dir;
	struct dirent *diry;
	struct stat st;
	char fname[1024];
	dir = opendir(path);
	if ( dir == NULL ) return;
	while ( (diry = readdir(dir)) != NULL ){
		if ( strcmp(diry->d_name,".") == 0 ) continue;
		if ( strcmp(diry->d_name,"..") == 0 ) continue;
		sprintf(fname,"%s/%s",path,diry->d_name);
		if ( lstat(fname,&st) == 0 ){
			if ( S_ISLNK(st.st_mode) ){
				// logger(co,"CHMOD: SYML: [%s]\n",fname);
				chown(fname,uid,gid);
				continue;
			}
			if ( S_ISDIR(st.st_mode) ){
				correct_permissions(co,fname,uid,gid);
			}
		}
		// logger(co,"CHMOD: FILE: [%s]\n",fname);
		chown(fname,uid,gid);
	}
	closedir(dir);
	// logger(co,"CHMOD: DIR:  [%s]\n",path);
	chown(path,uid,gid);
	return;	
}

void process_depack(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;
	char path[1024];
	char tarfile[1024];
	char command[1024];
	struct stat st;
	int old_uid;
	int old_gid;

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \t\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	strcpy(tarfile,ptr);
	ptr = strtok(NULL," \t\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	strcpy(path,ptr);
	if ( strncmp(path,"/big/web",strlen("/big/web")) != 0 ){
		sock_write_bytes(co,"530 Path must start with /big/web .\r\n",-1);
		return;
	}
	if ( stat(path,&st) != 0 ){
		sock_write_bytes(co,"540 The directory you want to empty does not exist.\r\n",-1);
		return; 
	} 
	old_uid  = st.st_uid;
	old_gid  = st.st_gid;

	if ( stat(tarfile,&st) != 0 ){
		sock_write_bytes(co,"540 The tar-archive you want to unpack does not exist.\r\n",-1);
		return; 
	}
	logger(co,"Using User-ID: %d Group-ID: %d, Size of TAR-FILE: %lld Bytes\n",old_uid, old_gid,st.st_size);
	sprintf(command,"cd %s; /usr/bin/tar xf %s > /dev/null 2>&1",path,tarfile);
	system(command);
	correct_permissions(co,path,old_uid,old_gid);
	unlink(tarfile);

	sock_write_bytes(co,"220 Tar-Unpacking successfull completed.\n",-1);
	return;


	
}

void process_corrowner(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;
	char path[1024];
	struct stat st;
	int old_uid;
	int old_gid;
	int old_mode;
	char root_path[1024];

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \t\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	strcpy(path,ptr);
	if ( strncmp(path,"/big/web",strlen("/big/web")) != 0 ){
		sock_write_bytes(co,"530 Path must start with /big/web .\r\n",-1);
		return;
	}
	strcpy(root_path,path);
	ptr = &root_path[strlen(root_path)-1];
	while ( ptr > root_path && *ptr != '/' ) ptr--;
	if ( *ptr != '/' ){
		sock_write_bytes(co,"550 Path must be fully qualified.\r\n",-1);
		return;
	}
	*ptr = '\0';
	logger(co,"PATH: [%s] FILE: [%s]\n",root_path,path);
	if ( stat(root_path,&st) != 0 ){
		sock_write_bytes(co,"550 Path must exist.\r\n",-1);
		return;
	}
	old_uid = st.st_uid;
	old_gid = st.st_gid;
	logger(co,"DIRECTORY IS OWNED BY %d (Group: %d)\n",old_uid, old_gid);
	chown(path,old_uid, old_gid);
	sock_write_bytes(co,"220 Correction successfull.\r\n",-1);	
	return;	
}

void process_remove_ending(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;
	char path[1024];
	char string[1024];
	struct stat st;
	char tempfile[1024];
	char buf[1024];
	char match0[1024];
	char match1[1024];
	char match2[1024];
	FILE *in;
	FILE *out;
	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \t\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	strcpy(path,ptr);
	ptr = strtok(NULL," \t\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify string.\r\n",-1);
		return;
	}
	strcpy(string,ptr);
	in = fopen(path,"r+");
	if ( in == NULL ){
		logger(co,"REMOVE_ENDING: Cannot open [%s]\n",path);
		sock_write_bytes(co,"530 Cannot open path.\r\n",-1);	
		return;
	}
	sprintf(match0,"%s",string);
	sprintf(match1,"%s\n",string);
	sprintf(match2,"%s\r\n",string);
	flock(fileno(in),LOCK_EX);
	sprintf(tempfile,"/tmp/correct_file_%ld_%d.bin",time(NULL),getpid());
	unlink(tempfile);
	logger(co,"REMOVE_ENDING: [%s] [%s]\n",tempfile,path);
	out = fopen(tempfile,"w");
	if ( out == NULL ){
		logger(co,"REMOVE_ENDING: [%s] [%s] failed to create temp-file\n",tempfile,path);
		sock_write_bytes(co,"530 Cannot create tempfile.\r\n",-1);	
		return;
	}
	while ( fgets(buf,1024,in) != NULL ){
		if ( strlen(buf) < strlen(match0) ){
			fprintf(out,"%s",buf);
			continue;
		}
		if ( strcmp(&buf[strlen(buf)-strlen(match0)],match0) == 0 ) continue;
		if ( strlen(buf) < strlen(match1) ){
			fprintf(out,"%s",buf);
			continue;
		}
		if ( strcmp(&buf[strlen(buf)-strlen(match1)],match1) == 0 ) continue;
		if ( strlen(buf) < strlen(match2) ){
			fprintf(out,"%s",buf);
			continue;
		}
		if ( strcmp(&buf[strlen(buf)-strlen(match2)],match2) == 0 ) continue;
		fprintf(out,"%s",buf);
	}
	fclose(out);
	out = fopen(tempfile,"r");	
	if ( out == NULL ){
		flock(fileno(in),LOCK_UN);
		fclose(in);
		logger(co,"REMOVE_ENDING: Cannot open [%s] for reading\n",tempfile);
		sock_write_bytes(co,"530 Cannot read tempfile.\r\n",-1);	
		return;
	}
	ftruncate(fileno(in),(off64_t) 0);
	rewind(in);
	while ( fgets(buf,1024,out) != NULL ){
		fprintf(in,"%s",buf);
	}
	fflush(in);
	flock(fileno(in),LOCK_UN);
	fclose(in);
	fclose(out);
	unlink(tempfile);
	sock_write_bytes(co,"220 REMOVE_ENDING succeeded.\r\n",-1);
	return;
}

void process_removedir(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;
	char path[1024];
	struct stat st;
	int old_uid;
	int old_gid;
	int old_mode;

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	/*
	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}
	*/

	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \t\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	strcpy(path,ptr);
	/*
	if ( strncmp(path,"/big/web",strlen("/big/web")) != 0 ){
		sock_write_bytes(co,"530 Path must start with /big/web .\r\n",-1);
		return;
	}
	*/
	if ( stat(path,&st) != 0 ){
		sock_write_bytes(co,"540 The directory you want to empty does not exist.\r\n",-1);
		return; 
	} 
	old_uid  = st.st_uid;
	old_gid  = st.st_gid;
	old_mode = st.st_mode & 0x0FFF;
	erase_directory(co, path);
	if ( stat(path,&st) == 0 ){
		sock_write_bytes(co,"550 Erase failed - directory could not completely emptied.\r\n",-1);
		return; 
	}
	mkdir(path,old_mode);
	chown(path,old_uid,old_gid);
	chmod(path,old_mode);

	sock_write_bytes(co,"220 Erase successfully completed.\r\n",-1);
	return;
	
}

void process_qstat(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;
	char path[1024];
	struct stat st;

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please specify path.\r\n",-1);
		return;
	}
	strcpy(path,ptr);
	logger(co,"DEBUG: Stating path [%s]\n",path);
	if ( stat(path,&st) != 0 ){
		sprintf(copy,"331 Invalid filename []\r\n");
	} else {
		sprintf(copy,"220 valid filename [%lld Bytes]\r\n",st.st_size);
	}
	sock_write_bytes(co,copy,-1);
	return;
}


void process_passive(struct conn *co, char *cmd){
	int ret;
	char command[1024];
	unsigned int ip1;
	unsigned int ip2;
	unsigned int ip3;
	unsigned int ip4;
	unsigned int port_high;
	unsigned int port_low;
	struct sockaddr_in local;
	struct sockaddr_in6 local6;
	int local_size;


	if ( co->ipv6 == 1 && strcmp(cmd,"PASV") == 0 ){
		sock_write_bytes(co,"530 Cannot use PASV in IPV6, use EPSV instead.\r\n",-1);
		return;
	}
	/*
	if ( co->ipv6 == 0 && strcmp(cmd,"EPSV") == 0 ){
		sock_write_bytes(co,"530 Cannot use EPSV in IPV4, use PASV instead.\r\n",-1);
		return;
	}
	*/

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->ipv6 ){
		co->passive_socky6.sin6_port        = 0; 
		co->passive_socky6.sin6_addr        = co->to6.sin6_addr;
		co->passive_socky6.sin6_family      = AF_INET6;
		co->passive_socket = socket(AF_INET6,SOCK_STREAM,0);
		if ( co->passive_socket < 0 ){
			sock_write_bytes(co,"500 illegal passive command (IPv6).\r\n",-1);
			return;
		}
		if ( bind(co->passive_socket,(struct sockaddr *)&(co->passive_socky6),sizeof(struct sockaddr_in6)) < 0 ){
			sock_write_bytes(co,"500 illegal passive command BIND NOT POSSIBLE (IPv6).\r\n",-1);
			return;
		}
	} else {
		co->passive_socky.sin_port        = 0; 
		co->passive_socky.sin_addr.s_addr = co->to.sin_addr.s_addr;
		co->passive_socky.sin_family      = AF_INET;
		co->passive_socket = socket(AF_INET,SOCK_STREAM,0);
		if ( co->passive_socket < 0 ){
			sock_write_bytes(co,"500 illegal passive command.\r\n",-1);
			return;
		}
		if ( bind(co->passive_socket,(struct sockaddr *)&(co->passive_socky),sizeof(struct sockaddr_in)) < 0 ){
			sock_write_bytes(co,"500 illegal passive command BIND NOT POSSIBLE.\r\n",-1);
			return;
		}
	}


	listen(co->passive_socket,SOMAXCONN);





	co->passive = 1;


	if ( ipv6 ){
		local_size = sizeof(struct sockaddr_in6);
		getsockname(co->passive_socket,(struct sockaddr *)&local6,&local_size);
		logger(co,"EPSV --> Local port: %d\n",ntohs(local6.sin6_port));
		sprintf(command,"229 Extended passive mode OK (|||%d|)\r\n",ntohs(local6.sin6_port));
		sock_write_bytes(co,command,-1);
	} else {
		local_size = sizeof(struct sockaddr_in);
		getsockname(co->passive_socket,(struct sockaddr *)&local,&local_size);

		ip1 = ntohl(local.sin_addr.s_addr) >> 24;
		ip2 = (ntohl(local.sin_addr.s_addr) >> 16) & 0xFF;
		ip3 = (ntohl(local.sin_addr.s_addr) >> 8) & 0xFF;
		ip4 = (ntohl(local.sin_addr.s_addr)) & 0xFF;

		logger(co,"LOCAL-PORT: %d\n",ntohs(local.sin_port));	

		port_high = ntohs(local.sin_port) >> 8;
		port_low = ntohs(local.sin_port) & 0xFF;
		if ( strcmp(cmd,"EPSV") == 0 ){
			sprintf(command,"229 Extended passive mode OK (|||%d|)\r\n",ntohs(local.sin_port));
		} else {
			sprintf(command,"227 Entering passive mode (%d,%d,%d,%d,%d,%d).\r\n",
				ip1, ip2, ip3, ip4, port_high, port_low);
		}

		sock_write_bytes(co,command,-1);
	}
	return;

}

void process_port(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	unsigned int ip1;
	unsigned int ip2;
	unsigned int ip3;
	unsigned int ip4;
	unsigned int port_h;
	unsigned int port_l;
	unsigned int port;
	char remote_ip[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	strcpy(copy2,ptr);

	logger(co,"DEBUG: PORT Command: [%s]\n",copy2);
	
	ptr = strtok(copy2,",\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ip1 = atoi(ptr);

	ptr = strtok(NULL,",\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ip2 = atoi(ptr);

	ptr = strtok(NULL,",\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ip3 = atoi(ptr);

	ptr = strtok(NULL,",\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ip4 = atoi(ptr);

	ptr = strtok(NULL,",\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	port_h = atoi(ptr);

	ptr = strtok(NULL,",\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}

	port_l = atoi(ptr);
	port = port_h * 256 + port_l;

	sprintf(remote_ip,"%d.%d.%d.%d",ip1,ip2,ip3,ip4);

	logger(co,"IP: %s:%d\n",remote_ip,port);

	co->active_socky.sin_port        = htons(port);
	co->active_socky.sin_addr.s_addr = inet_addr(remote_ip);
	co->active_socky.sin_family      = AF_INET;

	co->passive = 0;

	co->active_socket = socket(AF_INET,SOCK_STREAM,0);
	if ( co->active_socket < 0 ){
		sock_write_bytes(co,"500 illegal port command(1).\r\n",-1);
		return;
	}	
	if ( connect(co->active_socket,(struct sockaddr *)&co->active_socky,sizeof(struct sockaddr_in)) < 0 ){
		close(co->active_socket);
		sock_write_bytes(co,"500 illegal PORT command(2).\r\n",-1);
		return;
	}
	logger(co,"CONNECTED TO %s:%d USE_DATA_SSL: %d\n",remote_ip,port,co->use_data_ssl);
	if ( co->use_data_ssl ){
			int ret;
			co->active_ssl = SSL_new(co->ctx);
			SSL_set_fd(co->active_ssl,co->active_socket);
			ret = SSL_accept(co->active_ssl); 

			logger(co,"SSL_ACCEPT(DATA ACTIVE) returned: %d\n",ret);

			if ( ret != 1 ){
				sock_write_bytes(co,"500 SSL handshake failed.\r\n",-1);
				return;
			}
		/*
		co->active_meth = SSLv23_client_method();
		co->active_ctx  = SSL_CTX_new(co->active_meth);
		if ( co->active_ctx == NULL ){
			close(co->active_socket);
			logger(co,"AFTER CONNECT TO %s:%d USE_DATA_SSL: %d (LOCAL FAILURE)\n",remote_ip,port,co->use_data_ssl);
			sock_write_bytes(co,"500 ACTIVE SSL LOCAL Handshake failed.\r\n",-1);
			return;
		}
		SSL_CTX_set_options(co->active_ctx, SSL_OP_NO_SSLv2);
		co->active_ssl = SSL_new(co->active_ctx);
		SSL_set_fd(co->active_ssl,co->active_socket);
		logger(co,"BEFORE SSLCONNECT TO %s:%d USE_DATA_SSL: %d\n",remote_ip,port,co->use_data_ssl);
		if ( SSL_connect(co->active_ssl) != 1 ){
			close(co->active_socket);
			logger(co,"AFTER SSLCONNECT TO %s:%d USE_DATA_SSL: %d (FAILURE)\n",remote_ip,port,co->use_data_ssl);
			sock_write_bytes(co,"500 ACTIVE SSL Handshake failed.\r\n",-1);
			return;
		}
		logger(co,"AFTER SSLCONNECT TO %s:%d USE_DATA_SSL: %d (SUCCESS)\n",remote_ip,port,co->use_data_ssl);
		*/
	}
	
	sprintf(copy2,"250 PORT command successful.\r\n");
	sock_write_bytes(co,copy2,-1);

	return;


}

char *our_basename(char *file){
	char *result;

	result = &file[strlen(file)-1];
	while ( result > file && *result != '/' ) result--;
	if ( *result == '/' ) result++;
	return(result);
}
void process_stor(struct conn *co, char *cmd){
	int real_transmitted;
	struct stat st;
	char copy[1024];
	char copy2[1024];
	char wildcard[1024];
	char dirname[1024];
	char *ptr;
	int socket_to_write;
	off64_t ori_size;
	int i;
	DIR *dir;
	struct dirent *diry;
	char **filelist;
	FILE *fp;
	int c,cnt;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	if ( co->readonly ){
		sock_write_bytes(co,"550 Permission denied.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command need filename.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		strcpy(wildcard,".");
	} else {
		strcpy(wildcard,ptr);
	}

	if ( co->passive == 0 && co->active_socket == 0 ){
		sock_write_bytes(co,"425 Can't build data connection: Connection refused.\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive == 1 && co->passive_socket == 0 ){
		sock_write_bytes(co,"425 Cannot open data connection\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}





	fp = fopen(wildcard,"w");
	if ( fp == NULL ){
		sock_write_bytes(co,"553 Cannot create\r\n",-1);
		return;
		
	}
	sprintf(copy2,"150 Opening %s data connection for '%s' [%s].\r\n", co->transfer_mode ? "ASCII" : "BINARY", wildcard, co->data_mode == 'Z' ? "ZLIB" : "STREAM" );
	sock_write_bytes(co,copy2,-1);

	if ( co->passive ){
		co->passive_nsock = accept(co->passive_socket,NULL,NULL);
		if ( co->use_data_ssl ){
			int ret;
			co->passive_ssl = SSL_new(co->ctx);
			SSL_set_fd(co->passive_ssl,co->passive_nsock);
			ret = SSL_accept(co->passive_ssl); 

			logger(co,"SSL_ACCEPT(DATA PASSIVE) returned: %d\n",ret);

			if ( ret != 1 ){
				sock_write_bytes(co,"500 SSL handshake failed.\r\n",-1);
				return;
			}
		}
	}
	if ( co->data_mode == 'Z' ){
#ifdef HAVE_MODE_Z
		char buf_in[ZLIB_CHUNK];	
		char buf_out[ZLIB_CHUNK];	
		z_stream strm;
		off64_t have;
		int ret;

		real_transmitted = 0;
		strm.zalloc = Z_NULL;
		strm.zfree  = Z_NULL;
		strm.opaque = Z_NULL;
		ret = inflateInit(&strm);
		if ( ret != Z_OK ){
			logger(co,"INFLATE_INIT FAILED! (Probably out of memory) FALLING BACK...(%d) [%s]\n",ret,display_zerr(ret));
			sock_write_bytes(co,"500 ZLIB FAILED.\r\n",-1);
			if ( co->passive == 1 ){
				close(co->passive_socket);
				if ( co->use_data_ssl ){
					SSL_shutdown(co->passive_ssl);
				}
				close(co->passive_nsock);
				co->passive_socket = 0;
			} else {
				if ( co->use_data_ssl ){
					SSL_shutdown(co->active_ssl);
				}
				close(co->active_socket);
				co->active_socket = 0;
			}
			fclose(fp);
			return;
		}
		while ( (cnt = read_data_socket(co,buf_in,ZLIB_CHUNK)) > 0 ){
			strm.next_in = buf_in;
			strm.avail_in = cnt;
			real_transmitted += cnt;
			do {
				strm.avail_out = ZLIB_CHUNK;
				strm.next_out = buf_out;
				ret = inflate(&strm,Z_NO_FLUSH);
				have = ZLIB_CHUNK - strm.avail_out;
				if ( ret == Z_STREAM_END ){
					logger(co,"INFLATE -> [%d] (%s) (REAL_TRANSMITTED: %lld) HAVE: %lld Bytes (RECEIVED: %lld)\n",
						ret,display_zerr(ret),real_transmitted,have,cnt);
				}
				if ( ret != Z_OK && ret != Z_STREAM_END ){
					inflateEnd(&strm);
					logger(co,"INFLATE FAILED! (Probably out of memory) FALLING BACK...(%d)[%s]\n",ret,display_zerr(ret));
					sock_write_bytes(co,"501 ZLIB FAILED.\r\n",-1);
					if ( co->passive == 1 ){
						close(co->passive_socket);
						if ( co->use_data_ssl ){
							SSL_shutdown(co->passive_ssl);
						}
						close(co->passive_nsock);
						co->passive_socket = 0;
					} else {
						if ( co->use_data_ssl ){
							SSL_shutdown(co->active_ssl);
						}
						close(co->active_socket);
						co->active_socket = 0;
					}
					fclose(fp);
					return;
				}
				fwrite(buf_out,1,have,fp);
			} while(strm.avail_out == 0 );
		}
		inflateEnd(&strm);
				
#endif
	} else {
		real_transmitted = 0;
		while ( (cnt = read_data_socket(co,copy2,1024)) > 0 ){
			real_transmitted += cnt;
			fwrite(copy2,1,cnt,fp); 
		}
	}
	fclose(fp);
	if ( stat(wildcard,&st) != 0 ) st.st_size = -1;

	ori_size = st.st_size; 

	if ( co->transfer_mode == 1 ){
		char tempfile[1024];
		FILE *in, *out;
		int c;
		sprintf(tempfile,".jftpd-ftp_conversion_%ld_%d.bin",time(NULL),getpid());
		unlink(tempfile);	
		out = fopen(tempfile,"w");
		if ( out != NULL ){
			logger(co,"Converting ASCII-Mode file [%s] <> [%s]\n",wildcard,tempfile);
			in = fopen(wildcard,"r");
			if ( in == NULL ){
				fclose(out);
				unlink(tempfile);
				logger(co,"Converting ASCII-Mode file [%s] <> [%s] FAILED (CANNOT OPEN '%s' FOR READING)\n",wildcard,tempfile,wildcard);
			} else {
				while ( (c = fgetc(in)) != -1 ){
					if ( c == '\r' ){
						c = fgetc(in);
						ungetc(c,in);
						if ( c == '\n' ) continue;
						c = '\r';
					}
					fputc(c,out);
				}
				fclose(in);
				fclose(out);
				unlink(wildcard);
				rename(tempfile,wildcard);
				unlink(tempfile);
			}
		}	
				
	} 

	if ( stat(wildcard,&st) != 0 ) st.st_size = -1;
	
	

	sprintf(copy2,"226 Transfer complete (Received %llu Bytes (%llu original) (Transmitted: %lld Bytes) in %s mode)[%s].\r\n",
		st.st_size,ori_size,
		real_transmitted,
		co->transfer_mode ? "ASCII" : "BINARY", co->data_mode == 'Z' ? "ZLIB"  : "STREAM" );
	sock_write_bytes(co,copy2,-1);

	log_bytes(co,cmd,st.st_size);

	if ( co->passive == 1 ){
		close(co->passive_socket);
		if ( co->use_data_ssl ){
			SSL_shutdown(co->passive_ssl);
		}
		close(co->passive_nsock);
		co->passive_socket = 0;
	} else {
		if ( co->use_data_ssl ){
			SSL_shutdown(co->active_ssl);
		}
		close(co->active_socket);
		co->active_socket = 0;
	}

	return;


}


void process_iexe(struct conn *co, char *cmd){
	char copy[1024];

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,&cmd[5]);
	if ( copy[strlen(copy)-1] == '\r' || copy[strlen(copy)-1] == '\n' ) copy[strlen(copy)-1] = '\0';
	if ( copy[strlen(copy)-1] == '\r' || copy[strlen(copy)-1] == '\n' ) copy[strlen(copy)-1] = '\0';
	logger(co,"Executing: [%s] as User: %d, Real: %d\n",copy,co->uid,getuid());
	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}
	sock_write_bytes(co,"250-Program accepted.\r\n",-1);
	system(copy);
	sock_write_bytes(co,"250 Program executed.\r\n",-1);
	return;
}
void process_iupd(struct conn *co, char *cmd){
	int bytes_in;
	struct stat st;
	char copy[1024];
	char copy2[1024];
	char wildcard[1024];
	char wildstring[1024];
	char wildstring2[1024];
	char wildcard2[1024];
	char dirname[1024];
	char current_path2[1024];
	char *ptr;
	int socket_to_write;
	char buf[1024];
	int original_size;
	int i;
	DIR *dir;
	char fname[1024];
	char **filelist;
	FILE *fp;
	FILE *fp2;
	int c,cnt;
	struct dirent *diry;
	uint32_t siz;	
	uint32_t size;	


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command need filename.\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \r\n\t");
	if ( ptr == NULL ){
		strcpy(wildcard,".");
	} else {
		strcpy(wildcard,ptr);
	}

	ptr = strtok(NULL," \r\n\t");
	if ( ptr == NULL ){
		strcpy(wildstring,"");
	} else {
		strcpy(wildstring,ptr);
	}
	ptr = strtok(NULL," \r\n\t");
	if ( ptr == NULL ){
		strcpy(wildstring2,"");
	} else {
		strcpy(wildstring2,ptr);
	}

	if ( co->passive == 0 && co->active_socket == 0 ){
		sock_write_bytes(co,"425 Can't build data connection: Connection refused.\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive == 1 && co->passive_socket == 0 ){
		sock_write_bytes(co,"425 Cannot open data connection\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	bzero(current_path2,1024);
	getcwd(current_path2,1024);	

	sprintf(wildcard2,"%s.temp.%d",wildcard,getpid());
	unlink(wildcard2);

	logger(co,"--> IUPD: [%s]...current path: [%s] Command: [%s] TEMPFILE: [%s] STRING: [%s] STRING2: [%s]\n",wildcard,current_path2,cmd,wildcard2,wildstring,wildstring2);
		

	fp = fopen(wildcard,"r+");
	if ( fp == NULL ){
		logger(co,"--> Creating new file\n");
		fp = fopen(wildcard,"w+");
		if ( fp == NULL ){
			sock_write_bytes(co,"555 Cannot open/create file\r\n",-1);
			return;
		}
	}
	flock(fileno(fp),LOCK_EX);
	fp2 = fopen(wildcard2,"w");
	if ( fp2 == NULL ){
		flock(fileno(fp),LOCK_UN);
		fclose(fp);
		sock_write_bytes(co,"555 Cannot create temporary file\r\n",-1);
		return;
	}
	logger(co,"Cleaning up file: [%s] (TEMPFILE: [%s])\n",wildcard,wildcard2);
	while ( fgets(buf,1024,fp) != NULL ){
		if ( strstr(buf,wildstring) != NULL && ((strlen(wildstring2)   && (strstr(buf,wildstring2) != NULL) ) || strlen(wildstring2) == 0 ) ){
			continue;
		}
		fprintf(fp2,"%s",buf);
	}
	fclose(fp2);
	ftruncate(fileno(fp),0L);
	fseek(fp,0L,SEEK_SET);
	fp2 = fopen(wildcard2,"r");	

	if ( fp2 == NULL ){
		flock(fileno(fp),LOCK_UN);
		fclose(fp);
		sock_write_bytes(co,"555 Cannot open temporary file\r\n",-1);
		return;
	}

	while ( fgets(buf,1024,fp2) != NULL ){
		fprintf(fp,"%s",buf);
	}
	fclose(fp2);
	unlink(wildcard2);
	// Now append the rest :-)	
	
	
	



	sprintf(copy2,"150 Opening BINARY data connection for '%s'\n",wildcard);
	sock_write_bytes(co,copy2,-1);



	if ( co->passive ){
		co->passive_nsock = accept(co->passive_socket,NULL,NULL);
		if ( co->use_data_ssl ){
			int ret;
			co->passive_ssl = SSL_new(co->ctx);
			SSL_set_fd(co->passive_ssl,co->passive_nsock);
			ret = SSL_accept(co->passive_ssl); 

			logger(co,"SSL_ACCEPT(DATA PASSIVE) returned: %d\n",ret);

			if ( ret != 1 ){
				closedir(dir);
				sock_write_bytes(co,"500 SSL handshake failed.\r\n",-1);
				flock(fileno(fp),LOCK_UN);
				fclose(fp);
				return;
			}
		}
	}


	{
		char c;
		bytes_in = 0;
		while ( read_data_socket(co,&c,1) == 1 ){
			bytes_in++;
			fputc(c,fp);
		}
		fflush(fp);
		flock(fileno(fp),LOCK_UN);
		fclose(fp);
	}



	/*
	while ( (cnt = fread(copy2,1,1024,fp)) > 0 ){
		write_data_socket(co,copy2,cnt);
	}
	*/

	sprintf(buf,"226 Transfer complete (%lld Bytes received).\r\n",bytes_in);
	sock_write_bytes(co,buf,-1);

	if ( co->passive == 1 ){
		close(co->passive_socket);
		if ( co->use_data_ssl ){
			SSL_shutdown(co->passive_ssl);
		}
		close(co->passive_nsock);
		co->passive_socket = 0;
	} else {
		if ( co->use_data_ssl ){
			SSL_shutdown(co->active_ssl);
		}
		close(co->active_socket);
		co->active_socket = 0;
	}

	log_bytes(co,cmd,st.st_size);
	co->rest_pos = 0;

	return;


}

void process_idir(struct conn *co, char *cmd){
	struct stat st;
	char copy[1024];
	char copy2[1024];
	char wildcard[1024];
	char dirname[1024];
	char current_path2[1024];
	char *ptr;
	int socket_to_write;
	int original_size;
	int i;
	DIR *dir;
	char fname[1024];
	char **filelist;
	FILE *fp;
	int c,cnt;
	struct dirent *diry;
	uint32_t siz;	
	uint32_t size;	


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command need filename.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		strcpy(wildcard,".");
	} else {
		strcpy(wildcard,ptr);
	}

	if ( co->passive == 0 && co->active_socket == 0 ){
		sock_write_bytes(co,"425 Can't build data connection: Connection refused.\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive == 1 && co->passive_socket == 0 ){
		sock_write_bytes(co,"425 Cannot open data connection\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	bzero(current_path2,1024);
	getcwd(current_path2,1024);	
	logger(co,"--> STATING: [%s]...current path: [%s] Command: [%s]\n",wildcard,current_path2,cmd);

	if ( strncmp(cmd,"IDIR",4) == 0 ){
		if ( stat(wildcard,&st) || !S_ISDIR(st.st_mode) ){
			sock_write_bytes(co,"553 Cannot stat directory\r\n",-1);
			return;
		} 
		dir = opendir(wildcard);
		if ( dir == NULL ){
			sock_write_bytes(co,"553 Cannot open directory\r\n",-1);
			return;
		}
	} else {
		struct stat st4;
		if ( strncmp(cmd,"IDIM",4) == 0 ){
			cleanup_dir(wildcard);
			if ( mkdir(wildcard,00777) < 0 ){
				logger(co,"--> CANNOT CREATE [%s] (%s)\n",wildcard,strerror(errno));
				sock_write_bytes(co,"553 Cannot create directory\r\n",-1);
				return;
			}
			chmod(wildcard,0000777);
		} else {
			if ( stat(wildcard,&st4) != 0 ){
				logger(co,"--> WARNING: [%s] does not exist...trying to create\n",wildcard);
				if ( mkdir(wildcard,00777) < 0 ){
					logger(co,"--> CANNOT CREATE [%s] (%s)\n",wildcard,strerror(errno));
					sock_write_bytes(co,"553 Cannot create directory\r\n",-1);
					return;
				}
			}
		}
	}

	sprintf(copy2,"150 Opening BINARY data connection for '%s'\n",wildcard);
	sock_write_bytes(co,copy2,-1);



	if ( co->passive ){
		co->passive_nsock = accept(co->passive_socket,NULL,NULL);
		if ( co->use_data_ssl ){
			int ret;
			co->passive_ssl = SSL_new(co->ctx);
			SSL_set_fd(co->passive_ssl,co->passive_nsock);
			ret = SSL_accept(co->passive_ssl); 

			logger(co,"SSL_ACCEPT(DATA PASSIVE) returned: %d\n",ret);

			if ( ret != 1 ){
				closedir(dir);
				sock_write_bytes(co,"500 SSL handshake failed.\r\n",-1);
				return;
			}
		}
	}
	if ( strncmp(cmd,"IDIR",4) == 0 ){
		logger(co,"Sending directory [%s] to client\n",wildcard);
		/* Do transfer here */
		while ( (diry = readdir(dir)) != NULL ){
			char fname[1024];
			char buf[1024];
			FILE *fp;	
			int c;
			sprintf(fname,"%s/%s",wildcard,diry->d_name);
			if ( stat(fname,&st) || !S_ISREG(st.st_mode) ) continue; // Skip non-regular files
			fp = fopen(fname,"r");
			if ( fp == NULL ){
				logger(co,"Warning: Cannot open '%s'...skipping\n",fname);
				continue;
			}
			siz = htonl((uint32_t)strlen(diry->d_name));
			write_data_socket(co,(void *)&siz,sizeof(siz));	// Size of filename
			logger(co,"Filename: [%s], Sent filename-Length: %d\n",diry->d_name,ntohl(siz));
			write_data_socket(co,(void *)diry->d_name,ntohl(siz));
			siz = htonl((uint32_t)st.st_size);
			logger(co,"Filename: [%s], Sent Length: %d\n",diry->d_name,ntohl(siz));
			write_data_socket(co,(void *)&siz,sizeof(siz));	// Size of data
			while ( (c = fgetc(fp)) != -1 ){
				write_data_socket(co,(void *)&c,1);	// Size of data
			}
			fclose(fp);
		}
		closedir(dir);
		siz = htonl(0L);
		write_data_socket(co,(void *)&siz,sizeof(siz));	// Size of filename = 0 means end of transmission
	} else {
		char fname[1024];
		char buf[1024];
		FILE *fp;	
		char c;
		int i;
		logger(co,"Receiving directory [%s] from client\n",wildcard);
		while ( 1 ){
			if ( read_data_socket(co,(void *)&siz,sizeof(siz)) != sizeof(siz) ){
				sock_write_bytes(co,"500 Transfer aborted by client.\r\n",-1);
				return;	
			}
			size = ntohl(siz);
			if ( size == 0 ) break;
			bzero(buf,1024);
			if ( read_data_socket(co,(void *)buf,size) != size ){
				sock_write_bytes(co,"500 Transfer aborted by client (cannot receive filename).\r\n",-1);
				return;	
			}
			logger(co,"Receiving [%s] --> [%s]\n",buf,wildcard);
			sprintf(fname,"%s/%s",wildcard,buf);
			fp = fopen(fname,"w");
			if ( fp == NULL ){
				sock_write_bytes(co,"500 Transfer aborted by client (cannot open local filename).\r\n",-1);
				return;	
			}
			if ( read_data_socket(co,(void *)&siz,sizeof(siz)) != sizeof(siz) ){
				sock_write_bytes(co,"500 Transfer aborted by client.\r\n",-1);
				return;	
			}
			size = ntohl(siz);
			logger(co,"Receiving %lld Bytes of data\n",size);
			for ( i = 0;i < size; i++){
				if ( read_data_socket(co,&c,1) != 1 ){
					sock_write_bytes(co,"500 Transfer aborted by client (file-data).\r\n",-1);
					return;	
				}
				fputc(c,fp);
			}
			fclose(fp);	
				
		}
	}

	/*
	while ( (cnt = fread(copy2,1,1024,fp)) > 0 ){
		write_data_socket(co,copy2,cnt);
	}
	*/


	sock_write_bytes(co,"226 Transfer complete.\r\n",-1);

	if ( co->passive == 1 ){
		close(co->passive_socket);
		if ( co->use_data_ssl ){
			SSL_shutdown(co->passive_ssl);
		}
		close(co->passive_nsock);
		co->passive_socket = 0;
	} else {
		if ( co->use_data_ssl ){
			SSL_shutdown(co->active_ssl);
		}
		close(co->active_socket);
		co->active_socket = 0;
	}

	log_bytes(co,cmd,st.st_size);
	co->rest_pos = 0;

	return;


}
void process_retr(struct conn *co, char *cmd){
	struct stat st;
	off64_t real_transmitted;
	off64_t data_transmitted;
	char copy[1024];
	char copy2[1024];
	char wildcard[1024];
	char dirname[1024];
	char current_path2[1024];
	char *ptr;
	int socket_to_write;
	off64_t original_size;
	off64_t i;
	DIR *dir;
	struct dirent *diry;
	char fname[1024];
	char **filelist;
	FILE *fp;
	int c,cnt;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command need filename.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		strcpy(wildcard,".");
	} else {
		strcpy(wildcard,ptr);
	}

	if ( co->passive == 0 && co->active_socket == 0 ){
		sock_write_bytes(co,"425 Can't build data connection: Connection refused.\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive == 1 && co->passive_socket == 0 ){
		sock_write_bytes(co,"425 Cannot open data connection\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	bzero(current_path2,1024);
	getcwd(current_path2,1024);	
	logger(co,"--> STATING: [%s]...current path: [%s]\n",wildcard,current_path2);

	if ( stat(wildcard,&st) ){
		sock_write_bytes(co,"553 Cannot stat file\r\n",-1);
		return;
	} 
	original_size = st.st_size - co->rest_pos;

	if ( co->transfer_mode == 1 ){
		FILE *in, *out;
		int c,cold;
		sprintf(fname,".jftp-transfer.temp.__%ld_%d.jftp.chiam",time(NULL),getpid());	
		unlink(fname);
		in = fopen(wildcard,"r");
		if ( in != NULL ){
			out = fopen(fname,"w");
			if ( out == NULL ){
				fclose(in);
				strcpy(fname,wildcard);
			} else {
				logger(co,"Converting before transfer to ASCII (%s) <> (%s)\n",wildcard,fname);	
				cold = -1;
				while ( (c = fgetc(in)) != -1 ){
					if ( c == '\n' && cold != '\r' ){
						fputc('\r',out);
					}
					cold = c;
					fputc(c,out);
				}
				fclose(in);
				fclose(out);
			}
		}
	} else {
		strcpy(fname,wildcard);
	}
	logger(co,"--> (%s:%d) STATING: [%s]...\n",__FILE__,__LINE__,fname);
	if ( stat(fname,&st) ){
		sock_write_bytes(co,"553 Cannot stat file\r\n",-1);
		if ( strcmp(fname,wildcard) != 0 ) unlink(fname);
		return;
	} 
	logger(co,"--> (%s:%d) STATED: [%s]...(%lld)\n",__FILE__,__LINE__,fname,st.st_size);

	st.st_size -= co->rest_pos;


	fp = fopen(fname,"r");
	if ( fp == NULL ){
		sock_write_bytes(co,"553 Cannot open file\r\n",-1);
		if ( strcmp(fname,wildcard) != 0 ) unlink(fname);
		return;
	}
	sprintf(copy2,"150 Opening %s data connection for '%s' (%llu Bytes / %llu original. Offset: %llu) [%s].\r\n", 
		co->transfer_mode ? "ASCII" : "BINARY", wildcard,st.st_size,original_size,co->rest_pos,
		co->data_mode == 'Z' ? "ZLIB" : "STREAM" );
	sock_write_bytes(co,copy2,-1);

	if ( co->passive ){
		co->passive_nsock = accept(co->passive_socket,NULL,NULL);
		if ( co->use_data_ssl ){
			int ret;
			co->passive_ssl = SSL_new(co->ctx);
			SSL_set_fd(co->passive_ssl,co->passive_nsock);
			ret = SSL_accept(co->passive_ssl); 

			logger(co,"SSL_ACCEPT(DATA PASSIVE) returned: %d\n",ret);

			if ( ret != 1 ){
				if ( strcmp(fname,wildcard) != 0 ) unlink(fname);
				sock_write_bytes(co,"500 SSL handshake failed.\r\n",-1);
				return;
			}
		}
	}
	if ( co->rest_pos < 0 ) co->rest_pos = 0;
	if ( co->rest_pos ) fseek(fp,co->rest_pos,SEEK_SET); // Position for retransmitting
	real_transmitted = 0;
	data_transmitted = 0;
	if ( co->data_mode == 'Z' ){
#ifdef HAVE_MODE_Z
		char buf_in[ZLIB_CHUNK];
		char buf_out[ZLIB_CHUNK];
		int ret, flush;
		off64_t have;
		z_stream strm;
		
		strm.zalloc  = Z_NULL;
		strm.zfree   = Z_NULL;
		strm.opaque  = Z_NULL;
		if ( (ret = deflateInit(&strm,ZLIB_LEVEL)) != Z_OK ){
			sock_write_bytes(co,"500 ZLIB INIT FAILURE.\r\n",-1);
			logger(co,"DEFLATE INIT FAILED...FALLING BACK (%d) [%s]\n",ret,display_zerr(ret));
			if ( co->passive == 1 ){
				close(co->passive_socket);
				if ( co->use_data_ssl ){
					SSL_shutdown(co->passive_ssl);
				}
				close(co->passive_nsock);
				co->passive_socket = 0;
			} else {
				if ( co->use_data_ssl ){
					SSL_shutdown(co->active_ssl);
				}
				close(co->active_socket);
				co->active_socket = 0;
			}
			fclose(fp);
			return;
		}
		while ( (cnt = fread(buf_in,1,ZLIB_CHUNK,fp)) > 0 ){
			real_transmitted += cnt;
			strm.avail_in = cnt;
			flush = feof(fp) ? Z_FINISH : Z_NO_FLUSH;
			strm.next_in = buf_in;
			do {
				strm.avail_out = ZLIB_CHUNK;
				strm.next_out  = buf_out;
				ret = deflate(&strm, flush);
				have = ZLIB_CHUNK - strm.avail_out;
				data_transmitted += have;
				if ( ret == Z_STREAM_END ){
					logger(co,"DEFLATE -> [%d] (%s) (REAL_TRANSMITTED: %lld) HAVE: %d Bytes (RECEIVED: %lld)\n",
						ret,display_zerr(ret),real_transmitted,have,cnt);
				}
				if ( ret != Z_OK && ret != Z_STREAM_END ){
					deflateEnd(&strm);
					logger(co,"INFLATE FAILED! (Probably out of memory) FALLING BACK...(%d)[%s]\n",ret,display_zerr(ret));
					sock_write_bytes(co,"501 ZLIB FAILED.\r\n",-1);
					if ( co->passive == 1 ){
						close(co->passive_socket);
						if ( co->use_data_ssl ){
							SSL_shutdown(co->passive_ssl);
						}
						close(co->passive_nsock);
						co->passive_socket = 0;
					} else {
						if ( co->use_data_ssl ){
							SSL_shutdown(co->active_ssl);
						}
						close(co->active_socket);
						co->active_socket = 0;
					}
					fclose(fp);
					return;
				}
				write_data_socket(co,buf_out,have);
			} while ( strm.avail_out == 0 );
		}
		deflateEnd(&strm);
#endif
	} else {
		while ( (cnt = fread(copy2,1,1024,fp)) > 0 ){
			real_transmitted += cnt;
			data_transmitted += cnt;
			write_data_socket(co,copy2,cnt);
		}
	}
	fclose(fp);

	if ( strcmp(fname,wildcard) != 0 ) unlink(fname);
	{
		char dummy[1024];
		sprintf(dummy,"226 Transfer complete. DATA_TRANSMITTED: %lld, WIRE_TRANSMITTED: %lld [%s]\r\n",
			real_transmitted, data_transmitted,co->data_mode == 'Z' ? "ZLIB" : "STREAM");
		sock_write_bytes(co,dummy,-1);
	}

	if ( co->passive == 1 ){
		close(co->passive_socket);
		if ( co->use_data_ssl ){
			SSL_shutdown(co->passive_ssl);
		}
		close(co->passive_nsock);
		co->passive_socket = 0;
	} else {
		if ( co->use_data_ssl ){
			SSL_shutdown(co->active_ssl);
		}
		close(co->active_socket);
		co->active_socket = 0;
	}

	log_bytes(co,cmd,st.st_size);
	co->rest_pos = 0;

	return;


}

void process_tail(struct conn *co, char *cmd){ // This command never returns!!!
	struct stat st;
	char copy[1024];
	char copy2[1024];
	char wildcard[1024];
	char dirname[1024];
	char current_path2[1024];
	char *ptr;
	int socket_to_write;
	off64_t original_size;
	off64_t i;
	DIR *dir;
	struct dirent *diry;
	char fname[1024];
	char **filelist;
	FILE *fp;
	int c,cnt;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command need filename.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		strcpy(wildcard,".");
	} else {
		strcpy(wildcard,ptr);
	}

	if ( co->passive == 0 && co->active_socket == 0 ){
		sock_write_bytes(co,"425 Can't build data connection: Connection refused.\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive == 1 && co->passive_socket == 0 ){
		sock_write_bytes(co,"425 Cannot open data connection\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	bzero(current_path2,1024);
	getcwd(current_path2,1024);	
	logger(co,"--> STATING: [%s]...current path: [%s]\n",wildcard,current_path2);

	if ( stat(wildcard,&st) ){
		sock_write_bytes(co,"553 Cannot stat file\r\n",-1);
		return;
	} 
	original_size = st.st_size - co->rest_pos;

	if ( co->transfer_mode == 1 ){
		FILE *in, *out;
		int c,cold;
		sprintf(fname,".jftp-transfer.temp.__%ld_%d.jftp.chiam",time(NULL),getpid());	
		unlink(fname);
		in = fopen(wildcard,"r");
		if ( in != NULL ){
			out = fopen(fname,"w");
			if ( out == NULL ){
				fclose(in);
				strcpy(fname,wildcard);
			} else {
				logger(co,"Converting before transfer to ASCII (%s) <> (%s)\n",wildcard,fname);	
				cold = -1;
				while ( (c = fgetc(in)) != -1 ){
					if ( c == '\n' && cold != '\r' ){
						fputc('\r',out);
					}
					cold = c;
					fputc(c,out);
				}
				fclose(in);
				fclose(out);
			}
		}
	} else {
		strcpy(fname,wildcard);
	}
	logger(co,"--> (%s:%d) STATING: [%s]...\n",__FILE__,__LINE__,fname);
	if ( stat(fname,&st) ){
		sock_write_bytes(co,"553 Cannot stat file\r\n",-1);
		if ( strcmp(fname,wildcard) != 0 ) unlink(fname);
		return;
	} 
	logger(co,"--> (%s:%d) STATED: [%s]...(%lld)\n",__FILE__,__LINE__,fname,st.st_size);

	st.st_size -= co->rest_pos;


	fp = fopen(fname,"r");
	if ( fp == NULL ){
		sock_write_bytes(co,"553 Cannot open file\r\n",-1);
		if ( strcmp(fname,wildcard) != 0 ) unlink(fname);
		return;
	}
	sprintf(copy2,"150 Opening %s tailing data connection for '%s' (%llu Bytes / %llu original. Offset: %llu).\r\n", co->transfer_mode ? "ASCII" : "BINARY", wildcard,st.st_size,original_size,co->rest_pos);
	sock_write_bytes(co,copy2,-1);

	fclose(fp);	

	if ( co->passive ){
		co->passive_nsock = accept(co->passive_socket,NULL,NULL);
		if ( co->use_data_ssl ){
			int ret;
			co->passive_ssl = SSL_new(co->ctx);
			SSL_set_fd(co->passive_ssl,co->passive_nsock);
			ret = SSL_accept(co->passive_ssl); 

			logger(co,"SSL_ACCEPT(DATA PASSIVE) returned: %d\n",ret);

			if ( ret != 1 ){
				if ( strcmp(fname,wildcard) != 0 ) unlink(fname);
				sock_write_bytes(co,"500 SSL handshake failed.\r\n",-1);
				return;
			}
		}
	}
	if ( co->rest_pos ) fseek(fp,co->rest_pos,SEEK_SET); // Position for retransmitting
	{
#define MINREAD 1024
		off64_t oldpos;
		struct stat st;
		oldpos = 0;
		if ( stat(fname,&st) != 0 || !S_ISREG(st.st_mode) ){
			sock_write_bytes(co,"226 Transfer broken (File vanished).\r\n",-1);

			if ( co->passive == 1 ){
				close(co->passive_socket);
				if ( co->use_data_ssl ){
					SSL_shutdown(co->passive_ssl);
				}
				close(co->passive_nsock);
				co->passive_socket = 0;
			} else {
				if ( co->use_data_ssl ){
					SSL_shutdown(co->active_ssl);
				}
				close(co->active_socket);
				co->active_socket = 0;
			}

			log_bytes(co,cmd,st.st_size);
			co->rest_pos = 0;

			return;
		}	
		if ( st.st_size < MINREAD )
			oldpos = 0;
		else
			oldpos = st.st_size - MINREAD; 

		while ( 1 ){
			if ( stat(fname,&st) != 0 || !S_ISREG(st.st_mode) ){
				sock_write_bytes(co,"226 Transfer broken (File vanished).\r\n",-1);

				if ( co->passive == 1 ){
					close(co->passive_socket);
					if ( co->use_data_ssl ){
						SSL_shutdown(co->passive_ssl);
					}
					close(co->passive_nsock);
					co->passive_socket = 0;
				} else {
					if ( co->use_data_ssl ){
						SSL_shutdown(co->active_ssl);
					}
					close(co->active_socket);
					co->active_socket = 0;
				}
	
				log_bytes(co,cmd,st.st_size);
				co->rest_pos = 0;
	
				return;
			}	
			if ( oldpos > st.st_size ){
			    if ( st.st_size < MINREAD )
				    oldpos = 0;
			    else
				    oldpos = st.st_size - MINREAD; 
			}
			fp = fopen(fname,"r");	
			if ( fp ){
				unsigned char c;
				fseek(fp,oldpos,SEEK_SET);
				while ( (cnt = fread(copy2,1,1024,fp)) > 0 ) write_data_socket(co,copy2,cnt);
				oldpos = ftell(fp);
				fclose(fp);
			}
			usleep(100000);
		}
		/*		
		while ( (cnt = fread(copy2,1,1024,fp)) > 0 ){
			write_data_socket(co,copy2,cnt);
		}
		fclose(fp);
		*/
	}

	if ( strcmp(fname,wildcard) != 0 ) unlink(fname);

	sock_write_bytes(co,"226 Transfer complete.\r\n",-1);

	if ( co->passive == 1 ){
		close(co->passive_socket);
		if ( co->use_data_ssl ){
			SSL_shutdown(co->passive_ssl);
		}
		close(co->passive_nsock);
		co->passive_socket = 0;
	} else {
		if ( co->use_data_ssl ){
			SSL_shutdown(co->active_ssl);
		}
		close(co->active_socket);
		co->active_socket = 0;
	}

	log_bytes(co,cmd,st.st_size);
	co->rest_pos = 0;

	return;


}

/*

250-Begin
 type=dir;sizd=57;modify=20130327110949;UNIX.mode=0775;unique=2d5000cg5127ff; /pub
250 End.

*/

void process_mlist(struct conn *co, char *cmd,int lsmode){
	char copy[1024];
	char line[1024];
	char wildcard[1024];
	char *ptr;
	struct stat st;
	struct tm *tm;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command (Missing argument).\r\n",-1);
		return;
	}

	strcpy(wildcard,ptr);


	if ( stat(wildcard,&st) ){
		sock_write_bytes(co,"550 Cannot find file.\r\n",-1);
		return;
	}
	sock_write_bytes(co,"250-Begin\r\n",-1);

	sprintf(line," Type=%s;",get_file_type(&st,wildcard));
	if ( S_ISREG(st.st_mode) ){
		sprintf(&line[strlen(line)],"Size=%lld;",st.st_size);
	}
	tm = localtime(&st.st_mtime);
	sprintf(&line[strlen(line)],"Modify=%04d%02d%02d%02d%02d%02d;",
		tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
		tm->tm_hour, tm->tm_min, tm->tm_sec);
	tm = localtime(&st.st_ctime);
	sprintf(&line[strlen(line)],"Create=%04d%02d%02d%02d%02d%02d;",
		tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
		tm->tm_hour, tm->tm_min, tm->tm_sec);
	sprintf(&line[strlen(line)],"Unique=%ud%u;",st.st_ino,st.st_dev);
	sprintf(&line[strlen(line)],"UNIX.mode=0%03o;",(st.st_mode & 0x1FF));
	sprintf(&line[strlen(line)],"UNIX.uid=%d;",(st.st_uid));
	sprintf(&line[strlen(line)],"UNIX.gid=%d;",(st.st_gid));
	sprintf(&line[strlen(line)]," %s\r\n",wildcard);
	sock_write_bytes(co,line,-1);
	sock_write_bytes(co,"250 End.\r\n",-1);
	return;


}

char *fill_blanker(int count){
	if ( count == 1  ) return(" ");
	if ( count == 2  ) return("  ");
	if ( count == 3  ) return("   ");
	if ( count == 4  ) return("    ");
	if ( count == 5  ) return("     ");
	if ( count == 6  ) return("      ");
	if ( count == 7  ) return("       ");
	if ( count == 8  ) return("        ");
	if ( count == 9  ) return("         ");
	if ( count == 10 ) return("          ");
	if ( count == 11 ) return("           ");
	if ( count == 12 ) return("            ");
	return("");
}


void process_iexe2(struct conn *co, char *cmd){
	char copy[1024];
	char dummy[1024];
	int pid;
	int status;
	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,&cmd[6]);
	if ( copy[strlen(copy)-1] == '\r' || copy[strlen(copy)-1] == '\n' ) copy[strlen(copy)-1] = '\0';
	if ( copy[strlen(copy)-1] == '\r' || copy[strlen(copy)-1] == '\n' ) copy[strlen(copy)-1] = '\0';
	logger(co,"Executing: [%s] as User: %d, Real: %d\n",copy,co->uid,getuid());
	if ( co->uid != 0 ){
		sock_write_bytes(co,"530 Please login with root and PASS.\r\n",-1);
		return;
	}
	if ( co->use_data_ssl ){
		sock_write_bytes(co,"540 Command only available in PLAIN-Connection mode\r\n",-1);
		return;
	}
	if ( co->passive == 0  ){
		sock_write_bytes(co,"540 Command only available in PASSIVE-Connection mode\r\n",-1);
		return;
	}
	sprintf(dummy,"250 Command (%s) accepted. (Data is transmitted in STREAM-MODE)\r\n",copy);
	sock_write_bytes(co,dummy,-1);

	if ( co->passive == 0 && co->active_socket == 0 ){
		sock_write_bytes(co,"425 Can't build data connection: Connection refused.\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive == 1 && co->passive_socket == 0 ){
		sock_write_bytes(co,"425 Cannot open data connection\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive ){
		co->passive_nsock = accept(co->passive_socket,NULL,NULL);
	} else { 
		/* Do connect as SSL Client, when co->use_data_ssl is set! */
	}
	sprintf(dummy,"************* EXECUTION START ***************************\r\n");
	send(co->passive_nsock,dummy,strlen(dummy),0);
	signal(SIGCHLD,SIG_DFL);
	if ( (pid = fork()) == 0 ){
		int argc,i; 
		char **argv;
		argv = NULL;
		argc = 0;
		argv = (char **)realloc(argv, sizeof(char *) * (argc+1));
		argv[argc++] = strdup("/bin/bash");
		argv = (char **)realloc(argv, sizeof(char *) * (argc+1));
		argv[argc++] = strdup("-c");
		argv = (char **)realloc(argv, sizeof(char *) * (argc+1));
		argv[argc++] = strdup(copy);
		argv = (char **)realloc(argv, sizeof(char *) * (argc+1));
		argv[argc++] = NULL;
		dup2(STDOUT_FILENO,STDERR_FILENO);
		for ( i = 0; i < argc; i++ ){
			sprintf(dummy,"ARGV[%d] --> [%s]\n",i,argv[i] ? argv[i] : "NULL");
			send(co->passive_nsock,dummy,strlen(dummy),0);
		}
		if ( dup2(co->passive_nsock,STDOUT_FILENO) == -1 ){
			sprintf(dummy,"(%s:%d) DUP2 FAILED (%s)\r\n",__FILE__,__LINE__,strerror(errno));
			send(co->passive_nsock,dummy,strlen(dummy),0);
			exit(0);
		}
		// close(co->passive_nsock);
		if ( execv("/bin/bash",argv) < 0 ){
			sprintf(dummy,"(%s:%d) EXECUTION FAILED (%s)\r\n",__FILE__,__LINE__,strerror(errno));
			send(co->passive_nsock,dummy,strlen(dummy),0);
		}
		exit(0);
	}
	{
		int retval;
		while ( (retval = wait(&status)) != pid ){
			sprintf(dummy,"WAIT RETURNED: %d (%s)\n",retval,strerror(errno));
		}
	}
	
		

	
	sprintf(dummy,"************** EXECUTION END (EXIT-CODE: %d) ****************************\r\n",status);
	send(co->passive_nsock,dummy,strlen(dummy),0);
	sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
	if ( co->passive == 1 ){
		close(co->passive_socket);
		close(co->passive_nsock);
		co->passive_socket = 0;
	} else {
		close(co->active_socket);
		co->active_socket = 0;
	}
	return;
}

void process_list(struct conn *co, char *cmd,int lsmode){
	char copy[1024];
	char copy2[1024];
	char wildcard[1024];
	char dirname[1024];
	off64_t real_transmitted;
	off64_t data_transmitted;
	char *ptr;
	int socket_to_write;
	int i;
	DIR *dir;
	struct dirent *diry;
	char **filelist;
	int cnt;
	off64_t nmatch;
	char the_dir_temp_file[1024];
	FILE *fp;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		strcpy(wildcard,".");
	} else {
		strcpy(wildcard,ptr);
		if ( *ptr == '-' ) strcpy(wildcard,".");
	}


	if ( co->passive == 0 && co->active_socket == 0 ){
		sock_write_bytes(co,"425 Can't build data connection: Connection refused.\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive == 1 && co->passive_socket == 0 ){
		sock_write_bytes(co,"425 Cannot open data connection\r\n",-1);
		sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
		return;
	}
	if ( co->passive ){
		co->passive_nsock = accept(co->passive_socket,NULL,NULL);
		if ( co->use_data_ssl ){
			int ret;
			/*
			SSL_library_init();
			OpenSSL_add_all_algorithms();
			SSL_load_error_strings();
			*/
			co->passive_ssl = SSL_new(co->ctx);
			SSL_set_fd(co->passive_ssl,co->passive_nsock);
			logger(co,"SSL_ACCEPT(DATA PASSIVE) BEFORE SSL CONNECT\n");
			ret = SSL_accept(co->passive_ssl); 

			logger(co,"SSL_ACCEPT(DATA PASSIVE) returned: %d\n",ret);

			if ( ret != 1 ){
				sock_write_bytes(co,"500 SSL handshake failed.\r\n",-1);
				return;
			}
		}
	} else { 
		/* Do connect as SSL Client, when co->use_data_ssl is set! */
	}


	if ( strstr(wildcard,"*") != NULL || strstr(wildcard,"]") != NULL ) {
		sprintf(dirname,".");
		if ( lsmode == 2 )
			sock_write_bytes(co,"150 BINARY connection open for MLSD\r\n",-1);
		else
			sock_write_bytes(co,"150 Opening ASCII data connection for directory listing.\r\n",-1);
		dir = opendir(".");
		if ( dir == NULL ){
			sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
			if ( co->passive == 1 ){
				close(co->passive_socket);
				close(co->passive_nsock);
				co->passive_socket = 0;
			} else {
				close(co->active_socket);
				co->active_socket = 0;
			}
			return;
		}	
	} else {
		sprintf(dirname,"%s",wildcard);
		if ( lsmode == 2 )
			sock_write_bytes(co,"150 BINARY connection open for MLSD\r\n",-1);
		else
			sock_write_bytes(co,"150 Opening ASCII data connection for directory listing.\r\n",-1);
		dir = opendir(wildcard);
		if ( dir == NULL ){
			// OK...here we check for file!
			sock_write_bytes(co,"226 Transfer complete.\r\n",-1);
			if ( co->passive == 1 ){
				close(co->passive_socket);
				close(co->passive_nsock);
				co->passive_socket = 0;
			} else {
				close(co->active_socket);
				co->active_socket = 0;
			}
			return;
		}	
	}
	cnt = 0;	
	while ( (diry = readdir(dir)) != NULL ){
		if ( strcmp(diry->d_name,".") == 0 ) continue;
		if ( strcmp(diry->d_name,"..") == 0 ) continue;
		cnt++;
	}
	rewinddir(dir);

	filelist = (char **)malloc(sizeof(char *) * (cnt+20));


	cnt = 0;	
	while ( (diry = readdir(dir)) != NULL ){
		char fname[1024];
		/*
		if ( strcmp(diry->d_name,".") == 0 && lsmode < 2) continue;
		if ( strcmp(diry->d_name,"..") == 0 && lsmode < 2) continue;
		*/


		if ( ( strstr(wildcard,"*") == NULL && strstr(wildcard,"]") == NULL  ) || fnmatch(wildcard,diry->d_name,0) == 0 ){
			int retval;
			struct stat st;
			logger(co,"FILE: [%s] MATCHES PATTERN: [%s]\n",diry->d_name,wildcard);
			sprintf(fname,"%s/%s",dirname,diry->d_name);
			retval = stat(fname,&st);
			logger(co,"FNAME: [%s] --> STATING RETVAL: %d\n",fname,retval);
			if ( retval != 0 ) continue;
			if ( lsmode == 0 && !S_ISREG(st.st_mode) ) continue;
			filelist[cnt] = strdup(fname);
			cnt++;
		}
	}
	closedir(dir);
	qsort(filelist,cnt,sizeof(char *),fcmper);
	{
		struct timeval tv;
		gettimeofday(&tv,NULL);
		mkdir("/tmp",0777);
		sprintf(the_dir_temp_file,"/tmp/jftp_temp_dir_file.%d.%d.%d",tv.tv_sec,tv.tv_usec,getpid());
		unlink(the_dir_temp_file);
	}
	fp = fopen(the_dir_temp_file,"w");	
	for ( i = 0; i < cnt; i++ ){
		int blanker[1024];
		char permissions[1024];
		char dater[1024];
		struct tm *tm;
		char line[1024];
		struct stat st;
		logger(co,"WRITING TEMP FILE [%s], LSMODE: %d --> [%s]\n",filelist[i],lsmode,the_dir_temp_file);
		switch ( lsmode ){
			case 1: // Classical old mode
				if ( stat(filelist[i],&st) ){
					sprintf(permissions,"----------");
					st.st_size   = 0;
					st.st_uid    = 0;
					st.st_gid    = 0;
					st.st_nlink  = 0;
					st.st_mtime  = 0L;
				} else {
					sprintf(permissions,"----------");
				}
				update_perms(permissions,st.st_mode);
				tm = localtime(&st.st_mtime);
				if ( (st.st_mtime + (365 * 24 * 60 * 60)) < time(NULL)){
					sprintf(dater,"%s%s%d %04d",monthlist[tm->tm_mon], 
					fill_blanker(tm->tm_mday > 9 ? 1 : 2),
					tm->tm_mday, 
					tm->tm_year + 1900);
				} else {
					sprintf(dater,"%s%s%d %02d:%02d",monthlist[tm->tm_mon], 
					fill_blanker(tm->tm_mday > 9 ? 1 : 2),
					tm->tm_mday, tm->tm_hour, tm->tm_min );
				}
				/*
				sprintf(dater,"%04d-%02d-%02d %02d:%02d",
					tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
					tm->tm_hour, tm->tm_min);
				sprintf(dater,"Jun 3 1970");
				*/

				blanker[0] = 5;
				if ( st.st_nlink > 9 ){	
					blanker[0] = 4;
				}		
				if ( st.st_nlink > 99 ){	
					blanker[0] = 3;
				}		
				if ( st.st_nlink > 999 ){	
					blanker[0] = 2;
				}		
				if ( st.st_nlink > 9999 ){	
					blanker[0] = 1;
				}		
				blanker[1] = 5;
				if ( st.st_uid > 9 ){	
					blanker[1] = 4;
				}		
				if ( st.st_uid > 99 ){	
					blanker[1] = 3;
				}		
				if ( st.st_uid > 999 ){	
					blanker[1] = 2;
				}		
				if ( st.st_uid > 9999 ){	
					blanker[1] = 1;
				}		
				blanker[2] = 5;
				if ( st.st_gid > 9 ){	
					blanker[2] = 4;
				}		
				if ( st.st_gid > 99 ){	
					blanker[2] = 3;
				}		
				if ( st.st_gid > 999 ){	
					blanker[2] = 2;
				}		
				if ( st.st_gid > 9999 ){	
					blanker[2] = 1;
				}		
				blanker[3] = 10;
				if ( st.st_size > 9 ){	
					blanker[3] = 9;
				}		
				if ( st.st_size > 99 ){	
					blanker[3] = 8;
				}		
				if ( st.st_size > 999 ){	
					blanker[3] = 7;
				}		
				if ( st.st_size > 9999 ){	
					blanker[3] = 6;
				}		
				if ( st.st_size > 99999 ){	
					blanker[3] = 5;
				}		
				if ( st.st_size > 999999 ){	
					blanker[3] = 4;
				}		
				if ( st.st_size > 9999999 ){	
					blanker[3] = 3;
				}		
				if ( st.st_size > 99999999 ){	
					blanker[3] = 2;
				}		
				if ( st.st_size > 999999999 ){	
					blanker[3] = 1;
				}		

			        sprintf(line,"%s",permissions);
                                sprintf(&line[strlen(line)],"%s",fill_blanker(blanker[0]));
                                sprintf(&line[strlen(line)],"%d",st.st_nlink);
                                sprintf(&line[strlen(line)],"%s",fill_blanker(blanker[1]));
                                sprintf(&line[strlen(line)],"%d",st.st_uid);
                                sprintf(&line[strlen(line)],"%s",fill_blanker(blanker[2]));
                                sprintf(&line[strlen(line)],"%d",st.st_gid);
                                sprintf(&line[strlen(line)],"%s",fill_blanker(blanker[3]));
                                sprintf(&line[strlen(line)],"%llu",st.st_size);
                                sprintf(&line[strlen(line)]," %s",dater);
                                sprintf(&line[strlen(line)]," %s\r\n",our_basename(filelist[i]));
	

				break;
			case 2: // MLSD mode
				if ( stat(filelist[i],&st) )
					continue;
				logger(co,"MLSD FILE [%s], LSMODE: %d\n",filelist[i],lsmode);
				sprintf(line,"Type=%s;",get_file_type(&st,our_basename(filelist[i])));
				if ( S_ISREG(st.st_mode) ){
					sprintf(&line[strlen(line)],"Size=%lld;",st.st_size);
				}
				tm = localtime(&st.st_mtime);
				sprintf(&line[strlen(line)],"Modify=%04d%02d%02d%02d%02d%02d;",
					tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
					tm->tm_hour, tm->tm_min, tm->tm_sec);
				tm = localtime(&st.st_ctime);
				sprintf(&line[strlen(line)],"Create=%04d%02d%02d%02d%02d%02d;",
					tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
					tm->tm_hour, tm->tm_min, tm->tm_sec);
				sprintf(&line[strlen(line)],"Unique=%ud%u;",st.st_ino,st.st_dev);
				sprintf(&line[strlen(line)],"UNIX.mode=0%03o;",(st.st_mode & 0x1FF));
				sprintf(&line[strlen(line)],"UNIX.uid=%d;",(st.st_uid));
				sprintf(&line[strlen(line)],"UNIX.gid=%d;",(st.st_gid));
				sprintf(&line[strlen(line)]," %s\r\n",our_basename(filelist[i]));
				break;
			case 3: // Internic-Internal-Mode for LISTRAW
				if ( stat(filelist[i],&st) ){
					sprintf(permissions,"----------");
					st.st_size   = 0;
					st.st_uid    = 0;
					st.st_gid    = 0;
					st.st_nlink  = 0;
					st.st_mtime  = 0L;
				} else {
					sprintf(permissions,"----------");
				}
				update_perms(permissions,st.st_mode);
				sprintf(line,"%s;%d;%d;%llu;%ld;%s\r\n",permissions,st.st_uid,st.st_gid,st.st_size,st.st_mtime,our_basename(filelist[i]));
				break;
			default: // 0...
				sprintf(line,"%s\r\n",filelist[i]);
				break;
		}
		fwrite(line,1,strlen(line),fp);
		// write_data_socket(co,line,-1);
		free(filelist[i]);
	}
	free(filelist);
	fclose(fp);
	real_transmitted = 0;
	data_transmitted = 0;

	fp = fopen(the_dir_temp_file,"r");
	{
		char senda[1024];
		if ( co->data_mode == 'Z' ){
#ifdef HAVE_MODE_Z
			int ret, flush;
			z_stream strm;
			off64_t have;
			unsigned char buf_in[ZLIB_CHUNK];
			unsigned char buf_out[ZLIB_CHUNK];
			strm.zalloc   = Z_NULL;
			strm.zfree    = Z_NULL;
			strm.opaque   = Z_NULL;
			ret = deflateInit(&strm,ZLIB_LEVEL);
			if ( ret != Z_OK ){
				sock_write_bytes(co,"500 ZLIB INIT FAILURE.\r\n",-1);
				logger(co,"DEFLATE INIT FAILED...FALLING BACK (%d) [%s]\n",ret,display_zerr(ret));
				if ( co->passive == 1 ){
					close(co->passive_socket);
					if ( co->use_data_ssl ){
						SSL_shutdown(co->passive_ssl);
					}
					close(co->passive_nsock);
					co->passive_socket = 0;
				} else {
					if ( co->use_data_ssl ){
					SSL_shutdown(co->active_ssl);
					}
					close(co->active_socket);
					co->active_socket = 0;
				}
				fclose(fp);
				unlink(the_dir_temp_file);
				return;
			}
			while ( (cnt = fread(buf_in,1,ZLIB_CHUNK,fp)) > 0 ){
				real_transmitted += cnt;
				strm.avail_in = cnt;
				strm.next_in = buf_in;
				flush = feof(fp) ? Z_FINISH : Z_NO_FLUSH;
				do {
					strm.avail_out = ZLIB_CHUNK;
					strm.next_out  = buf_out; 
					ret = deflate(&strm, flush);
					have = ZLIB_CHUNK - strm.avail_out;
					data_transmitted += have;
					if ( ret == Z_STREAM_END ){
						logger(co,"DEFLATE -> [%d] (%s) (REAL_TRANSMITTED: %lld) HAVE: %lld Bytes (RECEIVED: %lld)\n",
							ret,display_zerr(ret),real_transmitted,have,cnt);
					}
					if ( ret != Z_OK && ret != Z_STREAM_END ){
						deflateEnd(&strm);
						logger(co,"INFLATE FAILED! (Probably out of memory) FALLING BACK...(%d)[%s]\n",ret,display_zerr(ret));
						sock_write_bytes(co,"501 ZLIB FAILED.\r\n",-1);
						if ( co->passive == 1 ){
							close(co->passive_socket);
							if ( co->use_data_ssl ){
								SSL_shutdown(co->passive_ssl);
							}
							close(co->passive_nsock);
							co->passive_socket = 0;
						} else {
							if ( co->use_data_ssl ){
								SSL_shutdown(co->active_ssl);
							}
							close(co->active_socket);
							co->active_socket = 0;
						}
						fclose(fp);
						unlink(the_dir_temp_file);
						return;
					}
					write_data_socket(co,buf_out,have);
				} while ( strm.avail_out == 0 );
			}
			deflateEnd(&strm);
			
#endif
		} else {
			while ( (cnt = fread(senda,1,1024,fp)) > 0 ){
				real_transmitted += cnt;
				data_transmitted += cnt;
				write_data_socket(co,senda,cnt);
			}
		}
		fclose(fp);
		unlink(the_dir_temp_file);
	}

	if ( lsmode == 2 ){
		sock_write_bytes(co,"226-Options: -a -l\r\n",-1);
		sprintf(copy2,"226 %d matches.\r\n",cnt);
		sock_write_bytes(co,copy2,-1);
	} else {
		char dummy[1024];
		sprintf(dummy,"226 Transfer complete. REAL_TRANSMITTED: %lld, DATA_TRANSMITTED: %lld [%s]\r\n",
			real_transmitted, data_transmitted, co->data_mode == 'Z' ? "ZLIB" : "STREAM");
		sock_write_bytes(co,dummy,-1);
	}

	if ( co->passive == 1 ){
		close(co->passive_socket);
		if ( co->use_data_ssl ){
			SSL_shutdown(co->passive_ssl);
		}
		close(co->passive_nsock);
		co->passive_socket = 0;
	} else {
		if ( co->use_data_ssl ){
			SSL_shutdown(co->active_ssl);
		}
		close(co->active_socket);
		co->active_socket = 0;
	}

	return;


}

void process_cdup(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	if ( chdir("..") ){
		sprintf(copy2,"550 Cannot change to parent directory.\r\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	} else {
		getcwd(copy,1024);
		strcpy(co->current_path,copy);
		sprintf(copy2,"250 CDUP command successful (Current path: %s).\r\n",copy);
		logger(co,"DEBUG: Canged directory to [%s]\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	}
	return;


}

void process_cwd(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	if ( chdir(ptr) ){
		sprintf(copy2,"550 %s: Cannot change path.\r\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	} else {
		getcwd(copy,1024);
		sprintf(copy2,"250 CWD command successful.\r\n");
		strcpy(co->current_path,copy);
		logger(co,"DEBUG: Canged directory to [%s]\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	}
	return;


}
void process_dele(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->readonly ){
		sock_write_bytes(co,"550 Permission denied.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	if ( unlink(ptr) ){
		sprintf(copy2,"550 %s: Cannot unlink file.\r\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	} else {
		sprintf(copy2,"250 DELE successfull.\r\n");
		logger(co,"DEBUG: Deleted file [%s]\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	}
	return;


}
void process_rmdir(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->readonly ){
		sock_write_bytes(co,"550 Permission denied.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	if ( rmdir(ptr) ){
		sprintf(copy2,"550 %s: Cannot remove directory.\r\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	} else {
		sprintf(copy2,"250 RMDIR successfull.\r\n");
		logger(co,"DEBUG: Deleted directory [%s]\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	}
	return;


}
void process_mkdir(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->readonly ){
		sock_write_bytes(co,"550 Permission denied.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	if ( mkdir(ptr,co->umask) ){
		sprintf(copy2,"521 %s: Cannot create directory.\r\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	} else {
		sprintf(copy2,"257 directory created.\r\n");
		logger(co,"DEBUG: Created directory [%s]\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	}
	return;


}
void process_mode(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
#ifdef HAVE_MODE_Z
	if ( *ptr == 'Z' || *ptr == 'z' ){
		co->data_mode = 'Z';
		sprintf(copy2,"200 TRANSFER-MODE set to ZLIB.\r\n");
		sock_write_bytes(co,copy2,-1);
		return;
	}
#endif
	if ( *ptr == 's' || *ptr == 'S' ){
		co->data_mode = 'S';
		sprintf(copy2,"200 TRANSFER-MODE set to STREAM.\r\n");
		sock_write_bytes(co,copy2,-1);
		return;
	}
	sock_write_bytes(co,"410 Bad mode.\r\n",-1); // Unknown transfer mode
	return;


}
void process_type(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Bad command.\r\n",-1);
		return;
	}
	if ( *ptr == 'a' || *ptr == 'A' ){
		co->transfer_mode = TRANSFER_MODE_ASCII;
		sprintf(copy2,"250 TYPE set to %s.\r\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	}
	if ( *ptr == 'i' || *ptr == 'I' ){
		co->transfer_mode = TRANSFER_MODE_BINARY;
		sprintf(copy2,"250 TYPE set to %s.\r\n",ptr);
		sock_write_bytes(co,copy2,-1);
		return;
	}
	sock_write_bytes(co,"500 Bad command.\r\n",-1); // Unknown transfer mode
	return;


}

/*

211-www.internic.at FTP server status:
     Version wu-2.6.2(1) Tue May 31 13:48:57 CEST 2011
     Connected to 212.126.67.221
     Logged in as ftp
     TYPE: ASCII, FORM: Nonprint; STRUcture: File; transfer MODE: Stream
     No data connection
     0 data bytes received in 0 files
     0 data bytes transmitted in 0 files
     0 data bytes total in 0 files
     41 traffic bytes received in 0 transfers
     1421 traffic bytes transmitted in 0 transfers
     1512 traffic bytes total in 0 transfers
211 End of status

*/

void process_noop(struct conn *co, char *cmd){
	sock_write_bytes(co,"200 Nothing to do, zzz...\r\n",-1);
	return;
}

void process_stat(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;
	struct hostent *hp;
	char hostname[1024];
	char pathname[1024];	
	DIR *dir;
	struct dirent *diry;
	char **filelist;




	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);

	ptr = strtok(copy," \r\n\t");

	if ( ptr == NULL ){
		sock_write_bytes(co,"590 Impossible error :-).\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \r\n\t");
	if ( ptr != NULL ){
		sock_write_bytes(co,"213-Status\r\n",-1);
		
		logger(co,"Opening directory: [%s]\n",ptr);
		dir = opendir(ptr);
		if ( dir != NULL ){
			int cnt;
			struct stat st;
			int i;
			cnt = 0;
			while ( (diry = readdir(dir)) != NULL ){
				char fname[1024];
				sprintf(fname,"%s/%s",ptr,diry->d_name);
				if ( stat(fname,&st)  == 0 ) cnt++;
			}	
			rewinddir(dir);
			filelist = (char **)malloc(sizeof(char *) * (cnt + 2));
			cnt = 0;
			while ( (diry = readdir(dir)) != NULL ){
				char fname[1024];
				sprintf(fname,"%s/%s",ptr,diry->d_name);
				if ( stat(fname,&st)  == 0 ) filelist[cnt++] = strdup(fname);
			}	
			qsort(filelist,cnt,sizeof(char *),fcmper);
			closedir(dir);
			for ( i = 0; i < cnt; i++ ){
				char line[1024];
				char dater[1024];
				char permissions[124];
				struct tm *tm;
				if ( stat(filelist[i],&st) ){
					sprintf(permissions,"----------");
					st.st_size   = 0;
					st.st_uid    = 0;
					st.st_gid    = 0;
					st.st_nlink  = 0;
					st.st_mtime  = 0L;
				} else {
					sprintf(permissions,"----------");
				}
				update_perms(permissions,st.st_mode);
				tm = localtime(&st.st_mtime);
				if ( (st.st_mtime + (365 * 24 * 60 * 60)) < time(NULL)){
					sprintf(dater,"%s %02d %04d",monthlist[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
				} else {
					sprintf(dater,"%s %02d %02d:%02d",monthlist[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min );
				}
				sprintf(line,"%s %d %d %d %lld %s %s\r\n",
					permissions, st.st_nlink, st.st_uid, st.st_gid, 
					st.st_size, dater, our_basename(filelist[i]));
				sock_write_bytes(co,line,-1);
			}
		}

		sock_write_bytes(co,"213 Status end.\r\n",-1);
		return;
	}





	hp = gethostbyaddr((struct sockaddr *)(&co->to.sin_addr),sizeof(struct sockaddr_in), AF_INET);
	if ( hp != NULL ){
		sprintf(hostname,"%s (%s)",hp->h_name,inet_ntoa(co->to.sin_addr));
	} else {
		sprintf(hostname,"%s",inet_ntoa(co->to.sin_addr));
	}
	

	sprintf(copy,"211-%s FTP server status:\r\n",hostname);
	sock_write_bytes(co,copy,-1);
	sprintf(copy," Connected to %s:%d\r\n",inet_ntoa(co->from.sin_addr),ntohs(co->from.sin_port));
	sock_write_bytes(co,copy,-1);
	sprintf(copy," Logged in as %s\r\n",co->username);
	sock_write_bytes(co,copy,-1);
	sprintf(copy," TYPE: %s, FORM: Nonprint; STRUcture: File; transfer MODE: %s\r\n",co->transfer_mode ? "ASCII" : "BINARY",co->data_mode == 'Z' ? "ZLIB" : "STREAM" );
	sock_write_bytes(co,copy,-1);
	sprintf(copy," Control-Bytes received: %llu\r\n",co->cbytes_in);
	sock_write_bytes(co,copy,-1);
	sprintf(copy," Control-Bytes sent: %llu\r\n",co->cbytes_out);
	sock_write_bytes(co,copy,-1);
	sprintf(copy," Data-Bytes received: %llu\r\n",co->bytes_in);
	sock_write_bytes(co,copy,-1);
	sprintf(copy," Data-Bytes sent: %llu\r\n",co->bytes_out);
	sock_write_bytes(co,copy,-1);
	sprintf(copy," Rest-Position: %llu\r\n",co->rest_pos);
	sock_write_bytes(co,copy,-1);
	sprintf(copy," Idle-timeout: %d seconds\r\n",co->idle_timeout);
	sock_write_bytes(co,copy,-1);
	sprintf(copy,"211 End of status.\r\n");
	sock_write_bytes(co,copy,-1);

	return;

}

/*
211-Features:
211-AUTH TLS
*/

void process_feat(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;



	sprintf(copy,"211-Features:\r\n");
	sock_write_bytes(co,copy,-1);
	/*
	sprintf(copy," MLST\r\n");
	sock_write_bytes(co,copy,-1);
	*/
	sprintf(copy," REST\r\n");
	sock_write_bytes(co,copy,-1);
	/*
	sprintf(copy," MLSD\r\n");
	sock_write_bytes(co,copy,-1);
	*/ 
	sprintf(copy," EPSV\r\n");
	sock_write_bytes(co,copy,-1);
	sprintf(copy," SIZE\r\n");
	sock_write_bytes(co,copy,-1);
	sprintf(copy," PBSZ\r\n");
	sock_write_bytes(co,copy,-1);
	sprintf(copy," PROT\r\n");
	sock_write_bytes(co,copy,-1);
	sprintf(copy," PASV\r\n");
	sock_write_bytes(co,copy,-1);
	sprintf(copy," AUTH TLS\r\n");
	sock_write_bytes(co,copy,-1);
#ifdef HAVE_MODE_Z
	sprintf(copy," MODE Z\r\n");
	sock_write_bytes(co,copy,-1);
#endif
	sprintf(copy,"211 END.\r\n");
	sock_write_bytes(co,copy,-1);
	return;

}
void process_pbsz(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;

	strcpy(copy,cmd);

	ptr = strtok(copy," \t\r\n");

	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax.\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \t\r\n");
	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax(invalid parameter).\r\n",-1);
		return;
	}




	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	if ( co->use_ssl == 0 ){
		sock_write_bytes(co,"500 Please use AUTH TLS first.\r\n",-1);
		return;
	}
	logger(co,"Requesting PBSZ: [%s]\n",ptr);

	sprintf(copy,"200 PBSZ command successfull.\r\n");
	sock_write_bytes(co,copy,-1);

	return;

}
void process_rnto(struct conn *co, char *cmd){
	struct stat st;
	char copy[1024];
	char *ptr;

	strcpy(copy,cmd);

	ptr = strtok(copy," \t\r\n");

	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\t\r\n");
	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax(invalid parameter).\r\n",-1);
		return;
	}

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->readonly ){
		sock_write_bytes(co,"550 Permission denied.\r\n",-1);
		return;
	}

	logger(co,"RNTO requested for '%s'\n",ptr);


	if ( strlen(co->rnfr) == 0 ){
		sprintf(copy,"503 RNTO requires RNFR command first.\r\n");
		sock_write_bytes(co,copy,-1);
	}

	if ( rename(co->rnfr,ptr)  ){
		strcpy(co->rnfr,"");
		sprintf(copy,"530 RNFR command failed.\r\n");
		sock_write_bytes(co,copy,-1);
		return;
	}
	strcpy(co->rnfr,"");

	sprintf(copy,"250 RNTO command successfull done.\r\n");
	sock_write_bytes(co,copy,-1);

	return;

}
void process_rest(struct conn *co, char *cmd){
	struct stat st;
	char copy[1024];
	char *ptr;

	strcpy(copy,cmd);

	ptr = strtok(copy," \t\r\n");

	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax.\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \t\r\n");
	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax(invalid parameter).\r\n",-1);
		return;
	}

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	logger(co,"REST requested for '%s'\n",ptr);
	if ( sscanf(ptr,"%llu",&(co->rest_pos)) != 1 ){
		logger(co,"REST requested for '%s' --> Conversion failed\n",ptr);
		co->rest_pos = (off64_t)0;
	} else {
		logger(co,"REST requested for '%s' --> New position: %llu\n",ptr,co->rest_pos);
	}
	// co->rest_pos = atoi(ptr);

	sprintf(copy,"350 restarting at %llu\r\n",co->rest_pos);
	sock_write_bytes(co,copy,-1);
	return;
}
void process_size(struct conn *co, char *cmd){
	struct stat st;
	char copy[1024];
	char *ptr;

	strcpy(copy,cmd);

	ptr = strtok(copy," \t\r\n");

	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\t\r\n");
	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax(invalid parameter).\r\n",-1);
		return;
	}

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->readonly ){
		sock_write_bytes(co,"550 Permission denied.\r\n",-1);
		return;
	}

	logger(co,"SIZE requested for '%s'\n",ptr);

	if ( stat(ptr,&st) ){
		sprintf(copy,"510 SIZE for %s failed.\r\n",ptr);
		sock_write_bytes(co,copy,-1);
		return;
	}

	sprintf(copy,"213 %llu\r\n",st.st_size);
	sock_write_bytes(co,copy,-1);

	return;
}
void process_rnfr(struct conn *co, char *cmd){
	struct stat st;
	char copy[1024];
	char *ptr;

	strcpy(copy,cmd);

	ptr = strtok(copy," \t\r\n");

	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\t\r\n");
	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax(invalid parameter).\r\n",-1);
		return;
	}

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	if ( co->readonly ){
		sock_write_bytes(co,"550 Permission denied.\r\n",-1);
		return;
	}

	logger(co,"RNFR requested for '%s'\n",ptr);

	if ( stat(ptr,&st) ){
		sprintf(copy,"530 RNFR command failed.\r\n");
		sock_write_bytes(co,copy,-1);
		return;
	}
	strcpy(co->rnfr,ptr);

	sprintf(copy,"350 RNFR command successfull ready for RNTO.\r\n");
	sock_write_bytes(co,copy,-1);

	return;
}
void process_prot(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;

	strcpy(copy,cmd);

	ptr = strtok(copy," \t\r\n");

	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax.\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \t\r\n");
	if (ptr == NULL){
		sock_write_bytes(co,"500 Bad Command syntax(invalid parameter).\r\n",-1);
		return;
	}




	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	if ( co->use_ssl == 0 ){
		sock_write_bytes(co,"500 Please use AUTH TLS first.\r\n",-1);
		return;
	}
	logger(co,"Requesting Security-Level: [%s]\n",ptr);

	if ( *ptr == 'P' || *ptr == 'p' ) 
		co->use_data_ssl = 1; 
	else
		co->use_data_ssl = 0;

	sprintf(copy,"200 PROT command successfull.\r\n");
	sock_write_bytes(co,copy,-1);

	return;

}
void process_syst(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	sprintf(copy,"215 UNIX Type: L8\r\n");
	sock_write_bytes(co,copy,-1);

	return;

}


int ichmod_recursive(struct conn *co,char *path, int file_mode, int dir_mode){
	DIR *dir;
	struct dirent *diry;
	char fname[1024];
	struct stat st;

	if ( chmod(path,dir_mode & 0x1FF) ){
		logger(co,"FAILED TO CHMOD [%s] User-ID: %d, Mode: 0%o\n",path,getuid(),dir_mode);
		return(-1);
	}
	dir = opendir(path);
	if ( dir != NULL ){
		while ( (diry = readdir(dir)) != NULL ){
			if ( strcmp(diry->d_name ,".") == 0 ) continue;
			if ( strcmp(diry->d_name ,"..") == 0 ) continue;
			sprintf(fname,"%s/%s",path,diry->d_name);
			if ( lstat(fname,&st) ){
				logger(co,"FAILED TO STAT [%s] User-ID: %d, Mode: 0%o\n",fname,getuid(),dir_mode);
				continue;
			}
			if ( S_ISDIR(st.st_mode) ){
				if ( ichmod_recursive(co,fname,file_mode, dir_mode) ){ closedir(dir); return(1);  }
				continue;
			}
			if ( S_ISREG(st.st_mode) ){
				if ( chmod(fname,file_mode) ){
					logger(co,"FAILED TO CHMOD [%s] User-ID: %d, Mode: 0%o\n",fname,getuid(),dir_mode);
					closedir(dir); 
					return(1);
				}
				continue;	
			}
		}
		closedir(dir);
	}


	return(0);		
}
#ifndef NOZIP

int add_file_archive_zip(struct conn *co, zip_t *zip, char *path){
	DIR *dir;
	struct dirent *diry;
	char fname[1024];
	struct stat st;
	int idx;


	logger(co,"Adding directory: [%s]\n",path);

	if ( strcmp(path,".") != 0 ){
		if ( (idx = zip_dir_add(zip,(const char *)&path[2],ZIP_FL_ENC_UTF_8)) == -1 ){
			logger(co,"Failed to add directory [%s] to ZIP-Archive\n",path);
			return(-1);
		}
		/*
		if ( zip_set_file_compression(zip,(zip_uint64_t) idx, ZIP_CM_DEFAULT,0)  ){
			closedir(dir);
			logger(co,"Failed to set dir-file-compression [%s] to archive (IDX: %d)\n",fname,idx);
			return(-1);
		}
		*/
	}
	dir = opendir(path);
	if ( dir == NULL ){
		logger(co,"Failed to open directory [%s]\n",path);
		return(-1);
	}
	while ( (diry  = readdir(dir)) != NULL ){
		if ( strcmp(diry->d_name,".") == 0 ) continue;
		if ( strcmp(diry->d_name,"..") == 0 ) continue;
		sprintf(fname,"%s/%s",path,diry->d_name);
		if ( lstat(fname,&st) != 0 ){
			closedir(dir);
			logger(co,"Failed to stat file [%s]\n",fname);
			return(-1);
		}
		if ( S_ISDIR(st.st_mode) ){
			if ( add_file_archive_zip(co, zip,fname) ){
				closedir(dir);
				logger(co,"Failed to add directory [%s] to archive\n",fname);
				return(-1);
			}
		}
		if ( S_ISREG(st.st_mode) ){
			zip_source_t *zipf;
			zipf = zip_source_file_create((const char *)fname,(zip_uint64_t) 0, (zip_uint64_t) 0, NULL);
			if ( zipf == NULL ){
				closedir(dir);
				logger(co,"Failed to add file [%s] to archive (Cannot create Z-Buffer\n",fname);
				return(-1);
			}			
			if ( (idx = zip_file_add(zip,(const char *)&fname[2],zipf,ZIP_FL_ENC_UTF_8)) == -1 ){
				zip_source_free(zipf);
				closedir(dir);
				logger(co,"Failed to add file [%s] to archive (Cannot add Z-Buffer\n",fname);
				return(-1);
			}
			/*
			zip_source_free(zipf);
			*/
			if ( zip_set_file_compression(zip,(zip_uint64_t) idx, ZIP_CM_DEFAULT,0)  ){
				closedir(dir);
				logger(co,"Failed to set file-compression [%s] to archive (IDX: %d)\n",fname,idx);
				return(-1);
			}
		}
	}
	closedir(dir);

	return(0);
}


void process_pack(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char cmdo[1024];
	char path[1024];
	char archive[1024];
	int file_mode;
	int dir_mode;
	char *ptr;
	FILE *fp;
	unsigned char data[2048];
	zip_t *zip;
	zip_stat_t zipsb;
	zip_file_t *zipf;
	int file_count;
	int i;
	char zip_fname[1024];
	char zip_path[1024];

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);

	ptr = strtok(copy," \r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid PACK command(1).\r\n",-1);
		return;
	}
	strcpy(cmdo,ptr);
	ptr = strtok(NULL," \r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid PACK command(1).\r\n",-1);
		return;
	}
	strcpy(path,ptr);
	ptr = strtok(NULL,"\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid PACK command(1).\r\n",-1);
		return;
	}
	while ( *ptr && ( *ptr == ' ' || *ptr == '\t') ) ptr++;
	strcpy(archive,ptr);
	logger(co,"Packing Archive [%s] at directory [%s]\n",archive,path);

	if ( chdir(path)  ){
		sock_write_bytes(co,"500 Cannot change-path.\r\n",-1);
		return;
	}

	unlink(archive);
	zip = zip_open(archive,ZIP_CREATE,NULL);	
	
	if ( zip == NULL ){
		logger(co,"ZIP-File cannot be opened (%s): [%s]\n",archive,zip_strerror(zip));
		sock_write_bytes(co,"500 Cannot create temporary ZIP-Archive.\r\n",-1);
		return;
	}

	if ( add_file_archive_zip(co, zip,".")  ){
		zip_close(zip);
		unlink(archive);
		sock_write_bytes(co,"500 Cannot create temporary ZIP-Archive.\r\n",-1);
		return;
	}
	if ( zip_close(zip) ){
		logger(co,"ZIP-File cannot be closed (%s): [%s]\n",archive,zip_strerror(zip));
		sock_write_bytes(co,"500 Cannot close temporary ZIP-Archive.\r\n",-1);
		return;
	}
	chmod(archive,0600);
	sock_write_bytes(co,"200 ZIP File successfully created.\r\n",-1);
	return;
	
}

void process_unpack(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char cmdo[1024];
	char path[1024];
	char archive[1024];
	int file_mode;
	int dir_mode;
	char *ptr;
	FILE *fp;
	unsigned char data[2048];
	zip_t *zip;
	zip_stat_t zipsb;
	zip_file_t *zipf;
	int file_count;
	int i;
	char zip_fname[1024];
	char zip_path[1024];

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);

	ptr = strtok(copy," \r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid UNPACK command(1).\r\n",-1);
		return;
	}
	strcpy(cmdo,ptr);
	ptr = strtok(NULL," \r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid UNPACK command(1).\r\n",-1);
		return;
	}
	strcpy(path,ptr);
	ptr = strtok(NULL,"\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid UNPACK command(1).\r\n",-1);
		return;
	}
	while ( *ptr && ( *ptr == ' ' || *ptr == '\t') ) ptr++;
	strcpy(archive,ptr);
	logger(co,"Unpacking Archive [%s] at directory [%s]\n",archive,path);

	if ( chdir(path)  ){
		sock_write_bytes(co,"500 Cannot change-path.\r\n",-1);
		return;
	}
	zip = zip_open(archive,ZIP_RDONLY,NULL);
	if ( zip == NULL ){
		sock_write_bytes(co,"500 Cannot open ZIP-Archive.\r\n",-1);
		return;
	}
	file_count = zip_get_num_entries(zip,0);
	logger(co,"Archive contains %d files\n",file_count);	

	for ( i = 0; i < file_count; i++ ){
		if ( (ptr = (char *)zip_get_name(zip,i,ZIP_FL_ENC_GUESS)) == NULL ){
			zip_close(zip);
			sock_write_bytes(co,"500 ZIP-Archive corrupted (directory).\r\n",-1);
			return;
		}
		strcpy(zip_fname,ptr);
		zip_stat_init(&zipsb);
		zipsb.valid = ZIP_STAT_NAME | ZIP_STAT_INDEX | ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_MTIME | ZIP_STAT_CRC | ZIP_STAT_COMP_METHOD | ZIP_STAT_FLAGS;
		if ( zip_stat_index(zip,i,0,&zipsb) ){
			zip_close(zip);
			sock_write_bytes(co,"500 ZIP-Archive corrupted (cannot stat file in zip-archive).\r\n",-1);
			return;
		}

		zipf = zip_fopen_index(zip,i,0);
		if ( zipf == NULL ){
			zip_close(zip);
			sock_write_bytes(co,"500 ZIP-Archive corrupted (cannot open file in zip-archive).\r\n",-1);
			return;
		}
		logger(co,"FILENAME[%d]: %s Size: [%llu Bytes] VALID: [%llu]\n",i,zip_fname,zipsb.size,zipsb.valid);
		if ( zip_fname[strlen(zip_fname)-1] == '/' ){ // A directory
			struct utimbuf utimmy;
			ptr = zip_fname;
			while  ( *ptr && *ptr == '/' ) ptr++;
			mkdir(ptr,0700);
			chmod(ptr,0700);	
			utimmy.actime = time(NULL);
			utimmy.modtime = zipsb.mtime;
			utime(ptr,&utimmy);
		} else {
			struct utimbuf utimmy;
			zip_int64_t bytes;
			ptr = zip_fname;
			while  ( *ptr && *ptr == '/' ) ptr++;
			unlink(ptr);
			fp = fopen(ptr,"w");
			if ( fp == NULL ){
				zip_fclose(zipf);
				zip_close(zip);
				sock_write_bytes(co,"500 ZIP-Archive not corrupted (but cannot  open local-file.\r\n",-1);
				return;
			}
			while ( (bytes = zip_fread(zipf,data,(zip_uint64_t)2048)) > 0 ){
				if ( fwrite(data,1,(off64_t) bytes, fp ) != (off64_t) bytes){
					fclose(fp);
					zip_fclose(zipf);
					zip_close(zip);
					sock_write_bytes(co,"500 ZIP-Archive not corrupted (but cannot  write to  local-file.\r\n",-1);
					return;
				}
			}
			fclose(fp);
			chmod(ptr,0600);	
			utimmy.actime = time(NULL);
			utimmy.modtime = zipsb.mtime;
			utime(ptr,&utimmy);
		
		}
		zip_fclose(zipf);
	}
	zip_close(zip);
	unlink(archive);
	sock_write_bytes(co,"200 Unpack successfully completed.\r\n",-1);	
	return;	
}
#else
void process_pack(struct conn *co, char *cmd){
	sock_write_bytes(co,"500 pack command not supported!\r\n",-1);	
	return;	
}
void process_unpack(struct conn *co, char *cmd){
	sock_write_bytes(co,"500 Unpack command not supported!\r\n",-1);	
	return;	
}
#endif

void process_ichmod(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char cmdo[1024];
	char filename[1024];
	int file_mode;
	int dir_mode;
	char *ptr;

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);

	ptr = strtok(copy," \r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid CHMOD command(1).\r\n",-1);
		return;
	}
	strcpy(cmdo,ptr);
	ptr = strtok(NULL," \r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid CHMOD command(1).\r\n",-1);
		return;
	}
	if ( strcmp(cmdo,"ICHMODR") == 0 ){
		dir_mode = atoi(ptr);
		ptr = strtok(NULL," \r\n\t");
		if ( ptr == NULL ){
			sock_write_bytes(co,"500 Invalid CHMOD command(1).\r\n",-1);
			return;
		}
		file_mode = atoi(ptr);
	} else {
		file_mode = atoi(ptr);
		dir_mode = file_mode;
	}
	ptr = strtok(NULL,"\r\n");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid CHMOD command(1).\r\n",-1);
		return;
	}
	while ( *ptr && (*ptr == ' ' || *ptr == '\t') ) ptr++;
	strcpy(filename,ptr);
	logger(co,"Command: [%s] FILE-MODE: 0%o DIR-MODE: 0%o File: [%s]\n",cmdo,file_mode,dir_mode,filename);

	if ( strcmp(cmdo,"ICHMOD") == 0 ){
		if ( chmod(filename,file_mode & 0x1FF) ){
			sock_write_bytes(co,"500 ICHMOD failed.\r\n",-1);
			return;
		}
	}
	if ( strcmp(cmdo,"ICHMODR") == 0 ){
		if  ( ichmod_recursive(co,filename,file_mode,dir_mode) ){
			sock_write_bytes(co,"500 ICHMODR failed.\r\n",-1);
			return;
		}
	}

	sock_write_bytes(co,"200 ICHMOD successfully done.\r\n",-1);
	return;
}




void process_chmod(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char filename[1024];
	char mode[1024];
	int _mode;
	char *ptr;

	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);

	ptr = strtok(copy," \r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid CHMOD command(1).\r\n",-1);
		return;
	}
	ptr = strtok(NULL," \r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid CHMOD command(1).\r\n",-1);
		return;
	}
	strcpy(mode,ptr);
	ptr = strtok(NULL,"\r\n\t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"500 Invalid CHMOD command(1).\r\n",-1);
		return;
	}
	strcpy(filename,ptr);

	if ( mode[0] == '0' ) ptr = &mode[1];
	else ptr = mode;

	sscanf(ptr,"%o",&_mode);

	_mode = _mode & 0x1FF; // SUID Bit may not be set!


	logger(co,"CHANGING Filemode to %s for file: %s (%o)\n",mode,filename,_mode);

	if ( chmod(filename,_mode) ){
		sock_write_bytes(co,"550 CHMOD failed.\r\n",-1);
		return;
	}

	sock_write_bytes(co,"200 CHMOD successfully done.\r\n",-1);
	return;

}


void process_site(struct conn *co, char *cmd){
	char copy[1024];
	char copy2[1024];
	char subcommand[1024];
	char *ptr;
	


	if ( co->state != STATE_LOGGEDIN ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	ptr = &cmd[4];

	if ( *ptr == '\r' || *ptr == '\n' ){
		sock_write_bytes(co,"500 Invalid SITE command(1).\r\n",-1);
		return;
	}
	while ( *ptr && (*ptr == ' ' || *ptr  == '\t') ) ptr++;

	if ( *ptr == '\0' || *ptr == '\r' || *ptr == '\n' ){
		sock_write_bytes(co,"500 Invalid SITE command(2).\r\n",-1);
		return;
	}
	strcpy(copy,ptr);
	logger(co,"SITE subcommand: [%s]\n",copy);


	if ( strncasecmp(copy,"CHMOD",5) == 0 ){
		process_chmod(co,copy);
		return;	
	}
	

	sprintf(copy2,"501 Bad SITE subcommand '%s'.\r\n",copy);
	sock_write_bytes(co,copy2,-1);

	return;

}

void process_pass(struct conn *co, char *cmd){
	char copy[1024];
	char *ptr;


	if ( co->state >= STATE_LOGGEDIN ){
		sock_write_bytes(co,"504 Already logged in.\r\n",-1);
		return;
	}

	if ( co->state != STATE_USERSENT ){
		sock_write_bytes(co,"503 Send user first.\r\n",-1);
		return;
	}

	strcpy(copy,cmd);
	ptr = strtok(copy,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	ptr = strtok(NULL,"\r\n \t");
	if ( ptr == NULL ){
		sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		return;
	}
	strcpy(co->password,ptr);
	logger(co,"DEBUG: Logging in with password [%s]\n",co->password);


	if ( check_login(co) ){
		co->state = STATE_CONNECTED;
		sprintf(copy,"530 Login incorrect.\r\n");
		sock_write_bytes(co,copy,-1);
	} else {
		co->state = STATE_LOGGEDIN;
		sprintf(copy,"230 User %s logged in.\r\n",co->username);
		sock_write_bytes(co,copy,-1);
	}

	return;

}


void process_client(struct conn *co){
	char cmd[1024];
	struct stat st;

	SSL_library_init();
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
	co->meth = SSLv23_server_method();
#else
	co->meth = TLS_server_method();
#endif
	co->ctx  = SSL_CTX_new(co->meth);
	SSL_CTX_set_timeout(co->ctx,7200);
	SSL_CTX_use_certificate_file(co->ctx,SSL_CRT,SSL_FILETYPE_PEM);
        SSL_CTX_use_PrivateKey_file(co->ctx,SSL_KEY,SSL_FILETYPE_PEM);	
	if ( stat(SSL_CA,&st) == 0 ){
		if ( SSL_CTX_load_verify_locations(co->ctx,SSL_CA,NULL) != 1 ){
			sock_write_bytes(co,"500 SSL-Control init failed locally(1).\r\n",-1);
			return;
		}
		if ( SSL_CTX_set_default_verify_paths(co->ctx) != 1 ){
			sock_write_bytes(co,"500 SSL-Control init failed locally(2).\r\n",-1);
			return;
		}
	}
	if ( !SSL_CTX_check_private_key(co->ctx) ){
		sock_write_bytes(co,"500 SSL-Control init failed locally(3).\r\n",-1);
		return;
	}

	logger(co,"DEBUG: New connection arrived!\n");
	send_welcome(co);
	co->state = STATE_CONNECTED;
	co->data_mode = 'S';

	co->idle_timeout = DEFAULT_IDLE_TIMEOUT;

	while ( sock_read_line(co,cmd,1024) > 0 ){
		if ( cmd[strlen(cmd)-1] == '\r' || cmd[strlen(cmd)-1] == '\n' ) cmd[strlen(cmd)-1] = '\0';
		if ( cmd[strlen(cmd)-1] == '\r' || cmd[strlen(cmd)-1] == '\n' ) cmd[strlen(cmd)-1] = '\0';
		logger(co,"DEBUG: Command: [%s]\n",cmd);

		if ( strncasecmp(cmd,"LISTRAW",7) == 0 ){
			process_list(co,cmd,3);
			continue;
		}


		if ( strncasecmp(cmd,"PACK",4) == 0 ){
			process_pack(co,cmd);
			continue;
		}

		if ( strncasecmp(cmd,"UNPACK",6) == 0 ){
			process_unpack(co,cmd);
			continue;
		}

		if ( strncasecmp(cmd,"ICHMOD",6) == 0 || strncasecmp(cmd,"ICHMODR",7) == 0 ){
			process_ichmod(co,cmd);
			continue;
		}

		if ( strncasecmp(cmd,"NOOP",4) == 0 ){
			process_noop(co,cmd);
			continue;
		}		
		if ( strncasecmp(cmd,"STAT",4) == 0 ){
			process_stat(co,cmd);
			continue;
		}		

		if ( strncasecmp(cmd,"QUIT",4) == 0 ){
			process_quit(co);
			check_logout(co,'Q');
			exit(0);
		}		
		if ( strncasecmp(cmd,"PBSZ",4) == 0 ){
			process_pbsz(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"PROT",4) == 0 ){
			process_prot(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"PASV",4) == 0 ){
			process_passive(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"EPSV",4) == 0 ){
			process_passive(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"PASS",4) == 0 ){
			process_pass(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"USER",4) == 0 ){
			process_user(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"SIZE",4) == 0 ){
			process_size(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"REST",4) == 0 ){
			process_rest(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"IUPD",4) == 0 ){
			process_iupd(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"IDIM",4) == 0 ){ // Delete old directory before transferring
			process_idir(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"IDIP",4) == 0 ){ // Just transfer...do not delete old directory
			process_idir(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"IDIR",4) == 0 ){
			process_idir(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"IEXE2",5) == 0 ){
			process_iexe2(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"IEXE",4) == 0 ){
			process_iexe(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"SITE",4) == 0 ){
			process_site(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"SYST",4) == 0 ){
			process_syst(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"TYPE",4) == 0 ){
			process_type(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"MODE",4) == 0 ){
			process_mode(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"AUTH",4) == 0 ){
			process_auth(co,cmd);
			continue;
		}

		if ( strncasecmp(cmd,"FEAT",4) == 0 ){
			process_feat(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"RNFR",4) == 0 ){
			process_rnfr(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"RNTO",4) == 0 ){
			process_rnto(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"PORT",4) == 0 ){
			process_port(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"STOR",4) == 0 ){
			process_stor(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"RETR",4) == 0 ){
			process_retr(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"TAIL",4) == 0 ){
			process_tail(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"NLST",4) == 0 ){
			process_list(co,cmd,0);
			continue;
		}
		if ( strncasecmp(cmd,"MLST",4) == 0 ){
			process_mlist(co,cmd,2);
			continue;
		}
		if ( strncasecmp(cmd,"MLSD",4) == 0 ){
			process_list(co,cmd,2);
			continue;
		}
		if ( strncasecmp(cmd,"QSTAT",5) == 0 ){
			process_qstat(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"LIST",4) == 0 ){
			process_list(co,cmd,1);
			continue;
		}
		if ( strncasecmp(cmd,"DELE",4) == 0 ){
			process_dele(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"CDUP",4) == 0 ){
			process_cdup(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"XRMD",4) == 0 || strncasecmp(cmd,"RMD",3) == 0 ){
			process_rmdir(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"XMKD",4) == 0 || strncasecmp(cmd,"MKD",3) == 0 ){
			process_mkdir(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"XPWD",4) == 0 || strncasecmp(cmd,"PWD",3) == 0 ){
			process_pwd(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"XCWD",4) == 0 || strncasecmp(cmd,"CWD",3) == 0 ){
			process_cwd(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"REMOVEDIR",strlen("REMOVEDIR")) == 0 ){
			process_removedir(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"REMOVE_ENDING",strlen("REMOVE_ENDING")) == 0 ){
			process_remove_ending(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"DEPACK",strlen("DEPACK")) == 0 ){
			process_depack(co,cmd);
			continue;
		}
		if ( strncasecmp(cmd,"CORROWNER",strlen("CORROWNER")) == 0 ){
			process_corrowner(co,cmd);
			continue;
		}

		if ( co->state < 1 ){
			sock_write_bytes(co,"530 Please login with USER and PASS.\r\n",-1);
		} else {
			sock_write_bytes(co,"500 Bad command.\r\n",-1);
		}
	}

}




int main(int argc, char **argv){
	int sock;
	int on;
	struct sockaddr_in socky;
	struct sockaddr_in6 socky6;



	if ( strncmp(argv[0],"[",1) != 0 ){
		if (  fork() == 0 ){
			execlp("/big/JFTP/DAEMON/bin/jftpd","[IPv4 JFTPD]",NULL);
			logger(NULL,"EXECLP (IPv4) failed\n");
			exit(0);
		}
		if (  fork() == 0 ){
			execlp("/big/JFTP/DAEMON/bin/jftpd","[IPv6 JFTPD]",NULL);
			logger(NULL,"EXECLP (IPv6) failed\n");
			exit(0);
		}
		while (1) sleep(10);
	}

	logger(NULL,"Called with: %s\n",argv[0]);

	if ( strstr(argv[0],"IPv4") != NULL ){
		ipv6 = 0;
		sock = socket(AF_INET,SOCK_STREAM,0);

		if ( sock < 0 ){
			logger(NULL,"FATAL: Cannot create socket!\n");
			exit(0);
		}	
	
		on = 1;

		if ( setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) ){
			logger(NULL,"SETSOCKOPT (IPv4) reuseaddr failed\n");
			exit(0);
		}

		socky.sin_port = htons(PORT);	
		socky.sin_family = AF_INET;
		socky.sin_addr.s_addr = INADDR_ANY;



		if ( bind(sock,(struct sockaddr *)&socky,sizeof(socky)) < 0 ){
			logger(NULL,"FATAL: Cannot bind IPv4 socket!\n");
			exit(0);
		}
	} else {
		ipv6 = 1;
		sock = socket(AF_INET6,SOCK_STREAM,0);

		if ( sock < 0 ){
			logger(NULL,"FATAL: Cannot create socket!\n");
			exit(0);
		}	
	
		on = 1;
#ifdef IPV6_V6ONLY
		if ( setsockopt(sock,IPPROTO_IPV6,IPV6_V6ONLY,&on,sizeof(on))  ){
			logger(NULL,"SETSOCKOPT (IPv6) V6 only failed\n");
			exit(0);
		}
#endif
		if ( setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) ){
			logger(NULL,"SETSOCKOPT (IPv6) reuseaddr failed\n");
			exit(0);
		}

		socky6.sin6_port = htons(PORT);	
		socky6.sin6_family = AF_INET6;
		socky6.sin6_addr = in6addr_any;



		if ( bind(sock,(struct sockaddr *)&socky6,sizeof(socky6)) < 0 ){
			logger(NULL,"FATAL: Cannot bind IPv6 socket!\n");
			exit(0);
		}
	}

	listen(sock,SOMAXCONN);

	logger(NULL,"INFO: JFTPD V%s waiting for connections.\n",JFTPD_VERSION);

	while ( 1 ){
		struct conn co;
		int nsock;
		int fromsize;
		int tosize;
		if ( ipv6 ){
			fromsize = sizeof(struct sockaddr_in6);
			tosize   = sizeof(struct sockaddr_in6);
		} else {
			fromsize = sizeof(struct sockaddr_in);
			tosize   = sizeof(struct sockaddr_in);
		}
		bzero(&co,sizeof(co));
		co.debug = DEBUG_LEVEL;
		if ( ipv6 ){
			co.ipv6 = 1;
			co.socket = accept(sock,(struct sockaddr *)&(co.from6),&fromsize);
			getsockname(co.socket,(struct sockaddr *)&(co.to6),&tosize);
		} else {
			co.ipv6 = 0;
			co.socket = accept(sock,(struct sockaddr *)&(co.from),&fromsize);
			getsockname(co.socket,(struct sockaddr *)&(co.to),&tosize);
		}
		signal(SIGCHLD,reaper);
		if ( fork() == 0 ){
			process_client(&co);
			exit(0);
		}
		close(co.socket);
		reaper();	

	}
	close(sock);	

}
