├── .gitignore ├── CMakeLists.txt ├── Dockerfile ├── TODO.md ├── lib ├── irc.h ├── mem-list.h ├── message-queue.h ├── socket.h └── user.h └── src ├── CMakeLists.txt ├── irc ├── CMakeLists.txt └── irc.c ├── mem-list ├── CMakeLists.txt └── mem-list.c ├── message-queue ├── CMakeLists.txt └── message-queue.c ├── neovim-irc.c ├── socket ├── CMakeLists.txt └── socket.c ├── test.c └── user ├── CMakeLists.txt └── user.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .clangd 3 | compile_commands.json 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | # set the project name 4 | set(CMAKE_BUILD_TYPE Debug) 5 | project(neovim-irc VERSION 1.0) 6 | 7 | set(PROJECT_LIB_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/lib) 8 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 9 | 10 | add_subdirectory(src) 11 | 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | WORKDIR /app 3 | ENV DEBIAN_FRONTEND noninteractive 4 | RUN ls -la 5 | RUN apt update && apt-get install -y build-essential cmake 6 | COPY . . 7 | RUN mkdir -p /app/build 8 | RUN chown -R nobody:nogroup /app 9 | USER nobody 10 | WORKDIR /app/build 11 | RUN cmake .. 12 | RUN make 13 | EXPOSE 1337/tcp 14 | CMD ["./src/neovim-irc"] 15 | 16 | 17 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## Things to do 2 | * MESSAGES: Clean up the print statements such that I can control the type of 3 | output. This would go obviously really nice for the YT video (getting pwn'd 4 | with segfaults and the likes). 5 | 6 | * If you provide a user name that is not yours, kick em 7 | 8 | * If you provide a server that is not irc.theprimeagen.tv, kick em 9 | 10 | * PINGS and PONGS are not implemented. 11 | 12 | -------------------------------------------------------------------------------- /lib/irc.h: -------------------------------------------------------------------------------- 1 | #ifndef IRC_HEADER_H 2 | #define IRC_HEADER_H 3 | 4 | #include 5 | 6 | #include "mem-list.h" 7 | #include "user.h" 8 | 9 | typedef struct IrcMessage { 10 | MemoryNode* original; 11 | MemoryNode* copied; 12 | char *ptr; 13 | int from_fd; 14 | char* from; 15 | char* to; 16 | char* message; 17 | char* cmd; 18 | int hasError; 19 | char* error; 20 | } IrcMessage; 21 | 22 | typedef enum IrcError { 23 | IrcMessageTooLong = -1, 24 | IrcNameTooLong = -2, 25 | IrcToNameTooLong = -3, 26 | IrcInvalidCharacters = -4, 27 | } IrcError; 28 | 29 | #define PRIVMSG "PRIVMSG" 30 | #define JOIN "JOIN" 31 | #define PING "PING" 32 | #define PONG "PONG" 33 | 34 | void irc_new_fd(int fd); 35 | void irc_parse_message(IrcMessage* msg); 36 | void irc_print_message(IrcMessage* msg); 37 | int irc_process_message(IrcMessage* msg); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /lib/mem-list.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM_LIST_HEADER_H 2 | #define MEM_LIST_HEADER_H 3 | 4 | #include 5 | 6 | #define MEMORY_MAX_SIZE 1024 7 | 8 | typedef struct MemoryNode { 9 | struct MemoryNode* prev; 10 | int length; 11 | char data[MEMORY_MAX_SIZE]; 12 | } MemoryNode; 13 | 14 | MemoryNode* get_memory(); 15 | void release_memory(MemoryNode* mem); 16 | 17 | #endif 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/message-queue.h: -------------------------------------------------------------------------------- 1 | #ifndef MESSAGE_QUEUE_HEADER_H 2 | #define MESSAGE_QUEUE_HEADER_H 3 | 4 | #include 5 | 6 | #include "mem-list.h" 7 | #include "user.h" 8 | 9 | typedef struct MessageQueue { 10 | pthread_mutex_t lock; 11 | } MessageQueue; 12 | 13 | #endif 14 | 15 | -------------------------------------------------------------------------------- /lib/socket.h: -------------------------------------------------------------------------------- 1 | #include "irc.h" 2 | #include "user.h" 3 | #include "mem-list.h" 4 | 5 | void read_line(int sock, User* user); 6 | ssize_t write_line(int sockd, char* vptr, size_t n); 7 | int read_from_socket(int conn); 8 | -------------------------------------------------------------------------------- /lib/user.h: -------------------------------------------------------------------------------- 1 | #ifndef USER_HEADER_H 2 | #define USER_HEADER_H 3 | 4 | #include "mem-list.h" 5 | 6 | typedef enum IrcConnectionState { 7 | IrcStateWaitingToJoin, 8 | IrcStateReady, 9 | IrcStateDisconnected, 10 | } IrcConnectionState; 11 | 12 | typedef enum UserState { 13 | UserStateWaitingForData, 14 | UserStatePartiallyRead, 15 | UserStateHasData, 16 | UserStateClosed, 17 | UserStateError, 18 | } UserState; 19 | 20 | typedef struct User { 21 | int from_fd; 22 | char* name; 23 | int relative_message_count; 24 | unsigned long long int last_pong_time; 25 | IrcConnectionState irc_state; 26 | UserState state; 27 | MemoryNode* scratch_data; 28 | } User; 29 | 30 | const char* user_state_to_string(IrcConnectionState state); 31 | int insert_user(User* user); 32 | User* create_user(int fd); 33 | User* find_user(int fd); 34 | void delete_user(User* user); 35 | User** get_user_list(); 36 | int get_users_size(); 37 | 38 | // PUT THIS SOMEWHERE ELSE 39 | long long current_timestamp(); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # add_subdirectory(system-commands) 2 | # add_subdirectory(hashmap) 3 | # add_subdirectory(twitch) 4 | add_subdirectory(socket) 5 | add_subdirectory(user) 6 | add_subdirectory(irc) 7 | add_subdirectory(mem-list) 8 | add_subdirectory(message-queue) 9 | # #add_subdirectory(comms) 10 | # 11 | # list(APPEND EXTRA_LIBS system-commands) 12 | # list(APPEND EXTRA_LIBS json-c) 13 | # list(APPEND EXTRA_LIBS twitch) 14 | # list(APPEND EXTRA_LIBS hashmap) 15 | # list(APPEND EXTRA_LIBS websockets) 16 | list(APPEND EXTRA_LIBS socket) 17 | list(APPEND EXTRA_LIBS user) 18 | list(APPEND EXTRA_LIBS irc) 19 | list(APPEND EXTRA_LIBS mem-list) 20 | list(APPEND EXTRA_LIBS message-queue) 21 | # 22 | # add the executable 23 | add_executable(neovim-irc neovim-irc.c) 24 | 25 | find_package(Threads REQUIRED) 26 | 27 | target_link_libraries(neovim-irc PUBLIC 28 | ${EXTRA_LIBS} 29 | Threads::Threads 30 | ) 31 | 32 | set(CMAKE_C_STANDARD 11) 33 | set(CMAKE_C_STANDARD_REQUIRED True) 34 | 35 | target_include_directories(neovim-irc PUBLIC 36 | "${PROJECT_BINARY_DIR}" 37 | ) 38 | 39 | target_include_directories(neovim-irc PRIVATE 40 | "${PROJECT_LIB_FOLDER}" 41 | ) 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/irc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(irc irc.c) 2 | 3 | target_include_directories(irc INTERFACE 4 | ${CMAKE_CURRENT_SOURCE_DIR} 5 | ) 6 | target_include_directories(irc PRIVATE 7 | ${PROJECT_LIB_FOLDER} 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /src/irc/irc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "user.h" 10 | 11 | int irc_validate_string(char* str) { 12 | int len = strlen(str); 13 | int retVal = 1; 14 | for (int i = 0; retVal && i < len; ++i) { 15 | retVal = isalnum(str[i]) || ispunct(str[i]); 16 | } 17 | return retVal; 18 | } 19 | 20 | // that seems pretty safe 21 | void add_name(User* user, char* name) { 22 | int len = strlen(name); 23 | user->name = (char*)malloc(sizeof(char) * len); 24 | if (!user->name) { 25 | printf("EVERYTHING IS BAD (add_name)\n"); 26 | exit(69420); 27 | } 28 | memcpy(user->name, name, len); 29 | } 30 | 31 | void delete_user_by_fd(int fd) { 32 | delete_user(find_user(fd)); 33 | } 34 | 35 | int irc_join(IrcMessage* msg) { 36 | User* usr = find_user(msg->from_fd); 37 | 38 | if (!usr) { 39 | printf("FAILED TO JOIN: could not find the user...\n"); 40 | return 0; 41 | } 42 | 43 | if (usr->irc_state != IrcStateWaitingToJoin) { 44 | printf("FAILED TO JOIN: usr->state != IrcStateWaitingToJoin\n"); 45 | return 0; 46 | } 47 | 48 | if (!irc_validate_string(msg->from)) { 49 | printf("FAILED TO JOIN: irc_valide_string = Name is invalid\n"); 50 | return 0; 51 | } 52 | 53 | usr->irc_state = IrcStateReady; 54 | 55 | return 1; 56 | } 57 | 58 | char* parse_till_token(char* buffer, char* token, int untilEnd) { 59 | char* next = strstr(buffer, token); 60 | 61 | // I think this will work... 62 | if (next == NULL && untilEnd) { 63 | next = strstr(buffer, "\r\n"); 64 | } 65 | 66 | if (next == NULL) { 67 | return NULL; 68 | } 69 | 70 | next[0] = 0; 71 | return ++next; 72 | } 73 | 74 | void irc_new_fd(int fd) { 75 | User* user = create_user(fd); 76 | user->irc_state = IrcStateWaitingToJoin; 77 | insert_user(user); 78 | } 79 | 80 | void irc_handle_pong(IrcMessage* msg) { 81 | User* usr = find_user(msg->from_fd); 82 | if (!usr) { 83 | // TODO:A does this happen? Lets not put logging and pretend it doesn. 84 | return; 85 | } 86 | usr->last_pong_time = current_timestamp(); 87 | } 88 | 89 | void irc_print_usr(User* usr) { 90 | printf("Usr(%d): %s\n", usr->from_fd, usr->name); 91 | printf("Count: %d Rel: %llu \n", usr->relative_message_count, current_timestamp() - usr->last_pong_time); 92 | } 93 | 94 | void irc_print_usr_by_msg(IrcMessage* msg) { 95 | User* usr = find_user(msg->from_fd); 96 | if (!usr) { 97 | printf("Usr(-1): Could not find User\n"); 98 | return; 99 | } 100 | irc_print_usr(usr); 101 | } 102 | 103 | void irc_print_message(IrcMessage* out) { 104 | printf("DEBUG MESSAGE %p\n", out->cmd); 105 | printf("IRC Message %s\n", out->cmd); 106 | if (out->hasError) { 107 | printf(" Has Error: %s\n", out->error); 108 | return; 109 | } 110 | 111 | if (strncmp(PRIVMSG, out->cmd, 7) == 0) { 112 | printf(" From: %s -> %s\n", out->from, out->to); 113 | printf(" Message: %s\n", out->message); 114 | } 115 | } 116 | 117 | void irc_parse_join(char* buffer, IrcMessage* out) { 118 | // TODO(future me): Who has lots of motivation, imagen a world with 119 | // channels... think about it 120 | // 121 | // Currently, do nothing, just a join is sufficient.... 122 | User* user = find_user(out->from_fd); 123 | add_name(user, out->from); 124 | } 125 | 126 | void irc_parse_message(IrcMessage* out) { 127 | // <:from> <:message> 128 | char* buffer = out->copied->data; 129 | char* next; 130 | 131 | next = parse_till_token(buffer, " ", 0); 132 | if (next == NULL) { 133 | out->hasError = 1; 134 | out->error = "Message started with ':' but hand no space delimiter"; 135 | return; 136 | } 137 | 138 | out->from = buffer; 139 | buffer = next; 140 | 141 | next = parse_till_token(buffer, " ", 1); 142 | if (next == NULL) { 143 | out->hasError = 1; 144 | out->error = "Message does not have a command."; 145 | return; 146 | } 147 | out->cmd = buffer; 148 | buffer = next; 149 | 150 | if (strncmp(JOIN, out->cmd, 4) == 0) { 151 | irc_parse_join(buffer, out); 152 | return; 153 | } 154 | 155 | next = parse_till_token(buffer, " ", 0); 156 | // MA X IM UM Performance 157 | // Everything, to touch each other 158 | if (next == NULL) { 159 | out->hasError = 1; 160 | out->error = "Message does not have a to user."; 161 | return; 162 | } 163 | 164 | out->to = buffer; 165 | buffer = next; 166 | 167 | next = parse_till_token(buffer, "\r\n", 0); 168 | if (next == NULL) { 169 | out->hasError = 1; 170 | out->error = "There is no Registered Nurse in the message."; 171 | return; 172 | } 173 | 174 | out->message = buffer + 1; 175 | } 176 | 177 | int irc_validate_message(IrcMessage* msg, User* user) { 178 | if (strncmp(msg->from, user->name, strlen(user->name)) != 0) { 179 | return 0; 180 | } 181 | 182 | return 1; 183 | } 184 | 185 | // this is terrible code 186 | int irc_process_message(IrcMessage* msg) { 187 | irc_print_usr_by_msg(msg); 188 | User* usr = find_user(msg->from_fd); 189 | 190 | // TODO: I hate this 191 | // TODONE: I'll love it 192 | if (strcmp(PONG, msg->cmd) == 0) { 193 | irc_handle_pong(msg); 194 | } else if (strcmp(PING, msg->cmd) == 0) { 195 | return 0; 196 | } else if (strcmp(JOIN, msg->cmd) == 0) { 197 | printf("processing the join command\n"); 198 | if (!irc_join(msg)) { 199 | return 0; 200 | } 201 | } else if (strcmp(PRIVMSG, msg->cmd) == 0 && irc_validate_message(msg, usr)) { 202 | irc_print_message(msg); 203 | } else { 204 | return 0; 205 | } 206 | 207 | return 1; 208 | } 209 | 210 | const char* irc_state_to_string(IrcConnectionState state) { 211 | switch (state) { 212 | case IrcStateDisconnected: 213 | return "Disconnected"; 214 | case IrcStateReady: 215 | return "Ready"; 216 | case IrcStateWaitingToJoin: 217 | default: 218 | return "WaitingToJoin"; 219 | } 220 | } 221 | 222 | -------------------------------------------------------------------------------- /src/mem-list/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(mem-list mem-list.c) 2 | 3 | target_include_directories(mem-list INTERFACE 4 | ${CMAKE_CURRENT_SOURCE_DIR} 5 | ) 6 | target_include_directories(mem-list PRIVATE 7 | ${PROJECT_LIB_FOLDER} 8 | ) 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/mem-list/mem-list.c: -------------------------------------------------------------------------------- 1 | #include "mem-list.h" 2 | 3 | #include "string.h" 4 | #include "stdio.h" 5 | 6 | MemoryNode* memories = NULL; 7 | int create_count = 0; 8 | 9 | // this will cause 3 segfaults before the nights over 10 | MemoryNode* create_memory() { 11 | MemoryNode* mem = (MemoryNode*)malloc(sizeof(MemoryNode)); 12 | mem->length = 0; 13 | mem->prev = NULL; 14 | 15 | memset(mem->data, 0, MEMORY_MAX_SIZE); 16 | 17 | return mem; 18 | } 19 | 20 | MemoryNode* get_memory() { 21 | if (!memories) { 22 | return create_memory(); 23 | } 24 | 25 | MemoryNode* mem = memories; 26 | memories = mem->prev; 27 | 28 | memset(mem->data, 0, MEMORY_MAX_SIZE); 29 | return mem; 30 | } 31 | 32 | void release_memory(MemoryNode* mem) { 33 | mem->prev = memories; 34 | memories = mem; 35 | } 36 | -------------------------------------------------------------------------------- /src/message-queue/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(message-queue message-queue.c) 2 | 3 | target_include_directories(message-queue INTERFACE 4 | ${CMAKE_CURRENT_SOURCE_DIR} 5 | ) 6 | target_include_directories(message-queue PRIVATE 7 | ${PROJECT_LIB_FOLDER} 8 | ) 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/message-queue/message-queue.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThePrimeagen/neovim-irc/f0666c756507fbd4717822aab0e83dcde4658068/src/message-queue/message-queue.c -------------------------------------------------------------------------------- /src/neovim-irc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "socket.h" 15 | #include "irc.h" 16 | #include "user.h" 17 | #include "mem-list.h" 18 | 19 | // This is the only port that works on every system guaranteed 20 | // 21 | #define ECHO_PORT 1337 22 | 23 | #define MAX_LINE 1000 24 | #define LISTENQ 1024 25 | 26 | int main() { 27 | int sock; 28 | struct sockaddr_in servaddr; 29 | char *endptr; 30 | 31 | short int port = (short int)ECHO_PORT; 32 | 33 | /* Create the listening socket */ 34 | 35 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 36 | fprintf(stderr, "ECHOSERV: Error creating listening socket.\n"); 37 | exit(EXIT_FAILURE); 38 | } 39 | 40 | int on = 1; 41 | int rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 42 | (char *)&on, sizeof(on)); 43 | if (rc < 0) { 44 | perror("setsockopt() failed"); 45 | close(sock); 46 | exit(-1); 47 | } 48 | 49 | rc = ioctl(sock, FIONBIO, (char *)&on); 50 | if (rc < 0) { 51 | perror("ioctl() failed"); 52 | close(sock); 53 | exit(-1); 54 | } 55 | 56 | /* Set all bytes in socket address structure to 57 | zero, and fill in the relevant data members */ 58 | 59 | memset(&servaddr, 0, sizeof(servaddr)); 60 | servaddr.sin_family = AF_INET; 61 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 62 | servaddr.sin_port = htons(port); 63 | 64 | /* Bind our socket addresss to the 65 | listening socket, and call listen() */ 66 | 67 | if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { 68 | fprintf(stderr, "ECHOSERV: Error calling bind()\n"); 69 | exit(EXIT_FAILURE); 70 | } 71 | 72 | if (listen(sock, LISTENQ) < 0) { 73 | fprintf(stderr, "ECHOSERV: Error calling listen()\n"); 74 | exit(EXIT_FAILURE); 75 | } 76 | 77 | fd_set master_set; 78 | fd_set active_set; 79 | 80 | FD_ZERO(&master_set); 81 | FD_SET(sock, &master_set); 82 | 83 | int max_sd = sock; 84 | struct sockaddr_in clientname; 85 | 86 | do { 87 | 88 | memcpy(&active_set, &master_set, sizeof(master_set)); 89 | 90 | int desc_count = select(max_sd + 1, &active_set, NULL, NULL, NULL); 91 | if (desc_count < 0) { 92 | perror("select() has failed"); 93 | exit(EXIT_FAILURE); 94 | } else if (desc_count == 0) { 95 | // Is this case for timeouts? 96 | } 97 | 98 | for (int i = 0; i <= max_sd; ++i) { 99 | if (FD_ISSET(i, &active_set)) { 100 | if (i == sock) { 101 | 102 | int conn; 103 | do { 104 | size_t size = sizeof(clientname); 105 | conn = accept(sock, NULL, NULL); 106 | if (conn < 0) { 107 | if (errno != EWOULDBLOCK) { 108 | perror("error calling accept()"); 109 | } 110 | continue; 111 | } 112 | 113 | rc = ioctl(conn, FIONBIO, (char *)&on); 114 | if (rc < 0) { 115 | perror("ioctl() failed INCOMING_CONNECTION."); 116 | close(conn); 117 | close(sock); 118 | } 119 | 120 | irc_new_fd(conn); 121 | printf("incoming connection %d\n", conn); 122 | FD_SET(conn, &master_set); 123 | if (conn > max_sd) { 124 | max_sd = conn; 125 | } 126 | } while (conn != -1); 127 | 128 | } else { 129 | int res = read_from_socket(i); 130 | printf("res rform read_from_socket %d\n", res); 131 | if (!res) { 132 | User *user = find_user(i); 133 | 134 | delete_user(user); 135 | close(i); 136 | FD_CLR(i, &master_set); 137 | if (i == max_sd) { 138 | while (FD_ISSET(max_sd, &master_set) == 0) { 139 | max_sd -= 1; 140 | } 141 | } 142 | } 143 | } 144 | } 145 | } 146 | } while (1); 147 | } 148 | 149 | -------------------------------------------------------------------------------- /src/socket/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(socket socket.c) 2 | 3 | target_include_directories(socket INTERFACE 4 | ${CMAKE_CURRENT_SOURCE_DIR} 5 | ) 6 | target_include_directories(socket PRIVATE 7 | ${PROJECT_LIB_FOLDER} 8 | ) 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/socket/socket.c: -------------------------------------------------------------------------------- 1 | #include "socket.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "irc.h" 15 | #include "user.h" 16 | #include "mem-list.h" 17 | 18 | // Efficiency please? 19 | // This will clearly break at MAX_LINE 20 | void read_line(int sock, User* user) { 21 | printf("int sock = %d User %p\n", sock, user); 22 | char* buffer = user->scratch_data->data; 23 | int done = 0; 24 | 25 | size_t length = 0; 26 | if (user->state == UserStatePartiallyRead) { 27 | length = user->scratch_data->length; 28 | } 29 | 30 | do { 31 | char data; 32 | int result = recv(sock, &data, 1, 0); 33 | 34 | if (result == 0) { 35 | user->state = UserStateClosed; 36 | break; 37 | } else if (result < 0) { 38 | if (errno != EWOULDBLOCK) { 39 | user->state = UserStateError; 40 | } 41 | break; 42 | } else { 43 | buffer[length] = data; 44 | length++; 45 | done = length >= 2 && buffer[length - 2] == '\r' && 46 | buffer[length - 1] == '\n'; 47 | } 48 | 49 | } while (!done && length < MEMORY_MAX_SIZE); 50 | 51 | user->scratch_data->length = length; 52 | if (done) { 53 | user->state = UserStateHasData; 54 | } else if (length == MEMORY_MAX_SIZE) { 55 | char* name = "UNKNOWN"; 56 | if (user->name) { 57 | name = user->name; 58 | } 59 | printf("You just tried to give me to much memory, stop it %s \n", name); 60 | 61 | user->state = UserStateError; 62 | } else if (user->state != UserStateClosed) { 63 | user->state = UserStatePartiallyRead; 64 | } 65 | } 66 | 67 | // SOLVE ME LATER, groooooowwwlll 68 | // cc @polarmutex, solve all my problems. 69 | ssize_t write_line(int sockd, char* buffer, size_t n) { 70 | size_t remaining = n; 71 | 72 | while (remaining > 0) { 73 | ssize_t nwritten = send(sockd, buffer, remaining, 0); 74 | 75 | // TODO: non blocking whattt? 76 | // can nwritten = 0? 77 | if (nwritten < 0) { 78 | if (errno == EWOULDBLOCK) { 79 | printf("Yayayaya EWOK problems!\n"); 80 | continue; // ? keep going? 81 | } else { 82 | return -1; 83 | } 84 | } 85 | 86 | buffer += nwritten; 87 | remaining -= nwritten; 88 | 89 | printf("write_line: remaining: %zu\n", remaining); 90 | } 91 | 92 | return n; 93 | } 94 | 95 | int read_from_socket(int conn) { 96 | User* usr = find_user(conn); 97 | read_line(conn, usr); 98 | 99 | printf("read_from_socket %d user state(%d)\n", conn, usr->state); 100 | if (usr->state == UserStateError || usr->state == UserStateClosed) { 101 | return 0; 102 | } else if (usr->state == UserStatePartiallyRead) { 103 | return 1; 104 | } 105 | 106 | IrcMessage msg; 107 | MemoryNode* node = usr->scratch_data; 108 | usr->scratch_data = get_memory(); 109 | 110 | MemoryNode* copy = get_memory(); 111 | memcpy(copy->data, node->data, node->length); 112 | 113 | // TODO: Refactor me, but future you. So not right now while you read 114 | // this. Or !so @polarmutex 115 | // 116 | // ^-- tj paid actually money in subs for me to write this. 117 | memcpy(copy->data, node->data, node->length); 118 | msg.original = node; 119 | msg.copied = copy; 120 | msg.from_fd = conn; 121 | msg.hasError = 0; 122 | 123 | irc_parse_message(&msg); 124 | irc_print_message(&msg); 125 | 126 | // This needs to be done on a different thread 127 | int returnValue = 0; 128 | if (irc_process_message(&msg)) { 129 | User** users = get_user_list(); 130 | for (int i = 0; i < get_users_size(); ++i) { 131 | write_line(users[i]->from_fd, msg.original->data, msg.original->length); 132 | } 133 | returnValue = 1; 134 | } 135 | 136 | // Free the memory 137 | release_memory(msg.original); 138 | release_memory(msg.copied); 139 | 140 | return returnValue; 141 | } 142 | -------------------------------------------------------------------------------- /src/test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThePrimeagen/neovim-irc/f0666c756507fbd4717822aab0e83dcde4658068/src/test.c -------------------------------------------------------------------------------- /src/user/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(user user.c) 2 | 3 | target_include_directories(user INTERFACE 4 | ${CMAKE_CURRENT_SOURCE_DIR} 5 | ) 6 | target_include_directories(user PRIVATE 7 | ${PROJECT_LIB_FOLDER} 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /src/user/user.c: -------------------------------------------------------------------------------- 1 | #include "user.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define MAX_USERS 1337 8 | 9 | // If I keep ruining everything, please just turn this into a dlinked list 10 | User* users[MAX_USERS]; 11 | int users_size = 0; 12 | 13 | User** get_user_list() { 14 | return users; 15 | } 16 | 17 | int get_users_size() { 18 | return users_size; 19 | } 20 | 21 | long long current_timestamp() { 22 | struct timeval te; 23 | gettimeofday(&te, NULL); // get current time 24 | long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds 25 | return milliseconds; 26 | } 27 | 28 | int insert_user(User* user) { 29 | printf("User#insert_user(%p): %d %d\n", user, user->from_fd, MAX_USERS); 30 | if (users_size == MAX_USERS) { 31 | return 0; 32 | } 33 | 34 | int i = 0; 35 | int inserted = 0; 36 | 37 | for (; inserted && i < users_size; ++i) { 38 | if (users[i] == NULL) { 39 | printf("User#insert_user(%p)# Inserting In Middle: %d \n", user, i); 40 | inserted = 1; 41 | users[i] = user; 42 | } 43 | } 44 | 45 | printf("User#insert_user(%p)# End Of Loop(%d) %d \n", user, inserted, i); 46 | if (!inserted) { 47 | inserted = 1; 48 | users[users_size++] = user; 49 | } 50 | 51 | return 1; 52 | } 53 | 54 | void remove_user(User* user) { 55 | int removed = 0; 56 | int i = 0; 57 | for (; removed && i < users_size; ++i) { 58 | if (users[i] != NULL && users[i]->from_fd == user->from_fd) { 59 | removed = 1; 60 | users[i] = NULL; 61 | } 62 | } 63 | 64 | if (i + 1 == users_size) { 65 | users_size--; 66 | } 67 | } 68 | 69 | User* create_user(int fd) { 70 | User* user = (User*)malloc(sizeof(User)); 71 | if (!user) { 72 | printf("EVERYTHING IS BAD (create_user)\n"); 73 | exit(69420); 74 | } 75 | 76 | user->from_fd = fd; 77 | user->name = NULL; 78 | user->scratch_data = get_memory(); 79 | user->last_pong_time = current_timestamp(); 80 | user->relative_message_count = 0; 81 | user->irc_state = IrcStateWaitingToJoin; 82 | user->state = UserStateWaitingForData; 83 | 84 | printf("Created a new user, %d - %p\n", fd, user->scratch_data); 85 | 86 | return user; 87 | } 88 | 89 | User* find_user(int fd) { 90 | User* user = NULL; 91 | 92 | printf("find_user#users_size: %d\n", users_size); 93 | for (int i = 0; !user && i < users_size; ++i) { 94 | User* u = users[i]; 95 | if (!u) { 96 | printf("Could not find user %d\n", i); 97 | continue; 98 | } 99 | 100 | printf("find_user#for(%d): %p - %d\n", i, u, u->from_fd); 101 | if (u->from_fd == fd) { 102 | user = u; 103 | } 104 | } 105 | 106 | return user; 107 | } 108 | 109 | void delete_user(User* user) { 110 | if (!user) { 111 | return; 112 | } 113 | remove_user(user); 114 | 115 | if (user->name) { 116 | free(user->name); 117 | } 118 | 119 | release_memory(user->scratch_data); 120 | free(user); 121 | } 122 | --------------------------------------------------------------------------------