aboutsummaryrefslogtreecommitdiff
path: root/weechat/tomsg.c
diff options
context:
space:
mode:
Diffstat (limited to 'weechat/tomsg.c')
-rw-r--r--weechat/tomsg.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/weechat/tomsg.c b/weechat/tomsg.c
new file mode 100644
index 0000000..25e62e5
--- /dev/null
+++ b/weechat/tomsg.c
@@ -0,0 +1,383 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <assert.h>
+#include "weechat-plugin.h"
+#include "net.h"
+
+WEECHAT_PLUGIN_NAME("tomsg")
+WEECHAT_PLUGIN_DESCRIPTION("tomsg client plugin")
+WEECHAT_PLUGIN_AUTHOR("Tom Smeding")
+WEECHAT_PLUGIN_VERSION("0.1")
+WEECHAT_PLUGIN_LICENSE("MIT")
+WEECHAT_PLUGIN_PRIORITY(1000)
+
+
+static const char *errpfx;
+
+
+struct roomdata{
+ char *name;
+ struct t_gui_buffer *buffer;
+ struct conndata *conn; // do not free
+};
+
+struct conndata{
+ int fd;
+ struct t_hook *fd_hook;
+
+ struct t_gui_buffer *buffer;
+
+ int nrooms,roomscap;
+ struct roomdata **rooms;
+
+ char *username,*pending_username;
+
+ i64 linebuf_sz,linebuf_len;
+ char *linebuf;
+};
+
+
+FILE *debugf;
+
+static struct t_weechat_plugin *weechat_plugin;
+
+static struct t_hashtable *conntable;
+
+
+static void close_room(struct roomdata *room){
+ fprintf(debugf,"close_room(room=%p)\n",room);
+ free(room->name);
+ if(room->buffer)weechat_buffer_close(room->buffer);
+ free(room);
+}
+
+static void message_net_callback(int fd,struct net_response res,void *payload){
+ (void)payload;
+ fprintf(debugf,"message_net_callback(fd=%d,res={.type=%d})\n",fd,res.type);
+ struct conndata *conn=weechat_hashtable_get(conntable,&fd);
+ assert(conn);
+ if(res.type==NET_ERROR){
+ weechat_printf(conn->buffer,"tomsg: send threw error: %s",res.error);
+ } else if(res.type!=NET_OK){
+ fprintf(debugf,"message_net_callback: res.type=%d\n",res.type);
+ }
+}
+
+static int room_input_cb(const void *room_vp,void *_d,struct t_gui_buffer *buffer,const char *input){
+ (void)_d; (void)buffer;
+ struct roomdata *room=(struct roomdata*)room_vp;
+ struct conndata *conn=room->conn;
+ const char *tosend;
+ const char *p=strchr(input,'\n');
+ if(p!=NULL){
+ fprintf(debugf,"room_input_cb: input contained newline <%s>\n",input);
+ tosend=strdup(input);
+ *strchr(tosend,'\n')='\0';
+ } else {
+ tosend=input;
+ }
+
+ net_sendf(conn->fd,message_net_callback,NULL,"send %s %s",room->name,tosend);
+ weechat_printf(room->buffer,"%s\t%s",conn->username,tosend);
+
+ if(tosend!=input){
+ free((void*)tosend);
+ }
+ return WEECHAT_RC_OK;
+}
+
+static int room_close_cb(const void *room_vp,void *_d,struct t_gui_buffer *buffer){
+ (void)_d; (void)buffer;
+ struct roomdata *room=(struct roomdata*)room_vp;
+ room->buffer=NULL;
+ return WEECHAT_RC_OK;
+}
+
+static void push_net_callback(int fd,struct net_response res,void *payload){
+ (void)payload;
+ fprintf(debugf,"push_net_callback(fd=%d,res={.type=%d})\n",fd,res.type);
+ struct conndata *conn=weechat_hashtable_get(conntable,&fd);
+ assert(conn);
+ if(res.type==NET_MESSAGE){
+ i64 roomi;
+ for(roomi=0;roomi<conn->nrooms;roomi++){
+ if(strcmp(conn->rooms[roomi]->name,res.room)==0){
+ break;
+ }
+ }
+ if(roomi==conn->nrooms){
+ fprintf(debugf,"push_net_callback: message to unknown room '%s'\n",res.room);
+ return;
+ }
+ struct roomdata *room=conn->rooms[roomi];
+ if(room->buffer==NULL){
+ room->buffer=weechat_buffer_new(
+ room->name,
+ room_input_cb,room,NULL,
+ room_close_cb,room,NULL);
+ }
+ weechat_printf_date_tags(
+ room->buffer,res.timestamp/1000000LL,NULL,
+ "%s\t%s",res.username,res.message);
+ } else {
+ fprintf(debugf,"push_net_callback: unknown response type %d\n",res.type);
+ }
+}
+
+static void history_push_net_callback(int fd,struct net_response res,void *payload){
+ push_net_callback(fd,res,payload);
+}
+
+static void history_net_callback(int fd,struct net_response res,void *payload){
+ struct roomdata *room=(struct roomdata*)payload;
+ assert(room);
+ fprintf(debugf,"history_net_callback(fd=%d,res={.type=%d})\n",fd,res.type);
+}
+
+static void roomlist_net_callback(int fd,struct net_response res,void *payload){
+ (void)payload;
+ fprintf(debugf,"roomlist_net_callback(fd=%d,res={.type=%d})\n",fd,res.type);
+ struct conndata *conn=weechat_hashtable_get(conntable,&fd);
+ assert(conn);
+ if(res.type==NET_LIST){
+ for(i64 i=0;i<conn->nrooms;i++){
+ close_room(conn->rooms[i]);
+ }
+ if(conn->roomscap<res.nitems){
+ conn->roomscap=res.nitems+1;
+ conn->rooms=realloc(conn->rooms,conn->roomscap*sizeof(struct roomdata*));
+ assert(conn->rooms);
+ }
+ conn->nrooms=res.nitems;
+ for(i64 i=0;i<res.nitems;i++){
+ conn->rooms[i]=malloc(sizeof(struct roomdata));
+ conn->rooms[i]->name=strdup(res.items[i]);
+ conn->rooms[i]->buffer=weechat_buffer_new(
+ conn->rooms[i]->name,
+ room_input_cb,conn->rooms[i],NULL,
+ room_close_cb,conn->rooms[i],NULL);
+ conn->rooms[i]->conn=conn;
+ net_sendf(fd,history_net_callback,conn->rooms[i],"history %s 10",conn->rooms[i]->name);
+ }
+ } else {
+ fprintf(debugf,"roomlist_net_callback: res.type=%d\n",res.type);
+ }
+}
+
+static void login_net_callback(int fd,struct net_response res,void *payload){
+ (void)payload;
+ fprintf(debugf,"login_net_callback(fd=%d,res={.type=%d})\n",fd,res.type);
+ struct conndata *conn=weechat_hashtable_get(conntable,&fd);
+ assert(conn);
+ if(res.type==NET_OK){
+ if(conn->username)free(conn->username);
+ conn->username=conn->pending_username;
+ conn->pending_username=NULL;
+
+ weechat_printf(conn->buffer,"Successfully logged in");
+ net_sendf(conn->fd,roomlist_net_callback,NULL,"list_rooms");
+ } else if(res.type==NET_ERROR){
+ weechat_printf(conn->buffer,"Error logging in: %s",res.error);
+ } else {
+ fprintf(debugf,"login_net_callback: res.type=%d\n",res.type);
+ }
+}
+
+static void conn_destroy(struct conndata *conn){
+ fprintf(debugf,"conn_destroy(conn=%p (fd=%d))\n",conn,conn->fd);
+ weechat_unhook(conn->fd_hook);
+ if(conntable)weechat_hashtable_remove(conntable,&conn->fd);
+ for(int i=0;i<conn->nrooms;i++){
+ close_room(conn->rooms[i]);
+ }
+ if(conn->username)free(conn->username);
+ if(conn->pending_username)free(conn->pending_username);
+ free(conn->rooms);
+ free(conn->linebuf);
+ close(conn->fd);
+ free(conn);
+}
+
+static int fd_hook_callback(const void *conn_vp,void *_d,int fd){
+ (void)_d;
+ struct conndata *conn=(struct conndata*)conn_vp;
+ fprintf(debugf,"fd_hook_callback(conn=%p (fd=%d))\n",conn,fd);
+ assert(fd==conn->fd);
+
+ if(conn->linebuf_len>conn->linebuf_sz/2){
+ conn->linebuf_sz*=2;
+ conn->linebuf=realloc(conn->linebuf,conn->linebuf_sz);
+ assert(conn->linebuf);
+ }
+ i64 nr=recv(fd,conn->linebuf+conn->linebuf_len,conn->linebuf_sz-conn->linebuf_len,0);
+ if(nr<=0){
+ fprintf(debugf,"fd_hook_callback: recv() <= 0\n");
+ weechat_printf(NULL,"tomsg: Connection dropped");
+ weechat_buffer_close(conn->buffer);
+ return WEECHAT_RC_OK;
+ }
+ conn->linebuf_len+=nr;
+ while(true){
+ char *p=memchr(conn->linebuf,'\n',conn->linebuf_len);
+ if(p==NULL)break;
+ *p='\0';
+ i64 lenp1=p-conn->linebuf+1;
+ net_handle_recv(conn->fd,conn->linebuf);
+ memmove(conn->linebuf,conn->linebuf+lenp1,conn->linebuf_len-lenp1);
+ conn->linebuf_len-=lenp1;
+ }
+ return WEECHAT_RC_OK;
+}
+
+static int conn_input_cb(const void *conn_vp,void *_d,struct t_gui_buffer *buffer,const char *input){
+ (void)_d;
+ struct conndata *conn=(struct conndata*)conn_vp;
+ fprintf(debugf,"conn_input_cb(conn=%p,buffer=%p,input=\"%s\")\n",conn,buffer,input);
+
+ char *input2=strdup(input);
+ assert(input2);
+ char *cursor=input2;
+
+ char *cmd=strsep(&cursor," ");
+ if(cmd==NULL){
+ free(input2);
+ return WEECHAT_RC_OK;
+ }
+ if(strcmp(cmd,"login")==0){
+ char *username=strsep(&cursor," ");
+ char *password=cursor;
+ if(conn->pending_username)free(conn->pending_username);
+ conn->pending_username=strdup(username);
+ net_sendf(conn->fd,login_net_callback,NULL,"login %s %s",username,password);
+ } else {
+ weechat_printf(conn->buffer,"Unknown command '%s'",cmd);
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+static int conn_close_cb(const void *conn_vp,void *_d,struct t_gui_buffer *buffer){
+ (void)_d; (void)buffer;
+ struct conndata *conn=(struct conndata*)conn_vp;
+ fprintf(debugf,"conn_close_cb(conn=%p,buffer=%p) fd=%d\n",conn,buffer,conn->fd);
+ conn_destroy(conn);
+ return WEECHAT_RC_OK;
+}
+
+static int connect_cb(const void *_p,void *hostname,int status,int _g,int fd,const char *err,const char *_i){
+ (void)_p; (void)_g; (void)_i;
+ switch(status){
+ case WEECHAT_HOOK_CONNECT_OK: {
+ struct conndata *conn=malloc(sizeof(struct conndata));
+ assert(conn);
+ conn->fd=fd;
+ conn->fd_hook=weechat_hook_fd(fd,1,0,0,fd_hook_callback,conn,NULL);
+ conn->buffer=weechat_buffer_new((char*)hostname,conn_input_cb,conn,NULL,conn_close_cb,conn,NULL);
+ conn->nrooms=0;
+ conn->roomscap=2;
+ conn->rooms=malloc(conn->roomscap*sizeof(struct roomdata));
+ assert(conn->rooms);
+ conn->username=NULL;
+ conn->pending_username=NULL;
+ conn->linebuf_sz=512;
+ conn->linebuf_len=0;
+ conn->linebuf=malloc(conn->linebuf_sz);
+ assert(conn->linebuf);
+
+ weechat_printf(conn->buffer,"Connected!");
+
+ weechat_hashtable_set(conntable,&fd,conn);
+
+ return WEECHAT_RC_OK;
+ }
+
+ default:
+ weechat_printf(NULL,"%stomsg: Could not connect to %s: %s",errpfx,(char*)hostname,err);
+ return WEECHAT_RC_ERROR;
+ }
+}
+
+static int cmd_tomsg_cb(const void *_p,void *_d,struct t_gui_buffer *buffer,int argc,char **argv,char **_a){
+ (void)_p; (void)_d; (void)_a;
+ if(argc<2){
+ weechat_printf(buffer,"%stomsg: Invalid number of arguments to /tomsg",errpfx);
+ return WEECHAT_RC_ERROR;
+ }
+ if(strcmp(argv[1],"connect")==0){
+ if(argc<3||argc>4){
+ weechat_printf(buffer,"%stomsg: Invalid number of arguments to /tomsg connect",errpfx);
+ return WEECHAT_RC_ERROR;
+ }
+
+ char *hostname=argv[2];
+ char *endp;
+ int port;
+ if(argc==4){
+ port=strtol(argv[3],&endp,10);
+ if(argv[3][0]=='\0'||*endp!='\0'||port<=0||port>=65536){
+ weechat_printf(buffer,"%sInvalid port number",errpfx);
+ return WEECHAT_RC_ERROR;
+ }
+ } else {
+ port=29536;
+ }
+ if(strlen(hostname)==0){
+ weechat_printf(buffer,"%stomsg: Invalid hostname",errpfx);
+ return WEECHAT_RC_ERROR;
+ }
+
+ fprintf(debugf,"Connecting to %s:%d\n",hostname,port);
+ char *hostname_copy=strdup(hostname);
+ weechat_hook_connect(
+ NULL,
+ hostname,port,1,0,
+ NULL,NULL,0,NULL,
+ NULL,
+ connect_cb,NULL,hostname_copy);
+ } else {
+ weechat_printf(buffer,"%stomsg: Unknown command \"%s\" to /tomsg",errpfx,argv[1]);
+ return WEECHAT_RC_ERROR;
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+int weechat_plugin_init(struct t_weechat_plugin *plugin,int argc,char **argv){
+ (void)argc; (void)argv;
+ weechat_plugin=plugin;
+
+ debugf=fopen("/Users/Tom/Desktop/debugf.txt","w");
+ setvbuf(debugf,NULL,_IONBF,0);
+
+ fprintf(debugf,"------\n");
+
+ errpfx=weechat_prefix("error");
+
+ weechat_hook_command(
+ "tomsg",
+ "Execute commands related to tomsg.",
+ "connect <hostname> [port]",
+ " connect: Connect to a tomsg server",
+ NULL,
+ cmd_tomsg_cb,NULL,NULL);
+
+ net_set_push_callback(push_net_callback);
+ net_set_history_callback(history_push_net_callback);
+
+ conntable=weechat_hashtable_new(
+ 16,WEECHAT_HASHTABLE_INTEGER,WEECHAT_HASHTABLE_POINTER,NULL,NULL);
+
+ return WEECHAT_RC_OK;
+}
+
+int weechat_plugin_end(struct t_weechat_plugin *plugin){
+ (void)plugin;
+ weechat_hashtable_free(conntable);
+ conntable=NULL;
+ fclose(debugf);
+ return WEECHAT_RC_OK;
+}