diff options
Diffstat (limited to 'src/plugins/irc')
-rw-r--r-- | src/plugins/irc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/irc/Makefile.am | 2 | ||||
-rw-r--r-- | src/plugins/irc/irc-protocol.c | 65 | ||||
-rw-r--r-- | src/plugins/irc/irc-tag.c | 280 | ||||
-rw-r--r-- | src/plugins/irc/irc-tag.h | 32 | ||||
-rw-r--r-- | src/plugins/irc/irc.c | 5 |
6 files changed, 323 insertions, 62 deletions
diff --git a/src/plugins/irc/CMakeLists.txt b/src/plugins/irc/CMakeLists.txt index da5647691..c638605e7 100644 --- a/src/plugins/irc/CMakeLists.txt +++ b/src/plugins/irc/CMakeLists.txt @@ -42,6 +42,7 @@ add_library(irc MODULE irc-redirect.c irc-redirect.h irc-sasl.c irc-sasl.h irc-server.c irc-server.h + irc-tag.c irc-tag.h irc-upgrade.c irc-upgrade.h ) set_target_properties(irc PROPERTIES PREFIX "") diff --git a/src/plugins/irc/Makefile.am b/src/plugins/irc/Makefile.am index b12570f8a..fa5833350 100644 --- a/src/plugins/irc/Makefile.am +++ b/src/plugins/irc/Makefile.am @@ -71,6 +71,8 @@ irc_la_SOURCES = irc.c \ irc-sasl.h \ irc-server.c \ irc-server.h \ + irc-tag.c \ + irc-tag.h \ irc-upgrade.c \ irc-upgrade.h diff --git a/src/plugins/irc/irc-protocol.c b/src/plugins/irc/irc-protocol.c index 356fc1347..2890496a7 100644 --- a/src/plugins/irc/irc-protocol.c +++ b/src/plugins/irc/irc-protocol.c @@ -53,9 +53,10 @@ #include "irc-modelist.h" #include "irc-msgbuffer.h" #include "irc-nick.h" +#include "irc-notify.h" #include "irc-sasl.h" #include "irc-server.h" -#include "irc-notify.h" +#include "irc-tag.h" /* @@ -200,66 +201,6 @@ irc_protocol_nick_address (struct t_irc_server *server, } /* - * Returns hashtable with tags for an IRC message. - * - * Example: - * if tags == "aaa=bbb;ccc;example.com/ddd=eee", - * hashtable will have following keys/values: - * "aaa" => "bbb" - * "ccc" => NULL - * "example.com/ddd" => "eee" - */ - -struct t_hashtable * -irc_protocol_get_message_tags (const char *tags) -{ - struct t_hashtable *hashtable; - char **items, *pos, *key; - int num_items, i; - - if (!tags || !tags[0]) - return NULL; - - hashtable = weechat_hashtable_new (32, - WEECHAT_HASHTABLE_STRING, - WEECHAT_HASHTABLE_STRING, - NULL, NULL); - if (!hashtable) - return NULL; - - items = weechat_string_split (tags, ";", NULL, - WEECHAT_STRING_SPLIT_STRIP_LEFT - | WEECHAT_STRING_SPLIT_STRIP_RIGHT - | WEECHAT_STRING_SPLIT_COLLAPSE_SEPS, - 0, &num_items); - if (items) - { - for (i = 0; i < num_items; i++) - { - pos = strchr (items[i], '='); - if (pos) - { - /* format: "tag=value" */ - key = weechat_strndup (items[i], pos - items[i]); - if (key) - { - weechat_hashtable_set (hashtable, key, pos + 1); - free (key); - } - } - else - { - /* format: "tag" */ - weechat_hashtable_set (hashtable, items[i], NULL); - } - } - weechat_string_free_split (items); - } - - return hashtable; -} - -/* * Parses date/time received in a "time" tag. * * Returns value of time (timestamp), 0 if error. @@ -6883,7 +6824,7 @@ irc_protocol_recv_command (struct t_irc_server *server, pos_space - (irc_message + 1)); if (tags) { - hash_tags = irc_protocol_get_message_tags (tags); + hash_tags = irc_tag_parse (tags); if (hash_tags) { date = irc_protocol_parse_time ( diff --git a/src/plugins/irc/irc-tag.c b/src/plugins/irc/irc-tag.c new file mode 100644 index 000000000..a0534e0ad --- /dev/null +++ b/src/plugins/irc/irc-tag.c @@ -0,0 +1,280 @@ +/* + * irc-tag.c - functions for IRC message tags + * + * Copyright (C) 2021 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "../weechat-plugin.h" +#include "irc.h" +#include "irc-tag.h" + + +/* + * Escapes a tag value, the following sequences are replaced: + * + * character | escaped value + * ---------------+------------------------- + * ; (semicolon) | \: (backslash and colon) + * SPACE | \s + * \ | \\ + * CR | \r + * LF | \n + * all others | the character itself + * + * See: https://ircv3.net/specs/extensions/message-tags#escaping-values + * + * Note: result must be freed after use. + */ + +char * +irc_tag_escape_value (const char *string) +{ + char **out, *result; + unsigned char *ptr_string; + int length; + + if (!string) + return NULL; + + length = strlen (string); + out = weechat_string_dyn_alloc (length + (length / 2) + 1); + if (!out) + return NULL; + + ptr_string = (unsigned char *)string; + while (ptr_string && ptr_string[0]) + { + switch (ptr_string[0]) + { + case ';': + weechat_string_dyn_concat (out, "\\:", -1); + ptr_string++; + break; + case ' ': + weechat_string_dyn_concat (out, "\\s", -1); + ptr_string++; + break; + case '\\': + weechat_string_dyn_concat (out, "\\\\", -1); + ptr_string++; + break; + case '\r': + weechat_string_dyn_concat (out, "\\r", -1); + ptr_string++; + break; + case '\n': + weechat_string_dyn_concat (out, "\\n", -1); + ptr_string++; + break; + default: + length = weechat_utf8_char_size ((char *)ptr_string); + if (length == 0) + length = 1; + weechat_string_dyn_concat (out, + (const char *)ptr_string, + length); + ptr_string += length; + break; + } + } + + result = *out; + weechat_string_dyn_free (out, 0); + + return result; +} + +/* + * Unescapes a tag value. + * + * See: https://ircv3.net/specs/extensions/message-tags#escaping-values + * + * Note: result must be freed after use. + */ + +char * +irc_tag_unescape_value (const char *string) +{ + char **out, *result; + unsigned char *ptr_string; + int length; + + if (!string) + return NULL; + + length = strlen (string); + out = weechat_string_dyn_alloc (length + (length / 2) + 1); + if (!out) + return NULL; + + ptr_string = (unsigned char *)string; + while (ptr_string && ptr_string[0]) + { + switch (ptr_string[0]) + { + case '\\': + ptr_string++; + switch (ptr_string[0]) + { + case ':': + weechat_string_dyn_concat (out, ";", -1); + ptr_string++; + break; + case 's': + weechat_string_dyn_concat (out, " ", -1); + ptr_string++; + break; + case '\\': + weechat_string_dyn_concat (out, "\\", -1); + ptr_string++; + break; + case 'r': + weechat_string_dyn_concat (out, "\r", -1); + ptr_string++; + break; + case 'n': + weechat_string_dyn_concat (out, "\n", -1); + ptr_string++; + break; + default: + if (ptr_string[0]) + { + length = weechat_utf8_char_size ((char *)ptr_string); + if (length == 0) + length = 1; + weechat_string_dyn_concat (out, + (const char *)ptr_string, + length); + ptr_string += length; + } + break; + } + break; + default: + length = weechat_utf8_char_size ((char *)ptr_string); + if (length == 0) + length = 1; + weechat_string_dyn_concat (out, + (const char *)ptr_string, + length); + ptr_string += length; + break; + } + } + + result = *out; + weechat_string_dyn_free (out, 0); + + return result; +} + +/* + * Callback for modifiers "irc_tag_escape_value" and "irc_tag_unescape_value". + * + * These modifiers can be used by other plugins to escape/unescape IRC message + * tags. + */ + +char * +irc_tag_modifier_cb (const void *pointer, void *data, + const char *modifier, const char *modifier_data, + const char *string) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) modifier_data; + + if (strcmp (modifier, "irc_tag_escape_value") == 0) + return irc_tag_escape_value (string); + + if (strcmp (modifier, "irc_tag_unescape_value") == 0) + return irc_tag_unescape_value (string); + + /* unknown modifier */ + return NULL; +} + +/* + * Parses tags received in an IRC message. + * Returns a hashtable with tags and their unescaped values. + * + * Example: + * if tags == "aaa=bbb;ccc;example.com/ddd=eee", + * hashtable will have following keys/values: + * "aaa" => "bbb" + * "ccc" => NULL + * "example.com/ddd" => "eee" + */ + +struct t_hashtable * +irc_tag_parse (const char *tags) +{ + struct t_hashtable *hashtable; + char **items, *pos, *key, *unescaped; + int num_items, i; + + if (!tags || !tags[0]) + return NULL; + + hashtable = weechat_hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + if (!hashtable) + return NULL; + + items = weechat_string_split (tags, ";", NULL, + WEECHAT_STRING_SPLIT_STRIP_LEFT + | WEECHAT_STRING_SPLIT_STRIP_RIGHT + | WEECHAT_STRING_SPLIT_COLLAPSE_SEPS, + 0, &num_items); + if (items) + { + for (i = 0; i < num_items; i++) + { + pos = strchr (items[i], '='); + if (pos) + { + /* format: "tag=value" */ + key = weechat_strndup (items[i], pos - items[i]); + if (key) + { + unescaped = irc_tag_unescape_value (pos + 1); + weechat_hashtable_set (hashtable, key, unescaped); + if (unescaped) + free (unescaped); + free (key); + } + } + else + { + /* format: "tag" */ + weechat_hashtable_set (hashtable, items[i], NULL); + } + } + weechat_string_free_split (items); + } + + return hashtable; +} diff --git a/src/plugins/irc/irc-tag.h b/src/plugins/irc/irc-tag.h new file mode 100644 index 000000000..2e70668af --- /dev/null +++ b/src/plugins/irc/irc-tag.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_IRC_TAG_H +#define WEECHAT_PLUGIN_IRC_TAG_H + +extern char *irc_tag_escape_value (const char *string); +extern char *irc_tag_unescape_value (const char *string); +extern char *irc_tag_modifier_cb (const void *pointer, + void *data, + const char *modifier, + const char *modifier_data, + const char *string); +extern struct t_hashtable *irc_tag_parse (const char *tags); + +#endif /* WEECHAT_PLUGIN_IRC_TAG_H */ diff --git a/src/plugins/irc/irc.c b/src/plugins/irc/irc.c index e1043aa29..9c1a1a3db 100644 --- a/src/plugins/irc/irc.c +++ b/src/plugins/irc/irc.c @@ -42,6 +42,7 @@ #include "irc-raw.h" #include "irc-redirect.h" #include "irc-server.h" +#include "irc-tag.h" #include "irc-upgrade.h" @@ -213,6 +214,10 @@ weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) &irc_color_modifier_cb, NULL, NULL); weechat_hook_modifier ("irc_color_decode_ansi", &irc_color_modifier_cb, NULL, NULL); + weechat_hook_modifier ("irc_tag_escape_value", + &irc_tag_modifier_cb, NULL, NULL); + weechat_hook_modifier ("irc_tag_unescape_value", + &irc_tag_modifier_cb, NULL, NULL); /* hook completions */ irc_completion_init (); |