#include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.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"

#define USERNAME    argv[1]
#define PASSWORD    argv[2]
#define SERVER      argv[3]
#define REMOTE_FILE argv[4]
#define LOCAL_FILE  argv[5]

#define BFSIZE 4096


int mode_put;
char print_ip[128];

int check_logout(struct conn *co, char reason){ // Dummy
	return(0);
}
#define now (&_now)

#define FACTOR1000 ((double)1024)
char *getthroughput(u_int64_t wire_data,u_int64_t file_data,struct timeval *start){
	static char info[1024];
	struct timeval _now;
	double delta;
	double throughput;
	if ( wire_data == (u_int64_t)0 ) return("");
	if ( file_data == (u_int64_t)0 ) return("");
	gettimeofday(now,NULL);
	delta = (((double) now->tv_sec) + (((double)now->tv_usec)/((double)1000000))) - 
		(((double) start->tv_sec) + (((double)start->tv_usec)/((double)1000000)));
	throughput = ((double)file_data)/delta;

	if ( throughput > (FACTOR1000 * FACTOR1000 * FACTOR1000 * FACTOR1000) ){
		sprintf(info,"%.02f TB/s", throughput / (FACTOR1000 * FACTOR1000 * FACTOR1000 * FACTOR1000));
	} else if ( throughput > (FACTOR1000 * FACTOR1000 * FACTOR1000) ){
		sprintf(info,"%.02f GB/s", throughput / (FACTOR1000 * FACTOR1000 * FACTOR1000));
	} else if ( throughput > (FACTOR1000 * FACTOR1000) ){
		sprintf(info,"%.02f MB/s", throughput / (FACTOR1000 * FACTOR1000));
	} else if ( throughput > (FACTOR1000) ){
		sprintf(info,"%.02f KB/s", throughput / (FACTOR1000));
	} else {
		sprintf(info,"%.02f Bytes/s", throughput );
	}

	if ( wire_data != file_data ){
		sprintf(&info[strlen(info)]," Compression: %.02f%%",(1.0 - ((double)wire_data) / ((double)file_data)) * 100.0 );

	}


	return(info);
}
#undef now

char *display_percent(u_int64_t total, u_int64_t done){
	static char info[1024];
	sprintf(info,"%.02f%%", (((double)done)/((double)total)) * 100.0);
	return(info);
}

char *displaytime(struct timeval *start, struct timeval *now){
	static char info[1024];
	double delta2;
	double delta;
	int hours;
	int mins;
	double secs;
	double _start;
	double _now;





	_now   = (((double) now->tv_sec) +   (((double)now->tv_usec)/((double)1000000)));
	_start = (((double) start->tv_sec) + (((double)start->tv_usec)/((double)1000000)));

	delta = _now - _start;




	printf("",delta);


	hours = (int)(delta / ((double) 3600));
	delta -= ((double)hours) * ((double)3600);
	mins = (int)(delta / ((double) 60));
	delta -= ((double)mins) * ((double)3600);
	secs = delta / ((double) 60);


	sprintf(info,"");

	if ( hours ) sprintf(&info[strlen(info)],"%d hours, ",hours);


	if ( mins ) sprintf(&info[strlen(info)],"%d minutes, ",hours);

	sprintf(&info[strlen(info)],"%.04f seconds. ",hours);

	return(info);

}

char *displaysize(u_int64_t sizer){
	static char info[1024];
	double throughput;

	throughput = (double) sizer;
	if ( throughput > (FACTOR1000 * FACTOR1000 * FACTOR1000 * FACTOR1000) ){
		sprintf(info,"%.02f TB", throughput / (FACTOR1000 * FACTOR1000 * FACTOR1000 * FACTOR1000));
	} else if ( throughput > (FACTOR1000 * FACTOR1000 * FACTOR1000) ){
		sprintf(info,"%.02f GB", throughput / (FACTOR1000 * FACTOR1000 * FACTOR1000));
	} else if ( throughput > (FACTOR1000 * FACTOR1000) ){
		sprintf(info,"%.02f MB", throughput / (FACTOR1000 * FACTOR1000));
	} else if ( throughput > (FACTOR1000) ){
		sprintf(info,"%.02f KB", throughput / (FACTOR1000));
	} else {
		sprintf(info,"%.02f Bytes", throughput );
	}
	return(info);
}

