1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-28 05:46:38 +02:00

irc: escape/unescape IRC message tags values (issue #1654)

Spec: https://ircv3.net/specs/extensions/message-tags#escaping-values
This commit is contained in:
Sébastien Helleu
2021-06-21 21:26:45 +02:00
parent b3b4ef648b
commit 23c46c3f2b
22 changed files with 564 additions and 97 deletions
+1
View File
@@ -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 "")
+2
View File
@@ -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
+3 -62
View File
@@ -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"
/*
@@ -199,66 +200,6 @@ irc_protocol_nick_address (struct t_irc_server *server,
return string;
}
/*
* 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.
*
@@ -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 (
+280
View File
@@ -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;
}
+32
View File
@@ -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 */
+5
View File
@@ -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 ();