From 5cbaa841d0bf07f75614205059324e5d2adbc5dd Mon Sep 17 00:00:00 2001
From: Joursoir <chat@joursoir.net>
Date: Fri, 27 Nov 2020 01:41:09 +0300
Subject: server: add different messages, support commands('/') and
 spec-messages

---
 src/server/chat.cpp  |  91 ++++++++++++++++++++---
 src/server/chat.hpp  |  18 +++--
 src/server/rooms.cpp | 205 ++++++++++++++++++++++++++++++++++-----------------
 src/server/rooms.hpp |  12 +--
 4 files changed, 239 insertions(+), 87 deletions(-)

(limited to 'src/server')

diff --git a/src/server/chat.cpp b/src/server/chat.cpp
index a9ecda1..0d6eef8 100644
--- a/src/server/chat.cpp
+++ b/src/server/chat.cpp
@@ -8,7 +8,7 @@
 #include "chat.hpp"
 #include "rooms.hpp"
 
-static const int qlen_for_listen = 6;
+const int qlen_for_listen = 6;
 
 void ChatSession::Send(const char *msg)
 {
@@ -61,7 +61,6 @@ 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->LeaveMessage(this);
         the_master->CloseSession(this);
         return;
     }
@@ -94,27 +93,39 @@ void ChatSession::CheckLines()
     }
 }
 
-void ChatSession::ChangeName(const char *n_name)
+const char *ChatSession::GetName()
+{
+    if(name[0])
+        return name;
+    return 0;
+}
+
+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, "Now your name is %s\n", name);
+    sprintf(msg, "#Your name: %s\n", name);
 
     this->Send(msg);
         
     delete[] msg;
 }
 
+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, true);
+    lobby = new ChatRoom(this, std_id_lobby);
 }
 
 Server::~Server()
@@ -150,6 +161,14 @@ Server *Server::Start(EventSelector *sel, int port)
     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)
@@ -164,6 +183,24 @@ 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()
@@ -178,19 +215,53 @@ int Server::AddRoom()
 	int id = -1;
 	for(int i = 0; i < room_len; i++) {
 		if(room[i] == 0) {
-			id = 0;
-			room[i] = new ChatRoom(this, false);
+			id = i;
+			room[i] = new ChatRoom(this, id);
+            break;
 		}
 	}
 
 	if(id == -1) {
 		ChatRoom **tmp = new ChatRoom*[room_len+1];
-		for(int i = 0; i < room_len; i++) {
+		for(int i = 0; i < room_len; i++)
 			tmp[i] = room[i];
-		}
-		tmp[room_len] = new ChatRoom(this, false);
+
+        id = room_len;
+		tmp[room_len] = new ChatRoom(this, id);
 		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);
+}
+
+bool Server::ChangeSessionRoom(ChatRoom *cur_room, ChatSession *s, int id)
+{
+    if(id >= room_len || !room[id])
+        return false;
+
+    cur_room->RemoveSession(s);
+    room[id]->AddSession(s);
+    s->SetRoom(room[id]);
+    return true;
 }
\ No newline at end of file
diff --git a/src/server/chat.hpp b/src/server/chat.hpp
index ca22519..6abfc48 100644
--- a/src/server/chat.hpp
+++ b/src/server/chat.hpp
@@ -27,29 +27,35 @@ class ChatSession : FdHandler {
 	void ReadAndIgnore();
     void ReadAndCheck();
     void CheckLines();
+
+    void SetRoom(ChatRoom *new_master);
 public:
-    const char *GetName() const { return name; }
+    const char *GetName();
 
-    void ChangeName(const char *n_name);
+    void SetName(const char *n_name);
     void Send(const char *msg);
 };
 
 class Server : public FdHandler {
     EventSelector *the_selector;
     ChatRoom **room;
-    ChatRoom *lobby;
-
     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();
-    // RemoveRoom();
-    // void AddSessionToRoom(ChatSession *s, int id);
+    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);
     void CloseConnection(ChatSession *s)
         { the_selector->Remove(s); delete s; }
 private:
diff --git a/src/server/rooms.cpp b/src/server/rooms.cpp
index 624eb88..1ffb89a 100644
--- a/src/server/rooms.cpp
+++ b/src/server/rooms.cpp
@@ -1,23 +1,44 @@
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
 
 #include "rooms.hpp"
 #include "chat.hpp"
 
 // hash of commands:
-#define CMD_NAME 210647350421
-#define CMD_CREATE 229394553991880
-
-const int cmd_num = 2;
-const char *commands[cmd_num] = {
-	"/name",
-	"/create"
-	// /rooms - print all public rooms
-};
+#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 **ParseCommand(const char *input, int &arrc);
+char **ParseToArg(const char *input, int &arrc);
 unsigned long hash(const char *str);
 
+struct cmd_info {
+	char name[15];
+	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 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) 
+
+	// IDEA: /clear - clear screen
+};
+
 ChatRoom::~ChatRoom()
 {
 	while(first) {
@@ -37,31 +58,18 @@ void ChatRoom::SendAll(const char *msg, ChatSession *except)
             p->s->Send(msg);
 }
 
-void ChatRoom::LeaveMessage(ChatSession *except)
-{
-	if(it_lobby)
-		return;
-
-	const char left_msg[] = " has left the chat";
-	const char *name = except->GetName();
-
-    int len = strlen(name);
-    char *lmsg = new char[len + sizeof(left_msg) + 2];
-    sprintf(lmsg, "%s%s\n", name, left_msg);
-    this->SendAll(lmsg, except);
-    delete[] lmsg;
-}
-
 void ChatRoom::HandleMessage(ChatSession *ses, const char *str)
 {
 	if(str[0] == '/') { // if user sent a command
 		int argc = 0;
-		char **argv = 0;
-		argv = ParseCommand(str, argc);
-
+		char **argv = ParseToArg(str, argc);
 		this->HandleCommand(ses, argc, argv);
+
+		for(int i = 0; i < argc; i++)
+			delete[] argv[i];
+		delete[] argv;
 	}
-	else if(!it_lobby) {
+	else if(code != std_id_lobby) {
 	    char *msg = new char[max_msg_len];
 	    sprintf(msg, "%s: %s\n", ses->GetName(), str);
 
@@ -71,54 +79,83 @@ void ChatRoom::HandleMessage(ChatSession *ses, const char *str)
 	}
 }
 
-void ChatRoom::HandleCommand(ChatSession *s, int cmd_counter,
-	char **commands)
+void ChatRoom::HandleCommand(ChatSession *ses, int count,
+	char **argvar)
 {
-	unsigned long cmd = -1;
-	cmd = hash(commands[0]);
+	unsigned long hash_cmd = -1;
+	hash_cmd = hash(argvar[0]);
 
-	switch(cmd)
-	{
-		case CMD_NAME: {
-			if(!it_lobby) {
-				s->Send("You can use this command only in lobby\n");
-				break;
-			}
+	int what_command = -1;
+	for(int i = 0; i < cmd_count; i++) {
+		if(cmd[i].name_hash == hash_cmd) {
+			what_command = i;
+			break;
+		}
+	}
 
-			if(cmd_counter == 1) {
-				s->Send("Usage: /name your_good_name\n");
-				break;
-			}
+	if(what_command == -1)
+		return ses->Send("#Unknown command. Use: /help\n");
 
-			int len = strlen(commands[1]);
-			if(len > max_name_len || len < 3) {
-				s->Send("Incorrect name. Name length from 3 to 18 chars\n");
-				break;
-			}
+	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";
+
+	// 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);
 
-			s->ChangeName(commands[1]);
+	// 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: {
-			if(!it_lobby) {
-				s->Send("You can use this command only in lobby\n");
-				break;
-			}
-			if(!s->GetName()) {
-				s->Send("Before you can use this command, use /name\n");
-				break;
-			}
+			int id = the_server->AddRoom();
 
-			//int id = the_s->AddRoom();
-			//the_s->AddSessionToRoom(s, id);
+			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);
+			ses->Send(cmsg);
+			delete[] cmsg;
 
+			the_server->ChangeSessionRoom(this, ses, id);
+			break;
+		}
+		case CMD_JOIN: {
+			int id = atoi(argvar[1]);
+			bool success = false;
+			if(id >= 0)
+				success = the_server->ChangeSessionRoom(this, ses, id);
+
+			if(!success)
+				ses->Send("#Room with that id didn't exist\n");
+
+			break;
+		}
+		case CMD_EXIT: {
+			the_server->GotoLobby(this, ses);
 			break;
 		}
+		case CMD_ROOMS: {
 
-		default: {
-			s->Send("Unknown command. Use: /help\n");
 			break;
 		}
+		default: break;
 	}
 }
 
@@ -128,10 +165,40 @@ void ChatRoom::AddSession(ChatSession *ses)
     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\n", 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);
+    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\n", name, left_msg);
+	    this->SendAll(lmsg, ses);
+	    delete[] lmsg;
+	}
+
 	item **p;
 	for(p = &first; *p; p = &((*p)->next)) {
 		if( ((*p)->s) == ses ) {
@@ -139,6 +206,10 @@ void ChatRoom::RemoveSession(ChatSession *ses)
 			*p = tmp->next;
 			// not delete ChatSession!
 			delete tmp;
+			
+			if(code != std_id_lobby && !first)
+				the_server->DeleteRoom(code);
+
 			return;
 		}
 	}
@@ -146,13 +217,14 @@ void ChatRoom::RemoveSession(ChatSession *ses)
 
 void ChatRoom::CloseSession(ChatSession *ses)
 {
+	Server *serv = the_server;
 	this->RemoveSession(ses);
-	the_server->CloseConnection(ses);
+	serv->CloseConnection(ses);
 }
 
 //////////////////////////////////////////////////////////////
 
-char **ParseCommand(const char *input, int &arrc)
+char **ParseToArg(const char *input, int &arrc)
 {
 	int max_argv = 5;
 	arrc = 0;
@@ -161,12 +233,13 @@ char **ParseCommand(const char *input, int &arrc)
 	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 == -1)
+			if(start == std_id_lobby)
 				continue;
 
 			int size = i - start;
-			arr[arrc] = new char[size];
+			arr[arrc] = new char[size+1];
 			memcpy(arr[arrc], input + start, sizeof(char) * size);
+			arr[arrc][size] = '\0';
 			
 			start = -1;
 			arrc++;
diff --git a/src/server/rooms.hpp b/src/server/rooms.hpp
index 08fffff..9423be1 100644
--- a/src/server/rooms.hpp
+++ b/src/server/rooms.hpp
@@ -1,12 +1,15 @@
 #ifndef ROOMREALIZATION_H
 #define ROOMREALIZATION_H
 
+const int std_id_lobby = -1;
+
 class Server;
 class ChatSession;
 
 class ChatRoom {
     Server *the_server;
-    const bool it_lobby;
+    const int code;
+    // code == -1 it's lobby
 
     struct item {
         ChatSession *s;
@@ -14,15 +17,14 @@ class ChatRoom {
     };
     item *first;
 public:
-    ChatRoom(Server *i_server, bool i_lobby)
-        : the_server(i_server), it_lobby(i_lobby), first(0) {}
+    ChatRoom(Server *i_server, int id)
+        : the_server(i_server), code(id), first(0) {}
     ~ChatRoom();
 
     void SendAll(const char *msg, ChatSession *except = 0);
-    void LeaveMessage(ChatSession *except);
 
     void HandleMessage(ChatSession *ses, const char *str);
-    void HandleCommand(ChatSession *s, int cmd_counter,
+    void HandleCommand(ChatSession *ses, int cmd_counter,
         char **commands);
 
     void AddSession(ChatSession *ses);
-- 
cgit v1.2.3-18-g5258