int getsockline(int sock, unsigned char *buf){
	int received;
	unsigned char c;
	received = 0;
	buf[received] = 0x00;
	while ( recv(sock,&c,1,0) == 1 ){
		if ( c == '\n' ) break;
		if ( c == '\r' ) continue;
		buf[received++] = c;
		buf[received] = 0x00;
	}
	return(received);
}


int main(int argc, char **argv){
	char the_throughput[256];
	char buf[1024];
	int sock;
	int dsock;
	struct timeval start;
	struct timeval end;
	u_int64_t transfer_size;
	struct addrinfo *res, *rp;
	struct addrinfo hints;
	int result;
	int has_mode_z;
	u_int64_t size;
	u_int64_t filesize;
	unsigned short port;
	struct sockaddr *remote;
	int remote_len;
	int local;
	u_int64_t file_data;
	u_int64_t wire_data;
	u_int64_t file_offset;

	if ( argc < 6 ){
		printf("Usage: %s <username> <password> <server> <remote-file> <local-file> [nocompress]\n",argv[0]);
		exit(2);
	}

	has_mode_z = 0;

	the_throughput[0] = 0x00;

	if ( strstr(argv[0],"put") )
		mode_put = 1;
	else
		mode_put = 0;

	if ( mode_put ){
		struct stat st;
		if ( stat(LOCAL_FILE,&st) == 0 && st.st_size ){
			printf("File [%s] has %ju Bytes\n",LOCAL_FILE,(u_int64_t)st.st_size);
			filesize = (u_int64_t)st.st_size;
		} else {
			printf("%s does not exist or has 0 size!\n",LOCAL_FILE);
			exit(2);
		}

	} else {
		struct stat st;
		if ( stat(LOCAL_FILE,&st) == 0 ){
			file_offset = (u_int64_t)st.st_size;
			printf("File [%s] has %ju Bytes, transfer will append from offset\n",LOCAL_FILE,file_offset);
		} else {
			file_offset = (u_int64_t)0;
		}
	}

	bzero(&hints,sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	result = getaddrinfo(SERVER,"21",&hints,&res);
	if ( result != 0 ){
		printf("Cannot resolve hostname '%s' (%s)\n",SERVER,gai_strerror(result));
		exit(2);
	}
	for ( rp = res; rp != NULL; rp = rp->ai_next ){
		int oldflags;
		int newflags;
		struct pollfd polly;
		if ( rp->ai_family != AF_INET && rp->ai_family != AF_INET6 ) continue;

		if ( rp->ai_family == AF_INET ){
			struct sockaddr_in *me;
			me = (struct sockaddr_in *)rp->ai_addr;
			inet_ntop(AF_INET,&me->sin_addr.s_addr,print_ip,128);
		} else if ( rp->ai_family == AF_INET6 ){
			struct sockaddr_in6 *me;
			me = (struct sockaddr_in6 *)rp->ai_addr;
			inet_ntop(AF_INET6,&me->sin6_addr,print_ip,128);
		}
		printf("Trying %s...",print_ip);
		sock = socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol); 
		if ( sock < 0 ){
			printf("FAIL!\nCannot create socket! (%s)\n",strerror(errno));
			continue;
		}	
		if ( ( oldflags = fcntl(sock,F_GETFL)) == -1 ){
			close(sock);
			printf("FAIL!\nCannot fcntl-get (NON-BLOCKING) socket! (%s)\n",strerror(errno));
			continue;
		}
		newflags = oldflags | O_NONBLOCK;

		if ( fcntl(sock,F_SETFL,newflags) == -1 ){
			close(sock);
			printf("FAIL!\nCannot fcntl-set (NON-BLOCKING) socket! (%s)\n",strerror(errno));
			continue;
		}
		
		if ( connect(sock,rp->ai_addr,rp->ai_addrlen) < 0 && errno != EINPROGRESS ){
			close(sock);
			printf("FAIL!\nCannot connect socket! (%s)\n",strerror(errno));
			continue;
		}
		fflush(stdout);

		polly.fd = sock;
		polly.events = POLLIN | POLLOUT | POLLHUP | POLLERR;
		polly.revents = 0;

		if ( (result =  poll(&polly,1,10000)) < 0 ){
			close(sock);
			printf("FAIL!\nCannot poll socket! (%s)\n",strerror(errno));
			continue;
		}
		if ( result == 0 ){
			printf("TIMEOUT!\n");
			close(sock);
			continue;
		}
		if ( result == 1 ){
			if ( (polly.revents & POLLIN) || (polly.revents & POLLOUT) ){
				if ( fcntl(sock,F_SETFL,oldflags) == -1 ){
					close(sock);
					printf("FAIL!\nCannot fcntl-set (BLOCKING) socket! (%s)\n",strerror(errno));
					continue;
				}
				printf("OK!\n");
				break;
			} else {
				close(sock);
				printf("FAILED!\n");
				continue;
			}
		} 
		close(sock);
	}
	if ( rp == NULL ){
		printf("\nFATAL!!!\nCould not connect!\n");
		exit(0);
	}
	while ( getsockline(sock,buf) > 0 ){
		printf(">> [%s]\n",buf);
		result = atoi(buf);
		if ( buf[3] != '-' ) break;
	}
	if ( result < 200 || result > 299 ){
		close(sock);
		printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
		exit(3);
	}
	sprintf(buf,"USER %s",USERNAME);
	printf(">> [%s]\n",buf); strcat(buf,"\r\n");
	send(sock,buf,strlen(buf),0);
	while ( getsockline(sock,buf) > 0 ){
		printf(">> [%s]\n",buf);
		result = atoi(buf);
		if ( buf[3] != '-' ) break;
	}
	if ( result >= 300 && result < 400 ){
		sprintf(buf,"PASS %s",PASSWORD);
		printf(">> [%s]\n",buf); strcat(buf,"\r\n");
		send(sock,buf,strlen(buf),0);
		while ( getsockline(sock,buf) > 0 ){
			printf(">> [%s]\n",buf);
			result = atoi(buf);
			if ( buf[3] != '-' ) break;
		}
	}
	if ( result < 200 || result > 299 ){
		close(sock);
		printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
		exit(3);
	}
	if ( argc < 7 ){
		sprintf(buf,"MODE Z");
		printf(">> [%s]\n",buf); strcat(buf,"\r\n");
		send(sock,buf,strlen(buf),0);
		while ( getsockline(sock,buf) > 0 ){
			printf(">> [%s]\n",buf);
			result = atoi(buf);
			if ( buf[3] != '-' ) break;
		}
		if ( result >= 200 && result < 300 ) 
			has_mode_z = 1;
		else
			has_mode_z = 0;
	}

	printf("** Use Compression: [%s]\n",has_mode_z ? "YES" : "NO");
	sprintf(buf,"TYPE I");
	printf(">> [%s]\n",buf); strcat(buf,"\r\n");
	send(sock,buf,strlen(buf),0);
	while ( getsockline(sock,buf) > 0 ){
		printf(">> [%s]\n",buf);
		result = atoi(buf);
		if ( buf[3] != '-' ) break;
	}
	if ( result < 200 || result > 299 ){
		close(sock);
		printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
		exit(3);
	}
	if ( mode_put == 0 ){
		sprintf(buf,"SIZE %s",REMOTE_FILE);
		printf(">> [%s]\n",buf); strcat(buf,"\r\n");
		send(sock,buf,strlen(buf),0);
		while ( getsockline(sock,buf) > 0 ){
			printf(">> [%s]\n",buf);
			result = atoi(buf);
			if ( buf[3] != '-' ) break;
		}
		if ( result < 200 || result > 299 ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		if ( sscanf(buf,"%d %ju",&result,&filesize) != 2 ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		printf("** Will receive %ju Bytes\n",filesize);
	}
	if ( rp->ai_family == AF_INET6 ){
		static struct sockaddr_in6 remoty;
		char *ptr;
		int lines;
		sprintf(buf,"EPSV");
		printf(">> [%s]\n",buf); strcat(buf,"\r\n");
		send(sock,buf,strlen(buf),0);
		while ( getsockline(sock,buf) > 0 ){
			printf(">> [%s]\n",buf);
			result = atoi(buf);
			if ( buf[3] != '-' ) break;
		}
		if ( result < 200 || result > 299 ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		lines = 0;
		ptr = buf;
		while ( *ptr ){
			if ( *ptr == '|' ) lines++;
			ptr++;
			if ( lines == 3 ) break;
		}
		if ( lines != 3 ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		port = (unsigned short)atoi(ptr);
		printf("** IPv6: Using Port: %d\n",port);
		bzero(&remoty,sizeof(struct sockaddr_in6));
		remoty.sin6_family = AF_INET6;
		remoty.sin6_port   = htons(port);
		inet_pton(AF_INET6,print_ip,&remoty.sin6_addr);
		remote = (struct sockaddr *)&remoty;
		remote_len = sizeof(struct sockaddr_in6);
		if ( (dsock = socket(AF_INET6,SOCK_STREAM,0)) < 0 ){
			close(sock);
			printf("[%s:%d] Cannot create data-socket (%s)\n",__FILE__,__LINE__,strerror(errno));
			exit(3);
		}
	} else {
		static struct sockaddr_in remoty;
		unsigned short low;
		unsigned short high;
		char *ptr;
		int lines;
		sprintf(buf,"PASV");
		printf(">> [%s]\n",buf); strcat(buf,"\r\n");
		send(sock,buf,strlen(buf),0);
		while ( getsockline(sock,buf) > 0 ){
			printf(">> [%s]\n",buf);
			result = atoi(buf);
			if ( buf[3] != '-' ) break;
		}
		if ( result < 200 || result > 299 ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		lines = 0;
		ptr = buf;
		while ( *ptr ){
			if ( *ptr == ',' ) lines++;
			ptr++;
			if ( lines == 4 ) break;
		}
		if ( lines != 4 ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		high = (unsigned short)atoi(ptr);
		ptr = strstr(ptr,",");
		if ( ptr == NULL ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		ptr++;
		low = (unsigned short)atoi(ptr);
		port = high * 256 + low;
		printf("** IPv4: Using Port: %d\n",port);
		bzero(&remoty,sizeof(struct sockaddr_in));
		remoty.sin_family = AF_INET;
		remoty.sin_port   = htons(port);
		remoty.sin_addr.s_addr = inet_addr(print_ip);
		remote = (struct sockaddr *)&remoty;
		remote_len = sizeof(struct sockaddr_in);
		if ( (dsock = socket(AF_INET,SOCK_STREAM,0)) < 0 ){
			close(sock);
			printf("[%s:%d] Cannot create data-socket (%s)\n",__FILE__,__LINE__,strerror(errno));
			exit(3);
		}
	}
	if ( connect(dsock,remote,remote_len) < 0 ){
			close(sock);
			printf("[%s:%d] Cannot connect data-socket (%s)\n",__FILE__,__LINE__,strerror(errno));
			exit(3);
	}
	printf("*** Data socket connected\n");
	if ( mode_put ){
		local = open(LOCAL_FILE,O_RDONLY);
		if ( local < 0 ){
			printf("FATAL!!! Cannot open '%s' for reading (%s)\n",LOCAL_FILE,strerror(errno));
			exit(3);
		}
		sprintf(buf,"STOR %s",REMOTE_FILE);
		printf(">> [%s]\n",buf); strcat(buf,"\r\n");
		send(sock,buf,strlen(buf),0);
		while ( getsockline(sock,buf) > 0 ){
			printf(">> [%s]\n",buf);
			result = atoi(buf);
			if ( buf[3] != '-' ) break;
		}
		if ( result < 100 || result > 199 ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		wire_data = (u_int64_t)0;
		file_data = (u_int64_t)0;
#define ZLIB_CHUNK2 128000
#define ZLIB_CHUNKSIZE 256000
		gettimeofday(&start,NULL);
		if ( has_mode_z ){
			char oldusage[128];
			char newusage[128];
			int buf_read;
			int real_sent;
			int nsent;
			int cnt;
			int have;
			int flush;
			char buf_in[ZLIB_CHUNK2];
			char buf_out[ZLIB_CHUNK2];
			off_t lseeker;
			z_stream strm;

			strm.zalloc = Z_NULL;
			strm.zfree  = Z_NULL;
			strm.opaque = Z_NULL;
			result = deflateInit(&strm,9);
			if ( result != Z_OK ){
				close(sock);
				printf("[%s:%d] FATAL ZLIB init failed!\n",__FILE__,__LINE__);
				exit(3);
			}
			while ( (cnt = read(local,buf_in,ZLIB_CHUNK2)) > 0 ){
				strm.next_in = buf_in;
				strm.avail_in = cnt;
				file_data += cnt;
				if ( (u_int64_t)(lseeker = lseek(local,0,SEEK_CUR)) == filesize ){
					flush = Z_FINISH;
				} else {
					flush = Z_NO_FLUSH;
				}
				do {
					strm.avail_out = ZLIB_CHUNK2;
					strm.next_out  = buf_out;
					result = deflate(&strm,flush);
					have = ZLIB_CHUNK2 - strm.avail_out;
					if ( result != Z_OK && result != Z_STREAM_END ){
						close(sock);
						printf("[%s:%d] FATAL ZLIB inflate failed!\n",__FILE__,__LINE__);
						exit(3);
					}
					wire_data += have;
					real_sent = 0;
					while ( real_sent != have ){
						nsent = send(dsock,&buf_out[real_sent],have - real_sent,0);
						if ( nsent <= 0 ){
							close(sock);
							printf("[%s:%d] Local write error (%s)!\n",__FILE__,__LINE__,strerror(errno));
							exit(3);
						}
						real_sent += nsent;
					}
					strcpy(newusage,display_percent(filesize,file_data));
					if ( strcmp(oldusage,newusage) != 0 ){
						strcpy(oldusage,newusage);
						printf("Data sent: %s (%s)\r",oldusage ,getthroughput(wire_data,file_data,&start));
						fflush(stdout);
					}
				} while(strm.avail_out == 0 );
				if ( result == Z_STREAM_END )  break;
			}
			deflateEnd(&strm);
			strcpy(the_throughput,getthroughput(wire_data,file_data,&start));
			close(local);
			close(dsock);
		} else {
			char oldusage[128];
			char newusage[128];
			int buf_read;
			int real_sent;
			int nsent;
			char buf[BFSIZE];
			strcpy(oldusage,"");
			while ( (buf_read = read(local,buf,BFSIZE)) > 0 ){
				wire_data += (u_int64_t) buf_read;
				file_data += (u_int64_t) buf_read;
				real_sent = 0;
				while ( real_sent != buf_read ){
					nsent = send(dsock,&buf[real_sent],buf_read - real_sent,0);
					if ( nsent <= 0 ){
						close(sock);
						printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
						exit(3);
					}
					real_sent += nsent;
				}
				strcpy(newusage,display_percent(filesize,file_data));
				if ( strcmp(oldusage,newusage) != 0 ){
					strcpy(oldusage,newusage);
					printf("Data sent: %s (%s)\r",oldusage ,getthroughput(wire_data,file_data,&start));
					fflush(stdout);
				}
					
			}
			strcpy(the_throughput,getthroughput(wire_data,file_data,&start));
			close(local);
			close(dsock);

		}
	} else {
		if ( file_offset ){
			local = open(LOCAL_FILE,O_APPEND | O_WRONLY);
			if ( local < 0 ){
				printf("FATAL!!! Cannot open '%s' for appending (%s)\n",LOCAL_FILE,strerror(errno));
				exit(3);
			}
			sprintf(buf,"REST %ju",file_offset);
			printf(">> [%s]\n",buf); strcat(buf,"\r\n");
			send(sock,buf,strlen(buf),0);
			while ( getsockline(sock,buf) > 0 ){
				printf(">> [%s]\n",buf);
				result = atoi(buf);
				if ( buf[3] != '-' ) break;
			}
			if ( result < 300 || result > 399 ){
				close(sock);
				printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
				exit(3);
			}
		} else {
			unlink(LOCAL_FILE);
			local = open(LOCAL_FILE,O_WRONLY | O_CREAT,0644);
			if ( local < 0 ){
				printf("FATAL!!! Cannot open '%s' for writing (%s)\n",LOCAL_FILE,strerror(errno));
				exit(3);
			}
		}
		sprintf(buf,"RETR %s",REMOTE_FILE);
		printf(">> [%s]\n",buf); strcat(buf,"\r\n");
		send(sock,buf,strlen(buf),0);
		while ( getsockline(sock,buf) > 0 ){
			printf(">> [%s]\n",buf);
			result = atoi(buf);
			if ( buf[3] != '-' ) break;
		}
		if ( result < 100 || result > 199 ){
			close(sock);
			printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
			exit(3);
		}
		gettimeofday(&start,NULL);
		if ( has_mode_z ){
			char oldusage[128];
			char newusage[128];
			int buf_read;
			int real_sent;
			int nsent;
			int cnt;
			int have;
			char buf_in[ZLIB_CHUNK2];
			char buf_out[ZLIB_CHUNKSIZE];
			z_stream strm;

			strm.zalloc = Z_NULL;
			strm.zfree  = Z_NULL;
			strm.opaque = Z_NULL;
			result = inflateInit(&strm);
			if ( result != Z_OK ){
				close(sock);
				printf("[%s:%d] FATAL ZLIB init failed!\n",__FILE__,__LINE__);
				exit(3);
			}
			while ( 1 ){
				int sock_received;
				sock_received = 0;
				while ( sock_received < ZLIB_CHUNK2 && (cnt = recv(dsock,&buf_in[sock_received],ZLIB_CHUNK2 - sock_received,0)) > 0 ){
					sock_received += cnt;
				}
				if ( sock_received == 0 ) break;
				if ( sock_received != ZLIB_CHUNK2 ){
					fprintf(stderr,"\nSOCK_RECEIVED: %d\n",sock_received);
				}
				cnt = sock_received;
				strm.next_in = buf_in;
				strm.avail_in = cnt;
				wire_data += cnt;
				do {
					strm.avail_out = ZLIB_CHUNKSIZE;
					strm.next_out  = buf_out;
					result = inflate(&strm,Z_SYNC_FLUSH);
					have = ZLIB_CHUNKSIZE - strm.avail_out;
					if ( result != Z_OK && result != Z_STREAM_END && result != Z_BUF_ERROR){
						close(sock);
						printf("[%s:%d] FATAL ZLIB inflate failed! (CNT: %d), RESULT: %d\n",__FILE__,__LINE__,cnt,result);
						exit(3);
					}
					file_data += have;
					real_sent = 0;
					while ( real_sent != have ){
						nsent = write(local,&buf_out[real_sent],have - real_sent);
						if ( nsent <= 0 ){
							close(sock);
							printf("[%s:%d] Local write error (%s)!\n",__FILE__,__LINE__,strerror(errno));
							exit(3);
						}
						real_sent += nsent;
					}
					strcpy(newusage,display_percent(filesize,file_data));
					if ( strcmp(oldusage,newusage) != 0 ){
						strcpy(oldusage,newusage);
						printf("Data recvd: %s (%s)\r",oldusage ,getthroughput(wire_data,file_data,&start));
						fflush(stdout);
					}
				} while(strm.avail_out == 0 );
				if ( result == Z_STREAM_END )  break;
			}
			inflateEnd(&strm);
			strcpy(the_throughput,getthroughput(wire_data,file_data,&start));
			close(local);
			close(dsock);
		} else {
			char oldusage[128];
			char newusage[128];
			int buf_read;
			int real_sent;
			int nsent;
			char buf[BFSIZE];
			strcpy(oldusage,"");
			while ( (buf_read = recv(dsock,buf,BFSIZE,0)) > 0 ){
				wire_data += (u_int64_t) buf_read;
				file_data += (u_int64_t) buf_read;
				real_sent = 0;
				while ( real_sent != buf_read ){
					nsent = write(local,&buf[real_sent],buf_read - real_sent);
					if ( nsent <= 0 ){
						close(sock);
						printf("[%s:%d] Local write error (%s)!\n",__FILE__,__LINE__,strerror(errno));
						exit(3);
					}
					real_sent += nsent;
				}
				strcpy(newusage,display_percent(filesize,file_data));
				if ( strcmp(oldusage,newusage) != 0 ){
					strcpy(oldusage,newusage);
					printf("Data recvd: %s (%s)\r",oldusage ,getthroughput(wire_data,file_data,&start));
					fflush(stdout);
				}
			}
			strcpy(the_throughput,getthroughput(wire_data,file_data,&start));
			close(local);
			close(dsock);
		}
	}	
	gettimeofday(&end,NULL);
	printf("\n");
	while ( getsockline(sock,buf) > 0 ){
		printf(">> [%s]\n",buf);
		result = atoi(buf);
		if ( buf[3] != '-' ) break;
	}
	if ( result < 200 || result > 299 ){
		close(sock);
		printf("[%s:%d] Protocol error!\n",__FILE__,__LINE__);
		exit(3);
	}
	printf("----------------------------------------------------------------------\n");
	printf("Mode:          %s\n",mode_put ? "PUT" : "GET");
	printf("Compression:   %s\n",has_mode_z ? "YES" : "NO");
	printf("Local-File:    %s\n",LOCAL_FILE);
	printf("Remte-File:    %s\n",REMOTE_FILE);
	printf("Total time:    %s\n",displaytime(&start,&end));
	printf("Wire-Transfer: %s\n",displaysize(wire_data));
	printf("File-Transfer: %s\n",displaysize(file_data));
	printf("Throughput:    %s\n",the_throughput);
	printf("----------------------------------------------------------------------\n");

	exit(0);
}


