@@ 0,0 1,125 @@
+/* sexchat
+ * Copyright (C) 2025 Aleteoryx.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* IRCv3 batch CAP */
+
+#include <string.h>
+
+#include "hexchat.h"
+#include "batch.h"
+#include "proto-irc.h"
+
+void
+batch_new (ircconn *serv, char *reftag, char *type, char **args)
+{
+ struct batch *bat;
+
+ if (batch_lookup (serv, reftag))
+ return;
+
+ if (strcmp (type, "soju.im/bouncer-networks") == 0)
+ {
+ bat = (struct batch *)g_new0 (struct batch_sojuim_bouncer_networks, 1);
+ bat->type = BATCH_SOJUIM_BOUNCER_NETWORKS;
+ } else
+ {
+ bat = (struct batch *)g_new0 (struct batch_replay, 1);
+ bat->type = BATCH_REPLAY;
+ }
+
+ bat->reftag = g_strdup (reftag);
+
+ serv->batches = g_slist_prepend (serv->batches, bat);
+}
+
+void
+batch_finalize (ircconn *serv, char *reftag)
+{
+ struct batch *bat;
+
+ bat = batch_lookup (serv, reftag);
+ if (bat == NULL)
+ return;
+
+ serv->batches = g_slist_remove (serv->batches, bat);
+
+ switch (bat->type)
+ {
+ case BATCH_SOJUIM_BOUNCER_NETWORKS:
+ break;
+
+ case BATCH_REPLAY:
+ {
+ struct batch_replay *bat2 = (struct batch_replay *)bat;
+ GSList *el;
+ char *line;
+
+ bat2->messages = g_slist_reverse (bat2->messages);
+ for (el = bat2->messages; el; el = el->next)
+ {
+ line = (char *)el->data;
+ irc_inline (serv, line, strlen (line));
+ g_free (line);
+ }
+
+ g_slist_free (bat2->messages);
+ }
+ }
+
+ g_free (bat);
+}
+
+gboolean
+batch_event (ircconn *serv, char *reftag, char *raw, char **word, char **word_eol)
+{
+ struct batch *bat;
+
+ bat = batch_lookup (serv, reftag);
+ if (bat == NULL)
+ return FALSE;
+
+ switch (bat->type)
+ {
+ case BATCH_SOJUIM_BOUNCER_NETWORKS:
+ break;
+ case BATCH_REPLAY:
+ {
+ struct batch_replay *bat2 = (struct batch_replay *)bat;
+ bat2->messages = g_slist_prepend (bat2->messages, g_strdup (raw));
+ }
+ }
+
+ return TRUE;
+}
+
+
+struct batch *
+batch_lookup (ircconn *serv, char *reftag)
+{
+ GSList *el;
+ struct batch *bat;
+
+ for (el = serv->batches; el; el = el->next)
+ {
+ bat = (struct batch *)el->data;
+ if (strcmp (bat->reftag, reftag) == 0)
+ return bat;
+ }
+
+ return NULL;
+}
@@ 44,6 44,7 @@
#include "hexchatc.h"
#include "url.h"
#include "servlist.h"
+#include "batch.h"
static void
irc_login (server *serv, char *user, char *realname)
@@ 1103,14 1104,14 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
}
return;
- case WORDL('P', 'I', 'N', 'G'):
+ case WORDL('P','I','N','G'):
tcp_sendf (sess->server, "PONG %s\r\n", word_eol[3]);
return;
case WORDL('P','O','N','G'):
inbound_ping_reply (serv->server_session,
- (word[4][0] == ':') ? word[4] + 1 : word[4],
- word[3], tags_data);
+ (word[4][0] == ':') ? word[4] + 1 : word[4],
+ word[3], tags_data);
return;
case WORDL('Q','U','I','T'):
@@ 1175,16 1176,16 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
inbound_account (serv, nick, STRIP_COLON(word, word_eol, 3), tags_data);
return;
- case WORDL('A', 'U', 'T', 'H'):
+ case WORDL('A','U','T','H'):
inbound_sasl_authenticate (sess->server, word_eol[3]);
return;
- case WORDL('C', 'H', 'G', 'H'):
+ case WORDL('C','H','G','H'):
inbound_user_info (sess, NULL, word[3], STRIP_COLON(word, word_eol, 4), NULL, nick, NULL,
NULL, 0xff, tags_data);
return;
- case WORDL('S', 'E', 'T', 'N'):
+ case WORDL('S','E','T','N'):
inbound_user_info (sess, NULL, NULL, NULL, NULL, nick, STRIP_COLON(word, word_eol, 3),
NULL, 0xff, tags_data);
return;
@@ 1310,8 1311,8 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
case WORDL('T','O','P','I'):
inbound_topicnew (serv, nick, word[3],
- (word_eol[4][0] == ':') ? word_eol[4] + 1 : word_eol[4],
- tags_data);
+ (word_eol[4][0] == ':') ? word_eol[4] + 1 : word_eol[4],
+ tags_data);
return;
case WORDL('W','A','L','L'):
@@ 1320,6 1321,13 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
text++;
EMIT_SIGNAL_TAGS (XP_TE_WALLOPS, sess, nick, text, NULL, NULL, 0, tags_data);
return;
+
+ case WORDL('B','A','T','C'):
+ if (word[3][0] == '-')
+ batch_finalize (serv, word[3]+1);
+ else
+ batch_new (serv, word[3]+1, word[4], word+5);
+ return;
}
}
@@ 1372,8 1380,8 @@ garbage:
/* handle named messages that DON'T start with a ':' */
static void
-process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol[],
- const message_tags_data *tags_data)
+process_named_servermsg (session *sess, char *buf, char *rawname, char *word[], char *word_eol[],
+ const message_tags_data *tags_data)
{
sess = sess->server->server_session;
@@ 1400,6 1408,14 @@ process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol
inbound_sasl_authenticate (sess->server, word_eol[2]);
return;
}
+ if (!strncmp (buf, "BATCH ", 6))
+ {
+ if (word[3][0] == '-')
+ batch_finalize (sess->server, word[3]+1);
+ else
+ batch_new (sess->server, word[3]+1, word[4], word+5);
+ return;
+ }
EMIT_SIGNAL_TAGS (XP_TE_SERVTEXT, sess, buf, sess->server->servername, rawname, NULL, 0, tags_data);
}
@@ 1503,7 1519,7 @@ handle_message_tag_time (const char *time, message_tags_data *tags_data)
*/
static void
handle_message_tags (server *serv, const char *tags_str,
- message_tags_data *tags_data)
+ message_tags_data *tags_data)
{
char **tags;
int i;
@@ 1527,8 1543,11 @@ handle_message_tags (server *serv, const char *tags_str,
if (serv->have_account_tag && !strcmp (key, "account"))
tags_data->account = g_strdup (value);
- if (serv->have_message_tags && !strcmp (key, "msgid"))
+ if (!strcmp (key, "msgid"))
tags_data->msgid = g_strdup (value);
+
+ if (!strcmp (key, "batch"))
+ tags_data->batchref = g_strdup (value);
if (serv->have_idmsg && strcmp (key, "solanum.chat/identified"))
tags_data->identified = TRUE;
@@ 1577,6 1596,9 @@ irc_inline (server *serv, char *buf, int len)
/* split line into words and words_to_end_of_line */
process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
+
+ if (tags_data.batchref && batch_event (serv, tags_data.batchref, buf, word, word_eol))
+ goto xit;
if (buf[0] == ':')
{
@@ 1595,7 1617,7 @@ irc_inline (server *serv, char *buf, int len)
word_eol[1] = buf; /* keep the ":" for plugins */
if (plugin_emit_server (sess, type, word, word_eol,
- tags_data.timestamp))
+ tags_data.timestamp))
goto xit;
word[1]++;
@@ 1612,7 1634,7 @@ irc_inline (server *serv, char *buf, int len)
if (buf[0] != ':')
{
- process_named_servermsg (sess, buf, word[0], word_eol, &tags_data);
+ process_named_servermsg (sess, buf, word[0], word, word_eol, &tags_data);
goto xit;
}