[
  {
    "path": "README.md",
    "content": "FTP Client-Server Implementation\n===========\nSimple implementation of a file transfer program. It includes custom client and server programs that provide functionality to authenticate a user, list remote files, and retrieve remote files.\n\n### Directory layout:\n\tftp/\n\t\tclient/\n\t\t\tftclient.c\n\t\t\tftclient.h\n\t\t\tmakefile\n\t\tcommon/\n\t\t\tcommon.c\n\t\t\tcommon.h\n\t\tserver/\n\t\t\tftserve.c\n\t\t\tftserve.h\n\t\t\tmakefile\n\t\t\t.auth\n\n###Usage\nTo compile and link ftserve:\n```\n\t$ cd server/\n\t$ make\n```\n\nTo compile and link ftclient:\n```\n\t$ cd client/\n\t$ make\n```\n\nTo run ftserve:\n```\n\t$ server/ftserve PORTNO\n```\n\nTo run ftclient:\n```\n\t$ client/ftclient HOSTNAME PORTNO\n\n\tCommands:\n\t\tlist\n\t\tget <filename>\n\t\tquit\n```\n\nAvailable commands:\n```\nlist            - retrieve list of files in the current remote directory\nget <filename>  - get the specified file\nquit            - end the ftp session\n```\n\nLogging In:\n```\n\tName: anonymous\n\tPassword: [empty]\n```\n"
  },
  {
    "path": "client/ftclient.c",
    "content": "#include \"ftclient.h\"\n\t\n\nint sock_control; \n\n\n/**\n * Receive a response from server\n * Returns -1 on error, return code on success\n */\nint read_reply(){\n\tint retcode = 0;\n\tif (recv(sock_control, &retcode, sizeof retcode, 0) < 0) {\n\t\tperror(\"client: error reading message from server\\n\");\n\t\treturn -1;\n\t}\t\n\treturn ntohl(retcode);\n}\n\n\n\n/**\n * Print response message\n */\nvoid print_reply(int rc) \n{\n\tswitch (rc) {\n\t\tcase 220:\n\t\t\tprintf(\"220 Welcome, server ready.\\n\");\n\t\t\tbreak;\n\t\tcase 221:\n\t\t\tprintf(\"221 Goodbye!\\n\");\n\t\t\tbreak;\n\t\tcase 226:\n\t\t\tprintf(\"226 Closing data connection. Requested file action successful.\\n\");\n\t\t\tbreak;\n\t\tcase 550:\n\t\t\tprintf(\"550 Requested action not taken. File unavailable.\\n\");\n\t\t\tbreak;\n\t}\n\t\n}\n\n\n/**\n * Parse command in cstruct\n */ \nint ftclient_read_command(char* buf, int size, struct command *cstruct)\n{\n\tmemset(cstruct->code, 0, sizeof(cstruct->code));\n\tmemset(cstruct->arg, 0, sizeof(cstruct->arg));\n\t\n\tprintf(\"ftclient> \");\t// prompt for input\t\t\n\tfflush(stdout); \t\n\n\t// wait for user to enter a command\n\tread_input(buf, size);\n\t\t\n\tchar *arg = NULL;\n\targ = strtok (buf,\" \");\n\targ = strtok (NULL, \" \");\n\n\tif (arg != NULL){\n\t\t// store the argument if there is one\n\t\tstrncpy(cstruct->arg, arg, strlen(arg));\n\t}\n\n\t// buf = command\n\tif (strcmp(buf, \"list\") == 0) {\n\t\tstrcpy(cstruct->code, \"LIST\");\t\t\n\t}\n\telse if (strcmp(buf, \"get\") == 0) {\n\t\tstrcpy(cstruct->code, \"RETR\");\t\t\n\t}\n\telse if (strcmp(buf, \"quit\") == 0) {\n\t\tstrcpy(cstruct->code, \"QUIT\");\t\t\n\t}\n\telse {//invalid\n\t\treturn -1;\n\t}\n\n\t// store code in beginning of buffer\n\tmemset(buf, 0, 400);\n\tstrcpy(buf, cstruct->code);\n\n\t// if there's an arg, append it to the buffer\n\tif (arg != NULL) {\n\t\tstrcat(buf, \" \");\n\t\tstrncat(buf, cstruct->arg, strlen(cstruct->arg));\n\t}\n\t\n\treturn 0;\n}\n\n\n\n/**\n * Do get <filename> command \n */\nint ftclient_get(int data_sock, int sock_control, char* arg)\n{\n    char data[MAXSIZE];\n    int size;\n    FILE* fd = fopen(arg, \"w\");\n    \n    while ((size = recv(data_sock, data, MAXSIZE, 0)) > 0) {\n        fwrite(data, 1, size, fd);\n    }\n\n    if (size < 0) {\n        perror(\"error\\n\");\n    }\n\n    fclose(fd);\n    return 0;\n}\n\n\n/**\n * Open data connection\n */\nint ftclient_open_conn(int sock_con)\n{\n\tint sock_listen = socket_create(CLIENT_PORT_ID);\n\n\t// send an ACK on control conn\n\tint ack = 1;\n\tif ((send(sock_con, (char*) &ack, sizeof(ack), 0)) < 0) {\n\t\tprintf(\"client: ack write error :%d\\n\", errno);\n\t\texit(1);\n\t}\t\t\n\n\tint sock_conn = socket_accept(sock_listen);\n\tclose(sock_listen);\n\treturn sock_conn;\n}\n\n\n\n\n/** \n * Do list commmand\n */\nint ftclient_list(int sock_data, int sock_con)\n{\n\tsize_t num_recvd;\t\t\t// number of bytes received with recv()\n\tchar buf[MAXSIZE];\t\t\t// hold a filename received from server\n\tint tmp = 0;\n\n\t// Wait for server starting message\n\tif (recv(sock_con, &tmp, sizeof tmp, 0) < 0) {\n\t\tperror(\"client: error reading message from server\\n\");\n\t\treturn -1;\n\t}\n\t\n\tmemset(buf, 0, sizeof(buf));\n\twhile ((num_recvd = recv(sock_data, buf, MAXSIZE, 0)) > 0) {\n        \tprintf(\"%s\", buf);\n\t\tmemset(buf, 0, sizeof(buf));\n\t}\n\t\n\tif (num_recvd < 0) {\n\t        perror(\"error\");\n\t}\n\n\t// Wait for server done message\n\tif (recv(sock_con, &tmp, sizeof tmp, 0) < 0) {\n\t\tperror(\"client: error reading message from server\\n\");\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\n\n\n/**\n * Input: cmd struct with an a code and an arg\n * Concats code + arg into a string and sends to server\n */\nint ftclient_send_cmd(struct command *cmd)\n{\n\tchar buffer[MAXSIZE];\n\tint rc;\n\n\tsprintf(buffer, \"%s %s\", cmd->code, cmd->arg);\n\t\n\t// Send command string to server\n\trc = send(sock_control, buffer, (int)strlen(buffer), 0);\t\n\tif (rc < 0) {\n\t\tperror(\"Error sending command to server\");\n\t\treturn -1;\n\t}\n\t\n\treturn 0;\n}\n\n\n\n/**\n * Get login details from user and\n * send to server for authentication\n */\nvoid ftclient_login()\n{\n\tstruct command cmd;\n\tchar user[256];\n\tmemset(user, 0, 256);\n\n\t// Get username from user\n\tprintf(\"Name: \");\t\n\tfflush(stdout); \t\t\n\tread_input(user, 256);\n\n\t// Send USER command to server\n\tstrcpy(cmd.code, \"USER\");\n\tstrcpy(cmd.arg, user);\n\tftclient_send_cmd(&cmd);\n\t\n\t// Wait for go-ahead to send password\n\tint wait;\n\trecv(sock_control, &wait, sizeof wait, 0);\n\n\t// Get password from user\n\tfflush(stdout);\t\n\tchar *pass = getpass(\"Password: \");\t\n\n\t// Send PASS command to server\n\tstrcpy(cmd.code, \"PASS\");\n\tstrcpy(cmd.arg, pass);\n\tftclient_send_cmd(&cmd);\n\t\n\t// wait for response\n\tint retcode = read_reply();\n\tswitch (retcode) {\n\t\tcase 430:\n\t\t\tprintf(\"Invalid username/password.\\n\");\n\t\t\texit(0);\n\t\tcase 230:\n\t\t\tprintf(\"Successful login.\\n\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tperror(\"error reading message from server\");\n\t\t\texit(1);\t\t\n\t\t\tbreak;\n\t}\n}\n\n\n\n\n\nint main(int argc, char* argv[]) \n{\t\t\n\tint data_sock, retcode, s;\n\tchar buffer[MAXSIZE];\n\tstruct command cmd;\t\n\tstruct addrinfo hints, *res, *rp;\n\n\tif (argc != 3) {\n\t\tprintf(\"usage: ./ftclient hostname port\\n\");\n\t\texit(0);\n\t}\n\n\tchar *host = argv[1];\n\tchar *port = argv[2];\n\n\t// Get matching addresses\n\tmemset(&hints, 0, sizeof(struct addrinfo));\n\thints.ai_family = AF_UNSPEC;\n\thints.ai_socktype = SOCK_STREAM;\n\t\n\ts = getaddrinfo(host, port, &hints, &res);\n\tif (s != 0) {\n\t\tprintf(\"getaddrinfo() error %s\", gai_strerror(s));\n\t\texit(1);\n\t}\n\t\n\t// Find an address to connect to & connect\n\tfor (rp = res; rp != NULL; rp = rp->ai_next) {\n\t\tsock_control = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n\n\t\tif (sock_control < 0)\n\t\t\tcontinue;\n\n\t\tif(connect(sock_control, res->ai_addr, res->ai_addrlen)==0) {\n\t\t\tbreak;\n\t\t} else {\n\t\t\tperror(\"connecting stream socket\");\n\t\t\texit(1);\n\t\t}\n\t\tclose(sock_control);\n\t}\n\tfreeaddrinfo(rp);\n\n\n\t// Get connection, welcome messages\n\tprintf(\"Connected to %s.\\n\", host);\n\tprint_reply(read_reply()); \n\t\n\n\t/* Get name and password and send to server */\n\tftclient_login();\n\n\twhile (1) { // loop until user types quit\n\n\t\t// Get a command from user\n\t\tif ( ftclient_read_command(buffer, sizeof buffer, &cmd) < 0) {\n\t\t\tprintf(\"Invalid command\\n\");\n\t\t\tcontinue;\t// loop back for another command\n\t\t}\n\n\t\t// Send command to server\n\t\tif (send(sock_control, buffer, (int)strlen(buffer), 0) < 0 ) {\n\t\t\tclose(sock_control);\n\t\t\texit(1);\n\t\t}\n\n\t\tretcode = read_reply();\t\t\n\t\tif (retcode == 221) {\n\t\t\t/* If command was quit, just exit */\n\t\t\tprint_reply(221);\t\t\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (retcode == 502) {\n\t\t\t// If invalid command, show error message\n\t\t\tprintf(\"%d Invalid command.\\n\", retcode);\n\t\t} else {\t\t\t\n\t\t\t// Command is valid (RC = 200), process command\n\t\t\n\t\t\t// open data connection\n\t\t\tif ((data_sock = ftclient_open_conn(sock_control)) < 0) {\n\t\t\t\tperror(\"Error opening socket for data connection\");\n\t\t\t\texit(1);\n\t\t\t}\t\t\t\n\t\t\t\n\t\t\t// execute command\n\t\t\tif (strcmp(cmd.code, \"LIST\") == 0) {\n\t\t\t\tftclient_list(data_sock, sock_control);\n\t\t\t} \n\t\t\telse if (strcmp(cmd.code, \"RETR\") == 0) {\n\t\t\t\t// wait for reply (is file valid)\n\t\t\t\tif (read_reply() == 550) {\n\t\t\t\t\tprint_reply(550);\t\t\n\t\t\t\t\tclose(data_sock);\n\t\t\t\t\tcontinue; \n\t\t\t\t}\n\t\t\t\tftclient_get(data_sock, sock_control, cmd.arg);\n\t\t\t\tprint_reply(read_reply()); \n\t\t\t}\n\t\t\tclose(data_sock);\n\t\t}\n\n\t} // loop back to get more user input\n\n\t// Close the socket (control connection)\n\tclose(sock_control);\n    return 0;  \n}\n"
  },
  {
    "path": "client/ftclient.h",
    "content": "/* ftclient.h\n *\n * Rebecca Sagalyn\n * 11/15/13\n *\n * Client side of TCP file transfer implementation, runs with custom server, \n * ftserve.c. Receives commands from input, and retreives list of files in current \n * and files. \n   * \n * Valid commands: \n *    get <filename>\n *    list\n *    quit\n *    \n * Usage: \n *    ./ftclient SERVER_HOSTNAME PORT#\n */\n\n#ifndef FTCLIENT_H\n#define FTCLIENT_H\n\n#include \"../common/common.h\"\n\n\n/**\n * Receive a response from server\n * Returns -1 on error, return code on success\n */\nint read_reply();\n\n\n/**\n * Print response message\n */\nvoid print_reply(int rc);\n\n\n/**\n * Parse command in cstruct\n */ \nint ftclient_read_command(char* buf, int size, struct command *cstruct);\n\n\n/**\n * Do get <filename> command \n */\nint ftclient_get(int data_sock, int sock_control, char* arg);\n\n\n/**\n * Open data connection\n */\nint ftclient_open_conn(int sock_con);\n\n\n/** \n * Do list commmand\n */\nint ftclient_list(int sock_data, int sock_con);\n\n\n/**\n * Input: cmd struct with an a code and an arg\n * Concats code + arg into a string and sends to server\n */\nint ftclient_send_cmd(struct command *cmd);\n\n\n/**\n * Get login details from user and\n * send to server for authentication\n */\nvoid ftclient_login();\n\n\n#endif\n"
  },
  {
    "path": "client/makefile",
    "content": "CC := gcc\nCFLAGS := -Wall -g -Os\n\nSHDIR := ../common\n\nOBJS = ftclient.o $(SHDIR)/common.o\n\nall: ftclient\n\nftclient: $(OBJS)\n\t@$(CC) -o ftclient $(CFLAGS) $(OBJS)\n\n$(OBJS) : %.o: %.c \n\t@$(CC) -c $(CFLAGS) $< -o $@\n\n.PHONY:\nclean:\n\t@rm -f *.o ftclient\n\t@rm -f ../common/*.o\n\t@echo Done cleaning\n"
  },
  {
    "path": "common/common.c",
    "content": "#include \"common.h\"\n\n\n/**\n * Create listening socket on remote host\n * Returns -1 on error, socket fd on success\n */\nint socket_create(int port)\n{\n\tint sockfd;\n\tint yes = 1;\n\tstruct sockaddr_in sock_addr;\n\n\t// create new socket\n\tif ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {\n\t\tperror(\"socket() error\"); \n\t\treturn -1; \n\t}\n\n\t// set local address info\n\tsock_addr.sin_family = AF_INET;\n\tsock_addr.sin_port = htons(port);\n\tsock_addr.sin_addr.s_addr = htonl(INADDR_ANY);\t\t\n\n\tif (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {\n\t\tclose(sockfd);\n\t\tperror(\"setsockopt() error\");\n\t\treturn -1; \n\t}\n\n\t// bind\n\tif (bind(sockfd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) {\n\t\tclose(sockfd);\n\t\tperror(\"bind() error\"); \n\t\treturn -1; \n\t}\n   \n\t// begin listening for incoming TCP requests\n\tif (listen(sockfd, 5) < 0) {\n\t\tclose(sockfd);\n\t\tperror(\"listen() error\");\n\t\treturn -1;\n\t}              \n\treturn sockfd;\n}\n\n\n\n\n\n/**\n * Create new socket for incoming client connection request\n * Returns -1 on error, or fd of newly created socket\n */\nint socket_accept(int sock_listen)\n{\n\tint sockfd;\n\tstruct sockaddr_in client_addr;\n\tsocklen_t len = sizeof(client_addr);\n\n\t// Wait for incoming request, store client info in client_addr\n\tsockfd = accept(sock_listen, (struct sockaddr *) &client_addr, &len);\n\t\n\tif (sockfd < 0) {\n\t\tperror(\"accept() error\"); \n\t\treturn -1; \n\t}\n\treturn sockfd;\n}\n\n\n\n\n/**\n * Connect to remote host at given port\n * Returns:\tsocket fd on success, -1 on error\n */\nint socket_connect(int port, char*host)\n{\n\tint sockfd;  \t\t\t\t\t\n\tstruct sockaddr_in dest_addr;\n\n\t// create socket\n\tif ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { \n        \tperror(\"error creating socket\");\n        \treturn -1;\n    }\n\n\t// create server address\n\tmemset(&dest_addr, 0, sizeof(dest_addr));\n\tdest_addr.sin_family = AF_INET;\n\tdest_addr.sin_port = htons(port);\n\tdest_addr.sin_addr.s_addr = inet_addr(host);\n\n\t// Connect on socket\n\tif(connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0 ) {\n        \tperror(\"error connecting to server\");\n\t\treturn -1;\n    \t}    \n\treturn sockfd;\n}\n\n\n\n/**\n * Receive data on sockfd\n * Returns -1 on error, number of bytes received \n * on success\n */\nint recv_data(int sockfd, char* buf, int bufsize){\n\tsize_t num_bytes;\n\tmemset(buf, 0, bufsize);\n\tnum_bytes = recv(sockfd, buf, bufsize, 0);\n\tif (num_bytes < 0) {\n\t\treturn -1;\n\t}\n\treturn num_bytes;\n}\n\n\n\n\n/**\n * Trim whiteshpace and line ending\n * characters from a string\n */\nvoid trimstr(char *str, int n)\n{\n\tint i;\n\tfor (i = 0; i < n; i++) {\n\t\tif (isspace(str[i])) str[i] = 0;\n\t\tif (str[i] == '\\n') str[i] = 0;\n\t}\n}\n\n\n/**\n * Send resposne code on sockfd\n * Returns -1 on error, 0 on success\n */\nint send_response(int sockfd, int rc)\n{\n\tint conv = htonl(rc);\n\tif (send(sockfd, &conv, sizeof conv, 0) < 0 ) {\n\t\tperror(\"error sending...\\n\");\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\n\n\n\n/** \n * Read input from command line\n */\nvoid read_input(char* buffer, int size)\n{\n\tchar *nl = NULL;\n\tmemset(buffer, 0, size);\n\n\tif ( fgets(buffer, size, stdin) != NULL ) {\n\t\tnl = strchr(buffer, '\\n');\n\t\tif (nl) *nl = '\\0'; // truncate, ovewriting newline\n\t}\n}\n\n\n"
  },
  {
    "path": "common/common.h",
    "content": "#ifndef COMMON_H\n#define COMMON_H\n\n#include <arpa/inet.h>\n#include <ctype.h>\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <netdb.h>\t\t// getaddrinfo()\n#include <netinet/in.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n\n\n/* constants */\n#define DEBUG\t\t\t\t1\n#define MAXSIZE \t\t\t512 \t// max buffer size\n#define CLIENT_PORT_ID\t\t30020\n\n\n/* Holds command code and argument */\nstruct command {\n\tchar arg[255];\n\tchar code[5];\n};\n\n\n\n\n/**\n * Create listening socket on remote host\n * Returns -1 on error, socket fd on success\n */\nint socket_create(int port);\n\n\n/**\n * Create new socket for incoming client connection request\n * Returns -1 on error, or fd of newly created socket\n */\nint socket_accept(int sock_listen);\n\n\n/**\n * Connect to remote host at given port\n * Returns socket fd on success, -1 on error\n */\nint socket_connect(int port, char *host);\n\n\n\n/**\n * Receive data on sockfd\n * Returns -1 on error, number of bytes received \n * on success\n */\nint recv_data(int sockfd, char* buf, int bufsize);\n\n\n/**\n * Send resposne code on sockfd\n * Returns -1 on error, 0 on success\n */\nint send_response(int sockfd, int rc);\n\n\n\n//------------------- UTILITY FUNCTIONS-------------------//\n\n/**\n * Trim whiteshpace and line ending\n * characters from a string\n */\nvoid trimstr(char *str, int n);\n\n\n\n/** \n * Read input from command line\n */\nvoid read_input(char* buffer, int size);\n\n\n#endif\n\n\n\n\n\n\n\n"
  },
  {
    "path": "server/.auth",
    "content": "user pass\ntest testpass\nanonymous \n"
  },
  {
    "path": "server/.auth~",
    "content": "user pass\ntest testpass\n"
  },
  {
    "path": "server/ftserve.c",
    "content": "#include \"ftserve.h\"\n\n\nint main(int argc, char *argv[])\n{\t\n\tint sock_listen, sock_control, port, pid;\n\n\tif (argc != 2) {\n\t\tprintf(\"usage: ./ftserve port\\n\");\n\t\texit(0);\n\t}\n\n\tport = atoi(argv[1]);\n\n\t// create socket\n\tif ((sock_listen = socket_create(port)) < 0 ) {\n\t\tperror(\"Error creating socket\");\n\t\texit(1);\n\t}\t\t\n\t\n\twhile(1) {\t// wait for client request\n\n\t\t// create new socket for control connection\n\t\tif ((sock_control = socket_accept(sock_listen))\t< 0 )\n\t\t\tbreak;\t\t\t\n\t\t\n\t\t// create child process to do actual file transfer\n\t\tif ((pid = fork()) < 0) { \n\t\t\tperror(\"Error forking child process\");\n\t\t} else if (pid == 0) { \n\t\t\tclose(sock_listen);\n\t\t\tftserve_process(sock_control);\t\t\n\t\t\tclose(sock_control);\n\t\t\texit(0);\n\t\t}\n\t\t\t\n\t\tclose(sock_control);\n\t}\n\n\tclose(sock_listen);\t\n\n\treturn 0;\n}\n\n\n\n/**\n * Send file specified in filename over data connection, sending\n * control message over control connection\n * Handles case of null or invalid filename\n */\nvoid ftserve_retr(int sock_control, int sock_data, char* filename)\n{\t\n\tFILE* fd = NULL;\n\tchar data[MAXSIZE];\n\tsize_t num_read;\t\t\t\t\t\t\t\n\t\t\n\tfd = fopen(filename, \"r\");\n\t\n\tif (!fd) {\t\n\t\t// send error code (550 Requested action not taken)\n\t\tsend_response(sock_control, 550);\n\t\t\n\t} else {\t\n\t\t// send okay (150 File status okay)\n\t\tsend_response(sock_control, 150);\n\t\n\t\tdo {\n\t\t\tnum_read = fread(data, 1, MAXSIZE, fd);\n\n\t\t\tif (num_read < 0) {\n\t\t\t\tprintf(\"error in fread()\\n\");\n\t\t\t}\n\n\t\t\t// send block\n\t\t\tif (send(sock_data, data, num_read, 0) < 0)\n\t\t\t\tperror(\"error sending file\\n\");\n\n\t\t} while (num_read > 0);\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\n\t\t// send message: 226: closing conn, file transfer successful\n\t\tsend_response(sock_control, 226);\n\n\t\tfclose(fd);\n\t}\n}\n\n\n\n\n\n/**\n * Send list of files in current directory\n * over data connection\n * Return -1 on error, 0 on success\n */\nint ftserve_list(int sock_data, int sock_control)\n{\n\tchar data[MAXSIZE];\n\tsize_t num_read;\t\t\t\t\t\t\t\t\t\n\tFILE* fd;\n\n\tint rs = system(\"ls -l | tail -n+2 > tmp.txt\");\n\tif ( rs < 0) {\n\t\texit(1);\n\t}\n\t\n\tfd = fopen(\"tmp.txt\", \"r\");\t\n\tif (!fd) {\n\t\texit(1);\n\t}\n\n\t/* Seek to the beginning of the file */\n\tfseek(fd, SEEK_SET, 0);\n\n\tsend_response(sock_control, 1); //starting\n\n\tmemset(data, 0, MAXSIZE);\n\twhile ((num_read = fread(data, 1, MAXSIZE, fd)) > 0) {\n\t\tif (send(sock_data, data, num_read, 0) < 0) {\n\t\t\tperror(\"err\");\n\t\t}\n\t\tmemset(data, 0, MAXSIZE);\n\t}\n\n\tfclose(fd);\n\n\tsend_response(sock_control, 226);\t// send 226\n\n\treturn 0;\t\n}\n\n\n\n\n\n\n/**\n * Open data connection to client \n * Returns: socket for data connection\n * or -1 on error\n */\nint ftserve_start_data_conn(int sock_control)\n{\n\tchar buf[1024];\t\n\tint wait, sock_data;\n\n\t// Wait for go-ahead on control conn\n\tif (recv(sock_control, &wait, sizeof wait, 0) < 0 ) {\n\t\tperror(\"Error while waiting\");\n\t\treturn -1;\n\t}\n\n\t// Get client address\n\tstruct sockaddr_in client_addr;\n\tsocklen_t len = sizeof client_addr;\n\tgetpeername(sock_control, (struct sockaddr*)&client_addr, &len);\n\tinet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf));\n\n\t// Initiate data connection with client\n\tif ((sock_data = socket_connect(CLIENT_PORT_ID, buf)) < 0)\n\t\treturn -1;\n\n\treturn sock_data;\t\t\n}\n\n\n\n\n\n/**\n * Authenticate a user's credentials\n * Return 1 if authenticated, 0 if not\n */\nint ftserve_check_user(char*user, char*pass)\n{\n\tchar username[MAXSIZE];\n\tchar password[MAXSIZE];\n\tchar *pch;\n\tchar buf[MAXSIZE];\n\tchar *line = NULL;\n\tsize_t num_read;\t\t\t\t\t\t\t\t\t\n\tsize_t len = 0;\n\tFILE* fd;\n\tint auth = 0;\n\t\n\tfd = fopen(\".auth\", \"r\");\n\tif (fd == NULL) {\n\t\tperror(\"file not found\");\n\t\texit(1);\n\t}\t\n\n\twhile ((num_read = getline(&line, &len, fd)) != -1) {\n\t\tmemset(buf, 0, MAXSIZE);\n\t\tstrcpy(buf, line);\n\t\t\n\t\tpch = strtok (buf,\" \");\n\t\tstrcpy(username, pch);\n\n\t\tif (pch != NULL) {\n\t\t\tpch = strtok (NULL, \" \");\n\t\t\tstrcpy(password, pch);\n\t\t}\n\n\t\t// remove end of line and whitespace\n\t\ttrimstr(password, (int)strlen(password));\n\n\t\tif ((strcmp(user,username)==0) && (strcmp(pass,password)==0)) {\n\t\t\tauth = 1;\n\t\t\tbreak;\n\t\t}\t\t\n\t}\n\tfree(line);\t\n\tfclose(fd);\t\n\treturn auth;\n}\n\n\n\n\n\n/** \n * Log in connected client\n */\nint ftserve_login(int sock_control)\n{\t\n\tchar buf[MAXSIZE];\n\tchar user[MAXSIZE];\n\tchar pass[MAXSIZE];\t\n\tmemset(user, 0, MAXSIZE);\n\tmemset(pass, 0, MAXSIZE);\n\tmemset(buf, 0, MAXSIZE);\n\t\n\t// Wait to recieve username\n\tif ( (recv_data(sock_control, buf, sizeof(buf)) ) == -1) {\n\t\tperror(\"recv error\\n\"); \n\t\texit(1);\n\t}\t\n\n\tint i = 5;\n\tint n = 0;\n\twhile (buf[i] != 0)\n\t\tuser[n++] = buf[i++];\n\t\n\t// tell client we're ready for password\n\tsend_response(sock_control, 331);\t\t\t\t\t\n\t\n\t// Wait to recieve password\n\tmemset(buf, 0, MAXSIZE);\n\tif ( (recv_data(sock_control, buf, sizeof(buf)) ) == -1) {\n\t\tperror(\"recv error\\n\"); \n\t\texit(1);\n\t}\n\t\n\ti = 5;\n\tn = 0;\n\twhile (buf[i] != 0) {\n\t\tpass[n++] = buf[i++];\n\t}\n\t\n\treturn (ftserve_check_user(user, pass));\n}\n\n\n\n\n\n/**\n * Wait for command from client and\n * send response\n * Returns response code\n */\nint ftserve_recv_cmd(int sock_control, char*cmd, char*arg)\n{\t\n\tint rc = 200;\n\tchar buffer[MAXSIZE];\n\t\n\tmemset(buffer, 0, MAXSIZE);\n\tmemset(cmd, 0, 5);\n\tmemset(arg, 0, MAXSIZE);\n\t\t\n\t// Wait to recieve command\n\tif ((recv_data(sock_control, buffer, sizeof(buffer)) ) == -1) {\n\t\tperror(\"recv error\\n\"); \n\t\treturn -1;\n\t}\n\t\n\tstrncpy(cmd, buffer, 4);\n\tchar *tmp = buffer + 5;\n\tstrcpy(arg, tmp);\n\t\n\tif (strcmp(cmd, \"QUIT\")==0) {\n\t\trc = 221;\n\t} else if((strcmp(cmd, \"USER\")==0) || (strcmp(cmd, \"PASS\")==0) ||\n\t\t\t(strcmp(cmd, \"LIST\")==0) || (strcmp(cmd, \"RETR\")==0)) {\n\t\trc = 200;\n\t} else { //invalid command\n\t\trc = 502;\n\t}\n\n\tsend_response(sock_control, rc);\t\n\treturn rc;\n}\n\n\n\n\n\n\n/** \n * Child process handles connection to client\n */\nvoid ftserve_process(int sock_control)\n{\n\tint sock_data;\n\tchar cmd[5];\n\tchar arg[MAXSIZE];\n\n\t// Send welcome message\n\tsend_response(sock_control, 220);\n\n\t// Authenticate user\n\tif (ftserve_login(sock_control) == 1) {\n\t\tsend_response(sock_control, 230);\n\t} else {\n\t\tsend_response(sock_control, 430);\t\n\t\texit(0);\n\t}\t\n\t\n\twhile (1) {\n\t\t// Wait for command\n\t\tint rc = ftserve_recv_cmd(sock_control, cmd, arg);\n\t\t\n\t\tif ((rc < 0) || (rc == 221)) {\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (rc == 200 ) {\n\t\t\t// Open data connection with client\n\t\t\tif ((sock_data = ftserve_start_data_conn(sock_control)) < 0) {\n\t\t\t\tclose(sock_control);\n\t\t\t\texit(1); \n\t\t\t}\n\n\t\t\t// Execute command\n\t\t\tif (strcmp(cmd, \"LIST\")==0) { // Do list\n\t\t\t\tftserve_list(sock_data, sock_control);\n\t\t\t} else if (strcmp(cmd, \"RETR\")==0) { // Do get <filename>\n\t\t\t\tftserve_retr(sock_control, sock_data, arg);\n\t\t\t}\n\t\t\n\t\t\t// Close data connection\n\t\t\tclose(sock_data);\n\t\t} \n\t}\n}\n\n\n"
  },
  {
    "path": "server/ftserve.h",
    "content": "/* ftserve.h\n *\n * Rebecca Sagalyn\n * CS372, Program 1\n * 11/15/13\n *\n * Server side of TCP file transfer implementation, runs with custom client, \n * ftclient.c. Sends list of files in current directory and files to ftclient. \n * Requires user login.\n * \n * Usage: \n *    ./ftserve PORT#\n */\n\n#ifndef FTSERVE_H\n#define FTSERVE_H\n\n#include \"../common/common.h\"\n\n\n/**\n * Send file specified in filename over data connection, sending\n * control message over control connection\n * Handles case of null or invalid filename\n */\nvoid ftserve_retr(int sock_control, int sock_data, char* filename);\n\n\n\n/**\n * Send list of files in current directory\n * over data connection\n * Return -1 on error, 0 on success\n */\nint ftserve_list(int sock_data, int sock_control);\n\n\n\n\n/**\n * Open data connection to client \n * Returns: socket for data connection\n * or -1 on error\n */\nint ftserve_start_data_conn(int sock_control);\n\n\n\n/**\n * Authenticate a user's credentials\n * Return 1 if authenticated, 0 if not\n */\nint ftserve_check_user(char*user, char*pass);\n\n\n\n/** \n * Log in connected client\n */\nint ftserve_login(int sock_control);\n\n\n/**\n * Wait for command from client and send response\n * Returns response code\n */\nint ftserve_recv_cmd(int sock_control, char*cmd, char*arg);\n\n\n\n/** \n * Child process handles connection to client\n */\nvoid ftserve_process(int sock_control);\n\n\n#endif\n"
  },
  {
    "path": "server/makefile",
    "content": "CC := gcc\nCFLAGS := -Wall -g -Os\n\nSHDIR := ../common\n\nOBJS = ftserve.o $(SHDIR)/common.o\n\nall: ftserve\n\nftserve: $(OBJS)\n\t@$(CC) -o ftserve $(CFLAGS) $(OBJS)\n\n$(OBJS) : %.o: %.c \n\t@$(CC) -c $(CFLAGS) $< -o $@\n\n.PHONY:\nclean:\n\t@rm -f *.o ftserve\n\t@rm -f ../common/*.o\n\t@echo Done cleaning\n"
  }
]