From 7385c0352c7fe7e9ceca343400a5caa5ddd999d6 Mon Sep 17 00:00:00 2001 From: Joursoir Date: Mon, 30 Nov 2020 02:29:18 +0300 Subject: refactoring and some feature client: type nick in argument server: basis for registration; change style add commands; edit send msg to user; feature: private room --- src/client/client.cpp | 9 ++- src/client/user.cpp | 37 ++++++++-- src/client/user.hpp | 5 +- src/const_vars.hpp | 10 ++- src/server/chat.cpp | 73 ++++++++++---------- src/server/chat.hpp | 32 +++++++-- src/server/rooms.cpp | 188 +++++++++++++++++++++++++++++++++----------------- src/server/rooms.hpp | 15 ++-- 8 files changed, 241 insertions(+), 128 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 7f092cc..8143b9e 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -10,6 +10,11 @@ static int port = 3030; int main(int argc, char *argv[]) { + if(argc < 2) { + printf("Usage: client *name*\n"); + return 1; + } + initscr(); //raw(); noecho(); @@ -22,10 +27,10 @@ int main(int argc, char *argv[]) return 1; } - Client *user = Client::Start(SERVER_IP, port); + Client *user = Client::Start(SERVER_IP, port, argv[1]); if(!user) { endwin(); - perror("client"); + perror("server"); return 1; } diff --git a/src/client/user.cpp b/src/client/user.cpp index 957665e..31cb5e5 100644 --- a/src/client/user.cpp +++ b/src/client/user.cpp @@ -10,7 +10,24 @@ #include "user.hpp" -Client *Client::Start(const char* ip, int port) +const int key_enter = 10; +const int key_escape = 27; +const int key_backspace = 127; + +// + +Client::Client(int i_fd, char *username) : fd(i_fd), in_buf_used(0), + out_buf_used(0) +{ + int len = strlen(username); + char *name = new char[len+2]; + sprintf(name, "%s\n", username); + + write(i_fd, name, strlen(name)); + delete[] name; +} + +Client *Client::Start(const char* ip, int port, char *username) { int client; client = socket(AF_INET, SOCK_STREAM, 0); @@ -32,7 +49,7 @@ Client *Client::Start(const char* ip, int port) sizeof(server_adress)); if(res == -1) return 0; - return new Client(client); + return new Client(client, username); } void Client::Run(ChatRoom *room) @@ -41,6 +58,7 @@ void Client::Run(ChatRoom *room) int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); + int cls = false; do { this->HandleButton(room); @@ -53,6 +71,13 @@ void Client::Run(ChatRoom *room) } else if(recive > 0) out_buf_used += recive; + else { + if(!cls) { + strcpy(out_buffer, "Server closed the connection. Use ESC to exit."); + room->AddMessage(out_buffer, system_msg); + cls = true; + } + } if(out_buf_used > 0) { /* warning: if we get a (message without '\n') > max_msg_len then @@ -62,14 +87,14 @@ void Client::Run(ChatRoom *room) out_buffer[i] = 0; // in first char have may spec-symbol, check it: - int spec_symbol = usually_msg; + int spec_msg = usual_msg; char *buf = out_buffer; - if(out_buffer[0] == '#') { - spec_symbol = system_msg; + if(out_buffer[0] == system_char) { + spec_msg = system_msg; buf += 1; } - room->AddMessage(buf, spec_symbol); + room->AddMessage(buf, spec_msg); memmove(out_buffer, out_buffer + i + 1, out_buf_used - i - 1); out_buf_used -= i + 1; break; diff --git a/src/client/user.hpp b/src/client/user.hpp index 8e2218a..1ae8427 100644 --- a/src/client/user.hpp +++ b/src/client/user.hpp @@ -14,12 +14,11 @@ class Client { bool exit_flag; - Client(int i_fd) - : fd(i_fd), in_buf_used(0), out_buf_used(0) { } + Client(int i_fd, char *username); public: ~Client() { close(fd); } - static Client *Start(const char* ip, int port); + static Client *Start(const char* ip, int port, char *username); void Run(ChatRoom *room); void BreakLoop() { exit_flag = true; } void HandleButton(ChatRoom *room); diff --git a/src/const_vars.hpp b/src/const_vars.hpp index d41f800..54ef70f 100644 --- a/src/const_vars.hpp +++ b/src/const_vars.hpp @@ -8,16 +8,14 @@ */ const int max_name_len = 18; +const int min_name_len = 3; const int oneline_len = 57; const int max_usermsg_len = oneline_len * 3 - max_name_len - 2; // ": " = 2 // 57 * 3 - 18 - 2 = 151 const int max_msg_len = oneline_len * 3; // 57*3 = 171 -const int key_enter = 10; -const int key_escape = 27; -const int key_backspace = 127; - -const int usually_msg = 0; // no first char -const int system_msg = 1; // first char: '#' +const int usual_msg = 0; // no first char +const int system_msg = 1; +const char system_char = '#'; #endif \ No newline at end of file diff --git a/src/server/chat.cpp b/src/server/chat.cpp index 0d6eef8..561a61d 100644 --- a/src/server/chat.cpp +++ b/src/server/chat.cpp @@ -10,10 +10,24 @@ const int qlen_for_listen = 6; -void ChatSession::Send(const char *msg) +void ChatSession::Send(const char *msg, const int spec_msg) { - CONSOLE_LOG("%s", msg); - write(GetFd(), msg, strlen(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) @@ -102,16 +116,7 @@ const char *ChatSession::GetName() void ChatSession::SetName(const char *n_name) { - for(int i = 0; i < max_name_len; i++) - name[i] = 0; - strcpy(name, n_name); - char *msg = new char[max_msg_len]; - sprintf(msg, "#Your name: %s\n", name); - - this->Send(msg); - - delete[] msg; } void ChatSession::SetRoom(ChatRoom *new_master) @@ -125,7 +130,7 @@ Server::Server(EventSelector *sel, int fd) : FdHandler(fd), the_selector(sel) { the_selector->Add(this); - lobby = new ChatRoom(this, std_id_lobby); + lobby = new ChatRoom(this, std_id_lobby, 0); } Server::~Server() @@ -183,27 +188,9 @@ void Server::Handle(bool r, bool w) ChatSession *s = new ChatSession(lobby, sd); lobby->AddSession(s); the_selector->Add(s); - - // welcome messages: - s->Send("#Welcome to WantChat!\n"); - s->Send(" \n"); - s->Send("#It is anonymous chat in retro-style 80s.\n"); - s->Send("#Use our chat-client for more immersed.\n"); - s->Send(" \n"); - s->Send("#This project is open source :)\n"); - s->Send("#Github: github.com/Joursoir/want-chat\n"); - s->Send(" \n"); - s->Send("#First, come up with a nickname using /name your_good_name\n"); - s->Send("#Thereafter you can join to room using /join room_id\n"); - s->Send("#You can find rooms using /rooms\n"); - s->Send(" \n"); - s->Send("#For more detailed info: /help. Good chatting!\n"); - s->Send(" \n"); - - } -int Server::AddRoom() +int Server::AddRoom(char *password) { if(!room) { room_len = 16; @@ -216,7 +203,7 @@ int Server::AddRoom() for(int i = 0; i < room_len; i++) { if(room[i] == 0) { id = i; - room[i] = new ChatRoom(this, id); + room[i] = new ChatRoom(this, id, password); break; } } @@ -227,7 +214,7 @@ int Server::AddRoom() tmp[i] = room[i]; id = room_len; - tmp[room_len] = new ChatRoom(this, id); + tmp[room_len] = new ChatRoom(this, id, password); room_len += 1; delete[] room; @@ -255,13 +242,23 @@ void Server::GotoLobby(ChatRoom *cur_room, ChatSession *s) s->SetRoom(lobby); } -bool Server::ChangeSessionRoom(ChatRoom *cur_room, ChatSession *s, int id) +handle_room_enter Server::ChangeSessionRoom(ChatRoom *cur_room, + ChatSession *s, int id, char *pass) { - if(id >= room_len || !room[id]) - return false; + 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 true; + return enter_success; } \ No newline at end of file diff --git a/src/server/chat.hpp b/src/server/chat.hpp index 6abfc48..4b6c8b1 100644 --- a/src/server/chat.hpp +++ b/src/server/chat.hpp @@ -6,34 +6,51 @@ #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), the_master(i_master) {} + 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 ReadAndIgnore(); void ReadAndCheck(); void CheckLines(); - - void SetRoom(ChatRoom *new_master); public: const char *GetName(); void SetName(const char *n_name); - void Send(const char *msg); + void Send(const char *msg, const int spec_msg = system_msg); }; class Server : public FdHandler { @@ -51,11 +68,12 @@ public: bool RoomExist(int id) const; - int AddRoom(); + int AddRoom(char *password); bool DeleteRoom(int id); // call only if room is empty void GotoLobby(ChatRoom *cur_room, ChatSession *s); - bool ChangeSessionRoom(ChatRoom *cur_room, ChatSession *s, int id); + handle_room_enter ChangeSessionRoom(ChatRoom *cur_room, + ChatSession *s, int id, char *pass); void CloseConnection(ChatSession *s) { the_selector->Remove(s); delete s; } private: diff --git a/src/server/rooms.cpp b/src/server/rooms.cpp index 1ffb89a..ba1ee1b 100644 --- a/src/server/rooms.cpp +++ b/src/server/rooms.cpp @@ -5,40 +5,46 @@ #include "rooms.hpp" #include "chat.hpp" -// hash of commands: -#define CMD_NAME 0 -#define CMD_CREATE 1 -#define CMD_JOIN 2 -#define CMD_EXIT 3 -#define CMD_ROOMS 4 - #define USE_IN_ROOM 0 #define USE_IN_LOBBY 1 #define USE_ANYWHERE 2 -char **ParseToArg(const char *input, int &arrc); -unsigned long hash(const char *str); +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]; - unsigned long name_hash; + 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_count = 5; +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] = { - {"/name", hash("/name"), USE_IN_LOBBY, 1, "#Usage: /name *your_good_name*\n"}, - {"/create", hash("/create"), USE_IN_LOBBY, 0, "#Usage: /create [pass-key]\n"}, - {"/join", hash("/join"), USE_IN_LOBBY, 1, "#Usage: /join *id* [pass-key]\n"}, - {"/exit", hash("/exit"), USE_IN_ROOM, 0, "#Usage: /exit\n"}, - {"/rooms", hash("/rooms"), USE_IN_LOBBY, 0, "#Usage: /rooms\n"} // print all public rooms - // if u add cmd, then add define too (see above) + {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) { @@ -49,17 +55,53 @@ ChatRoom::~ChatRoom() } } -void ChatRoom::SendAll(const char *msg, ChatSession *except) +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); + 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); @@ -71,12 +113,13 @@ void ChatRoom::HandleMessage(ChatSession *ses, const char *str) } else if(code != std_id_lobby) { char *msg = new char[max_msg_len]; - sprintf(msg, "%s: %s\n", ses->GetName(), str); + sprintf(msg, "%s: %s", ses->GetName(), str); - this->SendAll(msg); + 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, @@ -94,11 +137,10 @@ void ChatRoom::HandleCommand(ChatSession *ses, int count, } if(what_command == -1) - return ses->Send("#Unknown command. Use: /help\n"); + return ses->Send("Unknown command. Use: /help"); - const char onlyroom_msg[] = "#You can use this command only in rooms!\n"; - const char onlylobby_msg[] = "#You can use this command only in lobby!\n"; - const char initname_msg[] = "#First come up with name!\n"; + 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) @@ -110,55 +152,60 @@ void ChatRoom::HandleCommand(ChatSession *ses, int count, if(cmd[what_command].min_argc > count-1) return ses->Send(cmd[what_command].usage); - // name check: - if((!ses->GetName()) && what_command != CMD_NAME) - return ses->Send(initname_msg); - - switch(what_command) { - case CMD_NAME: { - int len = strlen(argvar[1]); - if(len > max_name_len || len < 3) - return ses->Send("#Incorrect name. Name length from 3 to 18 chars\n"); - - ses->SetName(argvar[1]); - break; - } - case CMD_CREATE: { - int id = the_server->AddRoom(); - - const char fcmsg[] = "#You create a room #"; - const char scmsg[] = " with password "; - char *cmsg = new char[strlen(fcmsg)+strlen(scmsg)+4]; - sprintf(cmsg, "%s%d\n", fcmsg, id); + 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); + the_server->ChangeSessionRoom(this, ses, id, pass); break; } - case CMD_JOIN: { + case cmd_id_join: { int id = atoi(argvar[1]); - bool success = false; - if(id >= 0) - success = the_server->ChangeSessionRoom(this, ses, id); + char *pass = argvar[2]; // if count == 2, then argvar[2] = 0 - if(!success) - ses->Send("#Room with that id didn't exist\n"); + 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_EXIT: { + case cmd_id_exit: { the_server->GotoLobby(this, ses); break; } - case CMD_ROOMS: { - + 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; @@ -175,12 +222,12 @@ void ChatRoom::AddSession(ChatSession *ses) const char *name = ses->GetName(); char *wmsg = new char[sizeof(welcome_msg) + 2]; - sprintf(wmsg, "#%s%d\n", welcome_msg, code); + sprintf(wmsg, "%s%d", welcome_msg, code); ses->Send(wmsg); delete[] wmsg; - char *emsg = new char[strlen(name) + sizeof(entered_msg) + 2]; - sprintf(emsg, "#%s%s\n", name, entered_msg); + char *emsg = new char[strlen(name) + sizeof(entered_msg)]; + sprintf(emsg, "%s%s", name, entered_msg); this->SendAll(emsg, ses); delete[] emsg; @@ -194,7 +241,7 @@ void ChatRoom::RemoveSession(ChatSession *ses) int len = strlen(name); char *lmsg = new char[len + sizeof(left_msg) + 2]; - sprintf(lmsg, "#%s%s\n", name, left_msg); + sprintf(lmsg, "%s%s", name, left_msg); this->SendAll(lmsg, ses); delete[] lmsg; } @@ -224,11 +271,11 @@ void ChatRoom::CloseSession(ChatSession *ses) ////////////////////////////////////////////////////////////// -char **ParseToArg(const char *input, int &arrc) +static char **ParseToArg(const char *input, int &arrc) { int max_argv = 5; arrc = 0; - char **arr = new char*[max_argv]; + char **arr = new char*[max_argv+1]; int start = 0; for(int i = 0; i < (int) strlen(input)+1; i++) { @@ -250,10 +297,11 @@ char **ParseToArg(const char *input, int &arrc) start = i; } + arr[arrc] = 0; return arr; } -unsigned long hash(const char *str) +static unsigned long hash(const char *str) { unsigned long hash = 5381; char c; @@ -262,4 +310,20 @@ unsigned long hash(const char *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 index 9423be1..4d71d0b 100644 --- a/src/server/rooms.hpp +++ b/src/server/rooms.hpp @@ -1,7 +1,10 @@ #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; @@ -9,7 +12,9 @@ class ChatSession; class ChatRoom { Server *the_server; const int code; - // code == -1 it's lobby + // if code == std_id_lobby then it's lobby + + char secret_pass[max_room_lenpass]; struct item { ChatSession *s; @@ -17,16 +22,18 @@ class ChatRoom { }; item *first; public: - ChatRoom(Server *i_server, int id) - : the_server(i_server), code(id), first(0) {} + ChatRoom(Server *i_server, int id, char *pass); ~ChatRoom(); - void SendAll(const char *msg, ChatSession *except = 0); + 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); -- cgit v1.2.3-18-g5258