/****************************************************************/
/* client.c                                                     */
/* Justin Hartman                                               */
/* EECS 338 - Spring 2003                                       */
/* Assignment 6                                                 */
/****************************************************************/

/****************************************************************/
/* Written in Microsoft Visual C++ .NET                         */
/* Compiled and executed on CWRU's EECS department              */
/* lab's (Olin 404.5) SUN boxes using SSH and                   */
/* secure FTP                                                   */
/****************************************************************/

#include "client.h"

int main(int argc, char *argv[]) {
    char			line_in[MAX_INPUT_SIZE+1];
	unsigned int	bytes;

    line_in[0] = '\0';

	/* Enforce the correct usage */
	if (argc != 2) {
		printf("\nUsage: \"client server-name-or-ip-address\"\n");
		exit(1);
	}

    /* Print a welcome and instructions */
	printf("\nWelcome to the EECS 338 HW6 StupidFTP program client!\n");
    printf("Valid commands are (case sensitive):\n");
    printf("\tput\t\tPlaces a local file in the remote directory\n");
    printf("\tget\t\tPlaces a remote file in the local directory\n");
    printf("\tls\t\tLists the contents of the remote directory\n");
    printf("\t!ls\t\tLists the contents of the local directory\n");
    printf("\tquit\t\tExits the program\n");

    printf("\n> "); /* First prompt */

    while (strncmp("quit", line_in, 4) != 0) { /* As long as "quit" wasn't entered */
        fgets(line_in, MAX_INPUT_SIZE, stdin); /* Get the input line */
		line_in[strlen(line_in)-1] = '\0'; /* Replace the newline that gets copied */

        if (strncmp("quit", line_in, 4) != 0) { /* If it's not "quit" */
            if (strncmp("put", line_in, 3) == 0) {
                /* PUT command... */
				if ((bytes=put(&(line_in[4]), argv[1]))) {
					printf("put successful: %d bytes sent to server", bytes);
				} else {
					printf("put unsuccessful...");
				}
            } else if (strncmp("get", line_in, 3) == 0) {
                /* GET command... */
                if ((bytes=get(&(line_in[4]), argv[1]))) {
					printf("get successful: %d bytes retrieved from server", bytes);
				} else {
					printf("get unsuccessful... file probably does not exist");
				}
            } else if (strncmp("ls", line_in, 2) == 0) {
                /* ls command... (remote) */
				if (ls(argv[1])==0) {
					printf("Unable to retrieve remote directory listing!");
				}
            } else if (strncmp("!ls", line_in, 3) == 0) {
                /* !ls command... (local) */
                ls_local();
            } else {
                printf("Unrecognized command!");
            }
            printf("\n> "); /* Next prompt */
        }
    }

    exit(0);
}

