aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--broadcast.c89
-rw-r--r--broadcast.h6
-rw-r--r--command.c3
-rw-r--r--main.c11
4 files changed, 107 insertions, 2 deletions
diff --git a/broadcast.c b/broadcast.c
new file mode 100644
index 0000000..0517dc3
--- /dev/null
+++ b/broadcast.c
@@ -0,0 +1,89 @@
+#include <string.h>
+#include "broadcast.h"
+#include "db.h"
+#include "net.h"
+#include "user_data.h"
+
+
+// Returns whether value was new and therefore inserted into the list.
+// If already present, does nothing and returns false.
+// Assumes enough space for the possible new item.
+static bool sorted_insert(i64 *buf,i64 len,i64 value){
+ if(len==0){
+ buf[0]=value;
+ return true;
+ }
+ i64 L=0,H=len-1;
+ if(buf[L]==value||buf[H]==value)return false;
+ if(value<buf[L]){
+ memmove(buf+1,buf,len*sizeof(i64));
+ buf[0]=value;
+ return true;
+ }
+ if(value>buf[H]){
+ buf[len]=value;
+ return true;
+ }
+ while(L<H){
+ i64 M=L+(H-L)/2;
+ if(buf[M]==value)return false;
+ if(buf[M]<value){
+ L=M+1;
+ if(buf[L]>value)break;
+ } else if(buf[M]>value){
+ H=M-1;
+ if(buf[H]<value){
+ L=M;
+ break;
+ }
+ }
+ }
+ if(buf[L]==value)return false;
+ // We insert left of L
+ memmove(buf+L+1,buf+L,len-L);
+ buf[L]=value;
+ return true;
+}
+
+void broadcast_online_change(i64 userid){
+ i64 seencap=16,seenlen=0;
+ i64 *seen=malloc(seencap,i64);
+
+ struct db_room_list rooms=db_list_rooms(userid);
+ for(i64 i=0;i<rooms.count;i++){
+ struct db_user_list members=db_list_members(rooms.list[i].id);
+ for(i64 j=0;j<members.count;j++){
+ if(members.list[j].id==userid)continue;
+ i64 nfds;
+ (void)userdata_online(members.list[j].id,&nfds);
+ if(nfds==0)continue;
+ if(seenlen==seencap){
+ seencap*=2;
+ seen=realloc(seen,seencap,i64);
+ }
+ bool inserted=sorted_insert(seen,seenlen,members.list[j].id);
+ seenlen+=inserted;
+ }
+ db_nullify_user_list(members);
+ }
+ db_nullify_room_list(rooms);
+
+ char *name=db_get_username(userid);
+ i64 numonline;
+ (void)userdata_online(userid,&numonline);
+
+ char *buf;
+ i64 len=asprintf(&buf,"_push online %" PRIi64 " %s\n",numonline,name);
+ free(name);
+
+ for(i64 i=0;i<seenlen;i++){
+ i64 nfds;
+ const int *fds=userdata_online(seen[i],&nfds);
+ for(i64 j=0;j<nfds;j++){
+ net_send_raw_text(fds[j],buf,len);
+ }
+ }
+
+ free(buf);
+ free(seen);
+}
diff --git a/broadcast.h b/broadcast.h
new file mode 100644
index 0000000..6ad5aad
--- /dev/null
+++ b/broadcast.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "global.h"
+
+
+void broadcast_online_change(i64 userid);
diff --git a/command.c b/command.c
index e66eb12..1bbde10 100644
--- a/command.c
+++ b/command.c
@@ -4,6 +4,7 @@
#include <errno.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include "broadcast.h"
#include "command.h"
#include "db.h"
#include "net.h"
@@ -38,11 +39,13 @@ static bool cmd_login(struct conn_data *data,const char *tag,const char **args){
free(pass);
if(data->userid!=-1){
userdata_unregister(data->userid,data->fd);
+ broadcast_online_change(data->userid);
}
if(success){
data->userid=userid;
userdata_register(userid,data->fd);
net_send_ok(data->fd,tag);
+ broadcast_online_change(userid);
} else {
data->userid=-1;
net_send_error(data->fd,tag,"Incorrect password");
diff --git a/main.c b/main.c
index 622e1b9..5afece8 100644
--- a/main.c
+++ b/main.c
@@ -7,6 +7,7 @@
#include <netinet/in.h>
#include <errno.h>
#include <assert.h>
+#include "broadcast.h"
#include "command.h"
#include "conn_data.h"
#include "db.h"
@@ -53,7 +54,10 @@ static void delete_conn_data(int fd){
struct hash_item *item=conn_hash[fd%CONN_HASH_SIZE];
assert(item);
if(item->cd.fd==fd){
- if(item->cd.userid!=-1)userdata_unregister(item->cd.userid,fd);
+ if(item->cd.userid!=-1){
+ userdata_unregister(item->cd.userid,fd);
+ broadcast_online_change(item->cd.userid);
+ }
conn_hash[fd%CONN_HASH_SIZE]=item->next;
conn_data_nullify(&item->cd);
free(item);
@@ -66,7 +70,10 @@ static void delete_conn_data(int fd){
}
assert(parent);
assert(item);
- if(item->cd.userid!=-1)userdata_unregister(item->cd.userid,fd);
+ if(item->cd.userid!=-1){
+ userdata_unregister(item->cd.userid,fd);
+ broadcast_online_change(item->cd.userid);
+ }
conn_data_nullify(&item->cd);
parent->next=item->next;
free(item);