From 6889e2d66b710c241b3884fc28610a9e6be4e610 Mon Sep 17 00:00:00 2001 From: Matt Strapp Date: Mon, 25 Apr 2022 17:48:52 -0500 Subject: A --- src/server.cpp | 796 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 796 insertions(+) create mode 100644 src/server.cpp (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp new file mode 100644 index 0000000..270a350 --- /dev/null +++ b/src/server.cpp @@ -0,0 +1,796 @@ +#include "server.hpp" + +#define MAX_CONCURRENCY_LIMIT 40000 // overkill +#define POLL_TIMEOUT 1000 // Milliseconds +#define S2C_SERVICE_FREQ 1.0 // Seconds +#define USER_DATABASE "user_database.txt" +#define FILE_DATABASE "file_database.txt" +#define FILES_DIR "files" + +int nConns; // total # of data sockets +struct pollfd + peers[MAX_CONCURRENCY_LIMIT + 1]; // sockets to be monitored by poll() +struct CONN_STAT + connStat[MAX_CONCURRENCY_LIMIT + 1]; // app-layer stats of the sockets +BYTE *buf[MAX_CONCURRENCY_LIMIT + 1]; + +// Duplicate exists in server and client +void RemoveConnection(int i) { + close(peers[i].fd); + if (i < nConns) { + memmove(peers + i, peers + i + 1, (nConns - i) * sizeof(struct pollfd)); + memmove(connStat + i, connStat + i + 1, + (nConns - i) * sizeof(struct CONN_STAT)); + free(buf[i]); + memmove(buf + i, buf + i + 1, (nConns - i) * sizeof(BYTE *)); + } + nConns--; + Log("[%s] %d active connections after removal", NAME, nConns); +} + +void DoServer(int svrPort) { + int maxConcurrency = 20; // TODO: Change this to be a #define + int listenFD = socket(AF_INET, SOCK_STREAM, 0); + if (listenFD < 0) { + Error("[%s] Cannot create listening socket.", NAME); + } + SetNonBlockIO(listenFD); + + struct sockaddr_in serverAddr; + memset(&serverAddr, 0, sizeof(struct sockaddr_in)); + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons((unsigned short)svrPort); + serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + + int optval = 1; + int r = + setsockopt(listenFD, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + if (r != 0) { + Error("[%s] Cannot enable SO_REUSEADDR option.", NAME); + } + + signal(SIGPIPE, SIG_IGN); + + if (bind(listenFD, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) != 0) { + Error("[%s] Cannot bind to port %d.", NAME, svrPort); + } + + if (listen(listenFD, 16) != 0) { + Error("[%s] Cannot listen to port %d.", NAME, svrPort); + } + + nConns = 0; + memset(peers, 0, sizeof(peers)); + peers[0].fd = listenFD; + peers[0].events = POLLRDNORM; + memset(connStat, 0, sizeof(connStat)); + + int connID = 0; + while (1) { + // monitor the listening sock and data socks, nConn+1 in total + int nReady = poll(peers, nConns + 1, POLL_TIMEOUT); + if (nReady < 0) { + Error("[%s] Invalid poll() return value.", NAME); + } + + struct sockaddr_in clientAddr; + socklen_t clientAddrLen = sizeof(clientAddr); + + struct timeb currentTime; + ftime(¤tTime); + + // new incoming connection + if ((peers[0].revents & POLLRDNORM) && (nConns < maxConcurrency)) { + int fd = accept(listenFD, (struct sockaddr *)&clientAddr, &clientAddrLen); + if (fd != -1) { + SetNonBlockIO(fd); + nConns++; + Log("[%s] Accepted connection %d", NAME, nConns); + peers[nConns].fd = fd; + peers[nConns].events = POLLRDNORM; + peers[nConns].revents = 0; + + memset(&connStat[nConns], 0, sizeof(struct CONN_STAT)); + ftime(&connStat[nConns].lastTime); + connStat[nConns].recInitHeader = + false; // waiting to recieve initial header + connStat[nConns].expectingHeader = + true; // message to be read is a header + connStat[nConns].nToDo = HEADER_LEN; + + buf[nConns] = (BYTE *)malloc(BUF_LEN); + memset(buf[nConns], 0, BUF_LEN); + } + + if (--nReady <= 0) + continue; + } + + for (int i = 1; i <= nConns; i++) { + // Log("nConns: %d peers[%d].revents: %02X (POLLRDNORM %02X, POLLWRNORM + // %02X, POLLERR %02X, POLLHUP %02X)", nConns, i, peers[i].revents, + // POLLRDNORM, POLLWRNORM, POLLERR, POLLHUP); + double timeDiff = + (currentTime.time + currentTime.millitm / (double)1000.0f) - + (connStat[i].lastTime.time + + connStat[i].lastTime.millitm / (double)1000.0f); + + if (peers[i].revents & + (POLLRDNORM | POLLERR | + POLLHUP)) { // TODO: break out POLLERR and POLLHUP? + // Log("Servicing connection %d, POLLHUP: %d", i, peers[i].revents & + // POLLHUP); + // Process initial communication + if (connStat[i].recInitHeader == false) { + if (Recv_NonBlocking(peers[i].fd, buf[i], &connStat[i], &peers[i]) < + 0) { + Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, + i); + RemoveConnection(i); + goto NEXT_CONNECTION; + } + + if (connStat[i].nToDo == 0) { + Header header; + header.decode(buf[i]); + // Log("Initial communication for connection %d:", i); + + if (!((header.m_command == CONNECT) | (header.m_command == SENDF) | + (header.m_command == SENDF2) | (header.m_command == GETF))) { + Log("[%s] WARNING: Unexpected initial connection command %s", + NAME, com2str(header.m_command)); + } + + connStat[i].recInitHeader = true; + connStat[i].expectingHeader = + true; // flag needed for processing below + if (processReception(i) != 0) { + Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, + i); + RemoveConnection(i); + goto NEXT_CONNECTION; + } + } + } // End initial communication + else { // Standard polling recieve + // Log("Standard polling recieve/send: connStat[i].direction: %02X", + // connStat[i].direction); + if (connStat[i].direction == C2S) { + if (Recv_NonBlocking(peers[i].fd, buf[i], &connStat[i], &peers[i]) < + 0) { + Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, + i); + RemoveConnection(i); + goto NEXT_CONNECTION; + } + + if (connStat[i].nToDo == 0) { + if (processReception(i) != 0) { + Log("[%s] Line %d: Removing connection %d", NAME, + __LINE__, i); + RemoveConnection(i); + goto NEXT_CONNECTION; + } + } + } // end C2S + } // End standard recieve + } // if POLLRDNORM + + // a data socket is writable + if (peers[i].revents & POLLWRNORM) { + // Log("Sending nonblocking!"); + if (Send_NonBlocking(peers[i].fd, buf[i], &connStat[i], &peers[i]) < + 0) { + Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, i); + RemoveConnection(i); + goto NEXT_CONNECTION; + } + } + + // a s2c connection hasn't been used in a while? Check that the client is + // still there with a ping + if ((connStat[i].direction == S2C) && (timeDiff > S2C_SERVICE_FREQ) && + (connStat[i].nToDo == 0) && (connStat[i].messageLen == 0) && + (connStat[i].nBuffered == 0)) { + // Log("[%s] Pinging [%s] on connection %d due to timeout", SERVER_NAME, + // connStat[i].name, i); + Header header; + header.setFlags(S2C, SUCCESS, PRIV, SIGN); + memcpy(header.m_name, NAME, MAX_USERNAME_LEN); + header.m_command = PING; + header.m_size = 0; + + header.encode(&buf[i][connStat[i].messageLen]); + connStat[i].nBuffered = HEADER_LEN + header.m_size; + connStat[i].messageLen = HEADER_LEN + header.m_size; + connStat[i].nToDo = HEADER_LEN + header.m_size; + + peers[i].events |= POLLWRNORM; + } + + NEXT_CONNECTION: + asm("nop"); + // if (--nReady <= 0) break; + } // for + } // while +} // do_server + +// buf has all buffers +// Stat has all stat structs +// i is the index of the link being processed +int processReception(int i) { + Header header; + header.decode(buf[i]); + // header.displayContents(true); + + if (connStat[i] + .expectingHeader) { // Expecting a header read sequence was completed + // Log("\nProcessing header reception. header.m_size: %d", header.m_size); + if (header.m_size > 0) { + connStat[i].expectingHeader = false; + connStat[i].nToDo = header.m_size; + } else if (header.m_size == 0) { + // printServerCommand(i); + doServerCommand(i); + connStat[i].expectingHeader = true; + connStat[i].nBuffered = 0; + connStat[i].nToDo = HEADER_LEN; + memset(buf[i], 0, BUF_LEN); + } else { + return -1; // Error, signal to caller to end connection + } + } else { // expecting a data read sequence was completed + // Log("\nProcessing data reception."); + // printServerCommand(i); + doServerCommand(i); + if (connStat[i].direction == C2S) { // CONNECT events for S2C can get here + connStat[i].expectingHeader = true; + connStat[i].nBuffered = 0; + connStat[i].nToDo = HEADER_LEN; + memset(buf[i], 0, BUF_LEN); + } + } + + if (connStat[i].shouldClose) { + Log("[%s] Line %d: Removing connection %d due to shouldClose", NAME, + __LINE__, i); + RemoveConnection(i); + } + + return 0; +} + +void doServerCommand(int i) { + // Assume the buf[i] has a message at the beginning of the buffers + Header header; + header.decode(buf[i]); + + struct timeb currentTime; + ftime(¤tTime); + + char message[5000] = {0}; + char username[20] = {0}; + char password[20] = {0}; + Header outHeader; + int numLoggedIn = 0; + bool foundActiveUser = false; + int s2cConnNumb = -1; + char server_filename[5000] = {0}; + char filename[5000] = {0}; + int filenameLen, fileLen, offset, nextFileId, fileId; + BYTE intbuf[5] = {0}; + FILE *fp; + + switch (header.m_command) { + case CONNECT: + buf2i(&buf[i][HEADER_LEN], connStat[i].id); + connStat[i].direction = header.m_direction; + connStat[i].linkType = MESSAGE_LINK; + connStat[i].lastTime = currentTime; + if (header.m_direction == C2S) { + Log("[---- : %s] id: %d, direction: C2S, connection number: %d.", + com2str(header.m_command), connStat[i].id, i); + connStat[i].expectingHeader = true; + connStat[i].nBuffered = 0; + connStat[i].nToDo = HEADER_LEN; + } else { + Log("[---- : %s] id: %d, direction: S2C, connection number: %d.", + com2str(header.m_command), connStat[i].id, i); + connStat[i].nBuffered = 0; + connStat[i].messageLen = 0; + connStat[i].nToDo = 0; + peers[i].events &= ~POLLRDNORM; + peers[i].events &= ~POLLWRNORM; + } + memset(buf[i], 0, BUF_LEN); + break; + case REGISTER: + memcpy(username, header.m_name, MAX_USERNAME_LEN); + memcpy(password, &buf[i][HEADER_LEN], header.m_size); + if (usernameQuery(USER_DATABASE, username) == true) { + Log("[---- : %s] Registration failed. Username [%s] is already " + "registered.", + com2str(header.m_command), username); + outHeader.setFlags(S2C, FAIL, PUB, SIGN); + outHeader.m_command = REGISTER; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Cannot register username ["); + strcat(message, username); + strcat(message, "]. Username already registered.\n"); + strcat(message, + "\tUsernames may only contain characters a-z, A-Z, or 0-9.\n"); + strcat(message, "\tUsernames may be 4 to 8 characters long."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + } else { + Log("[---- : %s] Successful registration (usr, pswd): %s %s", + com2str(header.m_command), username, password); + recordEntry(USER_DATABASE, username, password); + outHeader.setFlags(S2C, SUCCESS, PUB, SIGN); + outHeader.m_command = REGISTER; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Successfully registered "); + strcat(message, username); + strcat(message, "."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + } + break; + case LOGIN: + memcpy(username, header.m_name, MAX_USERNAME_LEN); + memcpy(password, &buf[i][HEADER_LEN], header.m_size); + if (loginQuery(USER_DATABASE, username, password) == true) { + // Check that user is not currently logged in + for (int x = 1; x <= nConns; x++) { + if ((connStat[x].isLoggedIn == true) && + (strcmp(username, connStat[x].name) == 0)) { + Log("[%s : %s] Login failed. User is already logged in.", + header.m_name, com2str(header.m_command)); + foundActiveUser = true; + outHeader.setFlags(S2C, FAIL, PUB, SIGN); + outHeader.m_command = LOGIN; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Login failed. "); + strcat(message, username); + strcat(message, " is logged in on another device."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + break; + } + } + + if (!foundActiveUser) { + for (int x = 1; x <= nConns; x++) { + if (connStat[i].id == connStat[x].id) { + memcpy(connStat[x].name, username, MAX_USERNAME_LEN); + // Log("Connection %d (%s) is loggin in.", x, connStat[x].name); + connStat[x].isLoggedIn = true; + } + } + Log("[%s : %s] Successful login (usr, pswd): %s %s", connStat[i].name, + com2str(header.m_command), username, password); + outHeader.setFlags(S2C, SUCCESS, PUB, SIGN); + outHeader.m_command = LOGIN; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Welcome "); + strcat(message, username); + strcat(message, "."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, true, outHeader, message); + } + } else { + Log("[---- : %s] Login failed (usr, pswd): %s %s", + com2str(header.m_command), username, password); + outHeader.setFlags(S2C, FAIL, PUB, SIGN); + outHeader.m_command = LOGIN; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Login failed: Invalid username or password."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + } + break; + case LOGOUT: + if (connStat[i].isLoggedIn) { + for (int x = 1; x <= nConns; x++) { + if (connStat[i].id == connStat[x].id) { + // Log("Connection %d (user %s) is logging off.", x, + // connStat[x].name); + connStat[x].isLoggedIn = false; + foundActiveUser = true; + } + } + Log("[%s : %s] Successful logout.", connStat[i].name, + com2str(header.m_command)); + outHeader.setFlags(S2C, SUCCESS, PUB, SIGN); + outHeader.m_command = LOGOUT; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Goodbye "); + strcat(message, connStat[i].name); + strcat(message, ". Come back soon."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + } else { + Log("[---- : %s] Failed: Connection %d is not logged in.", + com2str(header.m_command), i); + outHeader.setFlags(S2C, FAIL, PUB, SIGN); + outHeader.m_command = LOGOUT; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Logout failed. You are not logged in."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + } + break; + case SEND: + case SENDA: + case SEND2: + case SENDA2: + if (connStat[i].isLoggedIn) { + outHeader.setFlags(S2C, SUCCESS, PUB, header.m_trace); + outHeader.m_command = header.m_command; + if (header.m_trace == SIGN) + memcpy(outHeader.m_name, connStat[i].name, strlen(connStat[i].name)); + else + memcpy(outHeader.m_name, ANON, strlen(ANON)); + memcpy(message, &buf[i][HEADER_LEN], header.m_size); + outHeader.m_size = strlen(message); + if ((header.m_command == SEND) || (header.m_command == SENDA)) { + Log("[%s : %s] %s", connStat[i].name, com2str(header.m_command), + message); + sendMessageToAllLoggedIn(outHeader, message); + } else { + memcpy(username, header.m_name, MAX_USERNAME_LEN); + Log("[%s : %s %s] %s", connStat[i].name, com2str(header.m_command), + username, message); + sendMessageToName(username, outHeader, message); + } + } else { + Log("[%s : %s] Failed: Connection %d is not logged in.", connStat[i].name, + com2str(header.m_command), i); + outHeader.setFlags(S2C, FAIL, PUB, SIGN); + outHeader.m_command = header.m_command; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Users must be logged in to '"); + strcat(message, com2str(header.m_command)); + strcat(message, "'. Login with '"); + strcat(message, com2str(LOGIN)); + strcat(message, " [username] [password]'."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + } + break; + case SENDF: // Initial connection + case SENDF2: // Initial connection + buf2i(&buf[i][HEADER_LEN], connStat[i].id); + // Log("This connection's id: %d", connStat[i].id); + connStat[i].direction = header.m_direction; + connStat[i].linkType = FILE_LINK; + connStat[i].lastTime = currentTime; + connStat[i].expectingHeader = true; + connStat[i].nBuffered = 0; + connStat[i].nToDo = HEADER_LEN; + connStat[i].shouldClose = + true; // flag to close this connection once it's processed + + for (int x = 1; x <= nConns; x++) { + if ((connStat[x].direction == C2S) && + (connStat[x].linkType == MESSAGE_LINK) && + (connStat[x].id == connStat[i].id)) { + strcat(connStat[i].name, connStat[x].name); + connStat[i].isLoggedIn = connStat[x].isLoggedIn; + foundActiveUser = true; + s2cConnNumb = x; + break; + } + } + + if (s2cConnNumb == -1) { + Log("Enexpected state. Recieved %s on new connection without S2C " + "connection for messages.", + com2str(header.m_command)); + } else if (!connStat[i].isLoggedIn) { + Log("[%s : %s] Failed: Connection %d is not logged in.", connStat[i].name, + com2str(header.m_command), i); + outHeader.setFlags(S2C, FAIL, PUB, SIGN); + outHeader.m_command = SEND; // avoid file transfer logic on client side + memcpy(outHeader.m_name, NAME, strlen(NAME)); + strcat(message, "Users must be logged in to '"); + strcat(message, com2str(header.m_command)); + strcat(message, "'. Login with '"); + strcat(message, com2str(LOGIN)); + strcat(message, " [username] [password]'."); + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + } else { + // Save the file + nextFileId = getNumEntries(FILE_DATABASE); + if (nextFileId == -1) { + Log("Enexpected state. %s next File Id of -1.", FILE_DATABASE); + } else { + snprintf(server_filename, sizeof(server_filename), "%s/%d_", FILES_DIR, + nextFileId); + offset = HEADER_LEN + 4; + buf2i(&buf[i][offset], filenameLen); // first 4 bytes is client id + offset += 4; + memcpy(&server_filename[strlen(server_filename)], &buf[i][offset], + filenameLen); + memcpy(filename, &buf[i][offset], filenameLen); + offset += filenameLen; + buf2i(&buf[i][offset], fileLen); + offset += 4; + // Log("[%s : %s] Saving file: %s", server_filename); + fp = fopen(FILE_DATABASE, "a"); + fputs(server_filename, fp); + fputs("\n", fp); + fclose(fp); + buf2file(&buf[i][offset], fileLen, server_filename); + + // Send SENDF/SENDF2 message to interested parties + outHeader.setFlags(S2C, SUCCESS, header.m_recipient, + SIGN); // will update recipient as necessary + outHeader.m_command = header.m_command; + memcpy(outHeader.m_name, connStat[i].name, strlen(connStat[i].name)); + i2buf(nextFileId, intbuf); + memcpy(message, intbuf, 4); + offset = 4; + memcpy(&message[offset], &buf[i][HEADER_LEN + 8], filenameLen); + offset += filenameLen; + outHeader.m_size = offset; + + if (header.m_command == SENDF) { + Log("[%s : %s] %s", connStat[i].name, com2str(header.m_command), + filename); + sendMessageToAllLoggedInExceptSender(connStat[i].id, outHeader, + message); + } else { + memcpy(username, header.m_name, MAX_USERNAME_LEN); + Log("[%s : %s %s] %s", connStat[i].name, com2str(header.m_command), + username, filename); + sendMessageToName(username, outHeader, message); + } + } + } + break; + case GETF: + buf2i(&buf[i][HEADER_LEN], connStat[i].id); + // header name is origional sender. Find client's name with client id + for (int x = 1; x <= nConns; x++) { + if ((connStat[i].id == connStat[x].id) && + (connStat[x].direction == C2S) && + (connStat[x].linkType == MESSAGE_LINK)) { + memcpy(connStat[i].name, connStat[x].name, MAX_USERNAME_LEN); + break; + } + } + buf2i(&buf[i][HEADER_LEN + 4], fileId); + connStat[i].direction = S2C; + connStat[i].linkType = FILE_LINK; + connStat[i].lastTime = currentTime; + // no need for shouldClose. Client will close connection and periodic ping will + // detect closure + + snprintf(server_filename, sizeof(server_filename), "%s/%d_", FILES_DIR, + fileId); + memcpy(&server_filename[strlen(server_filename)], &buf[i][HEADER_LEN + 8], + header.m_size - 8); + + memcpy(filename, &buf[i][HEADER_LEN + 8], header.m_size - 8); + filenameLen = strlen(filename); + + // Log("Getting file %s", server_filename); + memset(buf[i], 0, BUF_LEN); + + // Check that the file exists + fp = fopen(server_filename, "r"); + if (fp) { + fclose(fp); + outHeader.setFlags(S2C, SUCCESS, header.m_recipient, + SIGN); // will update recipient as necessary + if (header.m_command == SENDF2) + outHeader.m_recipient = PRIV; + outHeader.m_command = header.m_command; + memcpy(outHeader.m_name, header.m_name, + MAX_USERNAME_LEN); // pass along origional sender + offset = HEADER_LEN; + i2buf(filenameLen, &buf[i][offset]); // encode filename length + offset += 4; + memcpy(&buf[i][offset], filename, + strlen(filename)); // encode the filename + offset += strlen(filename); + fileLen = file2buf(server_filename, &buf[i][offset + 4]); + i2buf(fileLen, &buf[i][offset]); + offset += 4 + fileLen; + outHeader.m_size = offset - HEADER_LEN; + outHeader.encode(buf[i]); + + connStat[i].nBuffered = offset; + connStat[i].messageLen = offset; + connStat[i].nToDo = offset; + peers[i].events &= ~POLLRDNORM; + peers[i].events |= POLLWRNORM; + + Log("[%s : %s] %s", connStat[i].name, com2str(header.m_command), + filename); + } else { + Log("File not found: %s", filename); + // TODO: Send message if file is not found + } + break; + case LIST: + Log("[%s : %s]", connStat[i].name, com2str(header.m_command)); + outHeader.setFlags(S2C, SUCCESS, PUB, SIGN); + outHeader.m_command = LIST; + memcpy(outHeader.m_name, NAME, strlen(NAME)); + for (int x = 1; x <= nConns; x++) { + if ((connStat[x].direction == C2S) && + (connStat[x].linkType == MESSAGE_LINK) && (connStat[x].isLoggedIn)) { + numLoggedIn++; + } + } + snprintf(message, sizeof(message), "%d", numLoggedIn); + strcat(message, " user(s) currently logged in:"); + for (int x = 1; x <= nConns; x++) { + if ((connStat[x].direction == C2S) && + (connStat[x].linkType == MESSAGE_LINK) && (connStat[x].isLoggedIn)) { + strcat(message, "\n\t"); + strcat(message, connStat[x].name); + } + } + outHeader.m_size = strlen(message); + sendMessageToId(connStat[i].id, false, outHeader, message); + break; + case PING: + // Log("Ping from %s", header.m_name); + asm("nop"); // do nothing + break; + default: + Log("No doServerCommand() for command %s", com2str(header.m_command)); + } +} + +// Send the message to every S2C with the specified id. If the reqLoggedIn +// flag is set, require that the connection is logged in. +void sendMessageToId(int id, bool reqLoggedIn, Header header, char *message) { + for (int i = 1; i <= nConns; i++) { + if ((connStat[i].direction == S2C) && (id == connStat[i].id)) { + if (reqLoggedIn) { + if (connStat[i].isLoggedIn) { + prepareMessage(i, header, message); + } + } else { + prepareMessage(i, header, message); + } + } + } +} + +// Send the message to every S2C which is logged in. +void sendMessageToAllLoggedIn(Header header, char *message) { + for (int i = 1; i <= nConns; i++) { + if ((connStat[i].direction == S2C) && connStat[i].isLoggedIn) { + prepareMessage(i, header, message); + } + } +} + +// Send the message to every s2cConnNumb +void sendMessageToAllLoggedInExceptSender(int senderID, Header header, + char *message) { + for (int i = 1; i <= nConns; i++) { + if ((connStat[i].direction == S2C) && connStat[i].isLoggedIn && + (connStat[i].id != senderID)) { + prepareMessage(i, header, message); + } + } +} + +// Send the message to all loged in users with the specified name. +void sendMessageToName(char *name, Header header, char *message) { + for (int i = 1; i <= nConns; i++) { + if ((connStat[i].direction == S2C) && connStat[i].isLoggedIn && + (strcmp(name, connStat[i].name) == 0)) { + prepareMessage(i, header, message); + } + } +} + +// Load a message into a connection +void prepareMessage(int i, Header header, char *message) { + if (connStat[i].direction == S2C) { + int offset = connStat[i].nBuffered; + + // Log("Preparing message for connection %d", i); + // header.displayContents(true); + // Log("Message: |%s|", message); + // Log("offset: %d, nBuffered: %d, nToDo: %d: messageLen: %d", offset, + // connStat[i].nBuffered, connStat[i].nToDo, connStat[i].messageLen); + + header.encode(&buf[i][offset]); + memcpy(&buf[i][offset + HEADER_LEN], message, header.m_size); + + connStat[i].nBuffered += HEADER_LEN + header.m_size; + if (connStat[i].nToDo == 0) { + connStat[i].messageLen = HEADER_LEN + header.m_size; + connStat[i].nToDo = HEADER_LEN + header.m_size; + } + + peers[i].events |= POLLWRNORM; + } else { + Log("[%s] WARNING: attempted to write to C2S connection %d", NAME, + i); + } +} + +void printServerCommand(int i) { + Header h; + h.decode(buf[i]); + + Log("[%s]: [%s, %s, %s, %s] [%s] [%s] [%d]", connStat[i].name, + !h.m_direction ? "C2S" : "S2C", !h.m_flag ? "SUCCESS" : "FAIL", + !h.m_recipient ? "PUB" : "PRIV", !h.m_trace ? "SIGN" : "ANNON", + com2str(h.m_command), h.m_name, h.m_size); + + if (h.m_size > 0) + Log("\tData: [%s]", buf[i] + HEADER_LEN); +} + +int main(int argc, char **argv) { + if (argc != 2) { + Log("Usage: %s reset", argv[0]); + Log("Usage: %s [server Port]", argv[0]); + return -1; + } + + if (strcmp("reset", argv[1]) == 0) { + Log("Resetting server databases"); + clearDatabase(USER_DATABASE); + clearDatabase(FILE_DATABASE); + + int status; + pid_t cpid; + + if ((cpid = fork()) == -1) { + perror("fork"); + return 1; + } + + if (cpid == 0) { // Child process executes "rm -rf users" + char command[40] = "rm -rf files"; // only way this worked + char *args[5] = {NULL}; + args[0] = strtok(command, " "); + for (int i = 1; i < 3; i++) + args[i] = strtok(NULL, " "); + execvp(args[0], args); + return 0; + } else { // Parent process waits for "rm -rf users" to finish + pid_t rpid; + int status; + if ((rpid = wait(NULL)) == -1) + perror("wait"); + else { + Log("Clearing file storage"); + if (mkdir(FILES_DIR, S_IRWXU | S_IRWXG | S_IRWXO) != 0) { + perror("mkdir"); + return -1; + } + } + return 0; + } + } + + // Make files directory if it doesn't exist + DIR *pDir; + struct stat s; + + if (stat("files", &s) != 0) { + // The files directory doesn't exist + if (mkdir("files", S_IRWXU | S_IRWXG | S_IRWXO) != 0) { + perror("mkdir"); + return -1; + } + } + + int port = atoi(argv[1]); + DoServer(port); + + return 0; +} -- cgit v1.2.3