From a8a0603375fa4c5c1e61bd8a0dc0ed7f3f77854e Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Tue, 29 Sep 2020 22:18:33 +0200 Subject: server: Implement room_leave --- TODO.txt | 3 +- command.c | 101 +++++++++++++++++++++++++++++++++++++------------ db.c | 11 ++++++ event.c | 10 +++++ event.h | 2 + plugin.c | 1 + plugin_client_header.h | 1 + 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{ -- cgit v1.2.3-70-g09d2