/*
 * XXXXX This is a very crude ftp client which acts like term's tupload
 */

#include <stdio.h>
#include <sys/socket.h>
#include <stdarg.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>

int control_s, data_s, s, addr1, addr2, addr3, addr4;
u_int32_t our_addr;

int debug;

#define DEBUG(x) { if (debug) printf x; fflush(stdout); }

char *alarm_message = "umm... I forget";

#define TIMEOUT(x) { alarm_message = x; alarm(10); }
#define UNTIMEOUT() { alarm(0); }

/*
 * Send a command
 */
void
SEND(char *format, ...)
{
	va_list args;
	char string[1024];
	int n;
	
	va_start(args, format);
	
	n = vsprintf(string, format, args);
	
	DEBUG(("Sending: %s", string));
	
	if (write(control_s, string, n) != n) {
		perror("Error: write()");
		exit(1);
	}
}

/*
 * Expect a reply
 * 
 * If there's a choice of replies to expect, the first argument
 * is the number or arguments that follow
 * 
 * Returns the received reply
 */
int
EXPECT(int num, ...)
{
	va_list args;
	char buff[256], *bptr;
	int reply, n, r, expecting;
	char buff2[256];
	static int last_reply;
	
	do {
		TIMEOUT("reading from control connection");
		if ((n = read(control_s, buff, 255)) <= 0) {
			perror("Error: read()");
			exit(1);
		}
		UNTIMEOUT();
		
		buff[n] = 0;
		DEBUG(("Read: %s", buff));
		r = 0;
		while (r < n) {
			if (sscanf(&buff[r], "%d %256s", &reply, buff2) != 2) {
				fprintf(stderr,"Error: bad reply: %s\n", &buff[r]);
				exit(1);
			}
			if (reply != last_reply || reply == num)
			   goto cont_1;
			r += 6+strlen(buff2);
		}
	} while (reply == last_reply);
cont_1:
	if (num >= 100) {
		DEBUG(("Expecting: %d\n", num));
		if (num == reply) {
			last_reply = reply;
			return reply;
		}
	} else {
		va_start(args, num);
		while (num--) {
			expecting = va_arg(args, int);
			DEBUG(("Expecting: %d - ", expecting));
			if (expecting == reply) {
				DEBUG(("found\n"));
				va_end(args);
				last_reply = reply;
				return reply;
			}
			DEBUG(("not found\n"));
		}
		va_end(args);
	}
	
	/* Make the \r|\n 0 */
	for (bptr = buff; *bptr != '\r', *bptr != '\n'; bptr++)
	   ;
	*bptr = 0;
	fprintf(stderr, "Error: got: %s\n", buff);
	exit(1);
}

void
getouraddr()
{
	char buff[256];
	struct hostent *he;
	struct in_addr our_inaddr;
	
	if (gethostname(buff,256) < 0)
	   return;
	
	if ((he = gethostbyname(buff)) == NULL)
	   return;
	
	our_inaddr = *(struct in_addr *)he->h_addr;
	
	our_addr = ntohl(our_inaddr.s_addr);
	
	addr1 = our_addr >> 24;
	addr2 = (our_addr >> 16) & 0xff;
	addr3 = (our_addr >> 8) & 0xff;
	addr4 = our_addr & 0xff;
}

void
usage()
{
	fprintf(stderr,"Usage: supload [-r|-d] FILE [FILE ...]\n");
	fprintf(stderr,"       -r    Act as \"remote\" (with respect to slirp)\n");
	fprintf(stderr,"             uploading to the slirp host (needs slirp.ftpd)\n");
	fprintf(stderr,"       -d    Show debugging info\n");
}


void
alrm(spam)
	int spam;
{
	fprintf(stderr,"Timed out %s, exiting\n", alarm_message);
	exit(1);
}

