From 2b86c851562f86f69acbf4b773107077680d5068 Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Fri, 7 Apr 2017 23:59:03 +0200 Subject: Push weechat plugin --- weechat/tomsg.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 weechat/tomsg.c (limited to 'weechat/tomsg.c') 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 +#include +#include +#include +#include +#include +#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;roominrooms;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;inrooms;i++){ + close_room(conn->rooms[i]); + } + if(conn->roomscaproomscap=res.nitems+1; + conn->rooms=realloc(conn->rooms,conn->roomscap*sizeof(struct roomdata*)); + assert(conn->rooms); + } + conn->nrooms=res.nitems; + for(i64 i=0;irooms[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;inrooms;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 [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; +} -- cgit v1.2.3-54-g00ecf