aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2020-09-29 22:18:33 +0200
committerTom Smeding <tom.smeding@gmail.com>2020-09-29 22:25:23 +0200
commita8a0603375fa4c5c1e61bd8a0dc0ed7f3f77854e (patch)
treec1749d16c7fe8fad8b0d04cc80092cd22c180a9d
parent1ecfdb8eef2e1cf5c9f017708b6313ea5d4093d7 (diff)
server: Implement room_leave
-rw-r--r--TODO.txt3
-rw-r--r--command.c101
-rw-r--r--db.c11
-rw-r--r--event.c10
-rw-r--r--event.h2
-rw-r--r--plugin.c1
-rw-r--r--plugin_client_header.h1
7 files changed, 103 insertions, 26 deletions
diff --git a/TODO.txt b/TODO.txt
index f180d0a..45d5421 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,10 +1,9 @@
- Server crashed with 'DIE userdata_unregister called while nonexistent'; fix!
-- Unit tests for hashtable
-- Room leave command
- Store message as UTF8 text, not blob
- Somehow communicate (push? poll?) to room members whether a particular user is currently _active_, not just online.
- A session is marked inactive 2 minutes after the latest activity on that session. Make the number "2" configurable and not a hard-coded constant.
- Use poll(2), not select(2)
+- Use single sql query in broadcast.c to get all receiving users, don't perform a JOIN in user code
- Fix OOM dos vector
- Public rooms, and public room join command
diff --git a/command.c b/command.c
index 69f40b2..f69593c 100644
--- a/command.c
+++ b/command.c
@@ -193,6 +193,57 @@ static struct cmd_retval cmd_create_room(struct conn_data *data,const char *tag,
return RET_CLOSE(closed);
}
+static struct cmd_retval cmd_leave_room(struct conn_data *data,const char *tag,const char **args){
+ (void)args;
+ if (data->userid == -1) {
+ net_send_error(data->fd, tag, "Not logged in");
+ return RET_OK;
+ }
+ userdata_mark_active(data->userid, data->fd, true);
+
+ const char *roomname = args[0];
+
+ i64 roomid = db_find_room(roomname);
+ if (roomid == -1) {
+ net_send_error(data->fd, tag, "Room not found");
+ return RET_OK;
+ }
+
+ if (!db_remove_member(roomid, data->userid)) {
+ net_send_error(data->fd, tag, "Not in that room");
+ return RET_OK;
+ }
+
+ char *username = db_get_username(data->userid);
+ char *msgbuf = NULL;
+ i64 msgbuflen = asprintf(&msgbuf, "_push leave %s %s\n", roomname, username);
+ free(username);
+
+ // First send the leave push message to all remaining room members
+ struct db_user_list members = db_list_members(roomid);
+ for (i64 i = 0; i < members.count; i++) {
+ i64 nfds;
+ const int *fds = userdata_online(members.list[i].id, &nfds);
+ for (i64 j = 0; j < nfds; j++) {
+ net_send_raw_text(fds[j], msgbuf, msgbuflen);
+ }
+ }
+ db_nullify_user_list(members);
+
+ // Then send the leave push message to the other sessions of the current user
+ i64 nfds;
+ const int *fds = userdata_online(data->userid, &nfds);
+ for (i64 j = 0; j < nfds; j++) {
+ if (fds[j] != data->fd) {
+ net_send_raw_text(fds[j], msgbuf, msgbuflen);
+ }
+ }
+
+ free(msgbuf);
+
+ return RET_CLOSE(net_send_ok(data->fd,tag));
+}
+
static struct cmd_retval cmd_invite(struct conn_data *data,const char *tag,const char **args){
if(data->userid==-1){
net_send_error(data->fd,tag,"Not logged in");
@@ -497,33 +548,34 @@ struct cmd_info{
// Use CommandHash.hs to re-generate this perfect hash function for a different
// list of commands.
-#define COMMAND_HASH_MODULUS 35
-#define COMMAND_HASH(cmd0, len) ((1 * cmd0 + 6 * len) % COMMAND_HASH_MODULUS)
+#define COMMAND_HASH_MODULUS 31
+#define COMMAND_HASH(cmd0, cmd1, len) ((9 * cmd0 + 1 * cmd1 + 3 * len) % COMMAND_HASH_MODULUS)
-#define COMMAND_ENTRY(cmd0, cmd, nargs, longlast, handler) \
- [COMMAND_HASH(cmd0, strlen(cmd))] = {cmd, nargs, longlast, handler}
+#define COMMAND_ENTRY(cmd0, cmd1, cmd, nargs, longlast, handler) \
+ [COMMAND_HASH(cmd0, cmd1, strlen(cmd))] = {cmd, nargs, longlast, handler}
-// First argument to COMMAND_ENTRY must be command[0]. This is because
+// First two arguments to COMMAND_ENTRY must be command[0] and [1]. This is because
// apparently, "abc"[0] is not a constant expression, while strlen("abc") is.
static const struct cmd_info commands[COMMAND_HASH_MODULUS] = {
- COMMAND_ENTRY('v', "version", 1, false, cmd_version),
- COMMAND_ENTRY('r', "register", 2, true, cmd_register),
- COMMAND_ENTRY('l', "login", 2, true, cmd_login),
- COMMAND_ENTRY('l', "logout", 0, false, cmd_logout),
- COMMAND_ENTRY('c', "change_password", 1, true, cmd_change_password),
- COMMAND_ENTRY('l', "list_rooms", 0, false, cmd_list_rooms),
- COMMAND_ENTRY('l', "list_members", 1, false, cmd_list_members),
- COMMAND_ENTRY('c', "create_room", 0, false, cmd_create_room),
- COMMAND_ENTRY('i', "invite", 2, false, cmd_invite),
- COMMAND_ENTRY('s', "send", 3, true, cmd_send),
- COMMAND_ENTRY('h', "history", 2, false, cmd_history),
- COMMAND_ENTRY('h', "history_before", 3, false, cmd_history_before),
- COMMAND_ENTRY('g', "get_message", 1, false, cmd_get_message),
- COMMAND_ENTRY('p', "ping", 0, false, cmd_ping),
- COMMAND_ENTRY('i', "is_online", 1, false, cmd_is_online),
- COMMAND_ENTRY('f', "firebase_token", 1, false, cmd_firebase_token),
- COMMAND_ENTRY('d', "delete_firebase_token", 1, false, cmd_delete_firebase_token),
- COMMAND_ENTRY('u', "user_active", 1, false, cmd_user_active),
+ COMMAND_ENTRY('v','e', "version", 1, false, cmd_version),
+ COMMAND_ENTRY('r','e', "register", 2, true, cmd_register),
+ COMMAND_ENTRY('l','o', "login", 2, true, cmd_login),
+ COMMAND_ENTRY('l','o', "logout", 0, false, cmd_logout),
+ COMMAND_ENTRY('c','h', "change_password", 1, true, cmd_change_password),
+ COMMAND_ENTRY('l','i', "list_rooms", 0, false, cmd_list_rooms),
+ COMMAND_ENTRY('l','i', "list_members", 1, false, cmd_list_members),
+ COMMAND_ENTRY('c','r', "create_room", 0, false, cmd_create_room),
+ COMMAND_ENTRY('l','e', "leave_room", 1, false, cmd_leave_room),
+ COMMAND_ENTRY('i','n', "invite", 2, false, cmd_invite),
+ COMMAND_ENTRY('s','e', "send", 3, true, cmd_send),
+ COMMAND_ENTRY('h','i', "history", 2, false, cmd_history),
+ COMMAND_ENTRY('h','i', "history_before", 3, false, cmd_history_before),
+ COMMAND_ENTRY('g','e', "get_message", 1, false, cmd_get_message),
+ COMMAND_ENTRY('p','i', "ping", 0, false, cmd_ping),
+ COMMAND_ENTRY('i','s', "is_online", 1, false, cmd_is_online),
+ COMMAND_ENTRY('f','i', "firebase_token", 1, false, cmd_firebase_token),
+ COMMAND_ENTRY('d','e', "delete_firebase_token", 1, false, cmd_delete_firebase_token),
+ COMMAND_ENTRY('u','s', "user_active", 1, false, cmd_user_active),
};
@@ -543,7 +595,8 @@ bool handle_input_line(struct conn_data *data,char *line,size_t linelen){
sepp=memchr(line,' ',linelen);
if(sepp==NULL)sepp=line+linelen;
const size_t cmdlen=sepp-line;
- const struct cmd_info *command=&commands[COMMAND_HASH(line[0],cmdlen)];
+ const struct cmd_info *command=NULL;
+ if(cmdlen>=2)command=&commands[COMMAND_HASH(line[0],line[1],cmdlen)];
if(!command->cmdname
||cmdlen!=strlen(command->cmdname)
||memcmp(line,command->cmdname,cmdlen)!=0){
diff --git a/db.c b/db.c
index 6912cb4..5020b08 100644
--- a/db.c
+++ b/db.c
@@ -186,6 +186,17 @@ bool db_add_member(i64 roomid,i64 userid){
return success;
}
+bool db_remove_member(i64 roomid,i64 userid){
+ assert(roomid!=-1&&userid!=-1);
+ static sqlite3_stmt *stmt = NULL;
+ if (!stmt) SQLITE(prepare_v2,database,"delete from Members where room = ? and user = ?",-1,&stmt,NULL);
+ SQLITE(bind_int64,stmt,1,roomid);
+ SQLITE(bind_int64,stmt,2,userid);
+ bool success=sqlite3_step(stmt)==SQLITE_DONE;
+ reset_stmt(stmt);
+ return success;
+}
+
bool db_is_member(i64 roomid,i64 userid){
static sqlite3_stmt *stmt = NULL;
if (!stmt) SQLITE(prepare_v2,database,"select 1 from Members where room = ? and user = ?",-1,&stmt,NULL);
diff --git a/event.c b/event.c
index e85eb45..20591a3 100644
--- a/event.c
+++ b/event.c
@@ -98,3 +98,13 @@ void event_emit_join(i64 timestamp,const char *user,const char *room){
event_emit(event);
event_item_free(event);
}
+
+void event_emit_leave(i64 timestamp,const char *user,const char *room){
+ struct event_item *event=event_item_alloc();
+ event->type=EVENT_LEAVE;
+ event->timestamp=timestamp;
+ event->user=strdup(user);
+ event->room=strdup(room);
+ event_emit(event);
+ event_item_free(event);
+}
diff --git a/event.h b/event.h
index 0254702..3ac7cb9 100644
--- a/event.h
+++ b/event.h
@@ -7,6 +7,7 @@ enum event_type{
EVENT_MESSAGE, // message, user, room
EVENT_ONLINE, // user, num
EVENT_JOIN, // user, room
+ EVENT_LEAVE, // user, room
};
struct event_item{
@@ -30,3 +31,4 @@ void event_emit(const struct event_item *event);
void event_emit_message(i64 timestamp,const char *message,const char *user,const char *room,i64 replyid);
void event_emit_online(i64 timestamp,const char *user,i64 numonline);
void event_emit_join(i64 timestamp,const char *user,const char *room);
+void event_emit_leave(i64 timestamp,const char *user,const char *room);
diff --git a/plugin.c b/plugin.c
index 14dce01..58c79f3 100644
--- a/plugin.c
+++ b/plugin.c
@@ -23,6 +23,7 @@ static void consume_event(const struct event_item *event){
case EVENT_MESSAGE: pe.type=PLUGIN_EVENT_MESSAGE; break;
case EVENT_ONLINE: pe.type=PLUGIN_EVENT_ONLINE; break;
case EVENT_JOIN: pe.type=PLUGIN_EVENT_JOIN; break;
+ case EVENT_LEAVE: pe.type=PLUGIN_EVENT_LEAVE; break;
default:
die("Unknown event type %d in plugin.c:consume_event",event->type);
}
diff --git a/plugin_client_header.h b/plugin_client_header.h
index d1be0b5..e69b1e5 100644
--- a/plugin_client_header.h
+++ b/plugin_client_header.h
@@ -7,6 +7,7 @@ enum plugin_event_type{
PLUGIN_EVENT_MESSAGE, // message, user, room
PLUGIN_EVENT_ONLINE, // user, num
PLUGIN_EVENT_JOIN, // user, room
+ PLUGIN_EVENT_LEAVE, // user, room
};
struct plugin_event{