// Author Name: Matt Strapp // Date: 25 April 2022 // x500: strap012 #include "util.hpp" #define VALID_CHARACTERS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" enum Command parse_command(char *line) { /* Find a valid command in the first word of the line (case sensitive) args: line (char*): string of characters from the input file returns: (command): Returns the found command. Returns else if first word is invalid. */ if (line == NULL) { return INVALID; } char *rest; char *tok = strtok_r(line, " ", &rest); if (tok == NULL) { return INVALID; } // REGISTER [username] [password] if (strcmp(tok, "REGISTER") == 0) { char *username = strtok_r(rest, " ", &rest); if (!isValidUsername(username)) return INVALID; char *password = strtok_r(rest, " ", &rest); if (password == NULL) return INVALID; if (!isValidPassword(password)) return INVALID; char *misc = strtok_r(rest, " ", &rest); if (misc != NULL) return INVALID; return REGISTER; } // LOGIN [username] [password] if (strcmp(tok, "LOGIN") == 0) { char *username = strtok_r(rest, " ", &rest); if (!isValidUsername(username)) return INVALID; char *password = strtok_r(rest, " ", &rest); if (password == NULL) return INVALID; if (!isValidPassword(password)) return INVALID; char *misc = strtok_r(rest, " ", &rest); if (misc != NULL) return INVALID; return LOGIN; } // LOGOUT if (strcmp(tok, "LOGOUT") == 0) { char *misc = strtok_r(rest, " ", &rest); if (misc != NULL) return INVALID; return LOGOUT; } // SEND [msg] if (strcmp(tok, "SEND") == 0) { if (int(strlen(rest) == 0)) return INVALID; return SEND; } // SEND2 [username] [msg] if (strcmp(tok, "SEND2") == 0) { char *username = strtok_r(rest, " ", &rest); if (!isValidUsername(username)) return INVALID; if (int(strlen(rest) == 0)) return INVALID; return SEND2; } // SENDA [msg] if (strcmp(tok, "SENDA") == 0) { if (int(strlen(rest) == 0)) return INVALID; return SENDA; } // SENDA2 [username] [msg] if (strcmp(tok, "SENDA2") == 0) { char *username = strtok_r(rest, " ", &rest); if (!isValidUsername(username)) return INVALID; if (int(strlen(rest) == 0)) return INVALID; return SENDA2; } // SENDF [local file] if (strcmp(tok, "SENDF") == 0) { char *file_name = strtok_r(rest, " ", &rest); if (file_name == NULL) return INVALID; if (int(strlen(rest) != 0)) return INVALID; return SENDF; } // SENDF2 [username] [local file] if (strcmp(tok, "SENDF2") == 0) { char *username = strtok_r(rest, " ", &rest); if (!isValidUsername(username)) return INVALID; char *file_name = strtok_r(rest, " ", &rest); if (file_name == NULL) return INVALID; if (int(strlen(rest) != 0)) return INVALID; return SENDF2; } // LIST if (strcmp(tok, "LIST") == 0) { char *misc = strtok_r(rest, " ", &rest); if (misc != NULL) return INVALID; return LIST; } // DELAY if (strcmp(tok, "DELAY") == 0) { char *delay_time = strtok_r(rest, " ", &rest); if (delay_time == NULL) return INVALID; std::string str(delay_time); std::size_t found = str.find_first_not_of("0123456789"); if (found != std::string::npos) return INVALID; if (int(strlen(rest) != 0)) return INVALID; return DELAY; } // default return INVALID; } bool isValidUsername(char *username) { /* Helper function to check if a username string is valid args: username (char*): Username to be tested. returns: bool: Username is valid ? true, false */ if (username == NULL) return false; else if ((strlen(username) > MAX_USERNAME_LEN) || (strlen(username) < MIN_USERNAME_LEN)) return false; else { std::string str(username); std::size_t found = str.find_first_not_of(VALID_CHARACTERS); if (found != std::string::npos) return false; } return true; } bool isValidPassword(char *password) { if (password == NULL) return false; else if ((strlen(password) > MAX_PASSWORD_LEN) || (strlen(password) < MIN_PASSWORD_LEN)) return false; else { std::string str(password); std::size_t found = str.find_first_not_of(VALID_CHARACTERS); if (found != std::string::npos) return false; } return true; } void Error(const char *format, ...) { char msg[4096]; va_list argptr; va_start(argptr, format); vsprintf(msg, format, argptr); va_end(argptr); fprintf(stderr, "Error: %s\n", msg); exit(-1); } void Log(const char *format, ...) { char msg[2048]; va_list argptr; va_start(argptr, format); vsprintf(msg, format, argptr); va_end(argptr); fprintf(stderr, "%s\n", msg); } // convert buf[0:3] to int void BigEndianToLittle(BYTE *buf, int &i) { // No way to avoid Segmentation fault. Some bytes may be 0 i = 0; i += buf[3]; i += ((int)(buf[2])) << 8; i += ((int)(buf[1])) << 16; i += ((int)(buf[0])) << 24; } // convert int to buf[4] where buf[0] contains MSB's and buf[3] contains LSB's void LittleEndianToBig(int &i, BYTE *buf) { // No way to avoid Segmentation fault. Some bytes may be 0 buf[0] = (BYTE)((i & 0xFF000000) >> 24); buf[1] = (BYTE)((i & 0x00FF0000) >> 16); buf[2] = (BYTE)((i & 0x0000FF00) >> 8); buf[3] = (BYTE)(i & 0x000000FF); } const char *com2str(enum Command command) { const char *com2str[] = {"REGISTER", "LOGIN", "LOGOUT", "SEND", "SEND2", "SENDA", "SENDA2", "SENDF", "SENDF2", "LIST", "DELAY", "GETF", "PING", "CONNECT", "", "INVALID"}; return com2str[command]; } // Computes the difference between times a and b in seconds // a - b = result double timeDifference(struct timeb a, struct timeb b) { return (a.time + a.millitm / (double)1000.0f) - (b.time + b.millitm / (double)1000.0f); } int getNumEntries(const char *database) { FILE *fp = fopen(database, "r"); if (!fp) { Log("Username check failed to open the file: %s", database); return -1; } int numEntries = 0; char line[LINE_SIZE] = {0}; while (fgets(line, LINE_SIZE, fp) != NULL) { char *new_line_pos = strchr(line, '\n'); if (new_line_pos != NULL) *new_line_pos = 0; // Including blank lines as entries. Don't use any blank lines. numEntries++; memset(line, 0, LINE_SIZE); } fclose(fp); return numEntries; } bool loginQuery(const char *database, char *username, char *password) { FILE *fp = fopen(database, "r"); if (!fp) { Log("Could not open user file: %s", database); return false; } char line[LINE_SIZE] = {0}; while (fgets(line, LINE_SIZE, fp) != NULL) { char *new_line_pos = strchr(line, '\n'); if (new_line_pos != NULL) *new_line_pos = 0; char *rest; char *usr = strtok_r(line, " ", &rest); char *pswd = strtok_r(rest, " ", &rest); if (usr == NULL || pswd == NULL) { Log("Registry check failed to parse the file: %s", database); fclose(fp); return false; } if ((strcmp(username, usr) == 0) && (strcmp(password, pswd) == 0)) { fclose(fp); return true; } memset(line, 0, LINE_SIZE); } fclose(fp); return false; } // User is in registry: true // User is not in registry: false bool usernameQuery(const char *database, char *username) { FILE *fp = fopen(database, "r"); if (!fp) { Log("Username check failed to open the file: %s", database); return false; } char line[LINE_SIZE] = {0}; while (fgets(line, LINE_SIZE, fp) != NULL) { char *new_line_pos = strchr(line, '\n'); if (new_line_pos != NULL) *new_line_pos = 0; char *rest; char *usr = strtok_r(line, " ", &rest); if (usr == NULL) continue; if (strcmp(username, usr) == 0) { fclose(fp); return true; } memset(line, 0, LINE_SIZE); } fclose(fp); return false; } void recordEntry(const char *database, char *key, char *value) { FILE *fp = fopen(database, "a"); if (!fp) { Log("Registry append failed to open the file: %s", database); return; } char line[LINE_SIZE] = {0}; strcat(line, key); strcat(line, " "); strcat(line, value); strcat(line, "\n"); fputs(line, fp); fclose(fp); } int buf2file(BYTE *buf, int nbytes, char *filename) { FILE *f = fopen(filename, "w"); if (!f) return -1; fwrite(buf, 1, nbytes, f); fclose(f); return 0; } int file2buf(char *filename, BYTE *buf) { FILE *f = fopen(filename, "r"); if (!f) { Log("Error: Cannot open %s", filename); return -1; } fseek(f, 0, SEEK_END); // jump to end of file long nbytes = ftell(f); // find current offset on file f fseek(f, 0, SEEK_SET); // jump to beginning of file if (nbytes > MAX_REQUEST_SIZE) { Log("Error: File %s is too big: %d", filename, nbytes); fclose(f); return -1; } int nread = fread(buf, 1, nbytes, f); if (nread != nbytes) { Log("Error: Byte count mismatch with fread: %d, %d", nbytes, nread); fclose(f); return -1; } fclose(f); return nbytes; }