From b12ea9fb05e636213b35d46ddaec3e255e1a6992 Mon Sep 17 00:00:00 2001
From: Tom Smeding <tom.smeding@gmail.com>
Date: Wed, 29 Jul 2020 22:32:24 +0200
Subject: weechat: Protocol version 2, display replies

---
 weechat/net.c   |  40 +++++++++++++----
 weechat/net.h   |   6 ++-
 weechat/tomsg.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 157 insertions(+), 22 deletions(-)

diff --git a/weechat/net.c b/weechat/net.c
index 897655a..ce8da6a 100644
--- a/weechat/net.c
+++ b/weechat/net.c
@@ -220,9 +220,9 @@ void net_handle_recv(int fd,const char *msg){
 		}
 		const char *roomp=p+1;
 		p=strchr(roomp,' ');
-		const char *q,*r,*s;
+		const char *q,*r,*s,*t;
 		if(p==NULL||(q=strchr(p+1,' '))==NULL||(r=strchr(q+1,' '))==NULL
-				||(s=strchr(r+1,' '))==NULL){
+				||(s=strchr(r+1,' '))==NULL||(t=strchr(s+1,' '))==NULL){
 			debugf("net_handle_recv: not enough arguments to 'message' <%s>\n",msg);
 			return;
 		}
