aboutsummaryrefslogtreecommitdiff
path: root/weechat/net.c
diff options
context:
space:
mode:
Diffstat (limited to 'weechat/net.c')
-rw-r--r--weechat/net.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/weechat/net.c b/weechat/net.c
new file mode 100644
index 0000000..12e3625
--- /dev/null
+++ b/weechat/net.c
@@ -0,0 +1,291 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <assert.h>
+#include "net.h"
+
+
+extern FILE *debugf;
+
+
+struct store_item{
+ char *id;
+ net_callback_t *cb;
+ void *payload;
+};
+
+static i64 store_cap=16,store_len=0;
+static struct store_item *store=NULL;
+
+static net_callback_t *push_callback=NULL;
+static net_callback_t *history_callback=NULL;
+
+
+#define ALPHABET "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+static void uniqid8(char *dst){
+ static i64 static_id=0;
+ static const int alphabet_len=strlen(ALPHABET);
+
+ int id=static_id;
+ static_id++;
+ for(int i=7;i>=0;i--){
+ dst[i]=ALPHABET[id%alphabet_len];
+ id/=alphabet_len;
+ }
+}
+
+void net_set_push_callback(net_callback_t *callback){
+ push_callback=callback;
+}
+
+void net_set_history_callback(net_callback_t *callback){
+ history_callback=callback;
+}
+
+__attribute__((format (printf, 4, 5)))
+bool net_sendf(int fd,net_callback_t *callback,void *payload,const char *format,...){
+ if(store==NULL){
+ store=malloc(store_cap*sizeof(struct store_item));
+ } else if(store_len==store_cap){
+ store_cap*=2;
+ store=realloc(store,store_cap*sizeof(struct store_item));
+ }
+ assert(store);
+
+ fprintf(debugf,"net_sendf(%d,%p,\"%s\",...)\n",fd,callback,format);
+
+ va_list ap,ap2;
+ va_start(ap,format);
+ va_copy(ap2,ap);
+ i64 len=vsnprintf(NULL,0,format,ap2);
+ va_end(ap2);
+ assert(len>=0);
+
+ char *buf=malloc(9+len+2);
+ assert(buf);
+ uniqid8(buf);
+ buf[8]=' ';
+ i64 len2=vsprintf(buf+9,format,ap);
+ va_end(ap);
+ assert(len2==len);
+ buf[9+len]='\n';
+ buf[9+len+1]='\0';
+
+ store[store_len].id=malloc(9);
+ assert(store[store_len].id);
+ memcpy(store[store_len].id,buf,8);
+ store[store_len].id[8]='\0';
+ store[store_len].cb=callback;
+ store[store_len].payload=payload;
+ store_len++;
+
+ i64 cursor=0,total=9+len+1;
+ while(cursor<total){
+ i64 nwr=send(fd,buf+cursor,total-cursor,0);
+ if(nwr<=0)return false;
+ cursor+=nwr;
+ }
+ free(buf);
+ return true;
+}
+
+void net_handle_recv(int fd,const char *msg){
+ i64 msglen=strlen(msg);
+ const char *p=strchr(msg,' ');
+ if(p==NULL){
+ fprintf(debugf,"net_handle_recv: no space in message <%s>\n",msg);
+ return;
+ }
+ i64 taglen=p-msg;
+
+ net_callback_t *cb=NULL;
+ void *payload=NULL;
+ if(taglen==5&&memcmp(msg,"_push",5)==0){
+ cb=push_callback;
+ } else if(*p!='\0'&&memcmp(p+1,"history_message",15)==0){
+ cb=history_callback;
+ } else {
+ if(taglen!=8){
+ fprintf(debugf,"net_handle_recv: tag not length 8 <%s>\n",msg);
+ return;
+ }
+ for(i64 i=0;i<store_len;i++){
+ if(memcmp(store[i].id,msg,taglen)==0){
+ cb=store[i].cb;
+ payload=store[i].payload;
+ memmove(store+i,store+(i+1),(store_len-i-1)*sizeof(struct store_item));
+ store_len--;
+ break;
+ }
+ }
+ if(cb==NULL){
+ fprintf(debugf,"net_handle_recv: no viable callback found <%s>\n",msg);
+ return;
+ }
+ }
+
+ const char *cmd=p+1;
+ p=strchr(cmd,' ');
+ if(p==NULL)p=msg+msglen;
+ i64 cmdlen=p-cmd;
+
+ // Now `p` points to the space (or '\0') after the command name
+
+ if(cmdlen==2&&memcmp(cmd,"ok",2)==0){
+ cb(fd,(struct net_response){.type=NET_OK},payload);
+ } else if(cmdlen==5&&memcmp(cmd,"error",5)==0){
+ struct net_response res=(struct net_response){
+ .type=NET_ERROR,
+ .error=strdup(*p=='\0'?p:p+1)
+ };
+ cb(fd,res,payload);
+ free(res.error);
+ } else if(cmdlen==4&&memcmp(cmd,"name",4)==0){
+ struct net_response res=(struct net_response){
+ .type=NET_NAME,
+ .name=strdup(*p=='\0'?p:p+1)
+ };
+ cb(fd,res,payload);
+ free(res.name);
+ } else if(cmdlen==4&&memcmp(cmd,"list",4)==0){
+ struct net_response res=(struct net_response){.type=NET_LIST};
+ if(*p=='\0'){
+ fprintf(debugf,"net_handle_recv: no list count <%s>\n",msg);
+ return;
+ }
+ const char *cursor=p+1;
+ p=strchr(cursor,' ');
+ res.nitems=strtol(cursor,NULL,10);
+ if(res.nitems<=0){
+ fprintf(debugf,"net_handle_recv: -- 0 items <%s>\n",msg);
+ res.nitems=0;
+ res.items=NULL;
+ cb(fd,res,payload);
+ return;
+ }
+ res.items=malloc(res.nitems*sizeof(char*));
+ assert(res.items);
+ cursor=p;
+ for(i64 i=0;i<res.nitems;i++){
+ if(cursor==NULL||*cursor=='\0'){
+ free(res.items);
+ fprintf(debugf,"net_handle_recv: short list <%s>\n",msg);
+ return;
+ }
+ cursor++;
+ p=strchr(cursor,' ');
+ if(p==NULL)p=msg+msglen;
+ res.items[i]=malloc(p-cursor+1);
+ assert(res.items[i]);
+ memcpy(res.items[i],cursor,p-cursor);
+ res.items[i][p-cursor]='\0';
+ fprintf(debugf,"net_handle_recv: -- item \"%s\" <%s>\n",res.items[i],msg);
+ cursor=p;
+ }
+ cb(fd,res,payload);
+ for(i64 i=0;i<res.nitems;i++){
+ free(res.items[i]);
+ }
+ free(res.items);
+ } else if(cmdlen==7&&memcmp(cmd,"message",7)==0){
+ if(*p=='\0'){
+ fprintf(debugf,"net_handle_recv: no arguments to 'message' <%s>\n",msg);
+ return;
+ }
+ const char *roomp=p+1;
+ p=strchr(roomp,' ');
+ const char *q,*r;
+ if(p==NULL||(q=strchr(p+1,' '))==NULL||(r=strchr(q+1,' '))==NULL){
+ fprintf(debugf,"net_handle_recv: not enough arguments to 'message' <%s>\n",msg);
+ return;
+ }
+ i64 roomlen=p-roomp;
+ const char *usernamep=p+1;
+ i64 usernamelen=q-usernamep;
+ const char *stampp=q+1;
+ i64 stamplen=r-stampp;
+ const char *textp=r+1;
+ i64 textlen=msglen-(textp-msg);
+
+ struct net_response res;
+ res.type=NET_MESSAGE;
+ const char *endp;
+ res.timestamp=strtoll(stampp,(char**)&endp,10);
+ if(endp-stampp!=stamplen){
+ fprintf(debugf,"net_handle_recv: timestamp not a number in 'message' <%s>\n",msg);
+ return;
+ }
+ res.room=malloc(roomlen+1);
+ res.username=malloc(usernamelen+1);
+ res.message=malloc(textlen+1);
+ assert(res.room&&res.username&&res.message);
+ memcpy(res.room,roomp,roomlen);
+ res.room[roomlen]='\0';
+ memcpy(res.username,usernamep,usernamelen);
+ res.username[usernamelen]='\0';
+ memcpy(res.message,textp,textlen);
+ res.message[textlen]='\0';
+ cb(fd,res,payload);
+ free(res.room);
+ free(res.username);
+ free(res.message);
+ } else if(cmdlen==7&&memcmp(cmd,"history",7)==0){
+ struct net_response res;
+ res.type=NET_OK;
+ cb(fd,res,payload);
+ } else if(cmdlen==15&&memcmp(cmd,"history_message",15)==0){
+ if(*p=='\0'){
+ fprintf(debugf,"net_handle_recv: no arguments to 'history_message' <%s>\n",msg);
+ return;
+ }
+ p=strchr(p+1,' ');
+ if(*p=='\0'){
+ fprintf(debugf,"net_handle_recv: no arguments past index to 'history_message' <%s>\n",msg);
+ return;
+ }
+ const char *roomp=p+1;
+ p=strchr(roomp,' ');
+ const char *q,*r;
+ if(p==NULL||(q=strchr(p+1,' '))==NULL||(r=strchr(q+1,' '))==NULL){
+ fprintf(debugf,"net_handle_recv: not enough arguments to 'history_message' <%s>\n",msg);
+ return;
+ }
+ i64 roomlen=p-roomp;
+ const char *usernamep=p+1;
+ i64 usernamelen=q-usernamep;
+ const char *stampp=q+1;
+ i64 stamplen=r-stampp;
+ const char *textp=r+1;
+ i64 textlen=msglen-(textp-msg);
+
+ struct net_response res;
+ res.type=NET_MESSAGE;
+ const char *endp;
+ res.timestamp=strtoll(stampp,(char**)&endp,10);
+ if(endp-stampp!=stamplen){
+ fprintf(debugf,"net_handle_recv: timestamp not a number in 'history_message' <%s>\n",msg);
+ return;
+ }
+ res.room=malloc(roomlen+1);
+ res.username=malloc(usernamelen+1);
+ res.message=malloc(textlen+1);
+ assert(res.room&&res.username&&res.message);
+ memcpy(res.room,roomp,roomlen);
+ res.room[roomlen]='\0';
+ memcpy(res.username,usernamep,usernamelen);
+ res.username[usernamelen]='\0';
+ memcpy(res.message,textp,textlen);
+ res.message[textlen]='\0';
+ cb(fd,res,payload);
+ free(res.room);
+ free(res.username);
+ free(res.message);
+ } else {
+ fprintf(debugf,"net_handle_recv: unknown command <%s>\n",msg);
+ }
+}