From ad0a2dc909fbb772d42ab6faf13bba74fbc2483d Mon Sep 17 00:00:00 2001 From: Joursoir Date: Wed, 2 Dec 2020 12:46:42 +0000 Subject: try to make the code is easier; start work with db --- src/server/ChatRoom.cpp | 360 +++++++++++++++++++++++++++++++++++++++++++++ src/server/ChatRoom.hpp | 42 ++++++ src/server/ChatServer.cpp | 155 +++++++++++++++++++ src/server/ChatServer.hpp | 63 ++++++++ src/server/Makefile | 10 +- src/server/UserInfo.cpp | 23 +++ src/server/UserInfo.hpp | 55 +++++++ src/server/UserInfo_io.cpp | 101 +++++++++++++ src/server/chat.cpp | 264 --------------------------------- src/server/chat.hpp | 84 ----------- src/server/main.cpp | 29 ++++ src/server/rooms.cpp | 329 ----------------------------------------- src/server/rooms.hpp | 42 ------ src/server/server.cpp | 22 --- 14 files changed, 833 insertions(+), 746 deletions(-) create mode 100644 src/server/ChatRoom.cpp create mode 100644 src/server/ChatRoom.hpp create mode 100644 src/server/ChatServer.cpp create mode 100644 src/server/ChatServer.hpp create mode 100644 src/server/UserInfo.cpp create mode 100644 src/server/UserInfo.hpp create mode 100644 src/server/UserInfo_io.cpp delete mode 100644 src/server/chat.cpp delete mode 100644 src/server/chat.hpp create mode 100644 src/server/main.cpp delete mode 100644 src/server/rooms.cpp delete mode 100644 src/server/rooms.hpp delete mode 100644 src/server/server.cpp diff --git a/src/server/ChatRoom.cpp b/src/server/ChatRoom.cpp new file mode 100644 index 0000000..a78670c --- /dev/null +++ b/src/server/ChatRoom.cpp @@ -0,0 +1,360 @@ +#include +#include +#include + +#include "ChatRoom.hpp" +#include "ChatServer.hpp" +#include "UserInfo.hpp" + +#define DB_BUFFER_SIZE 128 +#define USE_IN_ROOM 0 +#define USE_IN_LOBBY 1 +#define USE_ANYWHERE 2 + +static char **ParseToArg(const char *input, int &arrc); +static unsigned long hash(const char *str); +static bool checkForbiddenSymbols(const char *str); + +struct cmd_info { + int id; // never -1 !!! + char name[15]; + const unsigned long name_hash; + int lobby_cmd; // USE_IN_ROOM, USE_IN_LOBBY, USE_ANYWHERE + int min_argc; + char usage[64]; +}; + +const int cmd_id_help = 0; +const int cmd_id_create = 1; +const int cmd_id_join = 2; +const int cmd_id_exit = 3; +const int cmd_id_rooms = 4; + +const int cmd_count = 5; +const struct cmd_info cmd[cmd_count] = { + {cmd_id_help, "/help", hash("/help"), USE_ANYWHERE, 0, "Usage: /help"}, + {cmd_id_create, "/create", hash("/create"), USE_IN_LOBBY, 0, "Usage: /create [pass-key]"}, + {cmd_id_join, "/join", hash("/join"), USE_IN_LOBBY, 1, "Usage: /join *id* [pass-key]"}, + {cmd_id_exit, "/exit", hash("/exit"), USE_IN_ROOM, 0, "Usage: /exit"}, + {cmd_id_rooms, "/rooms", hash("/rooms"), USE_IN_LOBBY, 0, "Usage: /rooms"} // print all public rooms + + // IDEA: /clear - clear screen +}; + +ChatRoom::ChatRoom(ChatServer *i_server, int id, char *pass) + : the_server(i_server), code(id), first(0) +{ + if(pass) + strcpy(secret_pass, pass); + else secret_pass[0] = 0; +} + +ChatRoom::~ChatRoom() +{ + while(first) { + item *tmp = first; + first = first->next; + the_server->CloseConnection(tmp->u); + delete tmp; + } +} + +void ChatRoom::SendAll(const char *msg, UserInfo *except, + const int spec_msg) +{ + CONSOLE_LOG("Send message all: %s\n", msg); + item *p; + for(p = first; p; p = p->next) + if(p->u != except) + p->u->Send(msg, spec_msg); +} + +void ChatRoom::HandleMessage(UserInfo *u, const char *str) +{ + int status = u->GetStatus(); + if(status != no_wait) { + if(status == wait_name) { + // handle name, if OK - will send hello msg + int len = strlen(str); + if(len > max_name_len || len < min_name_len) { + u->Send("Incorrect name. Name length from 3 to 18 chars"); + CloseSession(u); + } + if(checkForbiddenSymbols(str)) { + u->Send("Incorrect name. You use forbidden symbols."); + CloseSession(u); + } + + //if(userInServer(char *nickname)) + // проверить если кто-то с данным именем.. + + u->SetName(str); + + char *msg = new char[DB_BUFFER_SIZE]; + sprintf(msg, "SELECT password FROM users WHERE name = '%s' LIMIT 1", str); + CONSOLE_LOG("%s\n", msg); + AnswerDB *ans = the_server->QuerySelect(msg); + if(ans) { + DB_ROW *row = ans->GetNextRow(); + + char *pass = 0; + if(row) { + pass = new char[max_player_lenpass]; + strcpy(pass, (*row)[0]); + + CONSOLE_LOG("account exist! \n"); + } + } + else CONSOLE_LOG("account noexist :( \n"); + delete[] msg; + + u->Send("Welcome to WantChat!"); + u->Send(" "); + u->Send("It is anonymous chat in retro-style 80s."); + u->Send("Use our chat-client for more immersed."); + u->Send(" "); + u->Send("This project is open source :)"); + u->Send("Github: github.com/Joursoir/want-chat"); + u->Send(" "); + u->Send("To join to room using /join room_id"); + u->Send("You can find rooms using /rooms"); + u->Send(" "); + u->Send("For more detailed info: /help. Good chatting!"); + u->Send(" "); + + u->SetStatus(no_wait); + } + + return; + } + + if(str[0] == '/') { // if user sent a command + int argc = 0; + char **argv = ParseToArg(str, argc); + this->HandleCommand(u, argc, argv); + + for(int i = 0; i < argc; i++) + delete[] argv[i]; + delete[] argv; + } + else if(code != std_id_lobby) { + char *msg = new char[max_msg_len]; + sprintf(msg, "%s: %s", u->GetName(), str); + + this->SendAll(msg, 0, usual_msg); + + delete[] msg; + } + else u->Send("In the lobby you can only write commands"); +} + +void ChatRoom::HandleCommand(UserInfo *u, int count, + char **argvar) +{ + unsigned long hash_cmd = -1; + hash_cmd = hash(argvar[0]); + + int what_command = -1; + for(int i = 0; i < cmd_count; i++) { + if(cmd[i].name_hash == hash_cmd) { + what_command = i; + break; + } + } + + if(what_command == -1) + return u->Send("Unknown command. Use: /help"); + + const char onlyroom_msg[] = "You can use this command only in rooms!"; + const char onlylobby_msg[] = "You can use this command only in lobby!"; + + // scope of command: + if(cmd[what_command].lobby_cmd == USE_IN_ROOM && code == std_id_lobby) + return u->Send(onlyroom_msg); + else if(cmd[what_command].lobby_cmd == USE_IN_LOBBY && code != std_id_lobby) + return u->Send(onlylobby_msg); + + // right usage: + if(cmd[what_command].min_argc > count-1) + return u->Send(cmd[what_command].usage); + + switch(cmd[what_command].id) { + case cmd_id_help: { + u->Send("Help info..."); + break; + } + case cmd_id_create: { + char *pass = 0; + if(count > 1) { + if(strlen(argvar[1]) > max_room_lenpass) + return u->Send("Maximum length of pass equals 24"); + pass = argvar[1]; + } + int id = the_server->AddRoom(pass); + + const char fcmsg[] = "You create a room #"; + const char scmsg[] = " with password: "; + char *cmsg = new char[strlen(fcmsg)+3+strlen(scmsg)+max_room_lenpass]; + if(pass) sprintf(cmsg, "%s%d%s%s", fcmsg, id, scmsg, pass); + else sprintf(cmsg, "%s%d", fcmsg, id); + u->Send(cmsg); + delete[] cmsg; + + the_server->ChangeSessionRoom(this, u, id, pass); + break; + } + case cmd_id_join: { + int id = atoi(argvar[1]); + char *pass = argvar[2]; // if count == 2, then argvar[2] = 0 + + int h_status = the_server->ChangeSessionRoom(this, u, id, pass); + if(h_status == enter_noexist) + return u->Send("Room with that ID didn't exist"); + else if(h_status == enter_private) + return u->Send("It is private room, join using password"); + else if(h_status == enter_uncorrect_pass) + return u->Send("Oops, this password is not valid"); + + u->Send("You has left the room"); + break; + } + case cmd_id_exit: { + the_server->GotoLobby(this, u); + break; + } + case cmd_id_rooms: { + // in development + break; + } + default: break; + } +} + +const char *ChatRoom::GetSecretPass() +{ + if(secret_pass[0] != 0) + return secret_pass; + return 0; +} + +void ChatRoom::AddSession(UserInfo *u) +{ + item *p = new item; + p->next = first; + p->u = u; + first = p; + + if(code == std_id_lobby) + return; + + const char welcome_msg[] = "Welcome to the room #"; + const char entered_msg[] = " has entered the room"; + + const char *name = u->GetName(); + + char *wmsg = new char[sizeof(welcome_msg) + 2]; + sprintf(wmsg, "%s%d", welcome_msg, code); + u->Send(wmsg); + delete[] wmsg; + + char *emsg = new char[strlen(name) + sizeof(entered_msg)]; + sprintf(emsg, "%s%s", name, entered_msg); + this->SendAll(emsg, u); + delete[] emsg; + +} + +void ChatRoom::RemoveSession(UserInfo *u) +{ + if(code != std_id_lobby) { + const char left_msg[] = " has left the room"; + const char *name = u->GetName(); + + int len = strlen(name); + char *lmsg = new char[len + sizeof(left_msg) + 2]; + sprintf(lmsg, "%s%s", name, left_msg); + this->SendAll(lmsg, u); + delete[] lmsg; + } + + item **p; + for(p = &first; *p; p = &((*p)->next)) { + if( ((*p)->u) == u ) { + item *tmp = *p; + *p = tmp->next; + // not delete UserInfo! + delete tmp; + + if(code != std_id_lobby && !first) + the_server->DeleteRoom(code); + + return; + } + } +} + +void ChatRoom::CloseSession(UserInfo *u) +{ + ChatServer *serv = the_server; + this->RemoveSession(u); + serv->CloseConnection(u); +} + +////////////////////////////////////////////////////////////// + +static char **ParseToArg(const char *input, int &arrc) +{ + int max_argv = 5; + arrc = 0; + char **arr = new char*[max_argv+1]; + + int start = 0; + for(int i = 0; i < (int) strlen(input)+1; i++) { + if(input[i] == ' ' || input[i] == '\0' || input[i] == '\n') { // end + if(start == std_id_lobby) + continue; + + int size = i - start; + arr[arrc] = new char[size+1]; + memcpy(arr[arrc], input + start, sizeof(char) * size); + arr[arrc][size] = '\0'; + + start = -1; + arrc++; + if(arrc == max_argv) + break; + } + else if(start == -1) + start = i; + } + + arr[arrc] = 0; + return arr; +} + +static unsigned long hash(const char *str) +{ + unsigned long hash = 5381; + char c; + + while( (c = *str++) ) + hash = ((hash << 5) + hash) + c; + + return hash; +} + +static bool checkForbiddenSymbols(const char *str) +{ + char banned_symbols[] = {'!', '@', '#', '\'', + '\"', '\\', '/', '^', '&', '*', ';', ','}; + + int num = sizeof(banned_symbols)/sizeof(char); + for(int i = 0; i < (int)strlen(str); i++) { + for(int j = 0; j < num; j++) { + if(str[i] == banned_symbols[j]) + return 1; + } + } + + return 0; +} \ No newline at end of file diff --git a/src/server/ChatRoom.hpp b/src/server/ChatRoom.hpp new file mode 100644 index 0000000..a9e2492 --- /dev/null +++ b/src/server/ChatRoom.hpp @@ -0,0 +1,42 @@ +#ifndef ROOMREALIZATION_H +#define ROOMREALIZATION_H + +#include "../const_vars.hpp" + +const int std_id_lobby = -1; +const int max_room_lenpass = 24; +const int max_player_lenpass = 24; + +class ChatServer; +class UserInfo; + +class ChatRoom { + ChatServer *the_server; + const int code; + // if code == std_id_lobby then it's lobby + char secret_pass[max_room_lenpass]; + + struct item { + UserInfo *u; + item *next; + }; + item *first; +public: + ChatRoom(ChatServer *i_server, int id, char *pass); + ~ChatRoom(); + + void SendAll(const char *msg, UserInfo *except = 0, + const int spec_msg = system_msg); + + void HandleMessage(UserInfo *u, const char *str); + void HandleCommand(UserInfo *u, int cmd_counter, + char **commands); + + const char *GetSecretPass(); + + void AddSession(UserInfo *u); + void RemoveSession(UserInfo *u); + void CloseSession(UserInfo *u); +}; + +#endif \ No newline at end of file diff --git a/src/server/ChatServer.cpp b/src/server/ChatServer.cpp new file mode 100644 index 0000000..6256e55 --- /dev/null +++ b/src/server/ChatServer.cpp @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include + +#include "ChatServer.hpp" +#include "UserInfo.hpp" +#include "ChatRoom.hpp" + +const int qlen_for_listen = 6; + +ChatServer::ChatServer(EventSelector *sel, DatabaseManager *db, int fd) + : FdHandler(fd), the_selector(sel), dbase(db) +{ + the_selector->Add(this); + lobby = new ChatRoom(this, std_id_lobby, 0); +} + +ChatServer::~ChatServer() +{ + if(room) + delete[] room; + + the_selector->Remove(this); +} + +ChatServer *ChatServer::Start(EventSelector *sel, DatabaseManager *db, int port) +{ + int in_d = socket(AF_INET, SOCK_STREAM, 0); + if(in_d == -1) + return 0; + + int opt = 1; + setsockopt(in_d, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + int res = bind(in_d, (struct sockaddr*) &addr, sizeof(addr)); + if(res == -1) + return 0; + + res = listen(in_d, qlen_for_listen); + if(res == -1) + return 0; + + return new ChatServer(sel, db, in_d); +} + +int ChatServer::AddRoom(char *password) +{ + if(!room) { + room_len = 16; + room = new ChatRoom*[room_len]; + for(int i = 0; i < room_len; i++) + room[i] = 0; + } + + int id = -1; + for(int i = 0; i < room_len; i++) { + if(room[i] == 0) { + id = i; + room[i] = new ChatRoom(this, id, password); + break; + } + } + + if(id == -1) { + ChatRoom **tmp = new ChatRoom*[room_len+1]; + for(int i = 0; i < room_len; i++) + tmp[i] = room[i]; + + id = room_len; + tmp[room_len] = new ChatRoom(this, id, password); + room_len += 1; + + delete[] room; + room = tmp; + } + + return id; +} + +bool ChatServer::DeleteRoom(int id) +{ + if(!room[id]) + return false; + + delete room[id]; + room[id] = 0; + + return true; +} + +bool ChatServer::RoomExist(int id) const +{ + if(!room[id]) + return false; + + return true; +} + +void ChatServer::GotoLobby(ChatRoom *cur_room, UserInfo *u) +{ + cur_room->RemoveSession(u); + lobby->AddSession(u); + u->SetRoom(lobby); +} + +handle_room_enter ChatServer::ChangeSessionRoom(ChatRoom *cur_room, + UserInfo *u, int id, char *pass) +{ + if(id < 0 || id >= room_len || !room[id]) + return enter_noexist; + + const char *secret_word = room[id]->GetSecretPass(); + CONSOLE_LOG("right pass: %s; user enter pass: %s\n", secret_word, pass); + if(secret_word != 0) { + if(!pass) + return enter_private; + if(strcmp(secret_word, pass) != 0) + return enter_uncorrect_pass; + } + + cur_room->RemoveSession(u); + room[id]->AddSession(u); + u->SetRoom(room[id]); + return enter_success; +} + +void ChatServer::CloseConnection(UserInfo *u) +{ + the_selector->Remove(u); + delete u; +} + +void ChatServer::Handle(bool r, bool w) +{ + if(!r) + return; + + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + int sd = accept(GetFd(), (struct sockaddr*) &addr, &len); + if(sd == -1) + return; + + UserInfo *u = new UserInfo(lobby, sd); + lobby->AddSession(u); + the_selector->Add(u); +} \ No newline at end of file diff --git a/src/server/ChatServer.hpp b/src/server/ChatServer.hpp new file mode 100644 index 0000000..e2ef154 --- /dev/null +++ b/src/server/ChatServer.hpp @@ -0,0 +1,63 @@ +#ifndef WC_CHATSERVER_H +#define WC_CHATSERVER_H + +#include "sockets.hpp" +#include "database.hpp" +#include "../const_vars.hpp" + +#define CONSOLE_LOG(f_, ...) printf((f_), ##__VA_ARGS__) + +enum handle_room_enter { + enter_noexist, + enter_private, + enter_uncorrect_pass, + enter_success +}; + +class ChatRoom; +class UserInfo; + +class ChatServer : public FdHandler { + EventSelector *the_selector; + DatabaseManager *dbase; + + ChatRoom **room; + int room_len; + ChatRoom *lobby; + + ChatServer(EventSelector *sel, DatabaseManager *db, int fd); +public: + ~ChatServer(); + static ChatServer *Start(EventSelector *sel, + DatabaseManager *db, int fd); + + int AddRoom(char *password); + bool DeleteRoom(int id); // call only if room is empty + bool RoomExist(int id) const; + + void GotoLobby(ChatRoom *cur_room, UserInfo *u); + handle_room_enter ChangeSessionRoom(ChatRoom *cur_room, + UserInfo *u, int id, char *pass); + + void CloseConnection(UserInfo *u); + + // work with database: + AnswerDB *QuerySelect(const char *sql) + { return dbase->QuerySelect(sql); } + +private: + virtual void Handle(bool r, bool w); +}; + +/*class StorageOfUsers { + struct item { + ChatSession *s; + item *next; + } + item *first; +public: + AddSession(ChatSession *s); + RemoveSession(ChatSession *s); +};*/ + +#endif \ No newline at end of file diff --git a/src/server/Makefile b/src/server/Makefile index be312d7..03836e8 100644 --- a/src/server/Makefile +++ b/src/server/Makefile @@ -1,6 +1,6 @@ CPP = g++ -CPPFLAGS = -Wall -g -SOURCES = server.cpp sockets.cpp chat.cpp rooms.cpp +CPPFLAGS = -Wall -g $(shell mariadb_config --cflags --libs) +SOURCES = main.cpp sockets.cpp ChatServer.cpp UserInfo.cpp UserInfo_io.cpp ChatRoom.cpp database.cpp OBJECTS = $(SOURCES:.cpp=.o) EXECUTABLE = server @@ -9,10 +9,10 @@ EXECUTABLE = server all: $(EXECUTABLE) clean: - rm -rf $(OBJECTS) $(EXECUTABLE) + @rm -rf $(OBJECTS) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) - $(CPP) $(CPPFLAGS) -o $(EXECUTABLE) $(OBJECTS) + @$(CPP) $(CPPFLAGS) -o $(EXECUTABLE) $(OBJECTS) $(OBJECTS): - $(CPP) -c $(CPPFLAGS) $(SOURCES) \ No newline at end of file + @$(CPP) -c $(CPPFLAGS) $(SOURCES) \ No newline at end of file diff --git a/src/server/UserInfo.cpp b/src/server/UserInfo.cpp new file mode 100644 index 0000000..e7c0334 --- /dev/null +++ b/src/server/UserInfo.cpp @@ -0,0 +1,23 @@ +#include "UserInfo.hpp" + +void UserInfo::SetRoom(ChatRoom *new_master) +{ + this->the_master = new_master; +} + +const char *UserInfo::GetName() const +{ + if(name[0]) + return name; + return 0; +} + +void UserInfo::SetName(const char *n_name) +{ + strcpy(name, n_name); +} + +void UserInfo::SetStatus(const enum_status e_s) +{ + state = e_s; +} diff --git a/src/server/UserInfo.hpp b/src/server/UserInfo.hpp new file mode 100644 index 0000000..e033b0c --- /dev/null +++ b/src/server/UserInfo.hpp @@ -0,0 +1,55 @@ +#ifndef WC_USER_H +#define WC_USER_H + +#include + +#include "sockets.hpp" +#include "ChatRoom.hpp" +#include "../const_vars.hpp" + +class ChatServer; + +enum enum_status { + wait_name, // expecting a username from player + wait_reg, // expecting registration + wait_log, // expecting login + no_wait +}; + +#define CONSOLE_LOG(f_, ...) printf((f_), ##__VA_ARGS__) // delete later + +class UserInfo : FdHandler { + friend class ChatServer; + + char name[max_name_len]; + char buffer[max_msg_len]; + int buf_used; + bool ignoring; + + enum_status state; + ChatRoom *the_master; + + UserInfo(ChatRoom *i_master, int i_fd) : FdHandler(i_fd), + buf_used(0), ignoring(false), state(wait_name), + the_master(i_master) {} + ~UserInfo() {} + + void SetRoom(ChatRoom *new_master); + + // === realization in UserInfo_io.cpp === + virtual void Handle(bool r, bool w); // w disabled, only read + void ReadAndIgnore(); + void ReadAndCheck(); + void CheckLines(); +public: + void Send(const char *msg, const int spec_msg = system_msg); + // === realization in UserInfo_io.cpp === + + const char *GetName() const; + void SetName(const char *n_name); + + enum_status GetStatus() const { return state; } + void SetStatus(const enum_status e_s); +}; + +#endif \ No newline at end of file diff --git a/src/server/UserInfo_io.cpp b/src/server/UserInfo_io.cpp new file mode 100644 index 0000000..d4d308a --- /dev/null +++ b/src/server/UserInfo_io.cpp @@ -0,0 +1,101 @@ +#include + +#include "UserInfo.hpp" + +void UserInfo::Handle(bool r, bool w) +{ + if(!r) + return; + + // this functions create for support any client, not one WantChat client: + if(buf_used >= (int)sizeof(buffer)) { + buf_used = 0; + ignoring = true; + } + if(ignoring) { + CONSOLE_LOG("Ignore the message, it's so big\n"); + ReadAndIgnore(); + } + else + ReadAndCheck(); +} + +void UserInfo::ReadAndIgnore() +{ + int rc = read(GetFd(), buffer, sizeof(buffer)); + CONSOLE_LOG("readI return %d bytes\n", rc); + if(rc < 1) { + the_master->CloseSession(this); + return; + } + + for(int i = 0; i < rc; i++) + if(buffer[i] == '\n') + { // stop ignoring! + CONSOLE_LOG("ReadAndIgnore: find \\n\n"); + int rest = rc - i - 1; + if(rest > 0) + memmove(buffer, buffer + i + 1, rest); + buf_used = rest; + ignoring = 0; + CheckLines(); + } +} + +void UserInfo::ReadAndCheck() +{ + int rc = read(GetFd(), buffer+buf_used, sizeof(buffer)-buf_used); + CONSOLE_LOG("readC return %d bytes\n", rc); + if(rc < 1) { + the_master->CloseSession(this); + return; + } + buf_used += rc; + CheckLines(); +} + +void UserInfo::CheckLines() +{ + if(buf_used <= 0) + return; + + for(int i = 0; i < buf_used; i++) { + if(buffer[i] == '\n') { + CONSOLE_LOG("[CheckLines] buffer[i] == \\n i = %d\n", i); + buffer[i] = 0; + if(i > 0 && buffer[i-1] == '\r') + buffer[i-1] = 0; + + CONSOLE_LOG("printed: %s\n", buffer); + the_master->HandleMessage(this, buffer); + + int rest = buf_used - i - 1; + memmove(buffer, buffer + i + 1, rest); + buf_used = rest; + CONSOLE_LOG("[CheckLines] new buf_used = %d\n", buf_used); + CheckLines(); + return; + } + } +} + +void UserInfo::Send(const char *msg, const int spec_msg) +{ + int len = strlen(msg); + char *tmp_msg = new char[len+1+2]; // for spec_symb + \n + + if(spec_msg == usual_msg) + sprintf(tmp_msg, "%s\n", msg); + else + { + char spec; + if(spec_msg == system_msg) spec = system_char; + sprintf(tmp_msg, "%c%s\n", spec, msg); + } + + CONSOLE_LOG("%s", tmp_msg); + write(GetFd(), tmp_msg, strlen(tmp_msg)); + + delete[] tmp_msg; +} + diff --git a/src/server/chat.cpp b/src/server/chat.cpp deleted file mode 100644 index 561a61d..0000000 --- a/src/server/chat.cpp +++ /dev/null @@ -1,264 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "chat.hpp" -#include "rooms.hpp" - -const int qlen_for_listen = 6; - -void ChatSession::Send(const char *msg, const int spec_msg) -{ - int len = strlen(msg); - char *tmp_msg = new char[len+1+2]; // for spec_symb + \n - - if(spec_msg == usual_msg) - sprintf(tmp_msg, "%s\n", msg); - else - { - char spec; - if(spec_msg == system_msg) spec = system_char; - sprintf(tmp_msg, "%c%s\n", spec, msg); - } - - CONSOLE_LOG("%s", tmp_msg); - write(GetFd(), tmp_msg, strlen(tmp_msg)); - - delete[] tmp_msg; -} - -void ChatSession::Handle(bool r, bool w) -{ - if(!r) - return; - - // this functions create for support any client, not one WantChat client: - if(buf_used >= (int)sizeof(buffer)) { - buf_used = 0; - ignoring = true; - } - if(ignoring) { - CONSOLE_LOG("Ignore the message, it's so big\n"); - ReadAndIgnore(); - } - else - ReadAndCheck(); -} - -void ChatSession::ReadAndIgnore() -{ - int rc = read(GetFd(), buffer, sizeof(buffer)); - CONSOLE_LOG("readI return %d bytes\n", rc); - if(rc < 1) { - the_master->CloseSession(this); - return; - } - - for(int i = 0; i < rc; i++) - if(buffer[i] == '\n') - { // stop ignoring! - CONSOLE_LOG("ReadAndIgnore: find \\n\n"); - int rest = rc - i - 1; - if(rest > 0) - memmove(buffer, buffer + i + 1, rest); - buf_used = rest; - ignoring = 0; - CheckLines(); - } -} - -void ChatSession::ReadAndCheck() -{ - int rc = read(GetFd(), buffer+buf_used, sizeof(buffer)-buf_used); - CONSOLE_LOG("readC return %d bytes\n", rc); - if(rc < 1) { - the_master->CloseSession(this); - return; - } - buf_used += rc; - CheckLines(); -} - -void ChatSession::CheckLines() -{ - if(buf_used <= 0) - return; - - for(int i = 0; i < buf_used; i++) { - if(buffer[i] == '\n') { - CONSOLE_LOG("[CheckLines] buffer[i] == \\n i = %d\n", i); - buffer[i] = 0; - if(i > 0 && buffer[i-1] == '\r') - buffer[i-1] = 0; - - CONSOLE_LOG("printed: %s\n", buffer); - the_master->HandleMessage(this, buffer); - - int rest = buf_used - i - 1; - memmove(buffer, buffer + i + 1, rest); - buf_used = rest; - CONSOLE_LOG("[CheckLines] new buf_used = %d\n", buf_used); - CheckLines(); - return; - } - } -} - -const char *ChatSession::GetName() -{ - if(name[0]) - return name; - return 0; -} - -void ChatSession::SetName(const char *n_name) -{ - strcpy(name, n_name); -} - -void ChatSession::SetRoom(ChatRoom *new_master) -{ - this->the_master = new_master; -} - -////////////////////////////////////////////////////////////// - -Server::Server(EventSelector *sel, int fd) - : FdHandler(fd), the_selector(sel) -{ - the_selector->Add(this); - lobby = new ChatRoom(this, std_id_lobby, 0); -} - -Server::~Server() -{ - if(room) - delete[] room; - - the_selector->Remove(this); -} - -Server *Server::Start(EventSelector *sel, int port) -{ - int in_d = socket(AF_INET, SOCK_STREAM, 0); - if(in_d == -1) - return 0; - - int opt = 1; - setsockopt(in_d, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); - - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(port); - - int res = bind(in_d, (struct sockaddr*) &addr, sizeof(addr)); - if(res == -1) - return 0; - - res = listen(in_d, qlen_for_listen); - if(res == -1) - return 0; - - return new Server(sel, in_d); -} - -bool Server::RoomExist(int id) const -{ - if(!room[id]) - return false; - - return true; -} - -void Server::Handle(bool r, bool w) -{ - if(!r) - return; - - struct sockaddr_in addr; - socklen_t len = sizeof(addr); - int sd = accept(GetFd(), (struct sockaddr*) &addr, &len); - if(sd == -1) - return; - - ChatSession *s = new ChatSession(lobby, sd); - lobby->AddSession(s); - the_selector->Add(s); -} - -int Server::AddRoom(char *password) -{ - if(!room) { - room_len = 16; - room = new ChatRoom*[room_len]; - for(int i = 0; i < room_len; i++) - room[i] = 0; - } - - int id = -1; - for(int i = 0; i < room_len; i++) { - if(room[i] == 0) { - id = i; - room[i] = new ChatRoom(this, id, password); - break; - } - } - - if(id == -1) { - ChatRoom **tmp = new ChatRoom*[room_len+1]; - for(int i = 0; i < room_len; i++) - tmp[i] = room[i]; - - id = room_len; - tmp[room_len] = new ChatRoom(this, id, password); - room_len += 1; - - delete[] room; - room = tmp; - } - - return id; -} - -bool Server::DeleteRoom(int id) -{ - if(!room[id]) - return false; - - delete room[id]; - room[id] = 0; - - return true; -} - -void Server::GotoLobby(ChatRoom *cur_room, ChatSession *s) -{ - cur_room->RemoveSession(s); - lobby->AddSession(s); - s->SetRoom(lobby); -} - -handle_room_enter Server::ChangeSessionRoom(ChatRoom *cur_room, - ChatSession *s, int id, char *pass) -{ - if(id < 0 || id >= room_len || !room[id]) - return enter_noexist; - - const char *secret_word = room[id]->GetSecretPass(); - CONSOLE_LOG("s: %s; pass: %s\n", secret_word, pass); - if(secret_word != 0) { - if(!pass) - return enter_private; - if(strcmp(secret_word, pass) != 0) - return enter_uncorrect_pass; - } - - cur_room->RemoveSession(s); - room[id]->AddSession(s); - s->SetRoom(room[id]); - return enter_success; -} \ No newline at end of file diff --git a/src/server/chat.hpp b/src/server/chat.hpp deleted file mode 100644 index 4b6c8b1..0000000 --- a/src/server/chat.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef CHATREALIZATION_H -#define CHATREALIZATION_H - -#include "sockets.hpp" -#include "../const_vars.hpp" - -#define CONSOLE_LOG(f_, ...) printf((f_), ##__VA_ARGS__) - -enum handle_room_enter { - enter_noexist, - enter_private, - enter_uncorrect_pass, - enter_success -}; - -enum enum_status { - wait_name, // expecting a username from player - wait_reg, // expecting registration - wait_log, // expecting login - no_wait -}; - -class ChatRoom; -class Server; - -class ChatSession : FdHandler { - friend class Server; - friend class ChatRoom; - - char name[max_name_len]; - char buffer[max_msg_len]; - int buf_used; - bool ignoring; - - enum_status state; - ChatRoom *the_master; - - ChatSession(ChatRoom *i_master, int i_fd) : FdHandler(i_fd), - buf_used(0), ignoring(false), state(wait_name), - the_master(i_master) {} - ~ChatSession() {} - - void SetRoom(ChatRoom *new_master); - - virtual void Handle(bool r, bool w); - void ReadAndIgnore(); - void ReadAndCheck(); - void CheckLines(); -public: - const char *GetName(); - - void SetName(const char *n_name); - void Send(const char *msg, const int spec_msg = system_msg); -}; - -class Server : public FdHandler { - EventSelector *the_selector; - ChatRoom **room; - int room_len; - - ChatRoom *lobby; - - Server(EventSelector *sel, int fd); -public: - ~Server(); - - static Server *Start(EventSelector *sel, int port); - - bool RoomExist(int id) const; - - int AddRoom(char *password); - bool DeleteRoom(int id); // call only if room is empty - - void GotoLobby(ChatRoom *cur_room, ChatSession *s); - handle_room_enter ChangeSessionRoom(ChatRoom *cur_room, - ChatSession *s, int id, char *pass); - void CloseConnection(ChatSession *s) - { the_selector->Remove(s); delete s; } -private: - virtual void Handle(bool r, bool w); -}; - - -#endif \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp new file mode 100644 index 0000000..2d0fea8 --- /dev/null +++ b/src/server/main.cpp @@ -0,0 +1,29 @@ +#include + +#include "ChatServer.hpp" +#include "database.hpp" +#include "../config.hpp" + +int main(int argc, char *argv[]) +{ + /* Event-driven programming */ + /* + 1) selection event + 2) event handling + */ + DatabaseManager *db = DatabaseManager::Connect(DATABASE_HOST, DATABASE_USER, + DATABASE_PASSWD, DATABASE_DB, DATABASE_PORT); + if(!db) { + perror("database"); + return 1; + } + + EventSelector *selector = new EventSelector; + ChatServer *serv = ChatServer::Start(selector, db, SERVER_PORT); + if(!serv) { + perror("server"); + return 1; + } + selector->Run(); + return 0; +} \ No newline at end of file diff --git a/src/server/rooms.cpp b/src/server/rooms.cpp deleted file mode 100644 index ba1ee1b..0000000 --- a/src/server/rooms.cpp +++ /dev/null @@ -1,329 +0,0 @@ -#include -#include -#include - -#include "rooms.hpp" -#include "chat.hpp" - -#define USE_IN_ROOM 0 -#define USE_IN_LOBBY 1 -#define USE_ANYWHERE 2 - -static char **ParseToArg(const char *input, int &arrc); -static unsigned long hash(const char *str); -static bool checkForbiddenSymbols(const char *str); - -struct cmd_info { - int id; // never -1 !!! - char name[15]; - const unsigned long name_hash; - int lobby_cmd; // USE_IN_ROOM, USE_IN_LOBBY, USE_ANYWHERE - int min_argc; - char usage[64]; -}; - -const int cmd_id_create = 1; -const int cmd_id_join = 2; -const int cmd_id_exit = 3; -const int cmd_id_rooms = 4; - -const int cmd_count = 4; -const struct cmd_info cmd[cmd_count] = { - {cmd_id_create, "/create", hash("/create"), USE_IN_LOBBY, 0, "Usage: /create [pass-key]"}, - {cmd_id_join, "/join", hash("/join"), USE_IN_LOBBY, 1, "Usage: /join *id* [pass-key]"}, - {cmd_id_exit, "/exit", hash("/exit"), USE_IN_ROOM, 0, "Usage: /exit"}, - {cmd_id_rooms, "/rooms", hash("/rooms"), USE_IN_LOBBY, 0, "Usage: /rooms"} // print all public rooms - - // IDEA: /clear - clear screen -}; - -ChatRoom::ChatRoom(Server *i_server, int id, char *pass) - : the_server(i_server), code(id), first(0) -{ - if(pass) - strcpy(secret_pass, pass); - else secret_pass[0] = 0; -} - -ChatRoom::~ChatRoom() -{ - while(first) { - item *tmp = first; - first = first->next; - the_server->CloseConnection(tmp->s); - delete tmp; - } -} - -void ChatRoom::SendAll(const char *msg, ChatSession *except, - const int spec_msg) -{ - CONSOLE_LOG("Send message all: %s\n", msg); - item *p; - for(p = first; p; p = p->next) - if(p->s != except) - p->s->Send(msg, spec_msg); -} - -void ChatRoom::HandleMessage(ChatSession *ses, const char *str) -{ - int status = ses->state; - if(status != no_wait) { - if(status == wait_name) { - // handle name, if OK - will send hello msg - int len = strlen(str); - if(len > max_name_len || len < min_name_len) { - ses->Send("Incorrect name. Name length from 3 to 18 chars"); - CloseSession(ses); - } - if(checkForbiddenSymbols(str)) { - ses->Send("Incorrect name. You use forbidden symbols."); - CloseSession(ses); - } - ses->SetName(str); - - ses->Send("Welcome to WantChat!"); - ses->Send(" "); - ses->Send("It is anonymous chat in retro-style 80s."); - ses->Send("Use our chat-client for more immersed."); - ses->Send(" "); - ses->Send("This project is open source :)"); - ses->Send("Github: github.com/Joursoir/want-chat"); - ses->Send(" "); - ses->Send("To join to room using /join room_id"); - ses->Send("You can find rooms using /rooms"); - ses->Send(" "); - ses->Send("For more detailed info: /help. Good chatting!"); - ses->Send(" "); - - ses->state = no_wait; - } - - return; - } - - if(str[0] == '/') { // if user sent a command - int argc = 0; - char **argv = ParseToArg(str, argc); - this->HandleCommand(ses, argc, argv); - - for(int i = 0; i < argc; i++) - delete[] argv[i]; - delete[] argv; - } - else if(code != std_id_lobby) { - char *msg = new char[max_msg_len]; - sprintf(msg, "%s: %s", ses->GetName(), str); - - this->SendAll(msg, 0, usual_msg); - - delete[] msg; - } - else ses->Send("In the lobby you can only write commands"); -} - -void ChatRoom::HandleCommand(ChatSession *ses, int count, - char **argvar) -{ - unsigned long hash_cmd = -1; - hash_cmd = hash(argvar[0]); - - int what_command = -1; - for(int i = 0; i < cmd_count; i++) { - if(cmd[i].name_hash == hash_cmd) { - what_command = i; - break; - } - } - - if(what_command == -1) - return ses->Send("Unknown command. Use: /help"); - - const char onlyroom_msg[] = "You can use this command only in rooms!"; - const char onlylobby_msg[] = "You can use this command only in lobby!"; - - // scope of command: - if(cmd[what_command].lobby_cmd == USE_IN_ROOM && code == std_id_lobby) - return ses->Send(onlyroom_msg); - else if(cmd[what_command].lobby_cmd == USE_IN_LOBBY && code != std_id_lobby) - return ses->Send(onlylobby_msg); - - // right usage: - if(cmd[what_command].min_argc > count-1) - return ses->Send(cmd[what_command].usage); - - switch(cmd[what_command].id) { - case cmd_id_create: { - char *pass = 0; - if(count > 1) { - if(strlen(argvar[1]) > max_room_lenpass) - return ses->Send("Maximum length of pass equals 24"); - pass = argvar[1]; - } - int id = the_server->AddRoom(pass); - - const char fcmsg[] = "You create a room #"; - const char scmsg[] = " with password: "; - char *cmsg = new char[strlen(fcmsg)+3+strlen(scmsg)+max_room_lenpass]; - if(pass) sprintf(cmsg, "%s%d%s%s", fcmsg, id, scmsg, pass); - else sprintf(cmsg, "%s%d", fcmsg, id); - ses->Send(cmsg); - delete[] cmsg; - - the_server->ChangeSessionRoom(this, ses, id, pass); - break; - } - case cmd_id_join: { - int id = atoi(argvar[1]); - char *pass = argvar[2]; // if count == 2, then argvar[2] = 0 - - int h_status = the_server->ChangeSessionRoom(this, ses, id, pass); - if(h_status == enter_noexist) - return ses->Send("Room with that ID didn't exist"); - else if(h_status == enter_private) - return ses->Send("It is private room, join using password"); - else if(h_status == enter_uncorrect_pass) - return ses->Send("Oops, this password is not valid"); - - break; - } - case cmd_id_exit: { - the_server->GotoLobby(this, ses); - break; - } - case cmd_id_rooms: { - // in development - break; - } - default: break; - } -} - -const char *ChatRoom::GetSecretPass() -{ - if(secret_pass[0] != 0) - return secret_pass; - return 0; -} - -void ChatRoom::AddSession(ChatSession *ses) -{ - item *p = new item; - p->next = first; - p->s = ses; - first = p; - - if(code == std_id_lobby) - return; - - const char welcome_msg[] = "Welcome to the room #"; - const char entered_msg[] = " has entered the room"; - - const char *name = ses->GetName(); - - char *wmsg = new char[sizeof(welcome_msg) + 2]; - sprintf(wmsg, "%s%d", welcome_msg, code); - ses->Send(wmsg); - delete[] wmsg; - - char *emsg = new char[strlen(name) + sizeof(entered_msg)]; - sprintf(emsg, "%s%s", name, entered_msg); - this->SendAll(emsg, ses); - delete[] emsg; - -} - -void ChatRoom::RemoveSession(ChatSession *ses) -{ - if(code != std_id_lobby) { - const char left_msg[] = " has left the room"; - const char *name = ses->GetName(); - - int len = strlen(name); - char *lmsg = new char[len + sizeof(left_msg) + 2]; - sprintf(lmsg, "%s%s", name, left_msg); - this->SendAll(lmsg, ses); - delete[] lmsg; - } - - item **p; - for(p = &first; *p; p = &((*p)->next)) { - if( ((*p)->s) == ses ) { - item *tmp = *p; - *p = tmp->next; - // not delete ChatSession! - delete tmp; - - if(code != std_id_lobby && !first) - the_server->DeleteRoom(code); - - return; - } - } -} - -void ChatRoom::CloseSession(ChatSession *ses) -{ - Server *serv = the_server; - this->RemoveSession(ses); - serv->CloseConnection(ses); -} - -////////////////////////////////////////////////////////////// - -static char **ParseToArg(const char *input, int &arrc) -{ - int max_argv = 5; - arrc = 0; - char **arr = new char*[max_argv+1]; - - int start = 0; - for(int i = 0; i < (int) strlen(input)+1; i++) { - if(input[i] == ' ' || input[i] == '\0' || input[i] == '\n') { // end - if(start == std_id_lobby) - continue; - - int size = i - start; - arr[arrc] = new char[size+1]; - memcpy(arr[arrc], input + start, sizeof(char) * size); - arr[arrc][size] = '\0'; - - start = -1; - arrc++; - if(arrc == max_argv) - break; - } - else if(start == -1) - start = i; - } - - arr[arrc] = 0; - return arr; -} - -static unsigned long hash(const char *str) -{ - unsigned long hash = 5381; - char c; - - while( (c = *str++) ) - hash = ((hash << 5) + hash) + c; - - return hash; -} - -static bool checkForbiddenSymbols(const char *str) -{ - char banned_symbols[] = {'!', '@', '#', '\'', - '\"', '\\', '/', '^', '&', '*', ';', ','}; - - int num = sizeof(banned_symbols)/sizeof(char); - for(int i = 0; i < (int)strlen(str); i++) { - for(int j = 0; j < num; j++) { - if(str[i] == banned_symbols[j]) - return 1; - } - } - - return 0; -} \ No newline at end of file diff --git a/src/server/rooms.hpp b/src/server/rooms.hpp deleted file mode 100644 index 4d71d0b..0000000 --- a/src/server/rooms.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef ROOMREALIZATION_H -#define ROOMREALIZATION_H - -#include "../const_vars.hpp" - -const int std_id_lobby = -1; -const int max_room_lenpass = 24; - -class Server; -class ChatSession; - -class ChatRoom { - Server *the_server; - const int code; - // if code == std_id_lobby then it's lobby - - char secret_pass[max_room_lenpass]; - - struct item { - ChatSession *s; - item *next; - }; - item *first; -public: - ChatRoom(Server *i_server, int id, char *pass); - ~ChatRoom(); - - void SendAll(const char *msg, ChatSession *except = 0, - const int spec_msg = system_msg); - - void HandleMessage(ChatSession *ses, const char *str); - void HandleCommand(ChatSession *ses, int cmd_counter, - char **commands); - - const char *GetSecretPass(); - - void AddSession(ChatSession *ses); - void RemoveSession(ChatSession *ses); - void CloseSession(ChatSession *ses); -}; - -#endif \ No newline at end of file diff --git a/src/server/server.cpp b/src/server/server.cpp deleted file mode 100644 index ab7c420..0000000 --- a/src/server/server.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include - -#include "chat.hpp" - -const int port = 3030; - -int main(int argc, char *argv[]) -{ - /* Event-driven programming */ - /* - 1) selection event - 2) event handling - */ - EventSelector *selector = new EventSelector; - Server *serv = Server::Start(selector, port); - if(!serv) { - perror("server"); - return 1; - } - selector->Run(); - return 0; -} \ No newline at end of file -- cgit v1.2.3-18-g5258