diff options
Diffstat (limited to 'src/plugins/irc/irc-message.c')
-rw-r--r-- | src/plugins/irc/irc-message.c | 520 |
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; +} |