/* * Copyright (c) 2003-2010 by FlashCode * See README for License detail, AUTHORS for developers list. * * This program 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. * * This program 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 this program. If not, see . */ /* irc-ctcp.c: IRC CTCP protocol */ #include #include #include #include #include #include #include "../weechat-plugin.h" #include "irc.h" #include "irc-ctcp.h" #include "irc-channel.h" #include "irc-color.h" #include "irc-config.h" #include "irc-nick.h" #include "irc-protocol.h" #include "irc-server.h" struct t_irc_ctcp_reply irc_ctcp_default_reply[] = { { "clientinfo", "$clientinfo" }, { "finger", "WeeChat $version" }, { "source", "$download" }, { "time", "$time" }, { "userinfo", "$username ($realname)" }, { "version", "WeeChat $version ($compilation)" }, { NULL, NULL }, }; /* * irc_ctcp_get_default_reply: get default reply for a CTCP query * return NULL if CTCP is unknown */ const char * irc_ctcp_get_default_reply (const char *ctcp) { int i; for (i = 0; irc_ctcp_default_reply[i].name; i++) { if (weechat_strcasecmp (irc_ctcp_default_reply[i].name, ctcp) == 0) return irc_ctcp_default_reply[i].reply; } /* unknown CTCP */ return NULL; } /* * irc_ctcp_get_reply: get reply for a CTCP query */ const char * irc_ctcp_get_reply (struct t_irc_server *server, const char *ctcp) { struct t_config_option *ptr_option; char option_name[512]; snprintf (option_name, sizeof (option_name), "%s.%s", server->name, ctcp); /* search for CTCP in config file, for server */ ptr_option = weechat_config_search_option (irc_config_file, irc_config_section_ctcp, option_name); if (ptr_option) return weechat_config_string (ptr_option); /* search for CTCP in config file */ ptr_option = weechat_config_search_option (irc_config_file, irc_config_section_ctcp, ctcp); if (ptr_option) return weechat_config_string (ptr_option); /* * no CTCP reply found in config, then return default reply, or NULL * for unknown CTCP */ return irc_ctcp_get_default_reply (ctcp); } /* * irc_ctcp_display_request: display CTCP requested by a nick */ void irc_ctcp_display_request (struct t_irc_server *server, const char *command, struct t_irc_channel *channel, const char *nick, const char *ctcp, const char *arguments, const char *reply) { /* CTCP blocked and user doesn't want to see message? then just return */ if (reply && !reply[0] && !weechat_config_boolean (irc_config_look_display_ctcp_blocked)) return; weechat_printf_tags ((channel) ? channel->buffer : server->buffer, irc_protocol_tags (command, "irc_ctcp"), _("%sCTCP requested by %s%s%s: %s%s%s%s%s%s"), weechat_prefix ("network"), IRC_COLOR_CHAT_NICK, nick, IRC_COLOR_CHAT, IRC_COLOR_CHAT_CHANNEL, ctcp, IRC_COLOR_CHAT, (arguments) ? " " : "", (arguments) ? arguments : "", (reply && !reply[0]) ? _(" (blocked)") : ""); } /* * irc_ctcp_display_reply_from_nick: display reply from a nick to a CTCP query */ void irc_ctcp_display_reply_from_nick (struct t_irc_server *server, const char *command, const char *nick, char *arguments) { char *pos_end, *pos_space, *pos_args, *pos_usec; struct timeval tv; long sec1, usec1, sec2, usec2, difftime; while (arguments && arguments[0]) { pos_end = strchr (arguments + 1, '\01'); if (pos_end) pos_end[0] = '\0'; pos_space = strchr (arguments + 1, ' '); if (pos_space) { pos_space[0] = '\0'; pos_args = pos_space + 1; while (pos_args[0] == ' ') { pos_args++; } if (strcmp (arguments + 1, "PING") == 0) { pos_usec = strchr (pos_args, ' '); if (pos_usec) { pos_usec[0] = '\0'; gettimeofday (&tv, NULL); sec1 = atol (pos_args); usec1 = atol (pos_usec + 1); sec2 = tv.tv_sec; usec2 = tv.tv_usec; difftime = ((sec2 * 1000000) + usec2) - ((sec1 * 1000000) + usec1); weechat_printf_tags (server->buffer, irc_protocol_tags (command, "irc_ctcp"), _("%sCTCP reply from %s%s%s: %s%s%s " "%ld.%ld %s"), weechat_prefix ("network"), IRC_COLOR_CHAT_NICK, nick, IRC_COLOR_CHAT, IRC_COLOR_CHAT_CHANNEL, arguments + 1, IRC_COLOR_CHAT, difftime / 1000000, (difftime % 1000000) / 1000, (NG_("second", "seconds", (difftime / 1000000)))); pos_usec[0] = ' '; } } else { weechat_printf_tags (server->buffer, irc_protocol_tags (command, "irc_ctcp"), _("%sCTCP reply from %s%s%s: %s%s%s%s%s"), weechat_prefix ("network"), IRC_COLOR_CHAT_NICK, nick, IRC_COLOR_CHAT, IRC_COLOR_CHAT_CHANNEL, arguments + 1, IRC_COLOR_CHAT, " ", pos_args); } pos_space[0] = ' '; } else { weechat_printf_tags (server->buffer, irc_protocol_tags (command, NULL), _("%sCTCP reply from %s%s%s: %s%s%s%s%s"), weechat_prefix ("network"), IRC_COLOR_CHAT_NICK, nick, IRC_COLOR_CHAT, IRC_COLOR_CHAT_CHANNEL, arguments + 1, "", "", ""); } if (pos_end) pos_end[0] = '\01'; arguments = (pos_end) ? pos_end + 1 : NULL; } } /* * irc_ctcp_reply_to_nick: display CTCP replied to a nick */ void irc_ctcp_reply_to_nick (struct t_irc_server *server, const char *command, struct t_irc_channel *channel, const char *nick, const char *ctcp, const char *arguments) { irc_server_sendf (server, IRC_SERVER_OUTQUEUE_PRIO_LOW, "NOTICE %s :\01%s%s%s\01", nick, ctcp, (arguments) ? " " : "", (arguments) ? arguments : ""); if (weechat_config_boolean (irc_config_look_display_ctcp_reply)) { weechat_printf_tags ((channel) ? channel->buffer : server->buffer, irc_protocol_tags (command, "irc_ctcp,irc_ctcp_reply"), _("%sCTCP reply to %s%s%s: %s%s%s%s%s"), weechat_prefix ("network"), IRC_COLOR_CHAT_NICK, nick, IRC_COLOR_CHAT, IRC_COLOR_CHAT_CHANNEL, ctcp, (arguments) ? IRC_COLOR_CHAT : "", (arguments) ? " " : "", (arguments) ? arguments : ""); } } /* * irc_ctcp_replace_variables: replace variables in CTCP format */ char * irc_ctcp_replace_variables (struct t_irc_server *server, const char *format) { char *res, *temp; const char *info; time_t now; char buf[1024]; struct utsname *buf_uname; /* clientinfo */ temp = weechat_string_replace (format, "$clientinfo", "ACTION DCC CLIENTINFO FINGER PING SOURCE " "TIME USERINFO VERSION"); if (!temp) return NULL; res = temp; /* version */ info = weechat_info_get ("version", ""); temp = weechat_string_replace (res, "$version", info); free (res); if (!temp) return NULL; res = temp; /* compilation date */ info = weechat_info_get ("date", ""); temp = weechat_string_replace (res, "$compilation", info); free (res); if (!temp) return NULL; res = temp; /* info about OS */ buf_uname = (struct utsname *)malloc (sizeof (struct utsname)); if (buf_uname && (uname (buf_uname) >= 0)) { snprintf (buf, sizeof (buf), "%s %s / %s", buf_uname->sysname, buf_uname->release, buf_uname->machine); free (buf_uname); temp = weechat_string_replace (res, "$osinfo", buf); free (res); if (!temp) return NULL; res = temp; } /* site */ info = weechat_info_get ("weechat_site", ""); temp = weechat_string_replace (res, "$site", info); free (res); if (!temp) return NULL; res = temp; /* site (download page) */ info = weechat_info_get ("weechat_site_download", ""); temp = weechat_string_replace (res, "$download", info); free (res); if (!temp) return NULL; res = temp; /* time */ now = time (NULL); snprintf (buf, sizeof (buf), "%s", ctime (&now)); buf[strlen (buf) - 1] = '\0'; temp = weechat_string_replace (res, "$time", buf); free (res); if (!temp) return NULL; res = temp; /* username */ temp = weechat_string_replace (res, "$username", IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_USERNAME)); free (res); if (!temp) return NULL; res = temp; /* realname */ temp = weechat_string_replace (res, "$realname", IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_REALNAME)); free (res); if (!temp) return NULL; res = temp; /* return result */ return res; } /* * irc_ctcp_recv_dcc: parse CTCP DCC */ void irc_ctcp_recv_dcc (struct t_irc_server *server, const char *nick, const char *arguments, char *message) { char *dcc_args, *pos, *pos_file, *pos_addr, *pos_port, *pos_size, *pos_start_resume; struct t_infolist *infolist; struct t_infolist_item *item; char plugin_id[128], charset_modifier[256]; if (!arguments || !arguments[0]) return; if (strncmp (arguments, "SEND ", 5) == 0) { arguments += 5; while (arguments[0] == ' ') { arguments++; } dcc_args = strdup (arguments); if (!dcc_args) { weechat_printf (server->buffer, _("%s%s: not enough memory for \"%s\" " "command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); return; } /* DCC filename */ pos_file = dcc_args; while (pos_file[0] == ' ') { pos_file++; } /* look for file size */ pos_size = strrchr (pos_file, ' '); if (!pos_size) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos = pos_size; pos_size++; while (pos[0] == ' ') { pos--; } pos[1] = '\0'; /* look for DCC port */ pos_port = strrchr (pos_file, ' '); if (!pos_port) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos = pos_port; pos_port++; while (pos[0] == ' ') { pos--; } pos[1] = '\0'; /* look for DCC IP address */ pos_addr = strrchr (pos_file, ' '); if (!pos_addr) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos = pos_addr; pos_addr++; while (pos[0] == ' ') { pos--; } pos[1] = '\0'; /* add DCC file via xfer plugin */ infolist = weechat_infolist_new (); if (infolist) { item = weechat_infolist_new_item (infolist); if (item) { weechat_infolist_new_var_string (item, "plugin_name", weechat_plugin->name); snprintf (plugin_id, sizeof (plugin_id), "%lx", (long unsigned int)server); weechat_infolist_new_var_string (item, "plugin_id", plugin_id); weechat_infolist_new_var_string (item, "type", "file_recv"); weechat_infolist_new_var_string (item, "protocol", "dcc"); weechat_infolist_new_var_string (item, "remote_nick", nick); weechat_infolist_new_var_string (item, "local_nick", server->nick); weechat_infolist_new_var_string (item, "filename", pos_file); weechat_infolist_new_var_string (item, "size", pos_size); weechat_infolist_new_var_string (item, "proxy", IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PROXY)); weechat_infolist_new_var_string (item, "address", pos_addr); weechat_infolist_new_var_integer (item, "port", atoi (pos_port)); weechat_hook_signal_send ("xfer_add", WEECHAT_HOOK_SIGNAL_POINTER, infolist); } weechat_infolist_free (infolist); } weechat_hook_signal_send ("irc_dcc", WEECHAT_HOOK_SIGNAL_STRING, message); free (dcc_args); } else if (strncmp (arguments, "RESUME ", 7) == 0) { arguments += 7; while (arguments[0] == ' ') { arguments++; } dcc_args = strdup (arguments); if (!dcc_args) { weechat_printf (server->buffer, _("%s%s: not enough memory for \"%s\" " "command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); return; } /* DCC filename */ pos_file = dcc_args; while (pos_file[0] == ' ') { pos_file++; } /* look for resume start position */ pos_start_resume = strrchr (pos_file, ' '); if (!pos_start_resume) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos = pos_start_resume; pos_start_resume++; while (pos[0] == ' ') { pos--; } pos[1] = '\0'; /* look for DCC port */ pos_port = strrchr (pos_file, ' '); if (!pos_port) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos = pos_port; pos_port++; while (pos[0] == ' ') { pos--; } pos[1] = '\0'; /* accept resume via xfer plugin */ infolist = weechat_infolist_new (); if (infolist) { item = weechat_infolist_new_item (infolist); if (item) { weechat_infolist_new_var_string (item, "plugin_name", weechat_plugin->name); snprintf (plugin_id, sizeof (plugin_id), "%lx", (long unsigned int)server); weechat_infolist_new_var_string (item, "plugin_id", plugin_id); weechat_infolist_new_var_string (item, "type", "file_recv"); weechat_infolist_new_var_string (item, "filename", pos_file); weechat_infolist_new_var_integer (item, "port", atoi (pos_port)); weechat_infolist_new_var_string (item, "start_resume", pos_start_resume); weechat_hook_signal_send ("xfer_accept_resume", WEECHAT_HOOK_SIGNAL_POINTER, infolist); } weechat_infolist_free (infolist); } weechat_hook_signal_send ("irc_dcc", WEECHAT_HOOK_SIGNAL_STRING, message); free (dcc_args); } else if (strncmp (arguments, "ACCEPT ", 7) == 0) { arguments += 7; while (arguments[0] == ' ') { arguments++; } dcc_args = strdup (arguments); if (!dcc_args) { weechat_printf (server->buffer, _("%s%s: not enough memory for \"%s\" " "command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); return; } /* DCC filename */ pos_file = dcc_args; while (pos_file[0] == ' ') { pos_file++; } /* look for resume start position */ pos_start_resume = strrchr (pos_file, ' '); if (!pos_start_resume) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos = pos_start_resume; pos_start_resume++; while (pos[0] == ' ') { pos--; } pos[1] = '\0'; /* look for DCC port */ pos_port = strrchr (pos_file, ' '); if (!pos_port) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos = pos_port; pos_port++; while (pos[0] == ' ') { pos--; } pos[1] = '\0'; /* resume file via xfer plugin */ infolist = weechat_infolist_new (); if (infolist) { item = weechat_infolist_new_item (infolist); if (item) { weechat_infolist_new_var_string (item, "plugin_name", weechat_plugin->name); snprintf (plugin_id, sizeof (plugin_id), "%lx", (long unsigned int)server); weechat_infolist_new_var_string (item, "plugin_id", plugin_id); weechat_infolist_new_var_string (item, "type", "file_recv"); weechat_infolist_new_var_string (item, "filename", pos_file); weechat_infolist_new_var_integer (item, "port", atoi (pos_port)); weechat_infolist_new_var_string (item, "start_resume", pos_start_resume); weechat_hook_signal_send ("xfer_start_resume", WEECHAT_HOOK_SIGNAL_POINTER, infolist); } weechat_infolist_free (infolist); } weechat_hook_signal_send ("irc_dcc", WEECHAT_HOOK_SIGNAL_STRING, message); free (dcc_args); } else if (strncmp (arguments, "CHAT ", 5) == 0) { arguments += 5; while (arguments[0] == ' ') { arguments++; } dcc_args = strdup (arguments); if (!dcc_args) { weechat_printf (server->buffer, _("%s%s: not enough memory for \"%s\" " "command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); return; } /* CHAT type */ pos_file = dcc_args; while (pos_file[0] == ' ') { pos_file++; } /* DCC IP address */ pos_addr = strchr (pos_file, ' '); if (!pos_addr) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos_addr[0] = '\0'; pos_addr++; while (pos_addr[0] == ' ') { pos_addr++; } /* look for DCC port */ pos_port = strchr (pos_addr, ' '); if (!pos_port) { weechat_printf (server->buffer, _("%s%s: cannot parse \"%s\" command"), weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg"); free (dcc_args); return; } pos_port[0] = '\0'; pos_port++; while (pos_port[0] == ' ') { pos_port++; } if (weechat_strcasecmp (pos_file, "chat") != 0) { weechat_printf (server->buffer, _("%s%s: unknown DCC CHAT type " "received from %s%s%s: \"%s\""), weechat_prefix ("error"), IRC_PLUGIN_NAME, IRC_COLOR_CHAT_NICK, nick, IRC_COLOR_CHAT, pos_file); free (dcc_args); return; } /* add DCC chat via xfer plugin */ infolist = weechat_infolist_new (); if (infolist) { item = weechat_infolist_new_item (infolist); if (item) { weechat_infolist_new_var_string (item, "plugin_name", weechat_plugin->name); snprintf (plugin_id, sizeof (plugin_id), "%lx", (long unsigned int)server); weechat_infolist_new_var_string (item, "plugin_id", plugin_id); weechat_infolist_new_var_string (item, "type", "chat_recv"); weechat_infolist_new_var_string (item, "remote_nick", nick); weechat_infolist_new_var_string (item, "local_nick", server->nick); snprintf (charset_modifier, sizeof (charset_modifier), "irc.%s.%s", server->name, nick); weechat_infolist_new_var_string (item, "charset_modifier", charset_modifier); weechat_infolist_new_var_string (item, "proxy", IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PROXY)); weechat_infolist_new_var_string (item, "address", pos_addr); weechat_infolist_new_var_integer (item, "port", atoi (pos_port)); weechat_hook_signal_send ("xfer_add", WEECHAT_HOOK_SIGNAL_POINTER, infolist); } weechat_infolist_free (infolist); } weechat_hook_signal_send ("irc_dcc", WEECHAT_HOOK_SIGNAL_STRING, message); free (dcc_args); } } /* * irc_ctcp_recv: receive a CTCP and if needed reply to query */ void irc_ctcp_recv (struct t_irc_server *server, const char *command, struct t_irc_channel *channel, const char *address, const char *nick, const char *remote_nick, char *arguments, char *message) { char *pos_end, *pos_space, *pos_args; const char *reply; char *decoded_reply; struct t_irc_channel *ptr_channel; struct t_irc_nick *ptr_nick; int nick_is_me; while (arguments && arguments[0]) { pos_end = strrchr (arguments + 1, '\01'); if (pos_end) pos_end[0] = '\0'; pos_args = NULL; pos_space = strchr (arguments + 1, ' '); if (pos_space) { pos_space[0] = '\0'; pos_args = pos_space + 1; while (pos_args[0] == ' ') { pos_args++; } } /* CTCP ACTION */ if (strcmp (arguments + 1, "ACTION") == 0) { if (channel) { ptr_nick = irc_nick_search (channel, nick); irc_channel_nick_speaking_add (channel, nick, (pos_args) ? weechat_string_has_highlight (pos_args, server->nick) : 0); irc_channel_nick_speaking_time_remove_old (channel); irc_channel_nick_speaking_time_add (channel, nick, time (NULL)); weechat_printf_tags (channel->buffer, irc_protocol_tags (command, "irc_action,notify_message"), "%s%s%s%s%s%s", weechat_prefix ("action"), (ptr_nick) ? ptr_nick->color : IRC_COLOR_CHAT_NICK, nick, (pos_args) ? IRC_COLOR_CHAT : "", (pos_args) ? " " : "", (pos_args) ? pos_args : ""); } else { nick_is_me = (strcmp (server->nick, nick) == 0); ptr_channel = irc_channel_search (server, remote_nick); if (!ptr_channel) { ptr_channel = irc_channel_new (server, IRC_CHANNEL_TYPE_PRIVATE, remote_nick, 0, 0); if (!ptr_channel) { weechat_printf (server->buffer, _("%s%s: cannot create new " "private buffer \"%s\""), weechat_prefix ("error"), IRC_PLUGIN_NAME, remote_nick); } } if (ptr_channel) { if (!ptr_channel->topic) irc_channel_set_topic (ptr_channel, address); weechat_printf_tags (ptr_channel->buffer, irc_protocol_tags (command, (nick_is_me) ? "irc_action,notify_private,no_highlight" : "irc_action,notify_private"), "%s%s%s%s%s%s", weechat_prefix ("action"), (nick_is_me) ? IRC_COLOR_CHAT_NICK_SELF : irc_nick_color_for_pv (ptr_channel, nick), nick, (pos_args) ? IRC_COLOR_CHAT : "", (pos_args) ? " " : "", (pos_args) ? pos_args : ""); weechat_hook_signal_send ("irc_pv", WEECHAT_HOOK_SIGNAL_STRING, message); } } } /* CTCP PING */ else if (strcmp (arguments + 1, "PING") == 0) { reply = irc_ctcp_get_reply (server, arguments + 1); irc_ctcp_display_request (server, command, channel, nick, arguments + 1, pos_args, reply); if (!reply || reply[0]) { irc_ctcp_reply_to_nick (server, command, channel, nick, arguments + 1, pos_args); } } /* CTCP DCC */ else if (strcmp (arguments + 1, "DCC") == 0) { irc_ctcp_recv_dcc (server, nick, pos_args, message); } /* other CTCP */ else { reply = irc_ctcp_get_reply (server, arguments + 1); if (reply) { irc_ctcp_display_request (server, command, channel, nick, arguments + 1, pos_args, reply); if (reply[0]) { decoded_reply = irc_ctcp_replace_variables (server, reply); if (decoded_reply) { irc_ctcp_reply_to_nick (server, command, channel, nick, arguments + 1, decoded_reply); free (decoded_reply); } } } else { if (weechat_config_boolean (irc_config_look_display_ctcp_unknown)) { weechat_printf_tags ((channel) ? channel->buffer : server->buffer, irc_protocol_tags (command, "irc_ctcp"), _("%sUnknown CTCP requested by %s%s%s: " "%s%s%s%s%s"), weechat_prefix ("network"), IRC_COLOR_CHAT_NICK, nick, IRC_COLOR_CHAT, IRC_COLOR_CHAT_CHANNEL, arguments + 1, (pos_args) ? IRC_COLOR_CHAT : "", (pos_args) ? " " : "", (pos_args) ? pos_args : ""); } } } weechat_hook_signal_send ("irc_ctcp", WEECHAT_HOOK_SIGNAL_STRING, message); if (pos_space) pos_space[0] = ' '; if (pos_end) pos_end[0] = '\01'; arguments = (pos_end) ? pos_end + 1 : NULL; } }