unsigned int put(char * filename, char * server) {
	int sck;
	struct sockaddr_in serv_adr;
	struct hostent *host;
	FILE *infile;
	unsigned char databuf[FILEBUF_SIZE];
	int bytes = 0, bytesread = 0;

	/* Open the file locally */
	if ((infile = fopen(filename, "r")) == 0) {
		perror("fopen failed to open file");
		return 0;
	}
	
	/* Attempt to get the IP for the server by hostname */
	host = gethostbyname(server);
	if (host == (struct hostent *) NULL) {
		perror("gethostbyname failed");
		return 0;
	}

	/* Setup the port for the connection */
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	memcpy(&serv_adr.sin_addr, host->h_addr, host->h_length);
	serv_adr.sin_port = htons(SERVICE_PORT);

	/* Get the socket */
	if ((sck = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("error on socket()");
		return 0;
	}

	/* Connect to the server */
	if (connect(sck, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) < 0) {
		perror("error on connect()");
		return 0;
	}

	/* Write the command & filename, followed by two newlines */
	write(sck, "PUT ", 4);
	write(sck, filename, strlen(filename));
	write(sck, "\n\n", 2);

	/* Read from the file and write to the socket */
	while ((bytes = read(fileno(infile), databuf, FILEBUF_SIZE)) > 0) {
		write(sck, databuf, bytes);
		bytesread += bytes;
	}

	/* Close the file */
	fclose(infile);

	if ((bytes = read(sck, databuf, 2)) > 0) {
		/* Read the status bytes from the server, print and return accordingly */
		if ((databuf[0] == 'E') && (databuf[1] == 'R')) {
			printf("Server error on PUT!\n");
			close(sck);
			return 0;
		} else if ((databuf[0] == 'N') && (databuf[1] == 'W')) {
			printf("File created on server\n");
			close(sck);
			return bytesread;
		} else if ((databuf[0] == 'U') && (databuf[1] == 'P')) {
			printf("File updated on server\n");
			close(sck);
			return bytesread;
		}
	} else return 0;
}

unsigned int get(char * filename, char * server) {
	int sck;
	struct sockaddr_in serv_adr;
	struct hostent *host;
	FILE *outfile;
	short file_open=0;
	unsigned char databuf[FILEBUF_SIZE];
	int bytes = 0, bytesread = 0;
	
	/* Attempt to get the IP for the server by hostname */
	host = gethostbyname(server);
	if (host == (struct hostent *) NULL) {
		perror("gethostbyname failed");
		return 0;
	}

	/* Setup the port for the connection */
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	memcpy(&serv_adr.sin_addr, host->h_addr, host->h_length);
	serv_adr.sin_port = htons(SERVICE_PORT);

	/* Get the socket */
	if ((sck = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("error on socket()");
		return 0;
	}

	/* Connect to the server */
	if (connect(sck, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) < 0) {
		perror("error on connect()");
		return 0;
	}

	/* Write the command & filename, followed by two newlines */
	write(sck, "GET ", 4);
	write(sck, filename, strlen(filename));
	write(sck, "\n\n", 2);

	/* Read from the socket and write to the file */
	while ((bytes = read(sck, databuf, FILEBUF_SIZE)) > 0) {
		if (file_open == 0) {
			/* Open the file the first time we actually read data */
			if ((outfile = fopen(filename, "w")) == 0) {
				perror("fopen failed to open file");
				close(sck);
				return 0;
			}
			file_open = 1;
		}
		write(fileno(outfile), databuf, bytes);
		bytesread += bytes;
	}

	/* Close the file and socket */
	if (file_open != 0) fclose(outfile);
	close(sck);

	return bytesread;
}

unsigned int ls(char * server) {
	int sck;
	struct sockaddr_in serv_adr;
	struct hostent *host;
	unsigned char databuf[FILEBUF_SIZE];
	int bytes = 0, bytesread = 0;
	
	/* Attempt to get the IP for the server by hostname */
	host = gethostbyname(server);
	if (host == (struct hostent *) NULL) {
		perror("gethostbyname failed");
		return 0;
	}

	/* Setup the port for the connection */
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	memcpy(&serv_adr.sin_addr, host->h_addr, host->h_length);
	serv_adr.sin_port = htons(SERVICE_PORT);

	/* Get the socket */
	if ((sck = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("error on socket()");
		return 0;
	}

	/* Connect to the server */
	if (connect(sck, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) < 0) {
		perror("error on connect()");
		return 0;
	}

	/* Write the command followed by two newlines */
	write(sck, "LS\n\n", 4);

	printf("Remote directory listing:\n");

	/* Read from the socket and write to stdout */
	while ((bytes = read(sck, databuf, FILEBUF_SIZE)) > 0) {
		write(fileno(stdout), databuf, bytes);
		bytesread += bytes;
	}

	close(sck); /* Close the socket */

	return bytesread;
}

void ls_local() {
	FILE	*fcmd;
	char	buffer[PIPE_BUF];
	int		n;

	printf("Local directory listing:\n");

	/* Open the pipe to the ls command */
	if ((fcmd = popen("ls -l", "r")) == 0) {
		perror("popen error");
		return;
	}

	/* Read from the pipe and write to stdout */
	while ((n = read(fileno(fcmd), buffer, PIPE_BUF)) > 0)
		write(fileno(stdout), buffer, n);

	/* Close the pipe */
	if (pclose(fcmd) != 0) {
		printf("Non-zero return value from \"ls -l\"");
	}
}