summaryrefslogtreecommitdiff
path: root/src/plugins/irc/irc-message.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/irc/irc-message.c')
-rw-r--r--src/plugins/irc/irc-message.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/plugins/irc/irc-message.c b/src/plugins/irc/irc-message.c
index c4314e0f6..28c965642 100644
--- a/src/plugins/irc/irc-message.c
+++ b/src/plugins/irc/irc-message.c
@@ -338,3 +338,523 @@ irc_message_replace_vars (struct t_irc_server *server,
/* return result */
return res;
}
+
+/*
+ * irc_message_split_add: add a message + arguments in hashtable
+ */
+
+void
+irc_message_split_add (struct t_hashtable *hashtable, int number,
+ const char *message, const char *arguments)
+{
+ char key[32];
+
+ if (message)
+ {
+ snprintf (key, sizeof (key), "msg%d", number);
+ weechat_hashtable_set (hashtable, key, message);
+ if (weechat_irc_plugin->debug >= 2)
+ {
+ weechat_printf (NULL,
+ "irc_message_split_add >> %s='%s' (%d bytes)",
+ key, message,
+ strlen (message));
+ }
+ }
+ if (arguments)
+ {
+ snprintf (key, sizeof (key), "args%d", number);
+ weechat_hashtable_set (hashtable, key, arguments);
+ if (weechat_irc_plugin->debug >= 2)
+ {
+ weechat_printf (NULL,
+ "irc_message_split_add >> %s='%s'",
+ key, arguments);
+ }
+ }
+}
+
+/*
+ * irc_message_split_string: split "arguments" using delimiter and max length
+ * messages added to hashtable are:
+ * host + command + target + XXX + suffix
+ * (where XXX is part of "arguments")
+ * return 1 if split ok, 0 if error
+ */
+
+int
+irc_message_split_string (struct t_hashtable *hashtable,
+ const char *host,
+ const char *command,
+ const char *target,
+ const char *prefix,
+ const char *arguments,
+ const char *suffix,
+ const char delimiter,
+ int max_length_host)
+{
+ const char *pos, *pos_max, *pos_next, *pos_last_delim;
+ char message[1024], *dup_arguments;
+ int max_length, number;
+
+ /*
+ * Examples of arguments for this function:
+ *
+ * message..: :nick!user@host.com PRIVMSG #channel :Hello world!
+ * arguments:
+ * host : ":nick!user@host.com"
+ * command : "PRIVMSG"
+ * target : "#channel"
+ * prefix : ":"
+ * arguments: "Hello world!"
+ * suffix : ""
+ *
+ * message..: :nick!user@host.com PRIVMSG #channel :\01ACTION is eating\01
+ * arguments:
+ * host : ":nick!user@host.com"
+ * command : "PRIVMSG"
+ * target : "#channel"
+ * prefix : ":\01ACTION "
+ * arguments: "is eating"
+ * suffix : "\01"
+ */
+
+ max_length = 510;
+ if (max_length_host >= 0)
+ max_length -= max_length_host;
+ else
+ max_length -= (host) ? strlen (host) + 1 : 0;
+ max_length -= strlen (command) + 1;
+ if (target)
+ max_length -= strlen (target);
+ if (prefix)
+ max_length -= strlen (prefix);
+ if (suffix)
+ max_length -= strlen (suffix);
+
+ if (max_length < 2)
+ return 0;
+
+ /* debug message */
+ if (weechat_irc_plugin->debug >= 2)
+ {
+ weechat_printf (NULL,
+ "irc_message_split_string: host='%s', command='%s', "
+ "target='%s', prefix='%s', arguments='%s', "
+ "suffix='%s', max_length=%d",
+ host, command, target, prefix, arguments, suffix,
+ max_length);
+ }
+
+ number = 1;
+
+ if (!arguments || !arguments[0])
+ {
+ snprintf (message, sizeof (message), "%s%s%s %s%s%s%s",
+ (host) ? host : "",
+ (host) ? " " : "",
+ command,
+ (target) ? target : "",
+ (target && target[0]) ? " " : "",
+ (prefix) ? prefix : "",
+ (suffix) ? suffix : "");
+ irc_message_split_add (hashtable, 1, message, "");
+ return 1;
+ }
+
+ while (arguments && arguments[0])
+ {
+ pos = arguments;
+ pos_max = pos + max_length;
+ pos_last_delim = NULL;
+ while (pos && pos[0])
+ {
+ if (pos[0] == delimiter)
+ pos_last_delim = pos;
+ pos_next = weechat_utf8_next_char (pos);
+ if (pos_next > pos_max)
+ break;
+ pos = pos_next;
+ }
+ if (pos[0] && pos_last_delim)
+ pos = pos_last_delim;
+ dup_arguments = weechat_strndup (arguments, pos - arguments);
+ if (dup_arguments)
+ {
+ snprintf (message, sizeof (message), "%s%s%s %s%s%s%s%s",
+ (host) ? host : "",
+ (host) ? " " : "",
+ command,
+ (target) ? target : "",
+ (target && target[0]) ? " " : "",
+ (prefix) ? prefix : "",
+ dup_arguments,
+ (suffix) ? suffix : "");
+ irc_message_split_add (hashtable, number, message, dup_arguments);
+ number++;
+ free (dup_arguments);
+ }
+ arguments = (pos == pos_last_delim) ? pos + 1 : pos;
+ }
+
+ return 1;
+}
+
+/*
+ * irc_message_split_join: split a JOIN message, taking care of keeping
+ * channel keys with channel names
+ * return 1 if split ok, 0 if error
+ */
+
+int
+irc_message_split_join (struct t_hashtable *hashtable,
+ const char *host, const char *arguments)
+{
+ int number, channels_count, keys_count, length, length_no_channel;
+ int length_to_add, index_channel;
+ char **channels, **keys, *pos, *str;
+ char msg_to_send[2048], keys_to_add[2048];
+
+ number = 1;
+
+ channels = NULL;
+ channels_count = 0;
+ keys = NULL;
+ keys_count = 0;
+
+ pos = strchr (arguments, ' ');
+ if (pos)
+ {
+ str = weechat_strndup (arguments, pos - arguments);
+ if (!str)
+ return 0;
+ channels = weechat_string_split (str, ",", 0, 0, &channels_count);
+ free (str);
+ while (pos[0] == ' ')
+ {
+ pos++;
+ }
+ if (pos[0])
+ keys = weechat_string_split (pos, ",", 0, 0, &keys_count);
+ }
+ else
+ {
+ channels = weechat_string_split (arguments, ",", 0, 0, &channels_count);
+ }
+
+ snprintf (msg_to_send, sizeof (msg_to_send), "%s%sJOIN",
+ (host) ? host : "",
+ (host) ? " " : "");
+ length = strlen (msg_to_send);
+ length_no_channel = length;
+ keys_to_add[0] = '\0';
+ index_channel = 0;
+ while (index_channel < channels_count)
+ {
+ length_to_add = 1 + strlen (channels[index_channel]);
+ if (index_channel < keys_count)
+ length_to_add += 1 + strlen (keys[index_channel]);
+ if ((length + length_to_add < 510) || (length == length_no_channel))
+ {
+ if (length + length_to_add < (int)sizeof (msg_to_send))
+ {
+ strcat (msg_to_send, (length == length_no_channel) ? " " : ",");
+ strcat (msg_to_send, channels[index_channel]);
+ }
+ if (index_channel < keys_count)
+ {
+ if (strlen (keys_to_add) + 1 +
+ strlen (keys[index_channel]) < (int)sizeof (keys_to_add))
+ {
+ strcat (keys_to_add, (keys_to_add[0]) ? "," : " ");
+ strcat (keys_to_add, keys[index_channel]);
+ }
+ }
+ length += length_to_add;
+ index_channel++;
+ }
+ else
+ {
+ strcat (msg_to_send, keys_to_add);
+ irc_message_split_add (hashtable, number,
+ msg_to_send,
+ msg_to_send + length_no_channel + 1);
+ number++;
+ snprintf (msg_to_send, sizeof (msg_to_send), "%s%sJOIN",
+ (host) ? host : "",
+ (host) ? " " : "");
+ length = strlen (msg_to_send);
+ keys_to_add[0] = '\0';
+ }
+ }
+
+ if (length > length_no_channel)
+ {
+ strcat (msg_to_send, keys_to_add);
+ irc_message_split_add (hashtable, number,
+ msg_to_send,
+ msg_to_send + length_no_channel + 1);
+ }
+
+ if (channels)
+ weechat_string_free_split (channels);
+ if (keys)
+ weechat_string_free_split (keys);
+
+ return 1;
+}
+
+/*
+ * irc_message_split_privmsg: split a PRIVMSG message, taking care of keeping
+ * the '\01' char used in CTCP messages
+ * return 1 if split ok, 0 if error
+ */
+
+int
+irc_message_split_privmsg (struct t_hashtable *hashtable,
+ char *host, char *command, char *target,
+ char *arguments, int max_length_host)
+{
+ char prefix[512], suffix[2], *pos, saved_char;
+ int length, rc;
+
+ /*
+ * message sent looks like:
+ * PRIVMSG #channel :hello world!
+ *
+ * when IRC server sends message to other people, message looks like:
+ * :nick!user@host.com PRIVMSG #channel :hello world!
+ */
+
+ /* for CTCP, target2 will be '\01xxxx' and suffix '\01' */
+ prefix[0] = '\0';
+ suffix[0] = '\0';
+ length = strlen (arguments);
+ if ((arguments[0] == '\01')
+ && (arguments[length - 1] == '\01'))
+ {
+ pos = strchr (arguments, ' ');
+ if (pos)
+ {
+ pos++;
+ saved_char = pos[0];
+ pos[0] = '\0';
+ snprintf (prefix, sizeof (prefix), ":%s", arguments);
+ pos[0] = saved_char;
+ arguments[length - 1] = '\0';
+ arguments = pos;
+ suffix[0] = '\01';
+ suffix[1] = '\0';
+ }
+ }
+ if (!prefix[0])
+ strcpy (prefix, ":");
+
+ rc = irc_message_split_string (hashtable, host, command, target,
+ prefix, arguments, suffix,
+ ' ', max_length_host);
+
+ return rc;
+}
+
+/*
+ * irc_message_split_005: split a 005 message (isupport)
+ * return 1 if split ok, 0 if error
+ */
+
+int
+irc_message_split_005 (struct t_hashtable *hashtable,
+ char *host, char *command, char *target, char *arguments)
+{
+ char *pos, suffix[512];
+
+ /*
+ * 005 message looks like:
+ * :server 005 mynick MODES=4 CHANLIMIT=#:20 NICKLEN=16 USERLEN=10
+ * HOSTLEN=63 TOPICLEN=450 KICKLEN=450 CHANNELLEN=30 KEYLEN=23
+ * CHANTYPES=# PREFIX=(ov)@+ CASEMAPPING=ascii CAPAB IRCD=dancer
+ * :are available on this server
+ */
+
+ /* search suffix */
+ suffix[0] = '\0';
+ pos = strstr (arguments, " :");
+ if (pos)
+ {
+ snprintf (suffix, sizeof (suffix), "%s", pos);
+ pos[0] = '\0';
+ }
+
+ return irc_message_split_string (hashtable, host, command, target,
+ NULL, arguments, suffix, ' ', -1);
+}
+
+/*
+ * irc_message_split: split an IRC message about to be sent to IRC server
+ * The maximum length of an IRC message is 510 bytes for
+ * user data + final "\r\n", so full size is 512 bytes.
+ * The split takes care about type of message to do a split
+ * at best place in message.
+ * The hashtable returned contains keys "msg1", "msg2", ...,
+ * "msgN" with split of message (these messages do not
+ * include the final "\r\n").
+ * Hashtable contains "args1", "args2", ..., "argsN" with
+ * split of arguments only (no host/command here).
+ * Each message in hashtable has command and arguments, and
+ * then is ready to be sent to IRC server.
+ */
+
+struct t_hashtable *
+irc_message_split (struct t_irc_server *server, const char *message)
+{
+ struct t_hashtable *hashtable;
+ char **argv, **argv_eol, *host, *command, *arguments, target[512];
+ int split_ok, argc, index_args, max_length_nick, max_length_host;
+
+ split_ok = 0;
+ host = NULL;
+ command = NULL;
+ arguments = NULL;
+ index_args = 0;
+
+ /* debug message */
+ if (weechat_irc_plugin->debug >= 2)
+ weechat_printf (NULL, "irc_message_split: message='%s'", message);
+
+ hashtable = weechat_hashtable_new (8,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!hashtable)
+ return NULL;
+
+ if (!message || !message[0])
+ goto end;
+
+ argv = weechat_string_split (message, " ", 0, 0, &argc);
+ argv_eol = weechat_string_split (message, " ", 1, 0, NULL);
+
+ if (argc < 2)
+ goto end;
+
+ if (argv[0][0] == ':')
+ {
+ if (argc < 3)
+ goto end;
+ host = argv[0];
+ command = argv[1];
+ arguments = argv_eol[2];
+ index_args = 2;
+ }
+ else
+ {
+ command = argv[0];
+ arguments = argv_eol[1];
+ index_args = 1;
+ }
+
+ max_length_nick = (server && (server->nick_max_length > 0)) ?
+ server->nick_max_length : 16;
+ max_length_host = 1 + /* ":" */
+ max_length_nick + /* nick */
+ 1 + /* "!" */
+ 63 + /* host */
+ 1; /* " " */
+
+ if ((weechat_strcasecmp (command, "ison") == 0)
+ || (weechat_strcasecmp (command, "wallops") == 0))
+ {
+ split_ok = irc_message_split_string (hashtable, host, command,
+ NULL, ":",
+ (argv_eol[index_args][0] == ':') ?
+ argv_eol[index_args] + 1 : argv_eol[index_args],
+ NULL, ' ', max_length_host);
+ }
+ else if (weechat_strcasecmp (command, "join") == 0)
+ {
+ /* split join (if it's more than 510 bytes) */
+ if (strlen (message) > 510)
+ split_ok = irc_message_split_join (hashtable, host, arguments);
+ }
+ else if (weechat_strcasecmp (command, "notice") == 0)
+ {
+ if (index_args + 1 <= argc - 1)
+ {
+ split_ok = irc_message_split_string (hashtable, host, command,
+ argv[index_args], ":",
+ (argv_eol[index_args + 1][0] == ':') ?
+ argv_eol[index_args + 1] + 1 : argv_eol[index_args + 1],
+ NULL, ' ', max_length_host);
+ }
+ }
+ else if (weechat_strcasecmp (command, "privmsg") == 0)
+ {
+ /* split privmsg */
+ if (index_args + 1 <= argc - 1)
+ {
+ split_ok = irc_message_split_privmsg (hashtable, host, command,
+ argv[index_args],
+ (argv_eol[index_args + 1][0] == ':') ?
+ argv_eol[index_args + 1] + 1 : argv_eol[index_args + 1],
+ max_length_host);
+ }
+ }
+ else if (weechat_strcasecmp (command, "005") == 0)
+ {
+ /* split 005 (isupport) */
+ if (index_args + 1 <= argc - 1)
+ {
+ split_ok = irc_message_split_005 (hashtable, host, command,
+ argv[index_args],
+ (argv_eol[index_args + 1][0] == ':') ?
+ argv_eol[index_args + 1] + 1 : argv_eol[index_args + 1]);
+ }
+ }
+ else if (weechat_strcasecmp (command, "353") == 0)
+ {
+ /*
+ * split 353 (list of users on channel):
+ * :server 353 mynick = #channel :mynick nick1 @nick2 +nick3
+ */
+ if (index_args + 2 <= argc - 1)
+ {
+ if (irc_channel_is_channel (argv[index_args + 1]))
+ {
+ snprintf (target, sizeof (target), "%s %s",
+ argv[index_args], argv[index_args + 1]);
+ split_ok = irc_message_split_string (hashtable, host, command,
+ target, ":",
+ (argv_eol[index_args + 2][0] == ':') ?
+ argv_eol[index_args + 2] + 1 : argv_eol[index_args + 2],
+ NULL, ' ', -1);
+ }
+ else
+ {
+ if (index_args + 3 <= argc - 1)
+ {
+ snprintf (target, sizeof (target), "%s %s %s",
+ argv[index_args], argv[index_args + 1],
+ argv[index_args + 2]);
+ split_ok = irc_message_split_string (hashtable, host, command,
+ target, ":",
+ (argv_eol[index_args + 3][0] == ':') ?
+ argv_eol[index_args + 3] + 1 : argv_eol[index_args + 3],
+ NULL, ' ', -1);
+ }
+ }
+ }
+ }
+
+end:
+ if (!split_ok
+ || (weechat_hashtable_get_integer (hashtable, "items_count") == 0))
+ {
+ irc_message_split_add (hashtable, 1, message, arguments);
+ }
+
+ weechat_string_free_split (argv);
+ weechat_string_free_split (argv_eol);
+
+ return hashtable;
+}