@@ -233,10 +233,12 @@ void net_handle_recv(int fd,const char *msg){
 		i64 stamplen=r-stampp;
 		const char *msgidp=r+1;
 		i64 msgidlen=s-msgidp;
-		const char *textp=s+1;
+		const char *replyidp=s+1;
+		i64 replyidlen=t-replyidp;
+		const char *textp=t+1;
 		i64 textlen=msglen-(textp-msg);
 
-		(void)msgidp; (void)msgidlen;
+		(void)msgidp; (void)msgidlen; (void)replyidlen;
 
 		struct net_response res;
 		res.type=NET_MESSAGE;
@@ -246,6 +248,16 @@ void net_handle_recv(int fd,const char *msg){
 			debugf("net_handle_recv: timestamp not a number in 'message' <%s>\n",msg);
 			return;
 		}
+		res.msgid=strtoll(msgidp,(char**)&endp,10);
+		if(endp-msgidp!=msgidlen){
+			debugf("net_handle_recv: msgid not a number in 'message' <%s>\n",msg);
+			return;
+		}
+		res.replyid=strtoll(replyidp,(char**)&endp,10);
+		if(endp-replyidp!=replyidlen){
+			debugf("net_handle_recv: replyid not a number in 'message' <%s>\n",msg);
+			return;
+		}
 		res.room=malloc(roomlen+1);
 		res.username=malloc(usernamelen+1);
 		res.message=malloc(textlen+1);
@@ -276,9 +288,9 @@ void net_handle_recv(int fd,const char *msg){
 		}
 		const char *roomp=p+1;
 		p=strchr(roomp,' ');
-		const char *q,*r,*s;
+		const char *q,*r,*s,*t;
 		if(p==NULL||(q=strchr(p+1,' '))==NULL||(r=strchr(q+1,' '))==NULL
-				||(s=strchr(r+1,' '))==NULL){
+				||(s=strchr(r+1,' '))==NULL||(t=strchr(s+1,' '))==NULL){
 			debugf("net_handle_recv: not enough arguments to 'history_message' <%s>\n",msg);
 			return;
 		}
@@ -289,11 +301,11 @@ void net_handle_recv(int fd,const char *msg){
 		i64 stamplen=r-stampp;
 		const char *msgidp=r+1;
 		i64 msgidlen=s-msgidp;
-		const char *textp=s+1;
+		const char *replyidp=s+1;
+		i64 replyidlen=t-replyidp;
+		const char *textp=t+1;
 		i64 textlen=msglen-(textp-msg);
 
-		(void)msgidp; (void)msgidlen;
-
 		struct net_response res;
 		res.type=NET_HISTORY;
 		const char *endp;
@@ -302,6 +314,16 @@ void net_handle_recv(int fd,const char *msg){
 			debugf("net_handle_recv: timestamp not a number in 'history_message' <%s>\n",msg);
 			return;
 		}
+		res.msgid=strtoll(msgidp,(char**)&endp,10);
+		if(endp-msgidp!=msgidlen){
+			debugf("net_handle_recv: msgid not a number in 'history_message' <%s>\n",msg);
+			return;
+		}
+		res.replyid=strtoll(replyidp,(char**)&endp,10);
+		if(endp-replyidp!=replyidlen){
+			debugf("net_handle_recv: replyid 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);
diff --git a/weechat/net.h b/weechat/net.h
index 63636e7..4a61bca 100644
--- a/weechat/net.h
+++ b/weechat/net.h
@@ -17,8 +17,8 @@ enum net_response_type{
 	NET_NAME,     // name
 	NET_LIST,     // nitems, items
 	NET_PONG,     // -
-	NET_MESSAGE,  // room, username, timestamp, message
-	NET_HISTORY,  // room, username, timestamp, message
+	NET_MESSAGE,  // room, username, timestamp, msgid, replyid, message
+	NET_HISTORY,  // room, username, timestamp, msgid, replyid, message
 	NET_JOIN,     // room, username
 	NET_INVITE,   // room, username
 	NET_ONLINE,   // online.username, online.num
@@ -38,6 +38,8 @@ struct net_response{
 			char *room;
 			char *username;
 			i64 timestamp;
+			i64 msgid;
+			i64 replyid;
 			char *message;
 		};
 		struct {
diff --git a/weechat/tomsg.c b/weechat/tomsg.c
index 04213ae..e008104 100644
--- a/weechat/tomsg.c
+++ b/weechat/tomsg.c
@@ -21,6 +21,8 @@ WEECHAT_PLUGIN_PRIORITY(1000)
 static const char *errpfx,*netpfx;
 
 
+#define PROTOCOL_VERSION 2
+
 #define NICK_COLOR "default"
 #define NICK_AWAY_COLOR "weechat.color.nicklist_away"
 
@@ -56,6 +58,86 @@ static struct t_weechat_plugin *weechat_plugin;
 static struct t_hashtable *conntable;
 
 
+static void display_message(
+	struct roomdata *room,
+	int64_t timestamp,
+	const char *username,
+	const char *message,
+	int64_t msgid,
+	bool is_reply
+) {
+	char tags[128];
+	int pos = snprintf(tags, sizeof tags, "tomsgid_%" PRIi64, msgid);
+	strcpy(tags + pos, room->nmembers <= 2 ? ",notify_private" : ",notify_message");
+	if (is_reply) {
+		strcat(tags, ",tomsg_reply");
+	}
+
+	weechat_printf_date_tags(
+		room->buffer, timestamp / 1000000LL,
+		tags,
+		"%s\t%s", username, message
+	);
+}
+
+static void edit_reply_message(
+	struct roomdata *room,
+	int64_t msgid,
+	const char *new_message
+) {
+	struct t_hdata *buffer_h = weechat_hdata_get("buffer");
+	struct t_hdata *lines_h = weechat_hdata_get("lines");
+	struct t_hdata *line_h = weechat_hdata_get("line");
+	struct t_hdata *line_data_h = weechat_hdata_get("line_data");
+
+	struct t_gui_lines *lines_ptr = weechat_hdata_pointer(buffer_h, room->buffer, "lines");
+	if (!lines_ptr) {
+		debugf("ERROR: Cannot get lines_ptr!");
+		return;
+	}
+
+	struct t_gui_line *line_ptr = weechat_hdata_pointer(lines_h, lines_ptr, "last_line");
+	struct t_gui_line_data *line_data_ptr;
+
+	while (line_ptr) {
+		line_data_ptr = weechat_hdata_pointer(line_h, line_ptr, "data");
+
+		const int tags_count = weechat_hdata_integer(line_data_h, line_data_ptr, "tags_count");
+		bool have_reply_tag = false;
+		bool have_msgid_tag = false;
+		for (int i = 0; i < tags_count; i++) {
+			char key[32];
+			snprintf(key, sizeof key, "%d|tags_array", i);
+			const char *tag = weechat_hdata_string(line_data_h, line_data_ptr, key);
+			if (strcmp(tag, "tomsg_reply") == 0) {
+				have_reply_tag = true;
+			} else if (memcmp(tag, "tomsgid_", 8) == 0) {
+				int64_t this_id = strtoll(tag + 8, NULL, 10);
+				if (this_id == msgid) have_msgid_tag = true;
+			}
+		}
+
+		if (have_reply_tag && have_msgid_tag) break;
+
+		line_ptr = weechat_hdata_pointer(line_h, line_ptr, "prev_line");
+	}
+
+	if (line_ptr) {
+		debugf("edit_reply_message: found line\n");
+        struct t_hashtable *hashtable =
+			weechat_hashtable_new(
+				8, WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, NULL, NULL);
+        if (hashtable) {
+            weechat_hashtable_set(hashtable, "message", new_message);
+            weechat_hdata_update(line_data_h, line_data_ptr, hashtable);
+            weechat_hashtable_free(hashtable);
+        }
+	} else {
+		debugf("edit_reply_message: not found!\n");
+	}
+}
+
+
 static void room_update_attributes(struct roomdata *room){
 	bool private=room->nmembers<=2;
 	weechat_buffer_set(room->buffer,"localvar_set_type",private?"private":"channel");
@@ -194,6 +276,28 @@ static void members_net_callback(int fd,struct net_response res,void *payload){
 	room_update_attributes(room);
 }
 
+struct room_and_msgid {
+	struct roomdata *room;
+	int64_t msgid;
+};
+
+static void reply_get_message_net_callback(int fd, struct net_response res, void *payload) {
+	(void)fd;
+	struct room_and_msgid *data = (struct room_and_msgid*)payload;
+
+	debugf("Got reply from get_message for msgid=%" PRIi64 "\n", res.msgid);
+
+	const char *green = weechat_color("green");
+
+	size_t prefixlen = strlen(green) + 3 + strlen(res.username) + 2;
+	char *buffer = malloc(prefixlen + strlen(res.message) + 1);
+	sprintf(buffer, "%s> <%s> %s", weechat_color("green"), res.username, res.message);
+	edit_reply_message(data->room, data->msgid, buffer);
+	free(buffer);
+
+	free(data);
+}
+
 static void push_net_callback(int fd,struct net_response res,void *payload){
 	(void)payload;
 	debugf("push_net_callback(fd=%d,res={.type=%d})\n",fd,res.type);
@@ -215,15 +319,22 @@ static void push_net_callback(int fd,struct net_response res,void *payload){
 			create_room_buffer(room);
 		}
 
-		if(res.type==NET_MESSAGE){
-			bool private=room->nmembers<=2;
-			weechat_printf_date_tags(
-				room->buffer,res.timestamp/1000000LL,private?"notify_private":"notify_message",
-				"%s\t%s",res.username,res.message);
-		} else if(res.type==NET_HISTORY){
-			weechat_printf_date_tags(
-				room->buffer,res.timestamp/1000000LL,NULL,
-				"%s\t%s",res.username,res.message);
+		if(res.type==NET_MESSAGE||res.type==NET_HISTORY){
+			if(res.replyid!=-1){
+				debugf("Found reply msgid=%" PRIi64 " replyid=%" PRIi64 "\n",res.msgid,res.replyid);
+				char str[128];
+				snprintf(str,sizeof str,"%s> ...",weechat_color("green"));
+				display_message(room,res.timestamp,res.username,str,res.msgid,true);
+				display_message(room,res.timestamp,"",res.message,res.msgid,false);
+
+				struct room_and_msgid *payload=malloc(sizeof(struct room_and_msgid));
+				assert(payload);
+				payload->room=room;
+				payload->msgid=res.msgid;
+				net_sendf(fd,reply_get_message_net_callback,payload,"get_message %" PRIi64,res.replyid);
+			} else {
+				display_message(room,res.timestamp,res.username,res.message,res.msgid,false);
+			}
 		} else if(res.type==NET_JOIN){
 			weechat_printf(room->buffer,"%sUser %s joined this room",netpfx,res.username);
 			if(room->buffer_nickgroup){
@@ -477,7 +588,7 @@ static void version_net_callback(int fd,struct net_response res,void *payload){
 		weechat_printf(conn->buffer,"Version negotiation complete.");
 	} else {
 		conn_destroy(conn);
-		weechat_printf(NULL,"tomsg: Server has incompatible protocol version (we want 1)!");
+		weechat_printf(NULL,"tomsg: Server has incompatible protocol version (we want %d)!",PROTOCOL_VERSION);
 	}
 }
 
@@ -506,7 +617,7 @@ static int connect_cb(const void *_p,void *hostname,int status,int _g,int fd,con
 
 			weechat_hashtable_set(conntable,&fd,conn);
 
-			net_sendf(fd,version_net_callback,NULL,"version 1");
+			net_sendf(fd,version_net_callback,NULL,"version %d", PROTOCOL_VERSION);
 
 			return WEECHAT_RC_OK;
 		}
-- 
cgit v1.2.3-70-g09d2