#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <glob.h>
#include <pwd.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "sftp.h"

sftp_channel *channel[MAXCHANNEL] = {NULL};

static int
do_chdir(char *dir) {
	char *str;
	glob_t globbuf;
	int globbed = 0;

	if (dir == NULL) {
		dir = getenv("HOME");
		if (dir == NULL) {
			str = "$HOME does not exist";
			send_message(1, _message(ERROR, str, strlen(str)+1));
			return -1;
		}
	}
	if (dir[0] == '~') {
#ifdef GLOB_TILDE
		if (glob(dir, GLOB_TILDE, NULL, &globbuf) == 0) {
			globbed = 1;
			dir = globbuf.gl_pathv[0];
		}
#else
		struct passwd *pwd;
		str = strchr(dir, '/');
		if (str != NULL)
			*str++ = 0;
		if (strcmp(dir, "~") == 0)
			pwd = getpwuid(getuid());
		else
			pwd = getpwnam(dir+1);
		if (pwd == NULL) {
			str = "invalid directory";
			send_message(1, _message(ERROR, str, strlen(str)+1));
			return -1;
		}
		if (str != NULL) {
			if (chdir(pwd->pw_dir)) {
				str = "No such directory";
				send_message(1, _message(ERROR,
							 str, strlen(str)+1));
				return -1;
			}
			dir = str;
		}
		else
			dir = pwd->pw_dir;
#endif
	}
	if (chdir(dir)) {
		str = "No such directory";
		send_message(1, _message(ERROR, str, strlen(str)+1));
		return -1;
	}
	send_message(1, _message(SUCCESS, NULL, 0));
	if (globbed)
		globfree(&globbuf);
	return 0;
}

static int
do_getdir(char *dir) {
	char buf[256];
	if (getcwd(buf, sizeof(buf)) != NULL) {
		send_message(1, _message(TELLDIR, buf, strlen(buf)+1));
		return 0;
	}
	else {
		char *str = "Failed to get current directory";
		send_message(1, _message(TELLDIR, str, strlen(str)+1));
		return -1;
	}
}

/* Returns the number of channels that still have data to send */
static int
do_output() {
	sftp_channel_t c;
	int total = 0, ret;
	for (c=1; c<MAXCHANNEL; c++)
		if (channel[c] && channel[c]->waiting_to_send) {
			ret = channel[c]->send_data(c);
			if (ret < 0)
				continue;
			if (channel[c]->waiting_to_send)
				total++;
		}
	return total;
}

int
main() {
	message m;
	sftp_channel_t c;
	int nwaiting, ret;

	fwrite("sftp", 1, strlen("sftp") + 1, stdout);
	fflush(stdout);
	while (1) {
		nwaiting = do_output();
		if (nwaiting > 0) {
			ret = query_message(0, &m);
			if (ret < 0)
				continue;
		}
		else {
			ret = recv_message(0, &m);
			if (ret < 0)
				exit(-1);
		}
		if (m.channel != 0) {
				if (channel[m.channel] == NULL)
					continue;
				channel[m.channel]->accept_data(m.channel, m);
		}
		else
			switch (m.command) {
				case REQUEST:
					c = new_sendfile_channel(m.data);
					break;
				case SENDFILE:
					c = new_recvfile_channel(*(sftp_channel_t *)m.data);
					break;
				case EXEC:
					c = new_exec_channel(m.data, m.len);
					break;
				case CHDIR:
					do_chdir(m.data);
					break;
				case GETDIR:
					do_getdir(m.data);
					break;
				case CLOSE:
					exit(-1);
			}
		if (m.data)
			free(m.data);
	}
}
