diff options
-rw-r--r-- | ChangeLog.adoc | 1 | ||||
-rw-r--r-- | src/plugins/irc/irc-batch.c | 26 | ||||
-rw-r--r-- | src/plugins/irc/irc-batch.h | 5 | ||||
-rw-r--r-- | src/plugins/irc/irc-protocol.c | 3 | ||||
-rw-r--r-- | src/plugins/irc/irc-tag.c | 156 | ||||
-rw-r--r-- | src/plugins/irc/irc-tag.h | 2 | ||||
-rw-r--r-- | tests/unit/plugins/irc/test-irc-batch.cpp | 44 | ||||
-rw-r--r-- | tests/unit/plugins/irc/test-irc-protocol.cpp | 26 | ||||
-rw-r--r-- | tests/unit/plugins/irc/test-irc-tag.cpp | 91 |
9 files changed, 327 insertions, 27 deletions
diff --git a/ChangeLog.adoc b/ChangeLog.adoc index 930ae486f..a2d52a239 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -39,6 +39,7 @@ Bug fixes:: * irc: fix display of country code in message 344 received as whois geo info (issue #1736) * irc: add missing "account-tag" in list of supported capabilities * irc: add channel in "autojoin" server option only when the channel is actually joined (issue #1990) + * irc: add missing tags on multiline messages (issue #1987) * script: fix cursor position after `/script list -i` or `/script list -il` * script: fix buffer used by command `/script list -i|-il|-o|-ol` diff --git a/src/plugins/irc/irc-batch.c b/src/plugins/irc/irc-batch.c index 76ff79ba2..337c29a0d 100644 --- a/src/plugins/irc/irc-batch.c +++ b/src/plugins/irc/irc-batch.c @@ -110,7 +110,7 @@ irc_batch_add_to_list (struct t_irc_server *server, struct t_irc_batch *batch) struct t_irc_batch * irc_batch_start_batch (struct t_irc_server *server, const char *reference, const char *parent_ref, const char *type, - const char *parameters) + const char *parameters, struct t_hashtable *tags) { struct t_irc_batch *ptr_batch; @@ -130,6 +130,7 @@ irc_batch_start_batch (struct t_irc_server *server, const char *reference, ptr_batch->parent_ref = (parent_ref) ? strdup (parent_ref) : NULL; ptr_batch->type = strdup (type); ptr_batch->parameters = (parameters) ? strdup (parameters) : NULL; + ptr_batch->tags = (tags) ? weechat_hashtable_dup (tags) : NULL; ptr_batch->start_time = time (NULL); ptr_batch->messages = NULL; ptr_batch->end_received = 0; @@ -188,6 +189,8 @@ irc_batch_free (struct t_irc_server *server, struct t_irc_batch *batch) free (batch->type); if (batch->parameters) free (batch->parameters); + if (batch->tags) + weechat_hashtable_free (batch->tags); if (batch->messages) weechat_string_dyn_free (batch->messages, 1); @@ -226,7 +229,7 @@ irc_batch_process_messages (struct t_irc_server *server, struct t_irc_batch *batch) { char **list_messages, *command, *channel, modifier_data[1024], *new_messages; - char *message; + char *message, *message2; int i, count_messages; if (!batch || !batch->messages) @@ -261,8 +264,13 @@ irc_batch_process_messages (struct t_irc_server *server, if (!message) continue; + message2 = irc_tag_add_tags_to_message (message, + batch->tags); + if (!message2) + continue; + irc_message_parse (server, - message, + message2, NULL, /* tags */ NULL, /* message_without_tags */ NULL, /* nick */ @@ -280,13 +288,13 @@ irc_batch_process_messages (struct t_irc_server *server, NULL); /* pos_text */ /* add raw message */ - irc_raw_print (server, IRC_RAW_FLAG_RECV, message); + irc_raw_print (server, IRC_RAW_FLAG_RECV, message2); /* call receive callback, ignoring batch tags */ - irc_protocol_recv_command (server, message, command, channel, 1); + irc_protocol_recv_command (server, message2, command, channel, 1); - if (message) - free (message); + free (message); + free (message2); if (command) free (command); if (channel) @@ -562,6 +570,10 @@ irc_batch_print_log (struct t_irc_server *server) weechat_log_printf (" parent_ref. . . . . : '%s'", ptr_batch->parent_ref); weechat_log_printf (" type. . . . . . . . : '%s'", ptr_batch->type); weechat_log_printf (" parameters. . . . . : '%s'", ptr_batch->parameters); + weechat_log_printf (" tags. . . . . . . . : 0x%lx (hashtable: '%s')", + ptr_batch->tags, + weechat_hashtable_get_string (ptr_batch->tags, + "keys_values")); weechat_log_printf (" start_time. . . . . : %lld", (long long)ptr_batch->start_time); weechat_log_printf (" message . . . . . . : 0x%lx ('%s')", ptr_batch->messages, diff --git a/src/plugins/irc/irc-batch.h b/src/plugins/irc/irc-batch.h index 04b718dad..4ecc1b995 100644 --- a/src/plugins/irc/irc-batch.h +++ b/src/plugins/irc/irc-batch.h @@ -22,6 +22,7 @@ #include <time.h> +struct t_hashtable; struct t_irc_server; struct t_irc_batch @@ -30,6 +31,7 @@ struct t_irc_batch char *parent_ref; /* ref of parent batch (optional) */ char *type; /* type */ char *parameters; /* parameters */ + struct t_hashtable *tags; /* batch message tags */ time_t start_time; /* start time (to auto-purge if */ /* batch end is not received) */ char **messages; /* messages separated by '\n' */ @@ -46,7 +48,8 @@ extern struct t_irc_batch *irc_batch_start_batch (struct t_irc_server *server, const char *reference, const char *parent_ref, const char *type, - const char *parameters); + const char *parameters, + struct t_hashtable *tags); extern int irc_batch_add_message (struct t_irc_server *server, const char *reference, const char *irc_message); diff --git a/src/plugins/irc/irc-protocol.c b/src/plugins/irc/irc-protocol.c index 1ff109e28..d5172cb29 100644 --- a/src/plugins/irc/irc-protocol.c +++ b/src/plugins/irc/irc-protocol.c @@ -688,7 +688,8 @@ IRC_PROTOCOL_CALLBACK(batch) params[0] + 1, /* reference */ weechat_hashtable_get (tags, "batch"), /* parent ref */ params[1], /* type */ - str_params); + str_params, + tags); if (str_params) free (str_params); } diff --git a/src/plugins/irc/irc-tag.c b/src/plugins/irc/irc-tag.c index f4eefb317..8dd184713 100644 --- a/src/plugins/irc/irc-tag.c +++ b/src/plugins/irc/irc-tag.c @@ -283,3 +283,159 @@ irc_tag_parse (const char *tags, return num_tags; } + +/* + * Adds tags to a dynamic string, separated by semicolons, with escaped + * tag values. + */ + +void +irc_tag_add_to_string_cb (void *data, + struct t_hashtable *hashtable, + const void *key, + const void *value) +{ + char **string, *escaped; + + /* make C compiler happy */ + (void) hashtable; + + string = (char **)data; + + if (*string[0]) + weechat_string_dyn_concat (string, ";", -1); + + weechat_string_dyn_concat (string, key, -1); + + if (value) + { + weechat_string_dyn_concat (string, "=", -1); + escaped = irc_tag_escape_value ((const char *)value); + weechat_string_dyn_concat (string, + (escaped) ? escaped : (const char *)value, + -1); + if (escaped) + free (escaped); + } +} + +/* + * Converts hashtable with tags to a string (tags and values are escaped). + * + * Note: result must be freed after use. + */ + +char * +irc_tag_hashtable_to_string (struct t_hashtable *tags) +{ + char **string; + + if (!tags) + return NULL; + + string = weechat_string_dyn_alloc (64); + if (!string) + return NULL; + + weechat_hashtable_map (tags, &irc_tag_add_to_string_cb, string); + + return weechat_string_dyn_free (string, 0); +} + +/* + * Adds tags to another hashtable. + */ + +void +irc_tag_add_to_hashtable_cb (void *data, + struct t_hashtable *hashtable, + const void *key, + const void *value) +{ + /* make C compiler happy */ + (void) hashtable; + + if (!weechat_hashtable_has_key ((struct t_hashtable *)data, key)) + weechat_hashtable_set ((struct t_hashtable *)data, key, value); +} + +/* + * Adds tags to an IRC message. + * Existing tags in message are kept unchanged. + * + * Note: result must be freed after use. + */ + +char * +irc_tag_add_tags_to_message (const char *message, struct t_hashtable *tags) +{ + char *msg_str_tags, **result, *new_tags; + const char *pos_space, *ptr_message; + struct t_hashtable *msg_hash_tags; + + if (!message) + return NULL; + + if (!tags) + return strdup (message); + + result = NULL; + msg_str_tags = NULL; + msg_hash_tags = NULL; + new_tags = NULL; + + if (message[0] == '@') + { + pos_space = strchr (message, ' '); + if (!pos_space) + goto end; + msg_str_tags = weechat_strndup (message + 1, pos_space - message - 1); + ptr_message = pos_space + 1; + while (ptr_message[0] == ' ') + { + ptr_message++; + } + } + else + { + ptr_message = message; + } + + msg_hash_tags = weechat_hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + if (!msg_hash_tags) + goto end; + + if (msg_str_tags) + irc_tag_parse (msg_str_tags, msg_hash_tags, NULL); + + weechat_hashtable_map (tags, &irc_tag_add_to_hashtable_cb, msg_hash_tags); + + result = weechat_string_dyn_alloc (64); + if (!result) + goto end; + + new_tags = irc_tag_hashtable_to_string (msg_hash_tags); + if (!new_tags) + goto end; + + if (new_tags[0]) + { + weechat_string_dyn_concat (result, "@", -1); + weechat_string_dyn_concat (result, new_tags, -1); + weechat_string_dyn_concat (result, " ", -1); + } + weechat_string_dyn_concat (result, ptr_message, -1); + +end: + if (msg_str_tags) + free (msg_str_tags); + if (msg_hash_tags) + weechat_hashtable_free (msg_hash_tags); + if (new_tags) + free (new_tags); + + return (result) ? weechat_string_dyn_free (result, 0) : NULL; +} diff --git a/src/plugins/irc/irc-tag.h b/src/plugins/irc/irc-tag.h index c363d3294..a62e069f9 100644 --- a/src/plugins/irc/irc-tag.h +++ b/src/plugins/irc/irc-tag.h @@ -30,5 +30,7 @@ extern char *irc_tag_modifier_cb (const void *pointer, extern int irc_tag_parse (const char *tags, struct t_hashtable *hashtable, const char *prefix_key); +extern char *irc_tag_add_tags_to_message (const char *message, + struct t_hashtable *tags); #endif /* WEECHAT_PLUGIN_IRC_TAG_H */ diff --git a/tests/unit/plugins/irc/test-irc-batch.cpp b/tests/unit/plugins/irc/test-irc-batch.cpp index 95687638e..774755430 100644 --- a/tests/unit/plugins/irc/test-irc-batch.cpp +++ b/tests/unit/plugins/irc/test-irc-batch.cpp @@ -26,6 +26,8 @@ extern "C" { #include <string.h> +#include "src/core/wee-hashtable.h" +#include "src/plugins/weechat-plugin.h" #include "src/plugins/irc/irc-batch.h" #include "src/plugins/irc/irc-server.h" } @@ -43,14 +45,23 @@ TEST(IrcBatch, Search) { struct t_irc_server *server; struct t_irc_batch *batch1, *batch2; + struct t_hashtable *tags; server = irc_server_alloc ("server"); CHECK(server); - batch1 = irc_batch_start_batch (server, "ref1", "parent_ref", "type", "params"); + tags = hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + CHECK(tags); + hashtable_set (tags, "time", "2023-08-09T07:43:01.830Z"); + hashtable_set (tags, "msgid", "icqfzy7zdbpix4gy8pvzuv49kw"); + + batch1 = irc_batch_start_batch (server, "ref1", "parent_ref", "type", "params", tags); CHECK(batch1); - batch2 = irc_batch_start_batch (server, "ref2", "parent_ref", "type", "params"); + batch2 = irc_batch_start_batch (server, "ref2", "parent_ref", "type", "params", NULL); CHECK(batch2); POINTERS_EQUAL(NULL, irc_batch_search (NULL, NULL)); @@ -105,19 +116,29 @@ TEST(IrcBatch, StartBatch) { struct t_irc_server *server; struct t_irc_batch *batch; + struct t_hashtable *tags; server = irc_server_alloc ("server"); CHECK(server); + tags = hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + CHECK(tags); + hashtable_set (tags, "time", "2023-08-09T07:43:01.830Z"); + hashtable_set (tags, "msgid", "icqfzy7zdbpix4gy8pvzuv49kw"); + POINTERS_EQUAL(NULL, server->batches); - batch = irc_batch_start_batch (server, "ref", NULL, "type", NULL); + batch = irc_batch_start_batch (server, "ref", NULL, "type", NULL, NULL); CHECK(batch); POINTERS_EQUAL(batch, server->batches); STRCMP_EQUAL("ref", batch->reference); POINTERS_EQUAL(NULL, batch->parent_ref); STRCMP_EQUAL("type", batch->type); POINTERS_EQUAL(NULL, batch->parameters); + POINTERS_EQUAL(NULL, batch->tags); CHECK(batch->start_time > 0); POINTERS_EQUAL(NULL, batch->messages); LONGS_EQUAL(0, batch->end_received); @@ -126,13 +147,19 @@ TEST(IrcBatch, StartBatch) POINTERS_EQUAL(NULL, server->batches); - batch = irc_batch_start_batch (server, "ref", "parent_ref", "type", "params"); + batch = irc_batch_start_batch (server, "ref", "parent_ref", "type", "params", + tags); CHECK(batch); POINTERS_EQUAL(batch, server->batches); STRCMP_EQUAL("ref", batch->reference); STRCMP_EQUAL("parent_ref", batch->parent_ref); STRCMP_EQUAL("type", batch->type); STRCMP_EQUAL("params", batch->parameters); + CHECK(batch->tags); + STRCMP_EQUAL("2023-08-09T07:43:01.830Z", + (const char *)hashtable_get (batch->tags, "time")); + STRCMP_EQUAL("icqfzy7zdbpix4gy8pvzuv49kw", + (const char *)hashtable_get (batch->tags, "msgid")); CHECK(batch->start_time > 0); POINTERS_EQUAL(NULL, batch->messages); @@ -159,7 +186,8 @@ TEST(IrcBatch, AddMessage) server = irc_server_alloc ("server"); CHECK(server); - batch = irc_batch_start_batch (server, "ref", "parent_ref", "type", "params"); + batch = irc_batch_start_batch (server, "ref", "parent_ref", "type", "params", + NULL); CHECK(batch); irc_batch_add_message (server, "ref", ":alice PRIVMSG #test: test1"); @@ -187,10 +215,12 @@ TEST(IrcBatch, FreeAll) server = irc_server_alloc ("server"); CHECK(server); - batch1 = irc_batch_start_batch (server, "ref1", "parent_ref", "type", "params"); + batch1 = irc_batch_start_batch (server, "ref1", "parent_ref", "type", "params", + NULL); CHECK(batch1); - batch2 = irc_batch_start_batch (server, "ref2", "parent_ref", "type", "params"); + batch2 = irc_batch_start_batch (server, "ref2", "parent_ref", "type", "params", + NULL); CHECK(batch2); POINTERS_EQUAL(batch1, server->batches); diff --git a/tests/unit/plugins/irc/test-irc-protocol.cpp b/tests/unit/plugins/irc/test-irc-protocol.cpp index e455d1cd3..101aa14ec 100644 --- a/tests/unit/plugins/irc/test-irc-protocol.cpp +++ b/tests/unit/plugins/irc/test-irc-protocol.cpp @@ -1626,7 +1626,8 @@ TEST(IrcProtocolWithServer, batch_with_batch_cap) POINTERS_EQUAL(NULL, irc_batch_search (ptr_server, "ref2")); /* multiline */ - RECV(":server BATCH +ref draft/multiline #test"); + RECV("@time=2023-08-09T07:43:01.830Z;msgid=icqfzy7zdbpix4gy8pvzuv49kw " + ":server BATCH +ref draft/multiline #test"); CHECK_NO_MSG; RECV("@batch=ref :bob!user_b@host_b PRIVMSG #test :line 1"); CHECK_NO_MSG; @@ -1634,13 +1635,14 @@ TEST(IrcProtocolWithServer, batch_with_batch_cap) CHECK_NO_MSG; RECV(":server BATCH -ref"); CHECK_CHAN("bob", "line 1", - "irc_privmsg,irc_tag_batch=ref,irc_batch_type_draft/multiline," - "notify_message,prefix_nick_248,nick_bob," - "host_user_b@host_b,log1"); + "irc_privmsg,irc_tag_batch=ref,irc_tag_time=2023-08-09T07:43:01.830Z," + "irc_tag_msgid=icqfzy7zdbpix4gy8pvzuv49kw," + "irc_batch_type_draft/multiline,notify_message,prefix_nick_248," + "nick_bob,host_user_b@host_b,log1"); CHECK_CHAN("bob", "line 2", - "irc_privmsg,irc_tag_batch=ref,irc_batch_type_draft/multiline," - "notify_message,prefix_nick_248,nick_bob," - "host_user_b@host_b,log1"); + "irc_privmsg,irc_tag_batch=ref,irc_tag_time=2023-08-09T07:43:01.830Z," + "irc_tag_msgid=icqfzy7zdbpix4gy8pvzuv49kw,irc_batch_type_draft/multiline," + "notify_message,prefix_nick_248,nick_bob,host_user_b@host_b,log1"); /* multiline with CTCP */ RECV(":server BATCH +ref draft/multiline #test"); @@ -1667,7 +1669,8 @@ TEST(IrcProtocolWithServer, batch_with_batch_cap) irc_server_buffer_set_input_multiline (ptr_server, 1); /* multiline */ - RECV(":server BATCH +ref draft/multiline #test"); + RECV("@time=2023-08-09T07:43:01.830Z;msgid=icqfzy7zdbpix4gy8pvzuv49kw " + ":server BATCH +ref draft/multiline #test"); CHECK_NO_MSG; RECV("@batch=ref :bob!user_b@host_b PRIVMSG #test :line 1"); CHECK_NO_MSG; @@ -1676,9 +1679,10 @@ TEST(IrcProtocolWithServer, batch_with_batch_cap) RECV(":server BATCH -ref"); CHECK_CHAN("bob", "line 1\n" "line 2", - "irc_privmsg,irc_tag_batch=ref,irc_batch_type_draft/multiline," - "notify_message,prefix_nick_248,nick_bob,host_user_b@host_b," - "log1"); + "irc_privmsg,irc_tag_batch=ref,irc_tag_time=2023-08-09T07:43:01.830Z," + "irc_tag_msgid=icqfzy7zdbpix4gy8pvzuv49kw," + "irc_batch_type_draft/multiline,notify_message,prefix_nick_248," + "nick_bob,host_user_b@host_b,log1"); /* multiline with CTCP */ RECV(":server BATCH +ref draft/multiline #test"); diff --git a/tests/unit/plugins/irc/test-irc-tag.cpp b/tests/unit/plugins/irc/test-irc-tag.cpp index a24afc0c8..b9993c8e1 100644 --- a/tests/unit/plugins/irc/test-irc-tag.cpp +++ b/tests/unit/plugins/irc/test-irc-tag.cpp @@ -21,6 +21,8 @@ #include "CppUTest/TestHarness.h" +#include "tests/tests.h" + extern "C" { #include <stdio.h> @@ -28,6 +30,8 @@ extern "C" #include "src/core/wee-hook.h" #include "src/plugins/irc/irc-tag.h" #include "src/plugins/plugin.h" + +extern char *irc_tag_hashtable_to_string (struct t_hashtable *tags); } #define WEE_CHECK_ESCAPE_VALUE(__result, __string) \ @@ -161,3 +165,90 @@ TEST(IrcTag, Parse) hashtable_free (hashtable); } + +/* + * Tests functions: + * irc_tag_add_to_string_cb + * irc_tag_hashtable_to_string + */ + +TEST(IrcTag, HashtableToString) +{ + char *str; + struct t_hashtable *tags; + + POINTERS_EQUAL(NULL, irc_tag_hashtable_to_string (NULL)); + + tags = hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + CHECK(tags); + + WEE_TEST_STR("", irc_tag_hashtable_to_string (tags)); + + hashtable_set (tags, "time", "2023-08-09T07:43:01.830Z"); + hashtable_set (tags, "msgid", "icqfzy7zdbpix4gy8pvzuv49kw"); + hashtable_set (tags, "test", "value with spaces"); + + WEE_TEST_STR("time=2023-08-09T07:43:01.830Z;" + "msgid=icqfzy7zdbpix4gy8pvzuv49kw;" + "test=value\\swith\\sspaces", + irc_tag_hashtable_to_string (tags)); + + hashtable_free (tags); +} + +/* + * Tests functions: + * irc_tag_add_to_hashtable_cb + * irc_tag_add_tags_to_message + */ + +TEST(IrcTag, AddTagsToMessage) +{ + char *str; + struct t_hashtable *tags; + + POINTERS_EQUAL(NULL, irc_tag_add_tags_to_message (NULL, NULL)); + + WEE_TEST_STR("", irc_tag_add_tags_to_message ("", NULL)); + WEE_TEST_STR(":nick!user@host PRIVMSG #test :hello", + irc_tag_add_tags_to_message ( + ":nick!user@host PRIVMSG #test :hello", NULL)); + WEE_TEST_STR("@tag1;tag2=value2 :nick!user@host PRIVMSG #test :hello", + irc_tag_add_tags_to_message ( + "@tag1;tag2=value2 :nick!user@host PRIVMSG #test :hello", + NULL)); + + tags = hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + CHECK(tags); + + WEE_TEST_STR(":nick!user@host PRIVMSG #test :hello", + irc_tag_add_tags_to_message ( + ":nick!user@host PRIVMSG #test :hello", tags)); + WEE_TEST_STR("@tag1;tag2=value2 :nick!user@host PRIVMSG #test :hello", + irc_tag_add_tags_to_message ( + "@tag1;tag2=value2 :nick!user@host PRIVMSG #test :hello", + tags)); + + hashtable_set (tags, "time", "2023-08-09T07:43:01.830Z"); + hashtable_set (tags, "msgid", "icqfzy7zdbpix4gy8pvzuv49kw"); + hashtable_set (tags, "test", "value with spaces"); + + WEE_TEST_STR("@time=2023-08-09T07:43:01.830Z;msgid=icqfzy7zdbpix4gy8pvzuv49kw;" + "test=value\\swith\\sspaces :nick!user@host PRIVMSG #test :hello", + irc_tag_add_tags_to_message ( + ":nick!user@host PRIVMSG #test :hello", tags)); + WEE_TEST_STR("@tag1;tag2=value2;time=2023-08-09T07:43:01.830Z;" + "msgid=icqfzy7zdbpix4gy8pvzuv49kw;test=value\\swith\\sspaces " + ":nick!user@host PRIVMSG #test :hello", + irc_tag_add_tags_to_message ( + "@tag1;tag2=value2 :nick!user@host PRIVMSG #test :hello", + tags)); + + hashtable_free (tags); +} |