//============================================================================ // Name : msstpclient.cpp // Author : Benjamin Bertka SID #23306103 // Email : bbertka@mss.icics.ubc.ca // Copyright : University of British Columbia 2011 // Description : CICS515 Project 1 FTP Client in C/C++ //============================================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define MAX 1380 //the largest message length /* here we have the only global variable, the control socket file descriptor * It is global because sometimes a control command needs to be carried out * in strange parts of the code, such as making a listing of directory contents. * Any data socket file descriptors are created on a need basis. */ int controlSockGlobal = 0; /** * Takes a socket file descriptor and MSSTPC command * and submits it to the MSSTPC server */ void makeRequest(int sockfd, const char *cmd); /** * If there is anything pending to do after a call to * makeRequest() it is done here */ int handleFeedback(char *message, const char *cmd); /** * An error message handler */ void handleFailure(const char* cmd, const char* msg); /** * A success message handler */ void handleSuccess(const char* cmd, const char* msg); /** * This function processes user input and fixes it so * its perfect, returns NULL in the case of unrecognized input */ const char* getCommand(); /** * Given a well formatted command, processes it according to *command */ int processCommand(int sockfd, const char* command); /** * Creates a new file descriptor when needed */ int getNewFileDescriptor(const char* server, const char* port); /** * Opens a new control connection */ void makeNewConnection(int sockfd, struct addrinfo hints, struct addrinfo *res); /** * Opens a new data connection */ void makeDataConnection(int sockfd, struct addrinfo hints, struct addrinfo *res); /** * Determines which kind of data to send back to client */ int getData(int sockfd,string cmd); /** * Processes the LIST command */ void listDirectory(int sockfd, string cmd); /** * Processes the RETR command */ void getFile(int sockfd, string filename); /** * returns a listing of files in current directory */ vector getFileList(int sockfd); /** * Returns directory or file info for any file in fileList */ vector getInfoList(vector fileList, string cmd); /** * uses file name and their directory info to format a nice * string for printing to stdout */ string formatDirectoryInfo(vector fileList, vector fileInfo); /** * Returns the type of file, regular or not */ int getFileType(string file); /** * Returns the size of a regular file */ long getFileSize(string file); /** * Prints the MSSTPC prompt to std out */ void printPrompt(); /** * Prints a help message to std out */ void printHelpMessage(); /** * Trims whitespace from leading and trailing character in string */ void trim (string &str); //============================================================================ // Method : main() // Description : The main entry point. Handles incoming arguments and // serves as infinite loop for the MSSTPC shell which waits for user // input to process commands // PRE: proper user input from the shell prompt // POST: An infinite loop is estableshed until QUIT is called // @param argc - the arg count // @param argv - the IP and port number in an array of two elements //============================================================================ int main(int argc, char *argv[]){ /* Check for the proper arguments, must be a valid port number greater than 1024 */ if(argc != 3){ cout << "Incorrect number of arguments" << endl; cout << "Usage: ./msstpclient [server address] [port number]\n"; exit(1); } for(int i = 0; i < (int)strlen(argv[2]); ++i){ if(!isdigit(argv[2][i])){ cout << "Invalid port number format, please enter digits.\n"; cout << "Usage: ./msstpclient [server address] [port number]\n"; exit(1); } } if( atoi(argv[2]) <= 1024 ){ cout << "Error, must use a port above 1024.\n"; exit(1); } cout << "\n//==========================================================//\n"; cout << "// *Welcome to MSSTPCLIENT!!!* //\n"; cout << "// Recognized client commands: //\n"; cout << "// PWD - Print path of working directory //\n"; cout << "// CWD [dir] - Set current working directory //\n"; cout << "// INFO [file/dir] - Returns file information //\n"; cout << "// LIST [dir] - Lists contents of specified directory //\n"; cout << "// RETR [file] - Gets the specified file, saves locally //\n"; cout << "// QUIT - Quits the session //\n"; cout << "// HELP - Displays this menu //\n"; cout << "//==========================================================//\n\n"; /* open a new connection, set global control socket FD */ controlSockGlobal = getNewFileDescriptor(argv[1], argv[2]); /* loop and process user commands */ while (processCommand(controlSockGlobal, getCommand()) == 1); close(controlSockGlobal); return 0; } //============================================================================ // Method : getNewFileDescriptor() // Description : This function takes a server host name and port number, make // new file descriptor. It is used for both the control, and // data connections in the program. // PRE: a good IP and port address // POST: either a data connection or control connection is made, FD returned // @param serverString - the server IP // @param portString - the server PORT number //============================================================================ int getNewFileDescriptor(const char* serverString, const char* portString){ struct addrinfo hints; struct addrinfo *res; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; getaddrinfo(serverString, portString, &hints, &res); int sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if( sockfd < 0 ){ if(controlSockGlobal == 0){ handleFailure("SOCK", "control create"); exit(1); }else{ handleFailure("SOCK", "data create"); } } /* assuming a good socket, go ahead and connect */ if(controlSockGlobal == 0){ makeNewConnection(sockfd, hints, res); }else{ makeDataConnection(sockfd, hints, res); } return sockfd; } //============================================================================ // Method : makeNewConnection() // Description : Given some well formatted socket FD and addr_info structs // this method tries to connect to the server for a CONTROL connection // PRE: a good IP and port address wrapped into a addr_info struct // POST: connects, or exits on failure to connect // @param sockfd - the socket file descriptor for the connection // @param addr_info - the connection hints and resolution address info //============================================================================ void makeNewConnection(int sockfd, struct addrinfo hints, struct addrinfo *res){ if( connect(sockfd, res->ai_addr, res->ai_addrlen) == -1 ){ handleFailure("CON", "control create"); exit(1); } } //============================================================================ // Method : makeDataConnection() // Description : Given some well formatted socket FD and addr_info structs // this method tries to connect to the server for DATA connection. // PRE: a good IP and port address wrapped into a addr_info struct // POST: connects, or exits on failure to connect // @param sockfd - the socket file descriptor for the connection // @param addr_info - the connection hints and resolution address info //============================================================================ void makeDataConnection(int sockfd, struct addrinfo hints, struct addrinfo *res){ if( connect(sockfd, res->ai_addr, res->ai_addrlen) == -1 ){ handleFailure("CON", "data create"); } } //============================================================================ // Method : processCommand() // Description : Takes a FD and user command as formatted by getCommand() // and takes the course of action to print help message, or send the request // to server, with makeRequest(). Does a check on RETR before proceeding // PRE: a valid socket FD and formatted command string // POST: returns 1 if the main loop should keep processing, or 0 if the // user entered a command to QUIT //============================================================================ int processCommand(int sockfd, const char* cmd){ if(cmd ==NULL){ //NULL commands are valid return 1; }else if(strcmp(cmd,"UNKNOWN") ==0){ //Unknown commands are OK too handleFailure("CMD", "unrecognized"); return 1; }else if(strcmp(cmd,"HELP") == 0){ //prints help when needed printHelpMessage(); return 1; }else if (strcmp(cmd, "QUIT\n") == 0){ //quit if we must makeRequest(sockfd, cmd); handleFailure("QUIT", "goodbye"); return 0; }else{ /* First, we check if RETR is entered, and check the file type * before making a request to process the command */ string prefix = cmd; if(strcmp(cmd, "RETRNULL\n") == 0){ //user must have had no arg handleFailure("RETR", "requires a parameter"); //go ahead if OK }else if (prefix.find("RETR") != string::npos){ string command = cmd; string name = command.substr(5); if(getFileType(name) == 1){ if(name.find("/") != string::npos){ handleFailure("RETR", "path"); }else{ //get the file if a file makeRequest(sockfd, cmd); } }else{ //fail if a directory handleFailure("RETR", "directory not allowed"); } }else{ makeRequest(sockfd, cmd); //otherwise just make the request } return 1; } } //============================================================================ // Method : makeRequest() // Description : Takes a FD and user command as formatted by getCommand() // and takes the course of action to print help message, or send the request // to server, with makeRequest(). Does a check on RETR before proceeding // PRE: a valid socket FD and formatted command string // POST: returns 1 if the main loop should keep processing, or 0 if the // user entered a command to QUIT //============================================================================ void makeRequest(int sockfd, const char *cmd){ char message[MAX]; int retBytes = 0; int flag = 0; bzero(message, MAX); int len = strlen(cmd); if (send(sockfd, cmd, len, 0) == -1){ handleFailure("CON", "make request"); } retBytes = recv(sockfd, message, sizeof(message), 0); /* If we have bytes returned, try to see if more can be received */ if(retBytes > 0){ flag = handleFeedback(message, cmd); if(flag == 1){ //finished with feedback handling bzero(message, MAX); retBytes = recv(sockfd, message, sizeof(message), 0); if(retBytes == 0){ //if no bytes, then the server closed on us handleFailure("CON", "control closed"); exit(1); }else if(retBytes < 0){ //server time out handleFailure("CON", "control timeout"); } /* these next conditions are good for debugging */ }else if(flag == 2){ //cout << "makeRequest: LIST Success" << endl; }else if(flag == 3){ //cout << "makeRequest: RETR Success" << endl; }else{ } //if no bytes, then the server closed on us }else if(retBytes == 0){ handleFailure("CON", "control closed"); exit(1); //server time out }else if(retBytes < 0){ handleFailure("CON", "control timeout"); }else{ handleFailure("CON", "control unknown"); } } //============================================================================ // Method : handleFeedback() // Description : Starts a data connection. Processes the returned message by // looking at the first character in the string. // S = success, F = error, P = more work to be done // In the case of P, the new IP and PORT are extracted from the message // and then a new FD for data connection is made with a call to // getNewFileDescriptor(). // This opens a new data connection, or is supposed to. Then getData() is called // to get the rest of the data. // PRE: a valid message and command // POST: returns 0 or number of bytes received // user entered a command to QUIT //============================================================================ int handleFeedback(char *message, const char *cmd){ string info; string command = cmd; string msg = message; string prefix = msg.substr(0,1); if(prefix.find("S") != string::npos){ handleSuccess(command.c_str(), msg.c_str()); }else if(prefix.find("F") != string::npos){ handleFailure(command.c_str(), msg.c_str()); }else if(prefix.find("P") != string::npos){ /* get the IP and port out of the returned string */ info = message; info = info.substr(0, info.find_first_of("\n")); string ip = info.substr(2, info.find_last_of(" ")-2); string port = info.substr(info.find_last_of(" ")+1, info.length()-1); /*here i am requesting a new FD with the new Port number */ int sockfd = getNewFileDescriptor(ip.c_str(), port.c_str()); if(sockfd > 0){ /* lets get the data and then close the data socket, * sending returned bytes up the chain */ int retval = getData(sockfd, command); close(sockfd); return retval; } } return 0; //no bytes to return } //============================================================================ // Method : getData() // Description : Checks the cmd string to see if a request for file listing // or request for data download is made // PRE: a valid socket FD and formatted command string // POST: returns 1 if for download file, or 2 for list or anything else //============================================================================ int getData(int sockfd, string cmd){ string prefix = cmd.substr(0, 1); string file = cmd.substr(5); if(prefix.find("L") != string::npos){ listDirectory(sockfd, cmd); //list the directory if needed return 2; }else if(prefix.find("R") != string::npos){ getFile(sockfd, file); //get the file if needed return 1; }else{ return 2; } } //============================================================================ // Method : getFileType() // Description : Checks the given file if its a directory or not // PRE: a valid file name // POST: 1 if a regular file, 0 anything else, exits on server close //============================================================================ int getFileType(string file){ int numbytes = 0; char buf[MAX]; string command = "INFO "+file; int s = send(controlSockGlobal, command.c_str(), (int)command.size(), 0); if (s == -1){ handleFailure("INFO", "fetching file information"); return 0; }else{ bzero(buf, MAX); numbytes = recv(controlSockGlobal, buf, MAX-1, 0); if(numbytes == 0){ handleFailure("CON", "control closed"); exit(1); }else if(numbytes < 0){ handleFailure("CON", "control timeout"); }else{ file = buf; if( file.find("directory") == string::npos && file.find("DIRECTORY") == string::npos ){ return 1; //regular file }else{ return 0; } } } return 0; } //============================================================================ // Method : getFileSize() // Description : Checks the given file for its size in bytes // PRE: a valid file name // POST:returns long value of bytes, or exits on server close //============================================================================ long getFileSize(string file){ int numbytes = 0; char buf[MAX]; string command = "INFO "+file; if (send(controlSockGlobal, command.c_str(), (int)command.size(), 0) == -1){ handleFailure("INFO", "fetching file information"); return 0; }else{ bzero(buf, MAX); numbytes = recv(controlSockGlobal, buf, MAX-1, 0); if(numbytes == 0){ handleFailure("CON", "control closed"); exit(1); }else if(numbytes < 0){ handleFailure("CON", "control timeout"); }else{ file = buf; return atol(file.substr(2).c_str()); } } return 0; } //============================================================================ // Method : getFile() // Description : Downloads the file using TCP data socket // PRE: an existing file on server, and valid data FD // POST:file save dto local directory, or error message, or exit on server // time out //============================================================================ void getFile(int sockfd, string filename){ int numbytes = 1; char buf[MAX]; long read = 0; FILE* file; filename = filename.substr(0, filename.length()-1); file = fopen(filename.c_str(), "w"); cout << "Retrieving: " << filename << endl; while(numbytes > 0){ bzero(buf, MAX); numbytes = recv(sockfd, buf, MAX-1, 0); if(numbytes < 0){ //if negative, we have problem handleFailure("CON", "data timeout"); exit(1); //exit on server timeout }else if(numbytes > 0){ //keep going if more to get fwrite(&buf, 1, numbytes, file); }else{ //do nothing } read = read + numbytes; //accumulate bytes received } fclose(file); //get the local user's directory for feedback to them char* localDir = get_current_dir_name(); if(localDir != NULL){ cout << "Wrote file: " << localDir << "/" << filename << endl; cout << "Total size: " << read << " bytes" << endl; }else{ cout << "Received: " << read << " bytes" << endl; } } //============================================================================ // Method : listDirectory() // Description : Gets the list of files from the open data socket, // then gets file info for each one on the control socket. // prints a formatted representation of the directory listing // PRE: an open data socket for data retrieving // POST:prints the directory info //============================================================================ void listDirectory(int sockfd, string cmd){ vector fileList = getFileList(sockfd); vector fileInfo = getInfoList(fileList, cmd); cout << formatDirectoryInfo(fileList, fileInfo); } //============================================================================ // Method : getFileList() // Description : Returns a vector of filenames in the current directory // PRE: an open data socket for data retrieving // POST:a vector of file names is sent back //============================================================================ vector getFileList(int sockfd){ vector fileList; char *token; int numbytes = 1; char buf[MAX]; string fileString = ""; while(numbytes > 0){ bzero(buf, MAX); numbytes = recv(sockfd, buf, MAX-1, 0); if(numbytes < 0){ handleFailure("CON", "data timeout"); exit(1); }else{ fileString.append(buf); } } close(sockfd); token = strtok(&fileString[0],"\n"); while (token != NULL){ fileList.push_back(token); token = strtok (NULL, "\n"); } /* clear out the control socket */ bzero(buf, MAX); numbytes = recv(controlSockGlobal, buf, sizeof(buf), 0); if(numbytes == 0){ handleFailure("CON", "control closed"); exit(1); }else if(numbytes < 0){ handleFailure("CON", "control timeout"); } return fileList; } //============================================================================ // Method : getInfoList() // Description : Returns a vector of file information on files in // the current working directory // PRE: fileList is full of files which exist // POST: a vector with file and directory information is returned //============================================================================ vector getInfoList(vector fileList, string cmd){ int numbytes = 1; string fileString = ""; string command; char buf[MAX]; char* token; vector fileInfo; string prefix; if(cmd.length()==5){ //user just wants to list current directory prefix = "INFO ."; }else{ //user has a prefix to the list command prefix = "INFO "+cmd.substr(5); prefix.erase(prefix.length()-1); } for(int i = 0; i < (int)fileList.size(); ++i){ command = prefix + "/"+ fileList[i]+"\n"; bzero(buf, MAX); if (send(controlSockGlobal, command.c_str(), (int)command.size(), 0) == -1){ handleFailure("INFO", "fetching file information"); }else{ bzero(buf, MAX); numbytes = recv(controlSockGlobal, buf, MAX-1, 0); if(numbytes == 0){ handleFailure("CON", "control closed"); exit(1); }else if(numbytes < 0){ handleFailure("CON", "control timeout"); } fileString.append(buf); } } token = strtok(&fileString[0],"\n"); while (token != NULL){ fileInfo.push_back(token); token = strtok (NULL, "\n"); } return fileInfo; } //============================================================================ // Method : formatDirectoryInfo() // Description : Takes file info and names then formats into a string to // be printed to std out. // PRE: fileList and file Info both have data in them // POST:a formatted string is returned ready for printing // //Example: //Directory Listing for Current Working Directory // Found 7 Item(s) // . //< ? > .. -access denied- // short_text_file.txt 23 bytes // dir1 // video_file_binary.ogv 178341115 bytes // copy // nohup.out 54483821 bytes // 3 Files(s) 232824959 bytes // 3 Dir(s) // 1 Inaccsesable // 0 Unknown type(s) //========================================================================== string formatDirectoryInfo(vector fileList, vector fileInfo){ char buf[MAX]; //file sizes above 256 bytes will be truncated string fileString = ""; string command; int numFiles = 0; int numDirs = 0; string bytes; long totalBytes = 0; int unknown = 0; int notAllowed = 0; int len; for(int i = 0; i < (int)fileInfo.size(); ++i){ len = fileInfo[i].length(); if(len == 11 && fileInfo[i].find("DIRECTORY") != string::npos){ command = " \t"; command.append(fileList[i]); fileInfo[i] = command; ++numDirs; }else if(len == 19 && fileInfo[i].find("denied") != string::npos){ if(fileList[i].find("..") != string::npos && fileList[i].length() == 2){ command = " \t.. -access denied-"; ++numDirs; ++notAllowed; }else{ command = "< ? > \t"+fileList[i] +" -access denied-"; ++notAllowed; } fileInfo[i] = command; }else if(len == 41 && fileInfo[i].find("file or directory") != string::npos){ command = "< ? > \t"+fileList[i]+" -unknown type-"; fileInfo[i] = command; ++unknown; }else{ bytes = fileInfo[i].substr(2, len); totalBytes += atol(bytes.c_str()); command = " \t"+fileList[i] +" "+bytes + " bytes"; fileInfo[i] = command; ++numFiles; } } fileString = "\nDirectory Listing for Current Working Directory\n"; bzero(buf, MAX); sprintf (buf, " Found %d Item(s)\n", (int)fileList.size()); fileString += buf; for(int i = 0; i < (int)fileList.size(); ++i){ fileString += fileInfo[i] + "\n"; } bzero(buf, MAX); sprintf (buf, " %d \tFiles(s) %ld bytes\n", numFiles, totalBytes); fileString += buf; bzero(buf, MAX); sprintf (buf, " %d \tDir(s)\n",numDirs); fileString += buf; bzero(buf, MAX); sprintf (buf, " %d \tInaccsesable\n",notAllowed); fileString += buf; bzero(buf, MAX); sprintf (buf, " %d \tUnknown type(s)\n\n",unknown); fileString += buf; return fileString; } //============================================================================ // Method : getCommand() // Description : Returns a nicley formatted user command string, trimming // extraneous whitespace, and forcing commands to uppercase. // PRE: user inputs data recognized by shell // POST: a command string is returned. NULL is returned if not recognized. // or if a failure was made after finding a recognized prefix. //============================================================================ const char* getCommand(){ string command; printPrompt(); getline(cin, command); //block until get input if(command.size() <= 0){ return NULL; } if(command.find("QUIT") != string::npos || command.find("quit") != string::npos){ trim(command); if(command.length() == 4){ return "QUIT\n"; }else{ handleFailure("QUIT", "parameter"); return NULL; } }else if(command.find("PWD") != string::npos || command.find("pwd") != string::npos){ trim(command); if(command.length() == 3){ return "PWD\n"; }else{ if(command.find("PWD ") != string::npos || command.find("pwd ") != string::npos){ handleFailure("PWD", "parameter"); return NULL; } handleFailure("CMD", "PWD"); return NULL; } }else if(command.find("CWD") != string::npos || command.find("cwd") != string::npos){ trim(command); if(command.length() > 3){ command = command.substr(3, command.length()); trim(command); if(command.find("//") != string::npos){ handleFailure("CWD", "invalid path"); return NULL; } command.append("\n"); string ret = "CWD "+ command; return ret.c_str(); }else if(command.length() == 3){ command = "CWD"; command.append("\n"); return command.c_str(); }else{ return NULL; } }else if(command.find("INFO") != string::npos || command.find("info") != string::npos){ trim(command); if(command.length() > 4){ if(command[4] != ' '){ handleFailure("CMD", "INFO"); return NULL; } command = command.substr(4, command.length()); trim(command); command.append("\n"); string ret = "INFO "+ command; return ret.c_str(); }else if(command.length() == 4){ command = "INFO"; command.append("\n"); return command.c_str(); }else{ return NULL; } }else if(command.find("LIST") != string::npos || command.find("list") != string::npos){ trim(command); if(command.length() > 4){ if(command[4] != ' '){ handleFailure("CMD", "LIST"); return NULL; } command = command.substr(4, command.length()); trim(command); command.append("\n"); string ret = "LIST "+ command; return ret.c_str(); }else if(command.length() == 4){ command = "LIST"; command.append("\n"); return command.c_str(); }else{ return NULL; } }else if(command.find("RETR") != string::npos || command.find("retr") != string::npos){ trim(command); if(command.length() > 4){ if(command[4] != ' '){ handleFailure("CMD", "RETR"); return NULL; } command = command.substr(4, command.length()); trim(command); command.append("\n"); string ret = "RETR "+ command; return ret.c_str(); }else if(command.length() == 4){ command = "RETRNULL"; command.append("\n"); return command.c_str(); }else{ return NULL; } }else if(command.find("HELP") != string::npos || command.find("help") != string::npos){ trim(command); if(command.length() == 4){ return "HELP"; }else if (command.length() > 4){ if(command[4] != ' '){ handleFailure("CMD", "HELP"); return NULL; }else{ handleFailure("HELP", "parameter"); return NULL; } } }else{ return "UNKNOWN"; } return "UNKNOWN"; } //============================================================================ // Method : handleFailure() // Description : Prints the failure messages this program is capable of // PRE: cmd and msg refer to recognized commands and messages // POST: failure information is printed //============================================================================ void handleFailure(const char* cmd, const char* msg){ string command = cmd; string message = msg; /* Failure handling for CWD commands */ if(command.find("CWD") != string::npos){ if(message.find("Permission denied") != string::npos){ cout << "You do not have enough permissions to do that\n"; }else if(message.find("Not a directory") != string::npos){ cout << "CWD parameter must be a directory\n"; }else if(message.find("requires a parameter") != string::npos){ cout << "CWD requires a directory name as parameter" << endl; }else if(message.find("file or directory") != string::npos){ cout << "That item does not exist\n"; }else if(message.find("invalid path") != string::npos){ cout << "Invalid path format, use a single slash instead\n"; } /* Failure handling for INFO commands */ }else if(command.find("INFO") != string::npos){ if(message.find("Permission denied") != string::npos){ cout << "You do not have enough permissions to do that\n"; }else if(message.find("requires a parameter") != string::npos){ cout << "INFO requires a file or directory name as parameter\n"; }else if(message.find("Parameter is not a file or a directory") != string::npos){ cout << "Parameter is not a file or a directory\n"; }else if(message.find("file or directory") != string::npos){ cout << "That item does not exist\n"; }else if(message.find("fetching file information") != string::npos){ cout << "Server could not complete request for file information\n"; } /* Failure handling for LIST commands */ }else if(command.find("LIST") != string::npos){ if(message.find("Permission denied") != string::npos){ cout << "You do not have enough permissions to do that\n"; }else if(message.find("file or a directory") != string::npos){ cout << "That item does not exist\n"; }else if(message.find("file or directory") != string::npos){ cout << "That item does not exist\n"; }else if(message.find("Not a directory") != string::npos){ cout << "Parameter must be a directory\n"; } /* Failure handling for RETR commands */ }else if(command.find("RETR") != string::npos){ if(message.find("Permission denied") != string::npos){ cout << "You do not have enough permissions to do that\n"; }else if(message.find("requires a parameter") != string::npos){ cout << "RETR requires a regular file as parameter\n"; }else if(message.find("file or a directory") != string::npos){ cout << "That item does not exist\n"; }else if(message.find("file or directory") != string::npos){ cout << "That item does not exist\n"; }else if(message.find("empty file") != string::npos){ cout << "File contains no data\n"; }else if(message.find("directory not allowed") != string::npos){ cout << "Please use a regular file as parameter\n"; }else if(message.find("path") != string::npos){ cout << "Files must be received from current working directory.\n"; } /* Failure handling for socket failures */ }else if(command.find("SOCK") != string::npos){ if(message.find("control create")!= string::npos){ cout << "Could not create control socket, exiting now\n"; }else if(message.find("data create")!= string::npos){ cout << "Could not create data socket\n"; } /* Failure handling for control connection failures */ }else if(command.find("CON") != string::npos){ if(message.find("control create")!= string::npos){ cout << "Could not create control connection, exiting now\n"; }else if(message.find("data create")!= string::npos){ cout << "Could not create data connection\n"; }else if(message.find("control closed")!= string::npos){ cout << "Server has closed control connection, exiting now\n"; }else if(message.find("data closed")!= string::npos){ cout << "Server has closed data connection\n"; }else if(message.find("data timeout")!= string::npos){ cout << "Server timeout for data receive\n"; }else if(message.find("control timeout")!= string::npos){ cout << "Server timeout for control receive\n"; }else if(message.find("control unknown")!= string::npos){ cout << "Server reports unknown error\n"; }else if(message.find("make request")!= string::npos){ cout << "Server could not make request\n"; } /* Failure handling for PWD failures */ }else if(command.find("PWD") != string::npos){ if(message.find("parameter")!= string::npos){ cout << "PWD does not accept parameter\n"; } /* Failure handling for QUIT failures */ }else if(command.find("QUIT") != string::npos){ if(message.find("parameter")!= string::npos){ cout << "QUIT does not accept parameter\n"; }else if(message.find("goodbye") != string::npos){ cout << "Connection closed, good bye!\n"; } /* Failure handling for unrecognized commands */ }else if(command.find("CMD") != string::npos){ cout << "Command not recognized. Type HELP for legal commands\n"; }else if(command.find("HELP") != string::npos){ cout << "HELP does not require a parameter\n"; }else{ /* Failure handling for any other command problem (debugging) */ //cout << "Unrecognized Failure from commands: " << command << endl; //cout << "Failure command result message: " << message << endl; } } //============================================================================ // Method : handleSuccess() // Description : Sometimes we need to print success messages. INFO, PWD // both result in succes messages to be printed // PRE: cmd and msg refer to recognized commands and messages // POST: command result information is formatted then printed //============================================================================ void handleSuccess(const char* cmd, const char* msg){ string command = cmd; string message = msg; if(command.find("INFO") != string::npos){ if(message.find("DIRECTORY") != string::npos){ cout << " \t"; cout << command.substr(5, command.length()-1); }else{ cout << " " << atoi(message.substr(2).c_str()) << "-bytes\t"; cout << command.substr(5, command.length()-1); } }else if(command.find("PWD") != string::npos){ cout << message.substr(2); } } //============================================================================ // Method : printHelpMessage() // Description : Prints a help message // PRE: user types "help" or "HELP" // POST: this message is printed //============================================================================ void printHelpMessage(){ cout << "//==========================================================//\n"; cout << "// *Help Menu for MSSTPCLIENT* //\n"; cout << "// Recognized client commands: //\n"; cout << "// PWD - Print path of working directory //\n"; cout << "// CWD [dir] - Set current working directory //\n"; cout << "// INFO [file/dir] - Returns file information //\n"; cout << "// LIST [dir] - Lists contents of specified directory //\n"; cout << "// RETR [file] - Gets the specified file, saves locally //\n"; cout << "// QUIT - Quits the session //\n"; cout << "// HELP - Displays this menu //\n"; cout << "//==========================================================//\n"; } //============================================================================ // Method : printPrompt() // Description : Prints a prompt // PRE: program is looping and unblocked // POST: this message is printed //============================================================================ void printPrompt(){ cout << "msstp> "; } //============================================================================ // Method : trim() // Description : Trims leading and trailing whitespace // PRE: string is a real string // POST: a string is returned without whitespace //============================================================================ void trim(string& str) { string::size_type pos = str.find_last_not_of(' '); if(pos != string::npos) { str.erase(pos + 1); pos = str.find_first_not_of(' '); if(pos != string::npos) str.erase(0, pos); } else str.erase(str.begin(), str.end()); }