#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chat.h" typedef enum { NO_MODE = 0, SERVER_MODE = 1, CLIENT_MODE = 2 } chatmode_t; typedef struct { sockaddr_in new_addr; socklen_t new_addr_siz; int aux; } wclient_args_t; chatmode_t mode = NO_MODE; const int LOG_LENGTH = 10; int line_position = 0; const int CURSOR_DEFAULT_POSITION_X = 2; const int CURSOR_DEFAULT_POSITION_Y = 12; struct timeval time_start; struct timeval time_end; int bytes_read; int bytes_written; int listen_port = 9999; std::string listen_ip_address = "127.0.0.1"; std::string log_path; int socket_descriptor_server; int socket_descriptor_client; pthread_t client_wait_thread; int main(int argc, char *argv[]) { char *usr_in = new char[1024]; bool quit = false; if (argc > 1 && (!strcmp(argv[1], "client"))) mode = CLIENT_MODE; else mode = SERVER_MODE; initscr(); use_default_colors(); line_position = count_lines(log_path) - LOG_LENGTH + 1; switch (mode) { case CLIENT_MODE: log_append(log_path, "CLIENT MODE"); client_start(); break; case SERVER_MODE: log_append(log_path, "SERVER MODE"); server_start(listen_port); break; default: goto panic_no_mode; break; } while (!quit) { file_disp(log_path, line_position, LOG_LENGTH); if (count_lines(log_path) > LOG_LENGTH) line_position++; /* prompt for input */ move(CURSOR_DEFAULT_POSITION_Y, 0); printw("> "); /* await input */ getstr(usr_in); log_append(log_path, usr_in); send(socket_descriptor_client, (char *)usr_in, strlen(usr_in), 0); } /* Final exit point if everything goes OK */ endwin(); switch (mode) { case CLIENT_MODE: client_stop(); break; case SERVER_MODE: server_stop(); break; default: goto panic_no_mode; break; } return 0; panic_no_mode: fprintf(stderr, "-----FATAL ERROR, THIS SHOULD NEVER HAPPEN-----\n"); fprintf(stderr, "You may ask yourself... how do I work this?\n"); fprintf(stderr, "And you may find yourself... past the point of return 0;!\n"); fprintf(stderr, "And you may ask yourself... well, how did I get here?\n"); if (mode == NO_MODE) fprintf(stderr, "Fatal: Mode was not set!\n"); else fprintf(stderr, "Fatal: Mode was set to invalid enum %i\n", mode); return 1; } void clear_rows(int s, int e) { /* preserve cursor location */ int x_bef; int y_bef; getyx(stdscr, y_bef, x_bef); for (int i = s; i < e; i++) { move(i, 0); clrtoeol(); } move(y_bef, x_bef); } int file_disp(std::string p, int lnstart, int lncount) { std::ifstream f(p); if (!f) return 1; int i = 0; int ln_no = 0; std::string ln; clear_rows(0, lncount + 1); /* clear chat window */ while (getline(f, ln) && i <= ln_no + lnstart + 1) { if (i >= lnstart) { move(ln_no, 0); printw("%s", ln.c_str()); ln_no++; } i++; } move(CURSOR_DEFAULT_POSITION_Y, CURSOR_DEFAULT_POSITION_X); refresh(); return 0; } int count_lines(std::string p) { int c = 0; std::ifstream f(p); if (!f) return c; /* Apparently this is the most efficient way to count all lines in a file. How revolting! */ c = std::count(std::istreambuf_iterator(f), std::istreambuf_iterator(), '\n'); return c; } void * await_client(void *a) { wclient_args_t *args = static_cast(a); socket_descriptor_client = accept(socket_descriptor_server, (sockaddr *)&args->new_addr, &args->new_addr_siz); if (socket_descriptor_client >= 0) { log_append(log_path, "client connected"); if (count_lines(log_path) > LOG_LENGTH) line_position++; file_disp(log_path, line_position, LOG_LENGTH); } delete args; return poll_client(); } void * poll_client(void) { char msg[1024]; for (;;) { /* listen for msg from client */ memset(&msg, 0, sizeof(msg)); /* clear buf */ bytes_read += recv(socket_descriptor_client, (char *)&msg, sizeof(msg), 0); log_append(log_path, msg); if (count_lines(log_path) > LOG_LENGTH) line_position++; file_disp(log_path, line_position, LOG_LENGTH); } return nullptr; } void server_start(int port) { auto *a = new wclient_args_t{}; sockaddr_in srv_addr; sockaddr_in newsock; socklen_t newsock_siz = sizeof(newsock); int bstat; int rc; /* set up socket and connection tools */ bzero((char *)&srv_addr, sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); srv_addr.sin_port = htons(port); /* open stream-oriented socket w inet addr and track sock descriptor */ socket_descriptor_server = socket(AF_INET, SOCK_STREAM, 0); if (socket_descriptor_server < 0) goto panic_if_no_server_sock; /* bind sock to its local addr */ bstat = bind(socket_descriptor_server, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); if (bstat < 0) goto panic_if_no_server_sock; log_append(log_path, "Waiting for a client to connect..."); /* listen for up to 5 simultaneous requests */ listen(socket_descriptor_server, 5); a->new_addr = newsock; a->new_addr_siz = newsock_siz; rc = pthread_create(&client_wait_thread, nullptr, await_client, a); pthread_detach(client_wait_thread); log_append(log_path, "Server started successfully."); gettimeofday(&time_start, NULL); return; panic_if_no_server_sock: endwin(); fprintf(stderr, "Fatal: Could not establish server socket!\n"); exit(1); } void server_stop(void) { long e = time_end.tv_sec - time_start.tv_sec; gettimeofday(&time_end, NULL); close(socket_descriptor_server); /* Don't just die silently */ puts("********Session********"); printf("Bytes written: %i\nBytes Read: %i\n", bytes_written, bytes_read); printf("Elapsed time: %ld\n secs\n", e); puts("Connection closed..."); } void client_stop(void) { long e = time_end.tv_sec - time_start.tv_sec; gettimeofday(&time_end, NULL); close(socket_descriptor_client); puts("********Session********"); printf("Bytes written: %i\nBytes read: %i\n", bytes_written, bytes_read); printf("Elapsed time: %ld\n secs\n", e); puts("Connection closed..."); } void * poll_server(void *args) { wclient_args_t *a = static_cast(args); int sock_desc = (int)a->aux; char msg[1024]; for (;;) { memset(&msg, 0, sizeof(msg)); bytes_read += recv(sock_desc, (char *)&msg, sizeof(msg), 0); log_append(log_path, msg); if (count_lines(log_path) > LOG_LENGTH) line_position++; file_disp(log_path, line_position, LOG_LENGTH); } } void client_start(void) { char msg[1024]; int bread; int bwrit; int rc; sockaddr_in send_addr; struct timeval time_start; struct timeval time_end; struct hostent *host = gethostbyname(listen_ip_address.c_str()); auto *a = new wclient_args_t{}; bzero((char *)&send_addr, sizeof(send_addr)); send_addr.sin_family = AF_INET; send_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)*host->h_addr_list)); send_addr.sin_port = htons(listen_port); socket_descriptor_client = socket(AF_INET, SOCK_STREAM, 0); attempt_client_connection: int stat = connect(socket_descriptor_client, (sockaddr *)&send_addr, sizeof(send_addr)); if (stat < 0) { log_append(log_path, "Error connecting to socket, retrying..."); goto attempt_client_connection; } log_append(log_path, "Connected to the server!"); bread = bwrit = 0; gettimeofday(&time_start, NULL); a->aux = socket_descriptor_client; rc = pthread_create(&client_wait_thread, nullptr, poll_server, a); pthread_detach(client_wait_thread); } int log_append(std::string p, std::string ln) { std::ofstream f; f.open(p, std::ios_base::app); if (!f.is_open()) return 1; f << ln << std::endl; f.close(); // "this probably would help but theres too much broken stuff right now to be sure" - scott // bool inclnum should be added to function if we do decide to use this block: // if (inclnum) // line_position++; return 0; }