int
main(argc, argv)
	int argc;
	char **argv;
{
	FILE *infile;
	char buff[256], *bptr;
	int slirp_remote = 0;
	int port = 0;
	int file, n, str_len;
	char buff2[1024];
	struct sockaddr_in addr;
	int addrlen = sizeof(addr);
	
	if (argc <= 1) {
		usage();
		exit(0);
	}
	
	getouraddr();
	
	signal(SIGALRM, alrm);
	
	while (--argc > 0) {
		
		argv++;
		
		if (**argv != '-') {
			/* End of options, filenames etc. are next */
			break; /* while */
		}
		
		(*argv)++;
		
		switch(**argv) {
		 case 'r':
			slirp_remote = 1;
			break;
		 case 'd':
			debug++;
			break;
		 default:
			fprintf(stderr,"Unknown option: -%c\n", **argv);
			usage();
			exit(1);
		}
	}
	
	if (!argc) {
		fprintf(stderr,"nothing to send\n");
		usage();
		exit(0);
	}
	
	addr.sin_family = AF_INET;
	if (!slirp_remote) {
		/*
		 * Find the port that's redirected for ftp
		 */
		if ((bptr = (char *)getenv("HOME")) == NULL) {
			fprintf(stderr, "Error: can't find your HOME\n");
			exit(1);
		}
		strcpy(buff, bptr);
		strcat(buff, "/.slirp_start");
		
		if ((infile = fopen(buff, "r")) == NULL) {
			fprintf(stderr, "Error: can't open your ~/.slirp_start file.\n");
			fprintf(stderr, "Make sure you put \"log start\" into your ~/.slirprc file\n");
			exit(1);
		}
		
		while (fgets(buff, 256, infile) != NULL) {
			if (sscanf(buff, "Redirecting TCP port %d to %*d.%*d.%*d.%*d:21", &port) == 1)
			   break;
		}
		
		if (!port) {
			fprintf(stderr,"Error: could not determine local port\n");
			fprintf(stderr,"Make sure you put \"redir ftp\" in your ~/.slirprc file");
			exit(1);
		}
		
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	} else {
		addr.sin_port = htons(21);
		addr.sin_addr.s_addr = inet_addr("10.0.2.1");
	}
	
	if ((control_s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Error: socket()");
		exit(1);
	}
	
	printf("Connecting to %s, port %d ... ", (char *)inet_ntoa(addr.sin_addr), port);
	
	TIMEOUT("connecting to the remote ftp daemon");
	if (connect(control_s, (struct sockaddr *)&addr, addrlen) < 0) {
		perror("Error: connect()");
		exit(1);
	}
	UNTIMEOUT();
	
	printf("\n");
	
	EXPECT(220);
	
	SEND("USER anonymous\r\n");
	if (EXPECT(2, 331, 230) == 331) {
		SEND("PASS %s@\r\n", getenv("USER"));
		EXPECT(230);
	}
	
	SEND("CWD incoming\r\n");
	EXPECT(250);
	
	SEND("TYPE I\r\n");
	EXPECT(200);
	
	while (argc-- > 0) {
		
		file = open(*argv, O_RDONLY);
		if (file < 0) {
			fprintf(stderr,"Warning: can't open %s\n", *argv);
			continue;
		}
		
		addr.sin_family = AF_INET;
		addr.sin_addr.s_addr = INADDR_ANY;
		addr.sin_port = 0;
		if (((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) ||
		    (bind(s, (struct sockaddr *)&addr, addrlen) < 0) ||
		    (getsockname(s, (struct sockaddr *)&addr, &addrlen) < 0) ||
		    (listen(s, 1) < 0)) {
			perror("Error: socket/bind/getsockname/listen");
			continue;
		}
		
		SEND("PORT %d,%d,%d,%d,%d,%d\r\n", addr1, addr2, addr3, addr4,
		     ntohs(addr.sin_port) >> 8, ntohs(addr.sin_port) & 0xff);
		EXPECT(200);
		
		str_len = strlen(*argv);
		for (bptr = *argv + str_len; str_len > 0 && *bptr != '/'; str_len--, bptr--)
		   ;
		
		if (*bptr == '/')
		   bptr++;
		
		SEND("STOR %s\r\n", bptr);
		EXPECT(150);
		
		/* We could block forever here */
		TIMEOUT("waiting for connection from ftp daemon");
		data_s = accept(s, (struct sockaddr *)&addr, &addrlen);
		UNTIMEOUT();
		
		printf("Sending %s ... ", bptr);
		fflush(stdout);
		
		while((n = read(file, buff2, 1024)) > 0)
		   write(data_s, buff2, n);
		
		close(file);
		close(data_s);
		
		printf("done\n");
		
		/* XXXXX Need to drain the connection first */
		
		EXPECT(226);
		
		argv++;
	}
	
	exit(0);
}

