diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/irc/CMakeLists.txt | 25 | ||||
-rw-r--r-- | src/plugins/irc/Makefile.am | 31 | ||||
-rw-r--r-- | src/plugins/irc/irc-channel.c | 469 | ||||
-rw-r--r-- | src/plugins/irc/irc-commands.c | 514 | ||||
-rw-r--r-- | src/plugins/irc/irc-dcc.c | 1939 | ||||
-rw-r--r-- | src/plugins/irc/irc-display.c | 487 | ||||
-rw-r--r-- | src/plugins/irc/irc-ignore.c | 440 | ||||
-rw-r--r-- | src/plugins/irc/irc-mode.c | 316 | ||||
-rw-r--r-- | src/plugins/irc/irc-nick.c | 454 | ||||
-rw-r--r-- | src/plugins/irc/irc-recv.c | 5058 | ||||
-rw-r--r-- | src/plugins/irc/irc-send.c | 2281 | ||||
-rw-r--r-- | src/plugins/irc/irc-server.c | 2415 | ||||
-rw-r--r-- | src/plugins/irc/irc.h | 613 |
13 files changed, 15042 insertions, 0 deletions
diff --git a/src/plugins/irc/CMakeLists.txt b/src/plugins/irc/CMakeLists.txt new file mode 100644 index 000000000..1942e6ceb --- /dev/null +++ b/src/plugins/irc/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2003-2007 FlashCode <flashcode@flashtux.org> +# +# 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 <http://www.gnu.org/licenses/>. +# + +SET(LIB_IRC_SRC irc.h irc-commands.c irc-send.c irc-recv.c irc-server.c +irc-channel.c irc-nick.c irc-mode.c irc-dcc.c irc-ignore.c irc-display.c) + +CHECK_INCLUDE_FILES("regex.h" HAVE_REGEX_H) +CHECK_FUNCTION_EXISTS(regexec HAVE_REGEXEC) +CHECK_FUNCTION_EXISTS(uname HAVE_UNAME) + +INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}) +ADD_LIBRARY(weechat_irc STATIC ${LIB_IRC_SRC}) diff --git a/src/plugins/irc/Makefile.am b/src/plugins/irc/Makefile.am new file mode 100644 index 000000000..2670aca85 --- /dev/null +++ b/src/plugins/irc/Makefile.am @@ -0,0 +1,31 @@ +# Copyright (c) 2003-2007 FlashCode <flashcode@flashtux.org> +# +# 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 <http://www.gnu.org/licenses/>. +# + +INCLUDES = -DLOCALEDIR=\"$(datadir)/locale\" $(GNUTLS_CFLAGS) + +noinst_LIBRARIES = lib_weechat_irc.a + +lib_weechat_irc_a_SOURCES = irc.h \ + irc-commands.c \ + irc-send.c \ + irc-recv.c \ + irc-server.c \ + irc-channel.c \ + irc-nick.c \ + irc-mode.c \ + irc-dcc.c \ + irc-ignore.c \ + irc-display.c diff --git a/src/plugins/irc/irc-channel.c b/src/plugins/irc/irc-channel.c new file mode 100644 index 000000000..ab5dfa63d --- /dev/null +++ b/src/plugins/irc/irc-channel.c @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-channel.c: manages a chat (channel or private chat) */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/log.h" +#include "../../common/utf8.h" +#include "../../common/util.h" +#include "../../common/weeconfig.h" +#include "../../gui/gui.h" + + +/* + * irc_channel_new: allocate a new channel for a server and add it to the + * server queue + */ + +t_irc_channel * +irc_channel_new (t_irc_server *server, int channel_type, char *channel_name) +{ + t_irc_channel *new_channel; + + /* alloc memory for new channel */ + if ((new_channel = (t_irc_channel *) malloc (sizeof (t_irc_channel))) == NULL) + { + fprintf (stderr, _("%s cannot allocate new channel"), WEECHAT_ERROR); + return NULL; + } + + /* initialize new channel */ + new_channel->type = channel_type; + new_channel->dcc_chat = NULL; + new_channel->name = strdup (channel_name); + new_channel->topic = NULL; + new_channel->modes = NULL; + new_channel->limit = 0; + new_channel->key = NULL; + new_channel->nicks_count = 0; + new_channel->checking_away = 0; + new_channel->away_message = NULL; + new_channel->cycle = 0; + new_channel->close = 0; + new_channel->display_creation_date = 0; + new_channel->nick_completion_reset = 0; + new_channel->nicks = NULL; + new_channel->last_nick = NULL; + new_channel->buffer = NULL; + new_channel->nicks_speaking = NULL; + new_channel->last_nick_speaking = NULL; + + /* add new channel to queue */ + new_channel->prev_channel = server->last_channel; + new_channel->next_channel = NULL; + if (server->channels) + server->last_channel->next_channel = new_channel; + else + server->channels = new_channel; + server->last_channel = new_channel; + + /* all is ok, return address of new channel */ + return new_channel; +} + +/* + * irc_channel_free: free a channel and remove it from channels queue + */ + +void +irc_channel_free (t_irc_server *server, t_irc_channel *channel) +{ + t_irc_channel *new_channels; + + if (!server || !channel) + return; + + /* close DCC CHAT */ + if (channel->dcc_chat) + { + ((t_irc_dcc *)(channel->dcc_chat))->channel = NULL; + if (!IRC_DCC_ENDED(((t_irc_dcc *)(channel->dcc_chat))->status)) + { + irc_dcc_close ((t_irc_dcc *)(channel->dcc_chat), IRC_DCC_ABORTED); + irc_dcc_redraw (1); + } + } + + /* remove channel from queue */ + if (server->last_channel == channel) + server->last_channel = channel->prev_channel; + if (channel->prev_channel) + { + (channel->prev_channel)->next_channel = channel->next_channel; + new_channels = server->channels; + } + else + new_channels = channel->next_channel; + + if (channel->next_channel) + (channel->next_channel)->prev_channel = channel->prev_channel; + + /* free data */ + if (channel->name) + free (channel->name); + if (channel->topic) + free (channel->topic); + if (channel->modes) + free (channel->modes); + if (channel->key) + free (channel->key); + irc_nick_free_all (channel); + if (channel->away_message) + free (channel->away_message); + if (channel->nicks_speaking) + weelist_remove_all (&(channel->nicks_speaking), + &(channel->last_nick_speaking)); + free (channel); + server->channels = new_channels; +} + +/* + * irc_channel_free_all: free all allocated channels + */ + +void +irc_channel_free_all (t_irc_server *server) +{ + /* remove all channels for the server */ + while (server->channels) + irc_channel_free (server, server->channels); +} + +/* + * irc_channel_search: returns pointer on a channel with name + * WARNING: DCC chat channels are not returned by this function + */ + +t_irc_channel * +irc_channel_search (t_irc_server *server, char *channel_name) +{ + t_irc_channel *ptr_channel; + + if (!server || !channel_name) + return NULL; + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if ((ptr_channel->type != IRC_CHANNEL_TYPE_DCC_CHAT) + && (ascii_strcasecmp (ptr_channel->name, channel_name) == 0)) + return ptr_channel; + } + return NULL; +} + +/* + * irc_channel_search_any: returns pointer on a channel with name + */ + +t_irc_channel * +irc_channel_search_any (t_irc_server *server, char *channel_name) +{ + t_irc_channel *ptr_channel; + + if (!server || !channel_name) + return NULL; + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ascii_strcasecmp (ptr_channel->name, channel_name) == 0) + return ptr_channel; + } + return NULL; +} + +/* + * irc_channel_search_any_without_buffer: returns pointer on a channel with name + * looks only for channels without buffer + */ + +t_irc_channel * +irc_channel_search_any_without_buffer (t_irc_server *server, char *channel_name) +{ + t_irc_channel *ptr_channel; + + if (!server || !channel_name) + return NULL; + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (!ptr_channel->buffer + && (ascii_strcasecmp (ptr_channel->name, channel_name) == 0)) + return ptr_channel; + } + return NULL; +} + +/* + * irc_channel_search_dcc: returns pointer on a DCC chat channel with name + */ + +t_irc_channel * +irc_channel_search_dcc (t_irc_server *server, char *channel_name) +{ + t_irc_channel *ptr_channel; + + if (!server || !channel_name) + return NULL; + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if ((ptr_channel->type == IRC_CHANNEL_TYPE_DCC_CHAT) + && (ascii_strcasecmp (ptr_channel->name, channel_name) == 0)) + return ptr_channel; + } + return NULL; +} + +/* + * irc_channel_is_channel: returns 1 if string is channel + */ + +int +irc_channel_is_channel (char *string) +{ + char first_char[2]; + + if (!string) + return 0; + + first_char[0] = string[0]; + first_char[1] = '\0'; + return (strpbrk (first_char, IRC_CHANNEL_PREFIX)) ? 1 : 0; +} + +/* + * irc_channel_remove_away: remove away for all nicks on a channel + */ + +void +irc_channel_remove_away (t_irc_channel *channel) +{ + t_irc_nick *ptr_nick; + + if (channel->type == IRC_CHANNEL_TYPE_CHANNEL) + { + for (ptr_nick = channel->nicks; ptr_nick; ptr_nick = ptr_nick->next_nick) + { + IRC_NICK_SET_FLAG(ptr_nick, 0, IRC_NICK_AWAY); + } + gui_nicklist_draw (channel->buffer, 0, 0); + } +} + +/* + * irc_channel_check_away: check for away on a channel + */ + +void +irc_channel_check_away (t_irc_server *server, t_irc_channel *channel, int force) +{ + if (channel->type == IRC_CHANNEL_TYPE_CHANNEL) + { + if (force || (cfg_irc_away_check_max_nicks == 0) || + (channel->nicks_count <= cfg_irc_away_check_max_nicks)) + { + channel->checking_away++; + irc_server_sendf (server, "WHO %s", channel->name); + } + else + irc_channel_remove_away (channel); + } +} + +/* + * irc_channel_set_away: set/unset away status for a channel + */ + +void +irc_channel_set_away (t_irc_channel *channel, char *nick, int is_away) +{ + t_irc_nick *ptr_nick; + + if (channel->type == IRC_CHANNEL_TYPE_CHANNEL) + { + ptr_nick = irc_nick_search (channel, nick); + if (ptr_nick) + irc_nick_set_away (channel, ptr_nick, is_away); + } +} + +/* + * irc_channel_create_dcc: create DCC CHAT channel + */ + +int +irc_channel_create_dcc (t_irc_dcc *ptr_dcc) +{ + t_irc_channel *ptr_channel; + + ptr_channel = irc_channel_search_dcc (ptr_dcc->server, ptr_dcc->nick); + if (!ptr_channel) + { + ptr_channel = irc_channel_new (ptr_dcc->server, + IRC_CHANNEL_TYPE_DCC_CHAT, + ptr_dcc->nick); + if (!ptr_channel) + return 0; + gui_buffer_new (gui_current_window, ptr_dcc->server, ptr_channel, + GUI_BUFFER_TYPE_STANDARD, 0); + } + + if (ptr_channel->dcc_chat && + (!IRC_DCC_ENDED(((t_irc_dcc *)(ptr_channel->dcc_chat))->status))) + return 0; + + ptr_channel->dcc_chat = ptr_dcc; + ptr_dcc->channel = ptr_channel; + gui_window_redraw_buffer (ptr_channel->buffer); + return 1; +} + +/* + * irc_channel_get_notify_level: get channel notify level + */ + +int +irc_channel_get_notify_level (t_irc_server *server, t_irc_channel *channel) +{ + char *name, *pos, *pos2; + int server_default_notify, notify; + + if ((!server) || (!channel)) + return GUI_NOTIFY_LEVEL_DEFAULT; + + if ((!server->notify_levels) || (!server->notify_levels[0])) + return GUI_NOTIFY_LEVEL_DEFAULT; + + server_default_notify = irc_server_get_default_notify_level (server); + if ((channel->type != IRC_CHANNEL_TYPE_CHANNEL) + && (server_default_notify == 1)) + server_default_notify = 2; + + name = (char *) malloc (strlen (channel->name) + 2); + strcpy (name, channel->name); + strcat (name, ":"); + pos = strstr (server->notify_levels, name); + free (name); + if (!pos) + return server_default_notify; + + pos2 = pos + strlen (channel->name); + if (pos2[0] != ':') + return server_default_notify; + pos2++; + if (!pos2[0]) + return server_default_notify; + + notify = (int)(pos2[0] - '0'); + if ((notify >= GUI_NOTIFY_LEVEL_MIN) && (notify <= GUI_NOTIFY_LEVEL_MAX)) + return notify; + + return server_default_notify; +} + +/* + * irc_channel_set_notify_level: set channel notify level + */ + +void +irc_channel_set_notify_level (t_irc_server *server, t_irc_channel *channel, + int notify) +{ + char level_string[2]; + + if ((!server) || (!channel)) + return; + + level_string[0] = notify + '0'; + level_string[1] = '\0'; + config_option_list_set (&(server->notify_levels), channel->name, level_string); +} + +/* + * irc_channel_add_nick_speaking: add a nick speaking on a channel + */ + +void +irc_channel_add_nick_speaking (t_irc_channel *channel, char *nick) +{ + int size, to_remove, i; + + weelist_add (&(channel->nicks_speaking), &(channel->last_nick_speaking), + nick, WEELIST_POS_END); + + size = weelist_get_size (channel->nicks_speaking); + if (size > IRC_CHANNEL_NICKS_SPEAKING_LIMIT) + { + to_remove = size - IRC_CHANNEL_NICKS_SPEAKING_LIMIT; + for (i = 0; i < to_remove; i++) + { + weelist_remove (&(channel->nicks_speaking), + &(channel->last_nick_speaking), + channel->nicks_speaking); + } + } +} + +/* + * irc_channel_print_log: print channel infos in log (usually for crash dump) + */ + +void +irc_channel_print_log (t_irc_channel *channel) +{ + weechat_log_printf ("=> channel %s (addr:0x%X)]\n", channel->name, channel); + weechat_log_printf (" type . . . . . . . . : %d\n", channel->type); + weechat_log_printf (" dcc_chat . . . . . . : 0x%X\n", channel->dcc_chat); + weechat_log_printf (" topic. . . . . . . . : '%s'\n", channel->topic); + weechat_log_printf (" modes. . . . . . . . : '%s'\n", channel->modes); + weechat_log_printf (" limit. . . . . . . . : %d\n", channel->limit); + weechat_log_printf (" key. . . . . . . . . : '%s'\n", channel->key); + weechat_log_printf (" checking_away. . . . : %d\n", channel->checking_away); + weechat_log_printf (" away_message . . . . : '%s'\n", channel->away_message); + weechat_log_printf (" cycle. . . . . . . . : %d\n", channel->cycle); + weechat_log_printf (" close. . . . . . . . : %d\n", channel->close); + weechat_log_printf (" display_creation_date: %d\n", channel->close); + weechat_log_printf (" nicks. . . . . . . . : 0x%X\n", channel->nicks); + weechat_log_printf (" last_nick. . . . . . : 0x%X\n", channel->last_nick); + weechat_log_printf (" buffer . . . . . . . : 0x%X\n", channel->buffer); + weechat_log_printf (" nicks_speaking . . . : 0x%X\n", channel->nicks_speaking); + weechat_log_printf (" last_nick_speaking . : 0x%X\n", channel->last_nick_speaking); + weechat_log_printf (" prev_channel . . . . : 0x%X\n", channel->prev_channel); + weechat_log_printf (" next_channel . . . . : 0x%X\n", channel->next_channel); + if (channel->nicks_speaking) + { + weechat_log_printf ("\n"); + weelist_print_log (channel->nicks_speaking, + "channel nick speaking element"); + } +} diff --git a/src/plugins/irc/irc-commands.c b/src/plugins/irc/irc-commands.c new file mode 100644 index 000000000..762008dd8 --- /dev/null +++ b/src/plugins/irc/irc-commands.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-commands.c: implementation of IRC commands, according to + RFC 1459,2810,2811,2812 */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/command.h" + + +t_irc_command irc_commands[] = +{ { "admin", N_("find information about the administrator of the server"), + N_("[target]"), + N_("target: server"), + NULL, 0, 1, 0, 1, NULL, irc_send_cmd_admin, NULL }, + { "ame", N_("send a CTCP action to all channels of all connected servers"), + N_("message"), + N_("message: message to send"), + "", 1, MAX_ARGS, 1, 1, NULL, irc_send_cmd_ame, NULL }, + { "amsg", N_("send message to all channels of all connected servers"), + N_("text"), + N_("text: text to send"), + "", 1, MAX_ARGS, 1, 1, NULL, irc_send_cmd_amsg, NULL }, + { "away", N_("toggle away status"), + N_("[-all] [message]"), + N_(" -all: toggle away status on all connected servers\n" + "message: message for away (if no message is given, away status is removed)"), + "-all", 0, MAX_ARGS, 1, 0, NULL, irc_send_cmd_away, NULL }, + { "ban", N_("bans nicks or hosts"), + N_("[channel] [nickname [nickname ...]]"), + N_(" channel: channel for ban\n" + "nickname: user or host to ban"), + "%N", 0, MAX_ARGS, 0, 1, NULL, irc_send_cmd_ban, NULL }, + { "ctcp", N_("send a CTCP message (Client-To-Client Protocol)"), + N_("receiver type [arguments]"), + N_(" receiver: nick or channel to send CTCP to\n" + " type: CTCP type (examples: \"version\", \"ping\", ..)\n" + "arguments: arguments for CTCP"), + "%c|%n action|ping|version", 2, MAX_ARGS, 1, 1, NULL, irc_send_cmd_ctcp, NULL }, + { "cycle", N_("leave and rejoin a channel"), + N_("[channel[,channel]] [part_message]"), + N_(" channel: channel name for cycle\n" + "part_message: part message (displayed to other users)"), + "%p", 0, MAX_ARGS, 0, 1, NULL, irc_send_cmd_cycle, NULL }, + { "dehalfop", N_("removes half channel operator status from nickname(s)"), + N_("[nickname [nickname]]"), "", + "", 0, MAX_ARGS, 0, 1, irc_send_cmd_dehalfop, NULL, NULL }, + { "deop", N_("removes channel operator status from nickname(s)"), + N_("[nickname [nickname]]"), "", + "", 0, MAX_ARGS, 0, 1, irc_send_cmd_deop, NULL, NULL }, + { "devoice", N_("removes voice from nickname(s)"), + N_("[nickname [nickname]]"), "", + "", 0, MAX_ARGS, 0, 1, irc_send_cmd_devoice, NULL, NULL }, + { "die", N_("shutdown the server"), "", "", + NULL, 0, 0, 0, 1, NULL, irc_send_cmd_die, NULL }, + { "error", N_("error received from IRC server"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_error }, + { "halfop", N_("gives half channel operator status to nickname(s)"), + N_("[nickname [nickname]]"), "", + "", 0, MAX_ARGS, 0, 1, irc_send_cmd_halfop, NULL, NULL }, + { "info", N_("get information describing the server"), + N_("[target]"), + N_("target: server name"), + NULL, 0, 1, 0, 1, NULL, irc_send_cmd_info, NULL }, + { "invite", N_("invite a nick on a channel"), + N_("nickname channel"), + N_("nickname: nick to invite\n" + " channel: channel to invite"), + "%n %c", 1, 2, 0, 1, irc_send_cmd_invite, NULL, irc_recv_cmd_invite }, + { "ison", N_("check if a nickname is currently on IRC"), + N_("nickname [nickname ...]"), + N_("nickname: nickname"), + "", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_ison, NULL }, + { "join", N_("join a channel"), + N_("channel[,channel] [key[,key]]"), + N_("channel: channel name to join\n" + " key: key to join the channel"), + "%C", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_join, irc_recv_cmd_join }, + { "kick", N_("forcibly remove a user from a channel"), + N_("[channel] nickname [comment]"), + N_(" channel: channel where user is\n" + "nickname: nickname to kick\n" + " comment: comment for kick"), + "%n %-", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_kick, irc_recv_cmd_kick }, + { "kickban", N_("kicks and bans a nick from a channel"), + N_("[channel] nickname [comment]"), + N_(" channel: channel where user is\n" + "nickname: nickname to kick and ban\n" + " comment: comment for kick"), + "%n %-", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_kickban, NULL }, + { "kill", N_("close client-server connection"), + N_("nickname comment"), + N_("nickname: nickname\n" + " comment: comment for kill"), + "%n %-", 2, MAX_ARGS, 0, 1, NULL, irc_send_cmd_kill, irc_recv_cmd_kill }, + { "links", N_("list all servernames which are known by the server answering the query"), + N_("[[server] server_mask]"), + N_(" server: this server should answer the query\n" + "server_mask: list of servers must match this mask"), + NULL, 0, 2, 0, 1, NULL, irc_send_cmd_links, NULL }, + { "list", N_("list channels and their topic"), + N_("[channel[,channel] [server]]"), + N_("channel: channel to list (a regexp is allowed)\nserver: server name"), + NULL, 0, MAX_ARGS, 0, 1, NULL, irc_send_cmd_list, NULL }, + { "lusers", N_("get statistics about the size of the IRC network"), + N_("[mask [target]]"), + N_(" mask: servers matching the mask only\n" + "target: server for forwarding request"), + NULL, 0, 2, 0, 1, NULL, irc_send_cmd_lusers, NULL }, + { "me", N_("send a CTCP action to the current channel"), + N_("message"), + N_("message: message to send"), + "", 0, MAX_ARGS, 1, 1, NULL, irc_send_cmd_me, NULL }, + { "mode", N_("change channel or user mode"), + N_("{ channel {[+|-]|o|p|s|i|t|n|b|v} [limit] [user] [ban mask] } | " + "{ nickname {[+|-]|i|w|s|o} }"), + N_("channel modes:\n" + " channel: channel name to modify\n" + " o: give/take channel operator privileges\n" + " p: private channel flag\n" + " s: secret channel flag\n" + " i: invite-only channel flag\n" + " t: topic settable by channel operator only flag\n" + " n: no messages to channel from clients on the outside\n" + " m: moderated channel\n" + " l: set the user limit to channel\n" + " b: set a ban mask to keep users out\n" + " e: set exception mask\n" + " v: give/take the ability to speak on a moderated channel\n" + " k: set a channel key (password)\n" + "user modes:\n" + " nickname: nickname to modify\n" + " i: mark a user as invisible\n" + " s: mark a user for receive server notices\n" + " w: user receives wallops\n" + " o: operator flag"), + "%c|%m", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_mode, irc_recv_cmd_mode }, + { "motd", N_("get the \"Message Of The Day\""), + N_("[target]"), + N_("target: server name"), + NULL, 0, 1, 0, 1, NULL, irc_send_cmd_motd, NULL }, + { "msg", N_("send message to a nick or channel"), + N_("receiver[,receiver] text"), + N_("receiver: nick or channel (may be mask, '*' = current channel)\n" + "text: text to send"), + "", 2, MAX_ARGS, 1, 1, NULL, irc_send_cmd_msg, NULL }, + { "names", N_("list nicknames on channels"), + N_("[channel[,channel]]"), + N_("channel: channel name"), + NULL, 0, 1, 0, 1, NULL, irc_send_cmd_names, NULL }, + { "nick", N_("change current nickname"), + N_("[-all] nickname"), + N_(" -all: set new nickname for all connected servers\n" + "nickname: new nickname"), + "-all", 1, 2, 0, 0, irc_send_cmd_nick, NULL, irc_recv_cmd_nick }, + { "notice", N_("send notice message to user"), + N_("nickname text"), + N_("nickname: user to send notice to\n" + " text: text to send"), + "%n %-", 2, MAX_ARGS, 1, 1, NULL, irc_send_cmd_notice, irc_recv_cmd_notice }, + { "op", N_("gives channel operator status to nickname(s)"), + N_("nickname [nickname]"), "", + "", 1, MAX_ARGS, 0, 1, irc_send_cmd_op, NULL, NULL }, + { "oper", N_("get operator privileges"), + N_("user password"), + N_("user/password: used to get privileges on current IRC server"), + NULL, 2, 2, 0, 1, NULL, irc_send_cmd_oper, NULL }, + { "part", N_("leave a channel"), + N_("[channel[,channel]] [part_message]"), + N_(" channel: channel name to leave\n" + "part_message: part message (displayed to other users)"), + "%p", 0, MAX_ARGS, 0, 1, NULL, irc_send_cmd_part, irc_recv_cmd_part }, + { "ping", N_("ping server"), + N_("server1 [server2]"), + N_("server1: server to ping\nserver2: forward ping to this server"), + NULL, 1, 2, 0, 1, NULL, irc_send_cmd_ping, irc_recv_cmd_ping }, + { "pong", N_("answer to a ping message"), + N_("daemon [daemon2]"), + N_(" daemon: daemon who has responded to Ping message\n" + "daemon2: forward message to this daemon"), + NULL, 1, 2, 0, 1, NULL, irc_send_cmd_pong, irc_recv_cmd_pong }, + { "privmsg", N_("message received"), "", "", + "", 0, 0, 1, 1, NULL, NULL, irc_recv_cmd_privmsg }, + { "query", N_("send a private message to a nick"), + N_("nickname [text]"), + N_("nickname: nickname for private conversation\n" + " text: text to send"), + "%n %-", 1, MAX_ARGS, 1, 1, NULL, irc_send_cmd_query, NULL }, + { "quit", N_("close all connections and quit"), + N_("[quit_message]"), + N_("quit_message: quit message (displayed to other users)"), + "%q", 0, MAX_ARGS, 1, 0, NULL, irc_send_cmd_quit, irc_recv_cmd_quit }, + { "quote", N_("send raw data to server without parsing"), + N_("data"), + N_("data: raw data to send"), + "", 1, MAX_ARGS, 1, 0, NULL, irc_send_cmd_quote, NULL }, + { "rehash", N_("tell the server to reload its config file"), "", "", + NULL, 0, 0, 0, 1, NULL, irc_send_cmd_rehash, NULL }, + { "restart", N_("tell the server to restart itself"), "", "", + NULL, 0, 0, 0, 1, NULL, irc_send_cmd_restart, NULL }, + { "service", N_("register a new service"), + N_("nickname reserved distribution type reserved info"), + N_("distribution: visibility of service\n" + " type: reserved for future usage"), + NULL, 6, 6, 0, 1, NULL, irc_send_cmd_service, NULL }, + { "servlist", N_("list services currently connected to the network"), + N_("[mask [type]]"), + N_("mask: list only services matching this mask\n" + "type: list only services of this type"), + NULL, 0, 2, 0, 1, NULL, irc_send_cmd_servlist, NULL }, + { "squery", N_("deliver a message to a service"), + N_("service text"), + N_("service: name of service\ntext: text to send"), + NULL, 2, MAX_ARGS, 1, 1, NULL, irc_send_cmd_squery, NULL }, + { "squit", N_("disconnect server links"), + N_("server comment"), + N_( "server: server name\n" + "comment: comment for quit"), + NULL, 2, 2, 1, 1, NULL, irc_send_cmd_squit, NULL }, + { "stats", N_("query statistics about server"), + N_("[query [server]]"), + N_(" query: c/h/i/k/l/m/o/y/u (see RFC1459)\n" + "server: server name"), + NULL, 0, 2, 0, 1, NULL, irc_send_cmd_stats, NULL }, + { "summon", N_("give users who are on a host running an IRC server a message " + "asking them to please join IRC"), + N_("user [target [channel]]"), + N_(" user: username\ntarget: server name\n" + "channel: channel name"), + NULL, 1, 3, 0, 1, NULL, irc_send_cmd_summon, NULL }, + { "time", N_("query local time from server"), + N_("[target]"), + N_("target: query time from specified server"), + NULL, 0, 1, 0, 1, NULL, irc_send_cmd_time, NULL }, + { "topic", N_("get/set channel topic"), + N_("[channel] [topic]"), + N_("channel: channel name\ntopic: new topic for channel " + "(if topic is \"-delete\" then topic is deleted)"), + "%t|-delete %-", 0, MAX_ARGS, 1, 1, NULL, irc_send_cmd_topic, irc_recv_cmd_topic }, + { "trace", N_("find the route to specific server"), + N_("[target]"), + N_("target: server"), + NULL, 0, 1, 0, 1, NULL, irc_send_cmd_trace, NULL }, + { "unban", N_("unbans nicks or hosts"), + N_("[channel] nickname [nickname ...]"), + N_(" channel: channel for unban\n" + "nickname: user or host to unban"), + "", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_unban, NULL }, + { "userhost", N_("return a list of information about nicknames"), + N_("nickname [nickname ...]"), + N_("nickname: nickname"), + "%n", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_userhost, NULL }, + { "users", N_("list of users logged into the server"), + N_("[target]"), + N_("target: server"), + NULL, 0, 1, 0, 1, NULL, irc_send_cmd_users, NULL }, + { "version", N_("gives the version info of nick or server (current or specified)"), + N_("[server | nickname]"), + N_(" server: server name\n" + "nickname: nickname"), + NULL, 0, 1, 0, 1, NULL, irc_send_cmd_version, NULL }, + { "voice", N_("gives voice to nickname(s)"), + N_("[nickname [nickname]]"), "", + "", 0, MAX_ARGS, 0, 1, irc_send_cmd_voice, NULL, NULL }, + { "wallops", N_("send a message to all currently connected users who have " + "set the 'w' user mode for themselves"), + N_("text"), + N_("text to send"), + NULL, 1, MAX_ARGS, 1, 1, NULL, irc_send_cmd_wallops, irc_recv_cmd_wallops }, + { "who", N_("generate a query which returns a list of information"), + N_("[mask [\"o\"]]"), + N_("mask: only information which match this mask\n" + " o: only operators are returned according to the mask supplied"), + "%C", 0, 2, 0, 1, NULL, irc_send_cmd_who, NULL }, + { "whois", N_("query information about user(s)"), + N_("[server] nickname[,nickname]"), + N_(" server: server name\n" + "nickname: nickname (may be a mask)"), + "", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_whois, NULL }, + { "whowas", N_("ask for information about a nickname which no longer exists"), + N_("nickname [,nickname [,nickname ...]] [count [target]]"), + N_("nickname: nickname to search\n" + " count: number of replies to return (full search if negative number)\n" + " target: reply should match this mask"), + "", 1, MAX_ARGS, 0, 1, NULL, irc_send_cmd_whowas, NULL }, + { "001", N_("a server message"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_001 }, + { "005", N_("a server message"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_005 }, + { "221", N_("user mode string"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_221 }, + { "301", N_("away message"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_301 }, + { "302", N_("userhost"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_302 }, + { "303", N_("ison"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_303 }, + { "305", N_("unaway"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_305 }, + { "306", N_("now away"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_306 }, + { "307", N_("whois (registered nick)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_whois_nick_msg }, + { "310", N_("whois (help mode)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_310 }, + { "311", N_("whois (user)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_311 }, + { "312", N_("whois (server)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_312 }, + { "313", N_("whois (operator)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_whois_nick_msg }, + { "314", N_("whowas"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_314 }, + { "315", N_("end of /who list"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_315 }, + { "317", N_("whois (idle)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_317 }, + { "318", N_("whois (end)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_whois_nick_msg }, + { "319", N_("whois (channels)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_319 }, + { "320", N_("whois (identified user)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_whois_nick_msg }, + { "321", N_("/list start"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_321 }, + { "322", N_("channel (for /list)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_322 }, + { "323", N_("/list end"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_323 }, + { "324", N_("channel mode"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_324 }, + { "326", N_("whois (has oper privs)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_whois_nick_msg }, + { "327", N_("whois (host)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_327 }, + { "329", N_("channel creation date"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_329 }, + { "331", N_("no topic for channel"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_331 }, + { "332", N_("topic of channel"), + N_("channel :topic"), + N_("channel: name of channel\n" + " topic: topic of the channel"), + NULL, 2, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_332 }, + { "333", N_("infos about topic (nick and date changed)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_333 }, + { "338", N_("whois (host)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_338 }, + { "341", N_("inviting"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_341 }, + { "344", N_("channel reop"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_344 }, + { "345", N_("end of channel reop list"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_345 }, + { "348", N_("channel exception list"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_348 }, + { "349", N_("end of channel exception list"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_349 }, + { "351", N_("server version"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_351 }, + { "352", N_("who"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_352 }, + { "353", N_("list of nicks on channel"), + N_("channel :[[@|+]nick ...]"), + N_("channel: name of channel\n" + " nick: nick on the channel"), + NULL, 2, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_353 }, + { "366", N_("end of /names list"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_366 }, + { "367", N_("banlist"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_367 }, + { "368", N_("end of banlist"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_368 }, + { "378", N_("whois (connecting from)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_whois_nick_msg }, + { "379", N_("whois (using modes)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_whois_nick_msg }, + { "401", N_("no such nick/channel"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "402", N_("no such server"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "403", N_("no such channel"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "404", N_("cannot send to channel"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "405", N_("too many channels"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "406", N_("was no such nick"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "407", N_("was no such nick"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "409", N_("no origin"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "410", N_("no services"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "411", N_("no recipient"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "412", N_("no text to send"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "413", N_("no toplevel"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "414", N_("wilcard in toplevel domain"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "421", N_("unknown command"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "422", N_("MOTD is missing"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "423", N_("no administrative info"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "424", N_("file error"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "431", N_("no nickname given"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "432", N_("erroneous nickname"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_432 }, + { "433", N_("nickname already in use"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_433 }, + { "436", N_("nickname collision"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "437", N_("resource unavailable"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "438", N_("not authorized to change nickname"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_438 }, + { "441", N_("user not in channel"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "442", N_("not on channel"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "443", N_("user already on channel"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "444", N_("user not logged in"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "445", N_("summon has been disabled"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "446", N_("users has been disabled"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "451", N_("you are not registered"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "461", N_("not enough parameters"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "462", N_("you may not register"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "463", N_("your host isn't among the privileged"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "464", N_("password incorrect"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "465", N_("you are banned from this server"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "467", N_("channel key already set"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "470", N_("forwarding to another channel"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "471", N_("channel is already full"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "472", N_("unknown mode char to me"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "473", N_("cannot join channel (invite only)"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "474", N_("cannot join channel (banned from channel)"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "475", N_("cannot join channel (bad channel key)"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "476", N_("bad channel mask"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "477", N_("channel doesn't support modes"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "481", N_("you're not an IRC operator"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "482", N_("you're not channel operator"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "483", N_("you can't kill a server!"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "484", N_("your connection is restricted!"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "485", N_("user is immune from kick/deop"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "487", N_("network split"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "491", N_("no O-lines for your host"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "501", N_("unknown mode flag"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "502", N_("can't change mode for other users"), "", "", + NULL, 0, 0, MAX_ARGS, 1, NULL, NULL, irc_recv_cmd_error }, + { "671", N_("whois (secure connection)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_671 }, + { "973", N_("whois (secure connection)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_server_mode_reason }, + { "974", N_("whois (secure connection)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_server_mode_reason }, + { "975", N_("whois (secure connection)"), "", "", + NULL, 0, 0, 0, 1, NULL, NULL, irc_recv_cmd_server_mode_reason }, + { NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 1, NULL, NULL, NULL } +}; diff --git a/src/plugins/irc/irc-dcc.c b/src/plugins/irc/irc-dcc.c new file mode 100644 index 000000000..6580b7110 --- /dev/null +++ b/src/plugins/irc/irc-dcc.c @@ -0,0 +1,1939 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-dcc.c: Direct Client-to-Client (DCC) communication (files & chat) */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <stdarg.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/log.h" +#include "../../common/hotlist.h" +#include "../../common/util.h" +#include "../../common/weeconfig.h" +#include "../../gui/gui.h" + + +t_irc_dcc *irc_dcc_list = NULL; /* DCC files & chat list */ +t_irc_dcc *irc_last_dcc = NULL; /* last DCC in list */ +char *irc_dcc_status_string[] = /* strings for DCC status */ +{ N_("Waiting"), N_("Connecting"), N_("Active"), N_("Done"), N_("Failed"), + N_("Aborted") }; + + +/* + * irc_dcc_redraw: redraw DCC buffer (and add to hotlist) + */ + +void +irc_dcc_redraw (int highlight) +{ + t_gui_buffer *ptr_buffer; + + ptr_buffer = gui_buffer_get_dcc (gui_current_window); + gui_window_redraw_buffer (ptr_buffer); + if (highlight && gui_add_hotlist && (ptr_buffer->num_displayed == 0)) + { + hotlist_add (highlight, NULL, NULL, ptr_buffer, 0); + gui_status_draw (gui_current_window->buffer, 0); + } +} + +/* + * irc_dcc_search: search a DCC + */ + +t_irc_dcc * +irc_dcc_search (t_irc_server *server, int type, int status, int port) +{ + t_irc_dcc *ptr_dcc; + + for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) + { + if ((ptr_dcc->server == server) + && (ptr_dcc->type == type) + && (ptr_dcc->status = status) + && (ptr_dcc->port == port)) + return ptr_dcc; + } + + /* DCC not found */ + return NULL; +} + +/* + * irc_dcc_port_in_use: return 1 if a port is in used + * (by an active or connecting DCC) + */ + +int +irc_dcc_port_in_use (int port) +{ + t_irc_dcc *ptr_dcc; + + /* skip any currently used ports */ + for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) + { + if ((ptr_dcc->port == port) && (!IRC_DCC_ENDED(ptr_dcc->status))) + return 1; + } + + /* port not in use */ + return 0; +} + +/* + * irc_dcc_file_is_resumable: check if a file can be used for resuming a download + */ + +int +irc_dcc_file_is_resumable (t_irc_dcc *ptr_dcc, char *filename) +{ + struct stat st; + + if (!cfg_dcc_auto_resume) + return 0; + + if (access (filename, W_OK) == 0) + { + if (stat (filename, &st) != -1) + { + if ((unsigned long) st.st_size < ptr_dcc->size) + { + ptr_dcc->start_resume = (unsigned long) st.st_size; + ptr_dcc->pos = st.st_size; + ptr_dcc->last_check_pos = st.st_size; + return 1; + } + } + } + + /* not resumable */ + return 0; +} + +/* + * irc_dcc_find_filename: find local filename for a DCC + * if type if file/recv, add a suffix (like .1) if needed + * if download is resumable, set "start_resume" to good value + */ + +void +irc_dcc_find_filename (t_irc_dcc *ptr_dcc) +{ + char *dir1, *dir2, *filename2; + + if (!IRC_DCC_IS_FILE(ptr_dcc->type)) + return; + + dir1 = weechat_strreplace (cfg_dcc_download_path, "~", getenv ("HOME")); + if (!dir1) + return; + dir2 = weechat_strreplace (dir1, "%h", weechat_home); + if (!dir2) + { + free (dir1); + return; + } + + ptr_dcc->local_filename = (char *) malloc (strlen (dir2) + + strlen (ptr_dcc->nick) + + strlen (ptr_dcc->filename) + 4); + if (!ptr_dcc->local_filename) + return; + + strcpy (ptr_dcc->local_filename, dir2); + if (ptr_dcc->local_filename[strlen (ptr_dcc->local_filename) - 1] != DIR_SEPARATOR_CHAR) + strcat (ptr_dcc->local_filename, DIR_SEPARATOR); + strcat (ptr_dcc->local_filename, ptr_dcc->nick); + strcat (ptr_dcc->local_filename, "."); + strcat (ptr_dcc->local_filename, ptr_dcc->filename); + + if (dir1) + free (dir1); + if (dir2 ) + free (dir2); + + /* file already exists? */ + if (access (ptr_dcc->local_filename, F_OK) == 0) + { + if (irc_dcc_file_is_resumable (ptr_dcc, ptr_dcc->local_filename)) + return; + + /* if auto rename is not set, then abort DCC */ + if (!cfg_dcc_auto_rename) + { + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + return; + } + + filename2 = (char *) malloc (strlen (ptr_dcc->local_filename) + 16); + if (!filename2) + { + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + return; + } + ptr_dcc->filename_suffix = 0; + do + { + ptr_dcc->filename_suffix++; + sprintf (filename2, "%s.%d", + ptr_dcc->local_filename, + ptr_dcc->filename_suffix); + if (access (filename2, F_OK) == 0) + { + if (irc_dcc_file_is_resumable (ptr_dcc, filename2)) + break; + } + else + break; + } + while (1); + + free (ptr_dcc->local_filename); + ptr_dcc->local_filename = strdup (filename2); + free (filename2); + } +} + +/* + * irc_dcc_calculate_speed: calculate DCC speed (for files only) + */ + +void +irc_dcc_calculate_speed (t_irc_dcc *ptr_dcc, int ended) +{ + time_t local_time, elapsed; + unsigned long bytes_per_sec_total; + + local_time = time (NULL); + if (ended || local_time > ptr_dcc->last_check_time) + { + if (ended) + { + /* calculate bytes per second (global) */ + elapsed = local_time - ptr_dcc->start_transfer; + if (elapsed == 0) + elapsed = 1; + ptr_dcc->bytes_per_sec = (ptr_dcc->pos - ptr_dcc->start_resume) / elapsed; + ptr_dcc->eta = 0; + } + else + { + /* calculate ETA */ + elapsed = local_time - ptr_dcc->start_transfer; + if (elapsed == 0) + elapsed = 1; + bytes_per_sec_total = (ptr_dcc->pos - ptr_dcc->start_resume) / elapsed; + if (bytes_per_sec_total == 0) + bytes_per_sec_total = 1; + ptr_dcc->eta = (ptr_dcc->size - ptr_dcc->pos) / bytes_per_sec_total; + + /* calculate bytes per second (since last check time) */ + elapsed = local_time - ptr_dcc->last_check_time; + if (elapsed == 0) + elapsed = 1; + ptr_dcc->bytes_per_sec = (ptr_dcc->pos - ptr_dcc->last_check_pos) / elapsed; + } + ptr_dcc->last_check_time = local_time; + ptr_dcc->last_check_pos = ptr_dcc->pos; + } +} + +/* + * irc_dcc_connect_to_sender: connect to sender + */ + +int +irc_dcc_connect_to_sender (t_irc_dcc *ptr_dcc) +{ + struct sockaddr_in addr; + struct hostent *hostent; + char *ip4; + int ret; + + if (cfg_proxy_use) + { + memset (&addr, 0, sizeof (addr)); + addr.sin_addr.s_addr = htonl (ptr_dcc->addr); + ip4 = inet_ntoa(addr.sin_addr); + + memset (&addr, 0, sizeof (addr)); + addr.sin_port = htons (cfg_proxy_port); + addr.sin_family = AF_INET; + if ((hostent = gethostbyname (cfg_proxy_address)) == NULL) + return 0; + memcpy(&(addr.sin_addr),*(hostent->h_addr_list), sizeof(struct in_addr)); + ret = connect (ptr_dcc->sock, (struct sockaddr *) &addr, sizeof (addr)); + if ((ret == -1) && (errno != EINPROGRESS)) + return 0; + if (irc_server_pass_proxy (ptr_dcc->sock, ip4, ptr_dcc->port, + ptr_dcc->server->username) == -1) + return 0; + } + else + { + memset (&addr, 0, sizeof (addr)); + addr.sin_port = htons (ptr_dcc->port); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl (ptr_dcc->addr); + ret = connect (ptr_dcc->sock, (struct sockaddr *) &addr, sizeof (addr)); + if ((ret == -1) && (errno != EINPROGRESS)) + return 0; + } + return 1; +} + +/* + * irc_dcc_connect: connect to another host + */ + +int +irc_dcc_connect (t_irc_dcc *ptr_dcc) +{ + if (ptr_dcc->type == IRC_DCC_CHAT_SEND) + ptr_dcc->status = IRC_DCC_WAITING; + else + ptr_dcc->status = IRC_DCC_CONNECTING; + + if (ptr_dcc->sock < 0) + { + ptr_dcc->sock = socket (AF_INET, SOCK_STREAM, 0); + if (ptr_dcc->sock < 0) + return 0; + } + + /* for chat or file sending, listen to socket for a connection */ + if (IRC_DCC_IS_SEND(ptr_dcc->type)) + { + if (fcntl (ptr_dcc->sock, F_SETFL, O_NONBLOCK) == -1) + return 0; + if (listen (ptr_dcc->sock, 1) == -1) + return 0; + if (fcntl (ptr_dcc->sock, F_SETFL, 0) == -1) + return 0; + } + + /* for chat receiving, connect to listening host */ + if (ptr_dcc->type == IRC_DCC_CHAT_RECV) + { + if (fcntl (ptr_dcc->sock, F_SETFL, O_NONBLOCK) == -1) + return 0; + irc_dcc_connect_to_sender (ptr_dcc); + } + + /* for file receiving, connection is made in child process (blocking) socket */ + + return 1; +} + +/* + * irc_dcc_free: free DCC struct and remove it from list + */ + +void +irc_dcc_free (t_irc_dcc *ptr_dcc) +{ + t_irc_dcc *new_dcc_list; + + if (!ptr_dcc) + return; + + /* DCC CHAT with channel => remove channel + (to prevent channel from becoming standard pv) */ + if (ptr_dcc->channel) + { + /* check if channel is used for another active DCC CHAT */ + if (!ptr_dcc->channel->dcc_chat + || (IRC_DCC_ENDED(((t_irc_dcc *)(ptr_dcc->channel->dcc_chat))->status))) + { + gui_buffer_free (ptr_dcc->channel->buffer, 1); + if (ptr_dcc->channel) + irc_channel_free (ptr_dcc->server, ptr_dcc->channel); + } + } + + /* remove DCC from list */ + if (irc_last_dcc == ptr_dcc) + irc_last_dcc = ptr_dcc->prev_dcc; + if (ptr_dcc->prev_dcc) + { + (ptr_dcc->prev_dcc)->next_dcc = ptr_dcc->next_dcc; + new_dcc_list = irc_dcc_list; + } + else + new_dcc_list = ptr_dcc->next_dcc; + if (ptr_dcc->next_dcc) + (ptr_dcc->next_dcc)->prev_dcc = ptr_dcc->prev_dcc; + + /* free data */ + if (ptr_dcc->nick) + free (ptr_dcc->nick); + if (ptr_dcc->unterminated_message) + free (ptr_dcc->unterminated_message); + if (ptr_dcc->filename) + free (ptr_dcc->filename); + + free (ptr_dcc); + irc_dcc_list = new_dcc_list; +} + +/* + * irc_dcc_file_child_kill: kill child process and close pipe + */ + +void +irc_dcc_file_child_kill (t_irc_dcc *ptr_dcc) +{ + /* kill process */ + if (ptr_dcc->child_pid > 0) + { + kill (ptr_dcc->child_pid, SIGKILL); + waitpid (ptr_dcc->child_pid, NULL, 0); + ptr_dcc->child_pid = 0; + } + + /* close pipe used with child */ + if (ptr_dcc->child_read != -1) + { + close (ptr_dcc->child_read); + ptr_dcc->child_read = -1; + } + if (ptr_dcc->child_write != -1) + { + close (ptr_dcc->child_write); + ptr_dcc->child_write = -1; + } +} + +/* + * irc_dcc_close: close a DCC connection + */ + +void +irc_dcc_close (t_irc_dcc *ptr_dcc, int status) +{ + t_gui_buffer *ptr_buffer; + struct stat st; + + ptr_dcc->status = status; + + if ((status == IRC_DCC_DONE) || (status == IRC_DCC_ABORTED) || (status == IRC_DCC_FAILED)) + { + if (IRC_DCC_IS_FILE(ptr_dcc->type)) + { + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_INFO); + gui_printf (ptr_dcc->server->buffer, + _("DCC: file %s%s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_dcc->filename, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + if (ptr_dcc->local_filename) + gui_printf (ptr_dcc->server->buffer, + _(" (local filename: %s%s%s)"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_dcc->local_filename, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + if (ptr_dcc->type == IRC_DCC_FILE_SEND) + gui_printf (ptr_dcc->server->buffer, _(" sent to ")); + else + gui_printf (ptr_dcc->server->buffer, _(" received from ")); + gui_printf (ptr_dcc->server->buffer, "%s%s%s: %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + ptr_dcc->nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (status == IRC_DCC_DONE) ? _("OK") : _("FAILED")); + irc_dcc_file_child_kill (ptr_dcc); + } + } + if (status == IRC_DCC_ABORTED) + { + if (IRC_DCC_IS_CHAT(ptr_dcc->type)) + { + if (ptr_dcc->channel) + ptr_buffer = ptr_dcc->channel->buffer; + else + ptr_buffer = ptr_dcc->server->buffer; + irc_display_prefix (ptr_dcc->server, ptr_buffer, GUI_PREFIX_INFO); + gui_printf (ptr_buffer, + _("DCC chat closed with %s%s %s(%s%d.%d.%d.%d%s)\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + ptr_dcc->nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + ptr_dcc->addr >> 24, + (ptr_dcc->addr >> 16) & 0xff, + (ptr_dcc->addr >> 8) & 0xff, + ptr_dcc->addr & 0xff, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + } + + /* remove empty file if received file failed and nothing was transfered */ + if (((status == IRC_DCC_FAILED) || (status == IRC_DCC_ABORTED)) + && IRC_DCC_IS_FILE(ptr_dcc->type) + && IRC_DCC_IS_RECV(ptr_dcc->type) + && ptr_dcc->local_filename + && ptr_dcc->pos == 0) + { + /* erase file only if really empty on disk */ + if (stat (ptr_dcc->local_filename, &st) != -1) + { + if ((unsigned long) st.st_size == 0) + unlink (ptr_dcc->local_filename); + } + } + + if (IRC_DCC_IS_FILE(ptr_dcc->type)) + irc_dcc_calculate_speed (ptr_dcc, 1); + + if (ptr_dcc->sock >= 0) + { + close (ptr_dcc->sock); + ptr_dcc->sock = -1; + } + if (ptr_dcc->file >= 0) + { + close (ptr_dcc->file); + ptr_dcc->file = -1; + } +} + +/* + * irc_dcc_channel_for_chat: create channel for DCC chat + */ + +void +irc_dcc_channel_for_chat (t_irc_dcc *ptr_dcc) +{ + if (!irc_channel_create_dcc (ptr_dcc)) + { + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s can't associate DCC chat with private buffer " + "(maybe private buffer has already DCC CHAT?)\n"), + WEECHAT_ERROR); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + return; + } + + irc_display_prefix (ptr_dcc->server, ptr_dcc->channel->buffer, + GUI_PREFIX_INFO); + gui_printf_type (ptr_dcc->channel->buffer, GUI_MSG_TYPE_MSG, + _("Connected to %s%s %s(%s%d.%d.%d.%d%s)%s via DCC chat\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + ptr_dcc->nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + ptr_dcc->addr >> 24, + (ptr_dcc->addr >> 16) & 0xff, + (ptr_dcc->addr >> 8) & 0xff, + ptr_dcc->addr & 0xff, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); +} + +/* + * irc_dcc_chat_remove_channel: remove a buffer for DCC chat + */ + +void +irc_dcc_chat_remove_channel (t_irc_channel *channel) +{ + t_irc_dcc *ptr_dcc; + + if (!channel) + return; + + for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) + { + if (ptr_dcc->channel == channel) + ptr_dcc->channel = NULL; + } +} + +/* + * irc_dcc_recv_connect_init: connect to sender and init file or chat + */ + +void +irc_dcc_recv_connect_init (t_irc_dcc *ptr_dcc) +{ + if (!irc_dcc_connect (ptr_dcc)) + { + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + } + else + { + /* DCC file => launch child process */ + if (IRC_DCC_IS_FILE(ptr_dcc->type)) + { + ptr_dcc->status = IRC_DCC_CONNECTING; + irc_dcc_file_recv_fork (ptr_dcc); + } + else + { + /* DCC CHAT => associate DCC with channel */ + ptr_dcc->status = IRC_DCC_ACTIVE; + irc_dcc_channel_for_chat (ptr_dcc); + } + } + irc_dcc_redraw (HOTLIST_MSG); +} + +/* + * irc_dcc_accept: accepts a DCC file or chat request + */ + +void +irc_dcc_accept (t_irc_dcc *ptr_dcc) +{ + if (IRC_DCC_IS_FILE(ptr_dcc->type) && (ptr_dcc->start_resume > 0)) + { + ptr_dcc->status = IRC_DCC_CONNECTING; + irc_server_sendf (ptr_dcc->server, + (strchr (ptr_dcc->filename, ' ')) ? + "PRIVMSG %s :\01DCC RESUME \"%s\" %d %u\01\n" : + "PRIVMSG %s :\01DCC RESUME %s %d %u\01", + ptr_dcc->nick, ptr_dcc->filename, + ptr_dcc->port, ptr_dcc->start_resume); + irc_dcc_redraw (HOTLIST_MSG); + } + else + irc_dcc_recv_connect_init (ptr_dcc); +} + +/* + * irc_dcc_accept_resume: accepts a resume and inform the receiver + */ + +void +irc_dcc_accept_resume (t_irc_server *server, char *filename, int port, + unsigned long pos_start) +{ + t_irc_dcc *ptr_dcc; + + ptr_dcc = irc_dcc_search (server, IRC_DCC_FILE_SEND, IRC_DCC_CONNECTING, port); + if (ptr_dcc) + { + ptr_dcc->pos = pos_start; + ptr_dcc->ack = pos_start; + ptr_dcc->start_resume = pos_start; + ptr_dcc->last_check_pos = pos_start; + irc_server_sendf (ptr_dcc->server, + (strchr (ptr_dcc->filename, ' ')) ? + "PRIVMSG %s :\01DCC ACCEPT \"%s\" %d %u\01\n" : + "PRIVMSG %s :\01DCC ACCEPT %s %d %u\01", + ptr_dcc->nick, ptr_dcc->filename, + ptr_dcc->port, ptr_dcc->start_resume); + + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_INFO); + gui_printf (ptr_dcc->server->buffer, + _("DCC: file %s%s%s resumed at position %u\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_dcc->filename, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_dcc->start_resume); + irc_dcc_redraw (HOTLIST_MSG); + } + else + gui_printf (server->buffer, + _("%s can't resume file \"%s\" (port: %d, start position: %u): DCC not found or ended\n"), + WEECHAT_ERROR, filename, port, pos_start); +} + +/* + * irc_dcc_start_resume: called when "DCC ACCEPT" is received (resume accepted by sender) + */ + +void +irc_dcc_start_resume (t_irc_server *server, char *filename, int port, + unsigned long pos_start) +{ + t_irc_dcc *ptr_dcc; + + ptr_dcc = irc_dcc_search (server, IRC_DCC_FILE_RECV, IRC_DCC_CONNECTING, port); + if (ptr_dcc) + { + ptr_dcc->pos = pos_start; + ptr_dcc->ack = pos_start; + ptr_dcc->start_resume = pos_start; + ptr_dcc->last_check_pos = pos_start; + irc_dcc_recv_connect_init (ptr_dcc); + } + else + gui_printf (server->buffer, + _("%s can't resume file \"%s\" (port: %d, start position: %u): DCC not found or ended\n"), + WEECHAT_ERROR, filename, port, pos_start); +} + +/* + * irc_dcc_alloc: allocate a new DCC file + */ + +t_irc_dcc * +irc_dcc_alloc () +{ + t_irc_dcc *new_dcc; + + /* create new DCC struct */ + if ((new_dcc = (t_irc_dcc *) malloc (sizeof (t_irc_dcc))) == NULL) + return NULL; + + /* default values */ + new_dcc->server = NULL; + new_dcc->channel = NULL; + new_dcc->type = 0; + new_dcc->status = 0; + new_dcc->start_time = 0; + new_dcc->start_transfer = 0; + new_dcc->addr = 0; + new_dcc->port = 0; + new_dcc->nick = NULL; + new_dcc->sock = -1; + new_dcc->child_pid = 0; + new_dcc->child_read = -1; + new_dcc->child_write = -1; + new_dcc->unterminated_message = NULL; + new_dcc->fast_send = cfg_dcc_fast_send; + new_dcc->file = -1; + new_dcc->filename = NULL; + new_dcc->local_filename = NULL; + new_dcc->filename_suffix = -1; + new_dcc->blocksize = cfg_dcc_blocksize; + new_dcc->size = 0; + new_dcc->pos = 0; + new_dcc->ack = 0; + new_dcc->start_resume = 0; + new_dcc->last_check_time = 0; + new_dcc->last_check_pos = 0; + new_dcc->last_activity = 0; + new_dcc->bytes_per_sec = 0; + new_dcc->eta = 0; + + new_dcc->prev_dcc = NULL; + new_dcc->next_dcc = irc_dcc_list; + if (irc_dcc_list) + irc_dcc_list->prev_dcc = new_dcc; + else + irc_last_dcc = new_dcc; + irc_dcc_list = new_dcc; + + return new_dcc; +} + +/* + * irc_dcc_add: add a DCC file to queue + */ + +t_irc_dcc * +irc_dcc_add (t_irc_server *server, int type, unsigned long addr, int port, char *nick, + int sock, char *filename, char *local_filename, unsigned long size) +{ + t_irc_dcc *new_dcc; + + new_dcc = irc_dcc_alloc (); + if (!new_dcc) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s not enough memory for new DCC\n"), + WEECHAT_ERROR); + return NULL; + } + + /* initialize new DCC */ + new_dcc->server = server; + new_dcc->channel = NULL; + new_dcc->type = type; + new_dcc->status = IRC_DCC_WAITING; + new_dcc->start_time = time (NULL); + new_dcc->start_transfer = time (NULL); + new_dcc->addr = addr; + new_dcc->port = port; + new_dcc->nick = strdup (nick); + new_dcc->sock = sock; + new_dcc->unterminated_message = NULL; + new_dcc->file = -1; + if (IRC_DCC_IS_CHAT(type)) + new_dcc->filename = strdup (_("DCC chat")); + else + new_dcc->filename = (filename) ? strdup (filename) : NULL; + new_dcc->local_filename = NULL; + new_dcc->filename_suffix = -1; + new_dcc->size = size; + new_dcc->pos = 0; + new_dcc->ack = 0; + new_dcc->start_resume = 0; + new_dcc->last_check_time = time (NULL); + new_dcc->last_check_pos = 0; + new_dcc->last_activity = time (NULL); + new_dcc->bytes_per_sec = 0; + new_dcc->eta = 0; + if (local_filename) + new_dcc->local_filename = strdup (local_filename); + else + irc_dcc_find_filename (new_dcc); + + gui_current_window->dcc_first = NULL; + gui_current_window->dcc_selected = NULL; + + /* write info message on server buffer */ + if (type == IRC_DCC_FILE_RECV) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("Incoming DCC file from %s%s%s (%s%d.%d.%d.%d%s)%s: %s%s%s, %s%lu%s bytes\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + addr >> 24, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + addr & 0xff, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + filename, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + size, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + irc_dcc_redraw (HOTLIST_MSG); + } + if (type == IRC_DCC_FILE_SEND) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("Sending DCC file to %s%s%s: %s%s%s " + "(local filename: %s%s%s), %s%lu%s bytes\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + filename, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + local_filename, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + size, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + irc_dcc_redraw (HOTLIST_MSG); + } + if (type == IRC_DCC_CHAT_RECV) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("Incoming DCC chat request from %s%s%s " + "(%s%d.%d.%d.%d%s)\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + addr >> 24, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + addr & 0xff, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + irc_dcc_redraw (HOTLIST_MSG); + } + if (type == IRC_DCC_CHAT_SEND) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("Sending DCC chat request to %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + irc_dcc_redraw (HOTLIST_MSG); + } + + if (IRC_DCC_IS_FILE(type) && (!new_dcc->local_filename)) + { + irc_dcc_close (new_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + return NULL; + } + + if (IRC_DCC_IS_FILE(type) && (new_dcc->start_resume > 0)) + { + irc_display_prefix (new_dcc->server, new_dcc->server->buffer, + GUI_PREFIX_INFO); + gui_printf (new_dcc->server->buffer, + _("DCC: file %s%s%s (local filename: %s%s%s) " + "will be resumed at position %u\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + new_dcc->filename, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + new_dcc->local_filename, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + new_dcc->start_resume); + irc_dcc_redraw (HOTLIST_MSG); + } + + /* connect if needed and redraw DCC buffer */ + if (IRC_DCC_IS_SEND(type)) + { + if (!irc_dcc_connect (new_dcc)) + { + irc_dcc_close (new_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + return NULL; + } + } + + if ( ( (type == IRC_DCC_CHAT_RECV) && (cfg_dcc_auto_accept_chats) ) + || ( (type == IRC_DCC_FILE_RECV) && (cfg_dcc_auto_accept_files) ) ) + irc_dcc_accept (new_dcc); + else + irc_dcc_redraw (HOTLIST_PRIVATE); + gui_status_draw (gui_current_window->buffer, 0); + + return new_dcc; +} + +/* + * irc_dcc_send_request: send DCC request (file or chat) + */ + +void +irc_dcc_send_request (t_irc_server *server, int type, char *nick, char *filename) +{ + char *dir1, *dir2, *filename2, *short_filename, *pos; + int spaces, args, port_start, port_end; + struct stat st; + int sock, port; + struct hostent *host; + struct in_addr tmpaddr; + struct sockaddr_in addr; + socklen_t length; + unsigned long local_addr; + t_irc_dcc *ptr_dcc; + + filename2 = NULL; + short_filename = NULL; + spaces = 0; + + if (type == IRC_DCC_FILE_SEND) + { + /* add home if filename not beginning with '/' or '~' (not for Win32) */ +#ifdef _WIN32 + filename2 = strdup (filename); +#else + if (filename[0] == '/') + filename2 = strdup (filename); + else if (filename[0] == '~') + filename2 = weechat_strreplace (filename, "~", getenv ("HOME")); + else + { + dir1 = weechat_strreplace (cfg_dcc_upload_path, "~", getenv ("HOME")); + if (!dir1) + return; + dir2 = weechat_strreplace (dir1, "%h", weechat_home); + if (!dir2) + { + free (dir1); + return; + } + filename2 = (char *) malloc (strlen (dir2) + + strlen (filename) + 4); + if (!filename2) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s not enough memory for DCC SEND\n"), + WEECHAT_ERROR); + return; + } + strcpy (filename2, dir2); + if (filename2[strlen (filename2) - 1] != DIR_SEPARATOR_CHAR) + strcat (filename2, DIR_SEPARATOR); + strcat (filename2, filename); + if (dir1) + free (dir1); + if (dir2) + free (dir2); + } +#endif + + /* check if file exists */ + if (stat (filename2, &st) == -1) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot access file \"%s\"\n"), + WEECHAT_ERROR, filename2); + if (filename2) + free (filename2); + return; + } + } + + /* get local IP address */ + + /* look up the IP address from dcc_own_ip, if set */ + local_addr = 0; + if (cfg_dcc_own_ip && cfg_dcc_own_ip[0]) + { + host = gethostbyname (cfg_dcc_own_ip); + if (host) + { + memcpy (&tmpaddr, host->h_addr_list[0], sizeof(struct in_addr)); + local_addr = ntohl (tmpaddr.s_addr); + } + else + gui_printf (server->buffer, + _("%s could not find address for '%s'. Falling back to local IP.\n"), + WEECHAT_WARNING, cfg_dcc_own_ip); + } + + /* use the local interface, from the server socket */ + memset (&addr, 0, sizeof (struct sockaddr_in)); + length = sizeof (addr); + getsockname (server->sock, (struct sockaddr *) &addr, &length); + addr.sin_family = AF_INET; + + /* fallback to the local IP address on the interface, if required */ + if (local_addr == 0) + local_addr = ntohl (addr.sin_addr.s_addr); + + /* open socket for DCC */ + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot create socket for DCC\n"), + WEECHAT_ERROR); + if (filename2) + free (filename2); + return; + } + + /* look for port */ + + port = 0; + + if (cfg_dcc_port_range && cfg_dcc_port_range[0]) + { + /* find a free port in the specified range */ + args = sscanf (cfg_dcc_port_range, "%d-%d", &port_start, &port_end); + if (args > 0) + { + port = port_start; + if (args == 1) + port_end = port_start; + + /* loop through the entire allowed port range */ + while (port <= port_end) + { + if (!irc_dcc_port_in_use (port)) + { + /* attempt to bind to the free port */ + addr.sin_port = htons (port); + if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) == 0) + break; + } + port++; + } + + if (port > port_end) + port = -1; + } + } + + if (port == 0) + { + /* find port automatically */ + addr.sin_port = 0; + if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) == 0) + { + length = sizeof (addr); + getsockname (sock, (struct sockaddr *) &addr, &length); + port = ntohs (addr.sin_port); + } + else + port = -1; + } + + if (port == -1) + { + /* Could not find any port to bind */ + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot find available port for DCC\n"), + WEECHAT_ERROR); + close (sock); + if (filename2) + free (filename2); + return; + } + + if (type == IRC_DCC_FILE_SEND) + { + /* extract short filename (without path) */ + pos = strrchr (filename2, DIR_SEPARATOR_CHAR); + if (pos) + short_filename = strdup (pos + 1); + else + short_filename = strdup (filename2); + + /* convert spaces to underscore if asked and needed */ + pos = short_filename; + spaces = 0; + while (pos[0]) + { + if (pos[0] == ' ') + { + if (cfg_dcc_convert_spaces) + pos[0] = '_'; + else + spaces = 1; + } + pos++; + } + } + + /* add DCC entry and listen to socket */ + if (type == IRC_DCC_CHAT_SEND) + ptr_dcc = irc_dcc_add (server, IRC_DCC_CHAT_SEND, local_addr, port, nick, sock, + NULL, NULL, 0); + else + ptr_dcc = irc_dcc_add (server, IRC_DCC_FILE_SEND, local_addr, port, nick, sock, + short_filename, filename2, st.st_size); + if (!ptr_dcc) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot send DCC\n"), + WEECHAT_ERROR); + close (sock); + if (short_filename) + free (short_filename); + if (filename2) + free (filename2); + return; + } + + /* send DCC request to nick */ + if (type == IRC_DCC_CHAT_SEND) + irc_server_sendf (server, + "PRIVMSG %s :\01DCC CHAT chat %lu %d\01", + nick, local_addr, port); + else + irc_server_sendf (server, + (spaces) ? + "PRIVMSG %s :\01DCC SEND \"%s\" %lu %d %u\01\n" : + "PRIVMSG %s :\01DCC SEND %s %lu %d %u\01", + nick, short_filename, local_addr, port, + (unsigned long) st.st_size); + + if (short_filename) + free (short_filename); + if (filename2) + free (filename2); +} + +/* + * irc_dcc_chat_send: send data to remote host via DCC CHAT + */ + +int +irc_dcc_chat_send (t_irc_dcc *ptr_dcc, char *buffer, int size_buf) +{ + if (!ptr_dcc) + return -1; + + return send (ptr_dcc->sock, buffer, size_buf, 0); +} + +/* + * irc_dcc_chat_sendf: send formatted data to remote host via DCC CHAT + */ + +void +irc_dcc_chat_sendf (t_irc_dcc *ptr_dcc, char *fmt, ...) +{ + va_list args; + static char buffer[4096]; + int size_buf; + + if (!ptr_dcc || (ptr_dcc->sock < 0)) + return; + + va_start (args, fmt); + size_buf = vsnprintf (buffer, sizeof (buffer) - 1, fmt, args); + va_end (args); + + if ((size_buf == 0) || (strcmp (buffer, "\r\n") == 0)) + return; + + buffer[sizeof (buffer) - 1] = '\0'; + if ((size_buf < 0) || (size_buf > (int) (sizeof (buffer) - 1))) + size_buf = strlen (buffer); +#ifdef DEBUG + buffer[size_buf - 2] = '\0'; + gui_printf (ptr_dcc->server->buffer, "[DEBUG] Sending to remote host (DCC CHAT) >>> %s\n", buffer); + buffer[size_buf - 2] = '\r'; +#endif + if (irc_dcc_chat_send (ptr_dcc, buffer, strlen (buffer)) <= 0) + { + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s error sending data to \"%s\" via DCC CHAT\n"), + WEECHAT_ERROR, ptr_dcc->nick); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + } +} + +/* + * irc_dcc_chat_recv: receive data from DCC CHAT host + */ + +void +irc_dcc_chat_recv (t_irc_dcc *ptr_dcc) +{ + fd_set read_fd; + static struct timeval timeout; + static char buffer[4096 + 2]; + char *buf2, *pos, *ptr_buf, *next_ptr_buf; + int num_read; + + FD_ZERO (&read_fd); + FD_SET (ptr_dcc->sock, &read_fd); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + /* something to read on socket? */ + if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) <= 0) + return; + + if (!FD_ISSET (ptr_dcc->sock, &read_fd)) + return; + + /* there's something to read on socket! */ + num_read = recv (ptr_dcc->sock, buffer, sizeof (buffer) - 2, 0); + if (num_read > 0) + { + buffer[num_read] = '\0'; + + buf2 = NULL; + ptr_buf = buffer; + if (ptr_dcc->unterminated_message) + { + buf2 = (char *) malloc (strlen (ptr_dcc->unterminated_message) + + strlen (buffer) + 1); + if (buf2) + { + strcpy (buf2, ptr_dcc->unterminated_message); + strcat (buf2, buffer); + } + ptr_buf = buf2; + free (ptr_dcc->unterminated_message); + ptr_dcc->unterminated_message = NULL; + } + + while (ptr_buf && ptr_buf[0]) + { + next_ptr_buf = NULL; + pos = strstr (ptr_buf, "\r\n"); + if (pos) + { + pos[0] = '\0'; + next_ptr_buf = pos + 2; + } + else + { + pos = strstr (ptr_buf, "\n"); + if (pos) + { + pos[0] = '\0'; + next_ptr_buf = pos + 1; + } + else + { + ptr_dcc->unterminated_message = strdup (ptr_buf); + ptr_buf = NULL; + next_ptr_buf = NULL; + } + } + + if (ptr_buf) + { + if (irc_recv_is_highlight (ptr_buf, ptr_dcc->server->nick)) + { + irc_display_nick (ptr_dcc->channel->buffer, NULL, ptr_dcc->nick, + GUI_MSG_TYPE_NICK | GUI_MSG_TYPE_HIGHLIGHT, 1, + GUI_COLOR_WIN_CHAT_HIGHLIGHT, 0); + if ((cfg_look_infobar_delay_highlight > 0) + && (ptr_dcc->channel->buffer != gui_current_window->buffer)) + { + gui_infobar_printf (cfg_look_infobar_delay_highlight, + GUI_COLOR_WIN_INFOBAR_HIGHLIGHT, + _("Private %s> %s"), + ptr_dcc->nick, ptr_buf); + } + } + else + irc_display_nick (ptr_dcc->channel->buffer, NULL, ptr_dcc->nick, + GUI_MSG_TYPE_NICK, 1, GUI_COLOR_WIN_NICK_PRIVATE, 0); + gui_printf_type (ptr_dcc->channel->buffer, GUI_MSG_TYPE_MSG, + "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_buf); + } + + ptr_buf = next_ptr_buf; + } + + if (buf2) + free (buf2); + } + else + { + irc_dcc_close (ptr_dcc, IRC_DCC_ABORTED); + irc_dcc_redraw (HOTLIST_MSG); + } +} + +/* + * irc_dcc_file_create_pipe: create pipe for communication with child process + * return 1 if ok, 0 if error + */ + +int +irc_dcc_file_create_pipe (t_irc_dcc *ptr_dcc) +{ + int child_pipe[2]; + + if (pipe (child_pipe) < 0) + { + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to create pipe\n"), + WEECHAT_ERROR); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + return 0; + } + + ptr_dcc->child_read = child_pipe[0]; + ptr_dcc->child_write = child_pipe[1]; + return 1; +} + +/* + * irc_dcc_file_write_pipe: write data into pipe + */ + +void +irc_dcc_file_write_pipe (t_irc_dcc *ptr_dcc, int status, int error) +{ + char buffer[1 + 1 + 12 + 1]; /* status + error + pos + \0 */ + + snprintf (buffer, sizeof (buffer), "%c%c%012lu", + status + '0', error + '0', ptr_dcc->pos); + write (ptr_dcc->child_write, buffer, sizeof (buffer)); +} + +/* + * irc_dcc_file_send_child: child process for sending file + */ + +void +irc_dcc_file_send_child (t_irc_dcc *ptr_dcc) +{ + int num_read, num_sent; + static char buffer[IRC_DCC_MAX_BLOCKSIZE]; + uint32_t ack; + time_t last_sent, new_time; + + last_sent = time (NULL); + while (1) + { + /* read DCC ACK (sent by receiver) */ + if (ptr_dcc->pos > ptr_dcc->ack) + { + /* we should receive ACK for packets sent previously */ + while (1) + { + num_read = recv (ptr_dcc->sock, (char *) &ack, 4, MSG_PEEK); + if ((num_read < 1) && + ((num_read != -1) || (errno != EAGAIN))) + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_SEND_BLOCK); + return; + } + if (num_read == 4) + { + recv (ptr_dcc->sock, (char *) &ack, 4, 0); + ptr_dcc->ack = ntohl (ack); + + /* DCC send ok? */ + if ((ptr_dcc->pos >= ptr_dcc->size) + && (ptr_dcc->ack >= ptr_dcc->size)) + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_DONE, IRC_DCC_NO_ERROR); + return; + } + } + else + break; + } + } + + /* send a block to receiver */ + if ((ptr_dcc->pos < ptr_dcc->size) && + (ptr_dcc->fast_send || (ptr_dcc->pos <= ptr_dcc->ack))) + { + lseek (ptr_dcc->file, ptr_dcc->pos, SEEK_SET); + num_read = read (ptr_dcc->file, buffer, ptr_dcc->blocksize); + if (num_read < 1) + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_READ_LOCAL); + return; + } + num_sent = send (ptr_dcc->sock, buffer, num_read, 0); + if (num_sent < 0) + { + /* socket is temporarily not available (receiver can't receive + amount of data we sent ?!) */ + if (errno == EAGAIN) + usleep (1000); + else + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_SEND_BLOCK); + return; + } + } + if (num_sent > 0) + { + ptr_dcc->pos += (unsigned long) num_sent; + new_time = time (NULL); + if (last_sent != new_time) + { + last_sent = new_time; + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_ACTIVE, IRC_DCC_NO_ERROR); + } + } + } + else + usleep (1000); + } +} + +/* + * irc_dcc_file_recv_child: child process for receiving file + */ + +void +irc_dcc_file_recv_child (t_irc_dcc *ptr_dcc) +{ + int num_read; + static char buffer[IRC_DCC_MAX_BLOCKSIZE]; + uint32_t pos; + time_t last_sent, new_time; + + /* first connect to sender (blocking) */ + if (!irc_dcc_connect_to_sender (ptr_dcc)) + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_CONNECT_SENDER); + return; + } + + /* connection is ok, change DCC status (inform parent process) */ + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_ACTIVE, IRC_DCC_NO_ERROR); + + last_sent = time (NULL); + while (1) + { + num_read = recv (ptr_dcc->sock, buffer, sizeof (buffer), 0); + if (num_read == -1) + { + /* socket is temporarily not available (sender is not fast ?!) */ + if (errno == EAGAIN) + usleep (1000); + else + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_RECV_BLOCK); + return; + } + } + else + { + if (num_read == 0) + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_RECV_BLOCK); + return; + } + + if (write (ptr_dcc->file, buffer, num_read) == -1) + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_WRITE_LOCAL); + return; + } + + ptr_dcc->pos += (unsigned long) num_read; + pos = htonl (ptr_dcc->pos); + + /* we don't check return code, not a problem if an ACK send failed */ + send (ptr_dcc->sock, (char *) &pos, 4, 0); + + /* file received ok? */ + if (ptr_dcc->pos >= ptr_dcc->size) + { + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_DONE, IRC_DCC_NO_ERROR); + return; + } + + new_time = time (NULL); + if (last_sent != new_time) + { + last_sent = new_time; + irc_dcc_file_write_pipe (ptr_dcc, IRC_DCC_ACTIVE, IRC_DCC_NO_ERROR); + } + } + } +} + +/* + * irc_dcc_file_child_read: read data from child via pipe + */ + +void +irc_dcc_file_child_read (t_irc_dcc *ptr_dcc) +{ + fd_set read_fd; + static struct timeval timeout; + char bufpipe[1 + 1 + 12 + 1]; + int num_read; + char *error; + + FD_ZERO (&read_fd); + FD_SET (ptr_dcc->child_read, &read_fd); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + /* something to read on child pipe? */ + if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) <= 0) + return; + + if (!FD_ISSET (ptr_dcc->child_read, &read_fd)) + return; + + /* there's something to read in pipe! */ + num_read = read (ptr_dcc->child_read, bufpipe, sizeof (bufpipe)); + if (num_read > 0) + { + error = NULL; + ptr_dcc->pos = strtol (bufpipe + 2, &error, 10); + ptr_dcc->last_activity = time (NULL); + irc_dcc_calculate_speed (ptr_dcc, 0); + + /* read error code */ + switch (bufpipe[1] - '0') + { + /* errors for sender */ + case IRC_DCC_ERROR_READ_LOCAL: + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to read local file\n"), + WEECHAT_ERROR); + break; + case IRC_DCC_ERROR_SEND_BLOCK: + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to send block to receiver\n"), + WEECHAT_ERROR); + break; + case IRC_DCC_ERROR_READ_ACK: + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to read ACK from receiver\n"), + WEECHAT_ERROR); + break; + /* errors for receiver */ + case IRC_DCC_ERROR_CONNECT_SENDER: + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to connect to sender\n"), + WEECHAT_ERROR); + break; + case IRC_DCC_ERROR_RECV_BLOCK: + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to receive block from sender\n"), + WEECHAT_ERROR); + break; + case IRC_DCC_ERROR_WRITE_LOCAL: + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to write local file\n"), + WEECHAT_ERROR); + break; + } + + /* read new DCC status */ + switch (bufpipe[0] - '0') + { + case IRC_DCC_ACTIVE: + if (ptr_dcc->status == IRC_DCC_CONNECTING) + { + /* connection was successful by child, init transfert times */ + ptr_dcc->status = IRC_DCC_ACTIVE; + ptr_dcc->start_transfer = time (NULL); + ptr_dcc->last_check_time = time (NULL); + irc_dcc_redraw (HOTLIST_MSG); + } + else + irc_dcc_redraw (HOTLIST_LOW); + break; + case IRC_DCC_DONE: + irc_dcc_close (ptr_dcc, IRC_DCC_DONE); + irc_dcc_redraw (HOTLIST_MSG); + break; + case IRC_DCC_FAILED: + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + break; + } + } +} + +/* + * irc_dcc_file_send_fork: fork process for sending file + */ + +void +irc_dcc_file_send_fork (t_irc_dcc *ptr_dcc) +{ + pid_t pid; + + if (!irc_dcc_file_create_pipe (ptr_dcc)) + return; + + ptr_dcc->file = open (ptr_dcc->local_filename, O_RDONLY | O_NONBLOCK, 0644); + + switch (pid = fork ()) + { + /* fork failed */ + case -1: + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to fork\n"), + WEECHAT_ERROR); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + return; + /* child process */ + case 0: + setuid (getuid ()); + irc_dcc_file_send_child (ptr_dcc); + _exit (EXIT_SUCCESS); + } + + /* parent process */ + ptr_dcc->child_pid = pid; +} + +/* + * irc_dcc_file_recv_fork: fork process for receiving file + */ + +void +irc_dcc_file_recv_fork (t_irc_dcc *ptr_dcc) +{ + pid_t pid; + + if (!irc_dcc_file_create_pipe (ptr_dcc)) + return; + + if (ptr_dcc->start_resume > 0) + ptr_dcc->file = open (ptr_dcc->local_filename, + O_APPEND | O_WRONLY | O_NONBLOCK); + else + ptr_dcc->file = open (ptr_dcc->local_filename, + O_CREAT | O_TRUNC | O_WRONLY | O_NONBLOCK, + 0644); + + switch (pid = fork ()) + { + /* fork failed */ + case -1: + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to fork\n"), + WEECHAT_ERROR); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + return; + /* child process */ + case 0: + setuid (getuid ()); + irc_dcc_file_recv_child (ptr_dcc); + _exit (EXIT_SUCCESS); + } + + /* parent process */ + ptr_dcc->child_pid = pid; +} + +/* + * irc_dcc_handle: receive/send data for all active DCC + */ + +void +irc_dcc_handle () +{ + t_irc_dcc *ptr_dcc; + fd_set read_fd; + static struct timeval timeout; + int sock; + struct sockaddr_in addr; + socklen_t length; + + for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) + { + /* check DCC timeout */ + if (IRC_DCC_IS_FILE(ptr_dcc->type) && !IRC_DCC_ENDED(ptr_dcc->status)) + { + if ((cfg_dcc_timeout != 0) && (time (NULL) > ptr_dcc->last_activity + cfg_dcc_timeout)) + { + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: timeout\n"), + WEECHAT_ERROR); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + continue; + } + } + + if (ptr_dcc->status == IRC_DCC_CONNECTING) + { + if (ptr_dcc->type == IRC_DCC_FILE_SEND) + { + FD_ZERO (&read_fd); + FD_SET (ptr_dcc->sock, &read_fd); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + /* something to read on socket? */ + if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) > 0) + { + if (FD_ISSET (ptr_dcc->sock, &read_fd)) + { + ptr_dcc->last_activity = time (NULL); + length = sizeof (addr); + sock = accept (ptr_dcc->sock, (struct sockaddr *) &addr, &length); + close (ptr_dcc->sock); + ptr_dcc->sock = -1; + if (sock < 0) + { + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to create socket for sending file\n"), + WEECHAT_ERROR); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + continue; + } + ptr_dcc->sock = sock; + if (fcntl (ptr_dcc->sock, F_SETFL, O_NONBLOCK) == -1) + { + irc_display_prefix (ptr_dcc->server, ptr_dcc->server->buffer, + GUI_PREFIX_ERROR); + gui_printf (ptr_dcc->server->buffer, + _("%s DCC: unable to set 'nonblock' option for socket\n"), + WEECHAT_ERROR); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + continue; + } + ptr_dcc->addr = ntohl (addr.sin_addr.s_addr); + ptr_dcc->status = IRC_DCC_ACTIVE; + ptr_dcc->start_transfer = time (NULL); + irc_dcc_redraw (HOTLIST_MSG); + irc_dcc_file_send_fork (ptr_dcc); + } + } + } + if (ptr_dcc->type == IRC_DCC_FILE_RECV) + { + if (ptr_dcc->child_read != -1) + irc_dcc_file_child_read (ptr_dcc); + } + } + + if (ptr_dcc->status == IRC_DCC_WAITING) + { + if (ptr_dcc->type == IRC_DCC_CHAT_SEND) + { + FD_ZERO (&read_fd); + FD_SET (ptr_dcc->sock, &read_fd); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + /* something to read on socket? */ + if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) > 0) + { + if (FD_ISSET (ptr_dcc->sock, &read_fd)) + { + length = sizeof (addr); + sock = accept (ptr_dcc->sock, (struct sockaddr *) &addr, &length); + close (ptr_dcc->sock); + ptr_dcc->sock = -1; + if (sock < 0) + { + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + continue; + } + ptr_dcc->sock = sock; + if (fcntl (ptr_dcc->sock, F_SETFL, O_NONBLOCK) == -1) + { + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + irc_dcc_redraw (HOTLIST_MSG); + continue; + } + ptr_dcc->addr = ntohl (addr.sin_addr.s_addr); + ptr_dcc->status = IRC_DCC_ACTIVE; + irc_dcc_redraw (HOTLIST_MSG); + irc_dcc_channel_for_chat (ptr_dcc); + } + } + } + } + + if (ptr_dcc->status == IRC_DCC_ACTIVE) + { + if (IRC_DCC_IS_CHAT(ptr_dcc->type)) + irc_dcc_chat_recv (ptr_dcc); + else + irc_dcc_file_child_read (ptr_dcc); + } + } +} + +/* + * irc_dcc_end: close all opened sockets (called when WeeChat is exiting) + */ + +void +irc_dcc_end () +{ + t_irc_dcc *ptr_dcc; + + for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) + { + if (ptr_dcc->sock >= 0) + { + if (ptr_dcc->status == IRC_DCC_ACTIVE) + weechat_log_printf (_("Aborting active DCC: \"%s\" from %s\n"), + ptr_dcc->filename, ptr_dcc->nick); + irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); + } + } +} + +/* + * irc_dcc_print_log: print DCC infos in log (usually for crash dump) + */ + +void +irc_dcc_print_log () +{ + t_irc_dcc *ptr_dcc; + + for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) + { + weechat_log_printf ("\n"); + weechat_log_printf ("[DCC (addr:0x%X)]\n", ptr_dcc); + weechat_log_printf (" server. . . . . . . : 0x%X\n", ptr_dcc->server); + weechat_log_printf (" channel . . . . . . : 0x%X\n", ptr_dcc->channel); + weechat_log_printf (" type. . . . . . . . : %d\n", ptr_dcc->type); + weechat_log_printf (" status. . . . . . . : %d\n", ptr_dcc->status); + weechat_log_printf (" start_time. . . . . : %ld\n", ptr_dcc->start_time); + weechat_log_printf (" start_transfer. . . : %ld\n", ptr_dcc->start_transfer); + weechat_log_printf (" addr. . . . . . . . : %lu\n", ptr_dcc->addr); + weechat_log_printf (" port. . . . . . . . : %d\n", ptr_dcc->port); + weechat_log_printf (" nick. . . . . . . . : '%s'\n", ptr_dcc->nick); + weechat_log_printf (" sock. . . . . . . . : %d\n", ptr_dcc->sock); + weechat_log_printf (" child_pid . . . . . : %d\n", ptr_dcc->child_pid); + weechat_log_printf (" child_read. . . . . : %d\n", ptr_dcc->child_read); + weechat_log_printf (" child_write . . . . : %d\n", ptr_dcc->child_write); + weechat_log_printf (" unterminated_message: '%s'\n", ptr_dcc->unterminated_message); + weechat_log_printf (" fast_send . . . . . : %d\n", ptr_dcc->fast_send); + weechat_log_printf (" file. . . . . . . . : %d\n", ptr_dcc->file); + weechat_log_printf (" filename. . . . . . : '%s'\n", ptr_dcc->filename); + weechat_log_printf (" local_filename. . . : '%s'\n", ptr_dcc->local_filename); + weechat_log_printf (" filename_suffix . . : %d\n", ptr_dcc->filename_suffix); + weechat_log_printf (" blocksize . . . . . : %d\n", ptr_dcc->blocksize); + weechat_log_printf (" size. . . . . . . . : %lu\n", ptr_dcc->size); + weechat_log_printf (" pos . . . . . . . . : %lu\n", ptr_dcc->pos); + weechat_log_printf (" ack . . . . . . . . : %lu\n", ptr_dcc->ack); + weechat_log_printf (" start_resume. . . . : %lu\n", ptr_dcc->start_resume); + weechat_log_printf (" last_check_time . . : %ld\n", ptr_dcc->last_check_time); + weechat_log_printf (" last_check_pos. . . : %lu\n", ptr_dcc->last_check_pos); + weechat_log_printf (" last_activity . . . : %ld\n", ptr_dcc->last_activity); + weechat_log_printf (" bytes_per_sec . . . : %lu\n", ptr_dcc->bytes_per_sec); + weechat_log_printf (" eta . . . . . . . . : %lu\n", ptr_dcc->eta); + weechat_log_printf (" prev_dcc. . . . . . : 0x%X\n", ptr_dcc->prev_dcc); + weechat_log_printf (" next_dcc. . . . . . : 0x%X\n", ptr_dcc->next_dcc); + } +} diff --git a/src/plugins/irc/irc-display.c b/src/plugins/irc/irc-display.c new file mode 100644 index 000000000..f1112eaa8 --- /dev/null +++ b/src/plugins/irc/irc-display.c @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-display.c: display functions for IRC */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/utf8.h" +#include "../../common/weeconfig.h" +#include "../../gui/gui.h" + + +/* + * irc_display_hide_password: hide IRC password(s) in a string + */ + +void +irc_display_hide_password (char *string, int look_for_nickserv) +{ + char *pos_nickserv, *pos, *pos_pwd; + + pos = string; + while (1) + { + if (look_for_nickserv) + { + pos_nickserv = strstr (pos, "nickserv "); + if (!pos_nickserv) + return; + pos = pos_nickserv + 9; + while (pos[0] == ' ') + pos++; + if ((strncmp (pos, "identify ", 9) == 0) + || (strncmp (pos, "register ", 9) == 0)) + pos_pwd = pos + 9; + else + pos_pwd = NULL; + } + else + { + pos_pwd = strstr (pos, "identify "); + if (!pos_pwd) + pos_pwd = strstr (pos, "register "); + if (!pos_pwd) + return; + pos_pwd += 9; + } + + if (pos_pwd) + { + while (pos_pwd[0] == ' ') + pos_pwd++; + + while (pos_pwd[0] && (pos_pwd[0] != ';') && (pos_pwd[0] != ' ') + && (pos_pwd[0] != '"')) + { + pos_pwd[0] = '*'; + pos_pwd++; + } + pos = pos_pwd; + } + } +} + +/* + * irc_display_prefix: display a prefix for action/info/error msg + * prefix must be 3 chars length + */ + +void +irc_display_prefix (t_irc_server *server, t_gui_buffer *buffer, char *prefix) +{ + int type; + char format[32]; + + type = GUI_MSG_TYPE_INFO | GUI_MSG_TYPE_PREFIX; + + if (!cfg_log_plugin_msg && (strcmp (prefix, GUI_PREFIX_PLUGIN) == 0)) + type |= GUI_MSG_TYPE_NOLOG; + + if (buffer) + { + if (cfg_look_align_other + && (GUI_BUFFER_IS_CHANNEL(buffer) || GUI_BUFFER_IS_PRIVATE(buffer))) + { + snprintf (format, 32, "%%-%ds", cfg_look_align_size - 2); + gui_printf_type (buffer, GUI_MSG_TYPE_NICK, format, " "); + } + } + + if (prefix[0] == prefix[2]) + { + gui_printf_type (buffer, type, "%s%c%s%c%s%c ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_PREFIX1), + prefix[0], + GUI_COLOR(GUI_COLOR_WIN_CHAT_PREFIX2), + prefix[1], + GUI_COLOR(GUI_COLOR_WIN_CHAT_PREFIX1), + prefix[2]); + } + else + { + if (strcmp (prefix, GUI_PREFIX_JOIN) == 0) + gui_printf_type (buffer, type, "%s%s ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_JOIN), prefix); + else if (strcmp (prefix, GUI_PREFIX_PART) == 0) + gui_printf_type (buffer, type, "%s%s ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_PART), prefix); + else + gui_printf_type (buffer, type, "%s%s ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_PREFIX1), prefix); + } + if (server && (server->buffer == buffer) && buffer->all_servers) + { + gui_printf_type (buffer, type, "%s[%s%s%s] ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), server->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + gui_printf_type (buffer, type, GUI_NO_COLOR); +} + +/* + * irc_display_nick: display nick in chat window + */ + +void +irc_display_nick (t_gui_buffer *buffer, t_irc_nick *nick, char *nickname, + int type, int display_around, int force_color, int no_nickmode) +{ + char format[32], *ptr_nickname; + int max_align, i, nickname_length, external_nick, length, spaces; + int disable_prefix_suffix; + + max_align = (cfg_look_align_size_max >= cfg_look_align_size) ? + cfg_look_align_size_max : cfg_look_align_size; + + ptr_nickname = strdup ((nick) ? nick->nick : nickname); + if (!ptr_nickname) + return; + nickname_length = utf8_width_screen (ptr_nickname); + external_nick = (!nick && !GUI_BUFFER_IS_PRIVATE(buffer)); + disable_prefix_suffix = ((cfg_look_align_nick != CFG_LOOK_ALIGN_NICK_NONE) + && ((int)strlen (cfg_look_nick_prefix) + + (int)strlen (cfg_look_nick_suffix) > max_align - 4)); + + /* calculate length to display, to truncate it if too long */ + length = nickname_length; + if (!disable_prefix_suffix && cfg_look_nick_prefix) + length += strlen (cfg_look_nick_prefix); + if (external_nick) + length += 2; + if (nick && cfg_look_nickmode) + { + if (nick->flags & (IRC_NICK_CHANOWNER | IRC_NICK_CHANADMIN | + IRC_NICK_CHANADMIN2 | IRC_NICK_OP | IRC_NICK_HALFOP | + IRC_NICK_VOICE | IRC_NICK_CHANUSER)) + length += 1; + else if (cfg_look_nickmode_empty && !no_nickmode) + length += 1; + } + if (!disable_prefix_suffix && cfg_look_nick_suffix) + length += strlen (cfg_look_nick_suffix); + + /* calculate number of spaces to insert before or after nick */ + spaces = 0; + if (cfg_look_align_nick != CFG_LOOK_ALIGN_NICK_NONE) + { + if (length > max_align) + spaces = max_align - length; + else if (length > cfg_look_align_size) + spaces = 0; + else + spaces = cfg_look_align_size - length; + } + + /* display prefix */ + if (display_around && !disable_prefix_suffix + && cfg_look_nick_prefix && cfg_look_nick_prefix[0]) + gui_printf_type (buffer, type, "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + cfg_look_nick_prefix); + + /* display spaces before nick, if needed */ + if (display_around + && (cfg_look_align_nick == CFG_LOOK_ALIGN_NICK_RIGHT) + && (spaces > 0)) + { + snprintf (format, 32, "%%-%ds", spaces); + gui_printf_type (buffer, type, format, " "); + } + + /* display nick mode */ + if (nick && cfg_look_nickmode) + { + if (nick->flags & IRC_NICK_CHANOWNER) + gui_printf_type (buffer, type, "%s~", + GUI_COLOR(GUI_COLOR_WIN_NICK_OP)); + else if (nick->flags & IRC_NICK_CHANADMIN) + gui_printf_type (buffer, type, "%s&", + GUI_COLOR(GUI_COLOR_WIN_NICK_OP)); + else if (nick->flags & IRC_NICK_CHANADMIN2) + gui_printf_type (buffer, type, "%s!", + GUI_COLOR(GUI_COLOR_WIN_NICK_OP)); + else if (nick->flags & IRC_NICK_OP) + gui_printf_type (buffer, type, "%s@", + GUI_COLOR(GUI_COLOR_WIN_NICK_OP)); + else if (nick->flags & IRC_NICK_HALFOP) + gui_printf_type (buffer, type, "%s%%", + GUI_COLOR(GUI_COLOR_WIN_NICK_HALFOP)); + else if (nick->flags & IRC_NICK_VOICE) + gui_printf_type (buffer, type, "%s+", + GUI_COLOR(GUI_COLOR_WIN_NICK_VOICE)); + else if (nick->flags & IRC_NICK_CHANUSER) + gui_printf_type (buffer, type, "%s-", + GUI_COLOR(GUI_COLOR_WIN_NICK_CHANUSER)); + else if (cfg_look_nickmode_empty && !no_nickmode) + gui_printf_type (buffer, type, "%s ", + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + + /* display nick */ + if (external_nick) + gui_printf_type (buffer, type, "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + "("); + if (display_around && (spaces < 0)) + { + i = nickname_length + spaces - 1; + if (i < 3) + { + if (nickname_length < 3) + i = nickname_length; + else + i = 3; + } + ptr_nickname[i] = '\0'; + } + if (display_around) + gui_printf_type_nick (buffer, type, + (nick) ? nick->nick : nickname, + "%s%s", + (force_color >= 0) ? + GUI_COLOR(force_color) : + GUI_COLOR((nick) ? nick->color : GUI_COLOR_WIN_CHAT), + ptr_nickname); + else + gui_printf_type (buffer, type, + "%s%s", + (force_color >= 0) ? + GUI_COLOR(force_color) : + GUI_COLOR((nick) ? nick->color : GUI_COLOR_WIN_CHAT), + ptr_nickname); + if (display_around && (spaces < 0)) + gui_printf_type (buffer, type, "%s+", + GUI_COLOR(GUI_COLOR_WIN_NICK_MORE)); + if (external_nick) + gui_printf_type (buffer, type, "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + ")"); + + /* display spaces after nick, if needed */ + if (display_around + && (cfg_look_align_nick == CFG_LOOK_ALIGN_NICK_LEFT) + && (spaces > 0)) + { + snprintf (format, 32, "%%-%ds", spaces); + gui_printf_type (buffer, type, format, " "); + } + + /* display suffix */ + if (display_around && !disable_prefix_suffix + && cfg_look_nick_suffix && cfg_look_nick_suffix[0]) + gui_printf_type (buffer, type, "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + cfg_look_nick_suffix); + + gui_printf_type (buffer, type, "%s%s", + GUI_NO_COLOR, + (display_around) ? " " : ""); + free (ptr_nickname); +} + +/* + * irc_display_away: display away on all channels of all servers + */ + +void +irc_display_away (t_irc_server *server, char *string1, char *string2) +{ + t_irc_channel *ptr_channel; + char format[32]; + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + { + if (cfg_look_align_other) + { + snprintf (format, 32, "%%-%ds", cfg_look_align_size + 1); + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_NICK, + format, " "); + } + gui_printf_nolog (ptr_channel->buffer, + "%s[%s%s%s %s: %s%s]\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + server->nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + string1, + string2, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + } +} + +/* + * irc_display_mode: display IRC message for mode change + */ + +void +irc_display_mode (t_irc_server *server, t_gui_buffer *buffer, + char *channel_name, char *nick_name, char set_flag, + char *symbol, char *nick_host, char *message, char *param) +{ + irc_display_prefix (server, buffer, GUI_PREFIX_INFO); + gui_printf (buffer, "%s[%s%s%s/%s%c%s%s] %s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + (channel_name) ? + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL) : + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + (channel_name) ? channel_name : nick_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + set_flag, + symbol, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick_host); + if (param) + gui_printf (buffer, " %s%s %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + message, + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + param); + else + gui_printf (buffer, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + message); +} + +/* + * irc_display_server: display server description + */ + +void +irc_display_server (t_irc_server *server, int with_detail) +{ + char *string; + int num_channels, num_pv; + + if (with_detail) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("%sServer: %s%s %s[%s%s%s]\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + server->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (server->is_connected) ? + _("connected") : _("not connected"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + + gui_printf (NULL, " server_autoconnect . . . . : %s%s\n", + (server->autoconnect) ? _("on") : _("off"), + (server->temp_server) ? + _(" (temporary server, will not be saved)") : ""); + gui_printf (NULL, " server_autoreconnect . . . : %s\n", + (server->autoreconnect) ? _("on") : _("off")); + gui_printf (NULL, " server_autoreconnect_delay : %d %s\n", + server->autoreconnect_delay, + _("seconds")); + gui_printf (NULL, " server_address . . . . . . : %s\n", + server->address); + gui_printf (NULL, " server_port . . . . . . . : %d\n", + server->port); + gui_printf (NULL, " server_ipv6 . . . . . . . : %s\n", + (server->ipv6) ? _("on") : _("off")); + gui_printf (NULL, " server_ssl . . . . . . . . : %s\n", + (server->ssl) ? _("on") : _("off")); + gui_printf (NULL, " server_password . . . . . : %s\n", + (server->password && server->password[0]) ? + _("(hidden)") : ""); + gui_printf (NULL, " server_nick1/2/3 . . . . . : %s %s/ %s%s %s/ %s%s\n", + server->nick1, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + server->nick2, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + server->nick3); + gui_printf (NULL, " server_username . . . . . : %s\n", + server->username); + gui_printf (NULL, " server_realname . . . . . : %s\n", + server->realname); + gui_printf (NULL, " server_hostname . . . . . : %s\n", + (server->hostname) ? server->hostname : ""); + if (server->command && server->command[0]) + string = strdup (server->command); + else + string = NULL; + if (string) + { + if (cfg_log_hide_nickserv_pwd) + irc_display_hide_password (string, 1); + gui_printf (NULL, " server_command . . . . . . : %s\n", + string); + free (string); + } + else + gui_printf (NULL, " server_command . . . . . . : %s\n", + (server->command && server->command[0]) ? + server->command : ""); + gui_printf (NULL, " server_command_delay . . . : %d %s\n", + server->command_delay, + _("seconds")); + gui_printf (NULL, " server_autojoin . . . . . : %s\n", + (server->autojoin && server->autojoin[0]) ? + server->autojoin : ""); + gui_printf (NULL, " server_notify_levels . . . : %s\n", + (server->notify_levels && server->notify_levels[0]) ? + server->notify_levels : ""); + } + else + { + gui_printf (NULL, " %s %s%s ", + (server->is_connected) ? "*" : " ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + server->name); + gui_printf (NULL, "%s[%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (server->is_connected) ? + _("connected") : _("not connected")); + if (server->is_connected) + { + num_channels = irc_server_get_channel_count (server); + num_pv = irc_server_get_pv_count (server); + gui_printf (NULL, ", "); + gui_printf (NULL, NG_("%d channel", "%d channels", num_channels), + num_channels); + gui_printf (NULL, ", "); + gui_printf (NULL, _("%d pv"), num_pv); + } + gui_printf (NULL, "%s]%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (server->temp_server) ? _(" (temporary)") : ""); + } +} diff --git a/src/plugins/irc/irc-ignore.c b/src/plugins/irc/irc-ignore.c new file mode 100644 index 000000000..c7e8b509d --- /dev/null +++ b/src/plugins/irc/irc-ignore.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-ignore.c: manages IRC ignore list */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/command.h" +#include "../../common/log.h" +#include "../../common/util.h" + + +t_irc_ignore *irc_ignore = NULL; +t_irc_ignore *last_irc_ignore = NULL; + + +/* + * irc_ignore_check_mask: return 1 is mask1 and mask2 are the same host + * anyone or both strings may have user and/or host after + */ + +int +irc_ignore_check_mask (char *mask1, char *mask2) +{ + char *m1, *m2, *pos; + int match; + + if (!mask1 || !mask1[0] || !mask2 || !mask2[0]) + return 0; + + m1 = strdup (mask1); + m2 = strdup (mask2); + + pos = strchr (m1, '!'); + if (!pos) + { + /* remove '!' from m2 */ + pos = strchr (m2, '!'); + if (pos) + pos[0] = '\0'; + } + pos = strchr (m2, '!'); + if (!pos) + { + /* remove '!' from m1 */ + pos = strchr (m1, '!'); + if (pos) + pos[0] = '\0'; + } + + /* TODO: use regexp to match both masks */ + match = ascii_strcasecmp (m1, m2); + + free (m1); + free (m2); + + return (match == 0); +} + +/* + * irc_ignore_match: check if pointed ignore matches with arguments + */ + +int +irc_ignore_match (t_irc_ignore *ptr_ignore, char *mask, char *type, + char *channel_name, char *server_name) +{ + /* check mask */ + if ((strcmp (mask, "*") != 0) && (strcmp (ptr_ignore->mask, "*") != 0) + && (!irc_ignore_check_mask (ptr_ignore->mask, mask))) + return 0; + + /* mask is matching, go on with type */ + if ((strcmp (type, "*") != 0) && (strcmp (ptr_ignore->type, "*") != 0) + && (ascii_strcasecmp (ptr_ignore->type, type) != 0)) + return 0; + + /* mask and type matching, go on with server */ + if (server_name && server_name[0]) + { + if ((strcmp (server_name, "*") != 0) && (strcmp (ptr_ignore->server_name, "*") != 0) + && (ascii_strcasecmp (ptr_ignore->server_name, server_name) != 0)) + return 0; + } + else + { + if (strcmp (ptr_ignore->server_name, "*") != 0) + return 0; + } + + /* mask, type and server matching, go on with channel */ + if (channel_name && channel_name[0]) + { + if ((strcmp (channel_name, "*") != 0) && (strcmp (ptr_ignore->channel_name, "*") != 0) + && (ascii_strcasecmp (ptr_ignore->channel_name, channel_name) != 0)) + return 0; + } + else + { + if (strcmp (ptr_ignore->channel_name, "*") != 0) + return 0; + } + + /* all is matching => we find a ignore! */ + return 1; +} + +/* + * irc_ignore_check: check if an ignore is set for arguments + * return 1 if at least one ignore exists (message should NOT be displayed) + * 0 if no ignore found (message will be displayed) + */ + +int +irc_ignore_check (char *mask, char *type, char *channel_name, char *server_name) +{ + t_irc_ignore *ptr_ignore; + + if (!mask || !mask[0] || !type || !type[0]) + return 0; + + for (ptr_ignore = irc_ignore; ptr_ignore; + ptr_ignore = ptr_ignore->next_ignore) + { + if (irc_ignore_match (ptr_ignore, mask, type, channel_name, server_name)) + return 1; + } + + /* no ignore found */ + return 0; +} + +/* + * irc_ignore_search: search for an ignore + */ + +t_irc_ignore * +irc_ignore_search (char *mask, char *type, char *channel_name, char *server_name) +{ + t_irc_ignore *ptr_ignore; + + for (ptr_ignore = irc_ignore; ptr_ignore; + ptr_ignore = ptr_ignore->next_ignore) + { + if ((ascii_strcasecmp (ptr_ignore->mask, mask) == 0) + && (ascii_strcasecmp (ptr_ignore->type, type) == 0) + && (ascii_strcasecmp (ptr_ignore->channel_name, channel_name) == 0) + && (ascii_strcasecmp (ptr_ignore->server_name, server_name) == 0)) + return ptr_ignore; + } + + /* ignore not found */ + return NULL; +} + +/* + * irc_ignore_add: add an ignore in list + */ + +t_irc_ignore * +irc_ignore_add (char *mask, char *type, char *channel_name, char *server_name) +{ + int type_index; + t_irc_command *command_ptr; + t_irc_ignore *new_ignore; + + if (!mask || !mask[0] || !type || !type[0] || !channel_name || !channel_name[0] + || !server_name || !server_name[0]) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s too few arguments for ignore\n"), + WEECHAT_ERROR); + return NULL; + } + +#ifdef DEBUG + weechat_log_printf ("Adding ignore: mask:'%s', type:'%s', channel:'%s', " + "server:'%s'\n", + mask, type, channel_name, server_name); +#endif + + type_index = -1; + command_ptr = NULL; + + if ((strcmp (mask, "*") == 0) && (strcmp (type, "*") == 0)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s mask or type/command should be non generic value for ignore\n"), + WEECHAT_ERROR); + return NULL; + } + + if (irc_ignore_search (mask, type, channel_name, server_name)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s ignore already exists\n"), + WEECHAT_ERROR); + return NULL; + } + + /* create new ignore */ + new_ignore = (t_irc_ignore *) malloc (sizeof (t_irc_ignore)); + if (new_ignore) + { + new_ignore->mask = strdup (mask); + new_ignore->type = strdup (type); + new_ignore->server_name = strdup (server_name); + new_ignore->channel_name = strdup (channel_name); + + /* add new ignore to queue */ + new_ignore->prev_ignore = last_irc_ignore; + new_ignore->next_ignore = NULL; + if (irc_ignore) + last_irc_ignore->next_ignore = new_ignore; + else + irc_ignore = new_ignore; + last_irc_ignore = new_ignore; + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s not enough memory to create ignore\n"), + WEECHAT_ERROR); + return NULL; + } + + return new_ignore; +} + +/* + * irc_ignore_add_from_config: add an ignore to list, read from config file + * (comma serparated values) + */ + +t_irc_ignore * +irc_ignore_add_from_config (char *string) +{ + t_irc_ignore *new_ignore; + char *string2; + char *pos_mask, *pos_type, *pos_channel, *pos_server; + + if (!string || !string[0]) + return NULL; + + new_ignore = NULL; + string2 = strdup (string); + + pos_mask = string2; + pos_type = strchr (pos_mask, ','); + if (pos_type) + { + pos_type[0] = '\0'; + pos_type++; + pos_channel = strchr (pos_type, ','); + if (pos_channel) + { + pos_channel[0] = '\0'; + pos_channel++; + pos_server = strchr (pos_channel, ','); + if (pos_server) + { + pos_server[0] = '\0'; + pos_server++; + new_ignore = irc_ignore_add (pos_mask, pos_type, pos_channel, pos_server); + } + } + } + + free (string2); + return new_ignore; +} + +/* + * irc_ignore_free: free an ignore + */ + +void +irc_ignore_free (t_irc_ignore *ptr_ignore) +{ + t_irc_ignore *new_irc_ignore; + + /* free data */ + if (ptr_ignore->mask) + free (ptr_ignore->mask); + if (ptr_ignore->type) + free (ptr_ignore->type); + if (ptr_ignore->channel_name) + free (ptr_ignore->channel_name); + if (ptr_ignore->server_name) + free (ptr_ignore->server_name); + + /* remove ignore from queue */ + if (last_irc_ignore == ptr_ignore) + last_irc_ignore = ptr_ignore->prev_ignore; + if (ptr_ignore->prev_ignore) + { + (ptr_ignore->prev_ignore)->next_ignore = ptr_ignore->next_ignore; + new_irc_ignore = irc_ignore; + } + else + new_irc_ignore = ptr_ignore->next_ignore; + + if (ptr_ignore->next_ignore) + (ptr_ignore->next_ignore)->prev_ignore = ptr_ignore->prev_ignore; + + free (ptr_ignore); + irc_ignore = new_irc_ignore; +} + +/* + * irc_ignore_free_all: free all ignores + */ + +void +irc_ignore_free_all () +{ + while (irc_ignore) + irc_ignore_free (irc_ignore); +} + +/* + * irc_ignore_search_free: search and free ignore(s) + * return: number of ignore found and deleted + * 0 if no ignore found + */ + +int +irc_ignore_search_free (char *mask, char *type, + char *channel_name, char *server_name) +{ + int found; + t_irc_ignore *ptr_ignore, *next_ignore; + + found = 0; + ptr_ignore = irc_ignore; + while (ptr_ignore) + { + if (irc_ignore_match (ptr_ignore, mask, type, channel_name, server_name)) + { + found++; + if (found == 1) + gui_printf (NULL, "\n"); + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + weechat_cmd_ignore_display (_("Removing ignore:"), ptr_ignore); + next_ignore = ptr_ignore->next_ignore; + irc_ignore_free (ptr_ignore); + ptr_ignore = next_ignore; + } + else + ptr_ignore = ptr_ignore->next_ignore; + } + + return found; +} + +/* + * irc_ignore_search_free_by_number: search and free ignore(s) by number + * return: 1 if ignore found and deleted + * 0 if ignore not found + */ + +int +irc_ignore_search_free_by_number (int number) +{ + int i; + t_irc_ignore *ptr_ignore; + + if (number < 1) + return 0; + + i = 0; + for (ptr_ignore = irc_ignore; ptr_ignore; + ptr_ignore = ptr_ignore->next_ignore) + { + i++; + if (i == number) + { + gui_printf (NULL, "\n"); + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + weechat_cmd_ignore_display (_("Removing ignore:"), ptr_ignore); + irc_ignore_free (ptr_ignore); + return 1; + } + } + + /* ignore number not found */ + return 0; +} + +/* + * irc_ignore_print_log: print ignore list in log (usually for crash dump) + */ + +void +irc_ignore_print_log () +{ + t_irc_ignore *ptr_ignore; + + weechat_log_printf ("[ignore list]\n"); + + for (ptr_ignore = irc_ignore; ptr_ignore; + ptr_ignore = ptr_ignore->next_ignore) + { + weechat_log_printf ("\n"); + weechat_log_printf (" -> ignore at 0x%X:\n", ptr_ignore); + weechat_log_printf (" mask. . . . . . . : %s\n", ptr_ignore->mask); + weechat_log_printf (" type. . . . . . . : %s\n", ptr_ignore->type); + weechat_log_printf (" channel_name. . . : %s\n", ptr_ignore->channel_name); + weechat_log_printf (" server_name . . . : %s\n", ptr_ignore->server_name); + weechat_log_printf (" prev_ignore . . . : 0x%X\n", ptr_ignore->prev_ignore); + weechat_log_printf (" next_ignore . . . : 0x%X\n", ptr_ignore->next_ignore); + } +} diff --git a/src/plugins/irc/irc-mode.c b/src/plugins/irc/irc-mode.c new file mode 100644 index 000000000..766b91b5d --- /dev/null +++ b/src/plugins/irc/irc-mode.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-mode.c: IRC channel/user modes management */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/util.h" +#include "../../gui/gui.h" + + +/* + * irc_mode_channel_set_nick: set a mode for a nick on a channel + */ + +void +irc_mode_channel_set_nick (t_irc_channel *channel, char *nick, + char set_flag, int flag) +{ + t_irc_nick *ptr_nick; + + if (nick) + { + ptr_nick = irc_nick_search (channel, nick); + if (ptr_nick) + { + IRC_NICK_SET_FLAG(ptr_nick, (set_flag == '+'), flag); + irc_nick_resort (channel, ptr_nick); + gui_nicklist_draw (channel->buffer, 1, 1); + } + } +} + +/* + * irc_mode_channel_get_flag: search for flag before current position + */ + +char +irc_mode_channel_get_flag (char *str, char *pos) +{ + char set_flag; + + set_flag = '+'; + pos--; + while (pos >= str) + { + if (pos[0] == '-') + return '-'; + if (pos[0] == '+') + return '+'; + pos--; + } + return set_flag; +} + +/* + * irc_mode_channel_set: set channel modes + */ + +void +irc_mode_channel_set (t_irc_server *server, t_irc_channel *channel, + char *modes) +{ + char *pos_args, set_flag, **argv, *pos, *ptr_arg; + int argc, current_arg; + + argc = 0; + argv = NULL; + current_arg = 0; + pos_args = strchr (modes, ' '); + if (pos_args) + { + pos_args[0] = '\0'; + pos_args++; + while (pos_args[0] == ' ') + pos_args++; + argv = explode_string (pos_args, " ", 0, &argc); + if (argc > 0) + current_arg = argc - 1; + } + + if (modes && modes[0]) + { + set_flag = '+'; + pos = modes + strlen (modes) - 1; + while (pos >= modes) + { + switch (pos[0]) + { + case ':': + case ' ': + case '+': + case '-': + break; + default: + set_flag = irc_mode_channel_get_flag (modes, pos); + switch (pos[0]) + { + case 'a': /* channel admin (unrealircd specific flag) */ + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + if (irc_mode_nick_prefix_allowed (server, '~')) + irc_mode_channel_set_nick (channel, ptr_arg, + set_flag, IRC_NICK_CHANADMIN); + break; + case 'b': /* ban (ignored) */ + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + break; + case 'h': /* half-op */ + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + if (irc_mode_nick_prefix_allowed (server, '%')) + irc_mode_channel_set_nick (channel, ptr_arg, + set_flag, IRC_NICK_HALFOP); + break; + case 'k': /* channel key */ + if (channel->key) + { + free (channel->key); + channel->key = NULL; + } + if (set_flag == '+') + { + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + if (ptr_arg) + channel->key = strdup (ptr_arg); + } + break; + case 'l': /* channel limit */ + if (set_flag == '-') + channel->limit = 0; + if (set_flag == '+') + { + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + if (ptr_arg) + channel->limit = atoi (ptr_arg); + } + break; + case 'o': /* op */ + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + if (irc_mode_nick_prefix_allowed (server, '@')) + irc_mode_channel_set_nick (channel, ptr_arg, + set_flag, IRC_NICK_OP); + break; + case 'q': /* channel owner (unrealircd specific flag) */ + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + if (irc_mode_nick_prefix_allowed (server, '~')) + irc_mode_channel_set_nick (channel, ptr_arg, + set_flag, IRC_NICK_CHANOWNER); + break; + case 'u': /* channel user */ + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + if (irc_mode_nick_prefix_allowed (server, '-')) + irc_mode_channel_set_nick (channel, ptr_arg, + set_flag, IRC_NICK_CHANUSER); + break; + case 'v': /* voice */ + ptr_arg = ((argc > 0) && (current_arg >= 0)) ? + argv[current_arg--] : NULL; + if (irc_mode_nick_prefix_allowed (server, '+')) + irc_mode_channel_set_nick (channel, ptr_arg, + set_flag, IRC_NICK_VOICE); + break; + } + break; + } + pos--; + } + } + + if (argv) + free_exploded_string (argv); +} + +/* + * irc_mode_user_add: add a user mode + */ + +void +irc_mode_user_add (t_irc_server *server, char mode) +{ + char str_mode[2]; + + str_mode[0] = mode; + str_mode[1] = '\0'; + + if (server->nick_modes) + { + if (!strchr (server->nick_modes, mode)) + { + server->nick_modes = (char *) realloc (server->nick_modes, + strlen (server->nick_modes) + 1 + 1); + strcat (server->nick_modes, str_mode); + gui_status_draw (gui_current_window->buffer, 1); + gui_input_draw (gui_current_window->buffer, 1); + } + } + else + { + server->nick_modes = (char *) malloc (2); + strcpy (server->nick_modes, str_mode); + gui_status_draw (gui_current_window->buffer, 1); + gui_input_draw (gui_current_window->buffer, 1); + } +} + +/* + * irc_mode_user_remove: remove a user mode + */ + +void +irc_mode_user_remove (t_irc_server *server, char mode) +{ + char *pos; + int new_size; + + if (server->nick_modes) + { + pos = strchr (server->nick_modes, mode); + if (pos) + { + new_size = strlen (server->nick_modes); + memmove (pos, pos + 1, strlen (pos + 1) + 1); + server->nick_modes = (char *) realloc (server->nick_modes, + new_size); + gui_status_draw (gui_current_window->buffer, 1); + gui_input_draw (gui_current_window->buffer, 1); + } + } +} + +/* + * irc_mode_user_set: set user modes + */ + +void +irc_mode_user_set (t_irc_server *server, char *modes) +{ + char set_flag; + + set_flag = '+'; + while (modes && modes[0]) + { + switch (modes[0]) + { + case ':': + case ' ': + break; + case '+': + set_flag = '+'; + break; + case '-': + set_flag = '-'; + break; + default: + if (set_flag == '+') + irc_mode_user_add (server, modes[0]); + else + irc_mode_user_remove (server, modes[0]); + break; + } + modes++; + } +} + +/* + * irc_mode_nick_prefix_allowed: return <> 0 if nick prefix is allowed by server + * for example : + * IRC: 005 (...) PREFIX=(ov)@+ + * => allowed prefixes: @+ + */ + +int +irc_mode_nick_prefix_allowed (t_irc_server *server, char prefix) +{ + char str[2]; + + /* if server did not send any prefix info, then use default prefixes */ + if (!server->prefix) + { + str[0] = prefix; + str[1] = '\0'; + return (strpbrk (str, IRC_DEFAULT_PREFIXES_LIST)) ? 1 : 0; + } + + return (strchr (server->prefix, prefix) != NULL); +} diff --git a/src/plugins/irc/irc-nick.c b/src/plugins/irc/irc-nick.c new file mode 100644 index 000000000..27e5378a8 --- /dev/null +++ b/src/plugins/irc/irc-nick.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-nick.c: manages nick list for channels */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/log.h" +#include "../../common/utf8.h" +#include "../../common/util.h" +#include "../../common/weeconfig.h" + + +/* + * irc_nick_find_color: find a color for a nick (according to nick letters) + */ + +int +irc_nick_find_color (t_irc_nick *nick) +{ + int i, color; + + color = 0; + for (i = strlen (nick->nick) - 1; i >= 0; i--) + { + color += (int)(nick->nick[i]); + } + color = (color % cfg_look_color_nicks_number); + + return GUI_COLOR_WIN_NICK_1 + color; +} + +/* + * irc_nick_score_for_sort: return score for sorting nick, according to privileges + */ + +int +irc_nick_score_for_sort (t_irc_nick *nick) +{ + if (nick->flags & IRC_NICK_CHANOWNER) + return -128; + if (nick->flags & IRC_NICK_CHANADMIN) + return -64; + if (nick->flags & IRC_NICK_CHANADMIN2) + return -32; + if (nick->flags & IRC_NICK_OP) + return -16; + if (nick->flags & IRC_NICK_HALFOP) + return -8; + if (nick->flags & IRC_NICK_VOICE) + return -4; + if (nick->flags & IRC_NICK_CHANUSER) + return -2; + return 0; +} + +/* + * irc_nick_compare: compare two nicks + * return: -1 is nick1 < nick2 + * 0 if nick1 = nick2 + * +1 if nick1 > nick2 + * status sort: operator > voice > normal nick + */ + +int +irc_nick_compare (t_irc_nick *nick1, t_irc_nick *nick2) +{ + int score1, score2, comp; + + score1 = irc_nick_score_for_sort (nick1); + score2 = irc_nick_score_for_sort (nick2); + + comp = ascii_strcasecmp (nick1->nick, nick2->nick); + if (comp > 0) + score1++; + if (comp < 0) + score2++; + + /* nick1 > nick2 */ + if (score1 > score2) + return 1; + /* nick1 < nick2 */ + if (score1 < score2) + return -1; + /* nick1 == nick2 */ + return 0; +} + +/* + * irc_nick_find_pos: find position for a nick (for sorting nick list) + */ + +t_irc_nick * +irc_nick_find_pos (t_irc_channel *channel, t_irc_nick *nick) +{ + t_irc_nick *ptr_nick; + + for (ptr_nick = channel->nicks; ptr_nick; ptr_nick = ptr_nick->next_nick) + { + if (irc_nick_compare (nick, ptr_nick) < 0) + return ptr_nick; + } + return NULL; +} + +/* + * irc_nick_insert_sorted: insert nick into sorted list + */ + +void +irc_nick_insert_sorted (t_irc_channel *channel, t_irc_nick *nick) +{ + t_irc_nick *pos_nick; + + if (channel->nicks) + { + pos_nick = irc_nick_find_pos (channel, nick); + + if (pos_nick) + { + /* insert nick into the list (before nick found) */ + nick->prev_nick = pos_nick->prev_nick; + nick->next_nick = pos_nick; + if (pos_nick->prev_nick) + pos_nick->prev_nick->next_nick = nick; + else + channel->nicks = nick; + pos_nick->prev_nick = nick; + } + else + { + /* add nick to the end */ + nick->prev_nick = channel->last_nick; + nick->next_nick = NULL; + channel->last_nick->next_nick = nick; + channel->last_nick = nick; + } + } + else + { + nick->prev_nick = NULL; + nick->next_nick = NULL; + channel->nicks = nick; + channel->last_nick = nick; + } +} + +/* + * irc_nick_resort: resort nick in the list + */ + +void +irc_nick_resort (t_irc_channel *channel, t_irc_nick *nick) +{ + /* temporarly remove nick from list */ + if (nick == channel->nicks) + channel->nicks = nick->next_nick; + else + nick->prev_nick->next_nick = nick->next_nick; + if (nick->next_nick) + nick->next_nick->prev_nick = nick->prev_nick; + if (nick == channel->last_nick) + channel->last_nick = nick->prev_nick; + + /* insert again nick into sorted list */ + irc_nick_insert_sorted (channel, nick); +} + +/* + * irc_nick_new: allocate a new nick for a channel and add it to the nick list + */ + +t_irc_nick * +irc_nick_new (t_irc_server *server, t_irc_channel *channel, char *nick_name, + int is_chanowner, int is_chanadmin, int is_chanadmin2, int is_op, + int is_halfop, int has_voice, int is_chanuser) +{ + t_irc_nick *new_nick; + + /* nick already exists on this channel? */ + if ((new_nick = irc_nick_search (channel, nick_name))) + { + /* update nick */ + IRC_NICK_SET_FLAG(new_nick, is_chanowner, IRC_NICK_CHANOWNER); + IRC_NICK_SET_FLAG(new_nick, is_chanadmin, IRC_NICK_CHANADMIN); + IRC_NICK_SET_FLAG(new_nick, is_chanadmin2, IRC_NICK_CHANADMIN2); + IRC_NICK_SET_FLAG(new_nick, is_op, IRC_NICK_OP); + IRC_NICK_SET_FLAG(new_nick, is_halfop, IRC_NICK_HALFOP); + IRC_NICK_SET_FLAG(new_nick, has_voice, IRC_NICK_VOICE); + IRC_NICK_SET_FLAG(new_nick, is_chanuser, IRC_NICK_CHANUSER); + irc_nick_resort (channel, new_nick); + return new_nick; + } + + /* alloc memory for new nick */ + if ((new_nick = (t_irc_nick *) malloc (sizeof (t_irc_nick))) == NULL) + return NULL; + + /* initialize new nick */ + new_nick->nick = strdup (nick_name); + new_nick->host = NULL; + new_nick->flags = 0; + IRC_NICK_SET_FLAG(new_nick, is_chanowner, IRC_NICK_CHANOWNER); + IRC_NICK_SET_FLAG(new_nick, is_chanadmin, IRC_NICK_CHANADMIN); + IRC_NICK_SET_FLAG(new_nick, is_chanadmin2, IRC_NICK_CHANADMIN2); + IRC_NICK_SET_FLAG(new_nick, is_op, IRC_NICK_OP); + IRC_NICK_SET_FLAG(new_nick, is_halfop, IRC_NICK_HALFOP); + IRC_NICK_SET_FLAG(new_nick, has_voice, IRC_NICK_VOICE); + IRC_NICK_SET_FLAG(new_nick, is_chanuser, IRC_NICK_CHANUSER); + if (ascii_strcasecmp (new_nick->nick, server->nick) == 0) + new_nick->color = GUI_COLOR_WIN_NICK_SELF; + else + new_nick->color = irc_nick_find_color (new_nick); + + irc_nick_insert_sorted (channel, new_nick); + + channel->nicks_count++; + + channel->nick_completion_reset = 1; + + /* all is ok, return address of new nick */ + return new_nick; +} + +/* + * irc_nick_change: change nickname and move it if necessary (list is sorted) + */ + +void +irc_nick_change (t_irc_channel *channel, t_irc_nick *nick, char *new_nick) +{ + int nick_is_me; + t_weelist *ptr_weelist; + + nick_is_me = (strcmp (nick->nick, GUI_SERVER(channel->buffer)->nick) == 0) ? 1 : 0; + + if (!nick_is_me && channel->nicks_speaking) + { + ptr_weelist = weelist_search (channel->nicks_speaking, nick->nick); + if (ptr_weelist && ptr_weelist->data) + { + free (ptr_weelist->data); + ptr_weelist->data = strdup (new_nick); + } + } + + /* change nickname */ + if (nick->nick) + free (nick->nick); + nick->nick = strdup (new_nick); + if (nick_is_me) + nick->color = GUI_COLOR_WIN_NICK_SELF; + else + nick->color = irc_nick_find_color (nick); + + /* insert again nick into sorted list */ + irc_nick_resort (channel, nick); +} + +/* + * irc_nick_free: free a nick and remove it from nicks queue + */ + +void +irc_nick_free (t_irc_channel *channel, t_irc_nick *nick) +{ + t_irc_nick *new_nicks; + + if (!channel || !nick) + return; + + /* remove nick from queue */ + if (channel->last_nick == nick) + channel->last_nick = nick->prev_nick; + if (nick->prev_nick) + { + (nick->prev_nick)->next_nick = nick->next_nick; + new_nicks = channel->nicks; + } + else + new_nicks = nick->next_nick; + + if (nick->next_nick) + (nick->next_nick)->prev_nick = nick->prev_nick; + + channel->nicks_count--; + + /* free data */ + if (nick->nick) + free (nick->nick); + if (nick->host) + free (nick->host); + free (nick); + channel->nicks = new_nicks; + + channel->nick_completion_reset = 1; +} + +/* + * irc_nick_free_all: free all allocated nicks for a channel + */ + +void +irc_nick_free_all (t_irc_channel *channel) +{ + if (!channel) + return; + + /* remove all nicks for the channel */ + while (channel->nicks) + irc_nick_free (channel, channel->nicks); + + /* sould be zero, but prevent any bug :D */ + channel->nicks_count = 0; +} + +/* + * irc_nick_search: returns pointer on a nick + */ + +t_irc_nick * +irc_nick_search (t_irc_channel *channel, char *nickname) +{ + t_irc_nick *ptr_nick; + + if (!nickname) + return NULL; + + for (ptr_nick = channel->nicks; ptr_nick; + ptr_nick = ptr_nick->next_nick) + { + if (ascii_strcasecmp (ptr_nick->nick, nickname) == 0) + return ptr_nick; + } + return NULL; +} + +/* + * irc_nick_count: returns number of nicks (total, op, halfop, voice) on a channel + */ + +void +irc_nick_count (t_irc_channel *channel, int *total, int *count_op, + int *count_halfop, int *count_voice, int *count_normal) +{ + t_irc_nick *ptr_nick; + + (*total) = 0; + (*count_op) = 0; + (*count_halfop) = 0; + (*count_voice) = 0; + (*count_normal) = 0; + for (ptr_nick = channel->nicks; ptr_nick; + ptr_nick = ptr_nick->next_nick) + { + (*total)++; + if ((ptr_nick->flags & IRC_NICK_CHANOWNER) || + (ptr_nick->flags & IRC_NICK_CHANADMIN) || + (ptr_nick->flags & IRC_NICK_CHANADMIN2) || + (ptr_nick->flags & IRC_NICK_OP)) + (*count_op)++; + else + { + if (ptr_nick->flags & IRC_NICK_HALFOP) + (*count_halfop)++; + else + { + if (ptr_nick->flags & IRC_NICK_VOICE) + (*count_voice)++; + else + (*count_normal)++; + } + } + } +} + +/* + * irc_nick_get_max_length: returns longer nickname on a channel + */ + +int +irc_nick_get_max_length (t_irc_channel *channel) +{ + int length, max_length; + t_irc_nick *ptr_nick; + + max_length = 0; + for (ptr_nick = channel->nicks; ptr_nick; ptr_nick = ptr_nick->next_nick) + { + length = utf8_width_screen (ptr_nick->nick); + if (length > max_length) + max_length = length; + } + return max_length; +} + +/* + * irc_nick_set_away: set/unset away status for a channel + */ + +void +irc_nick_set_away (t_irc_channel *channel, t_irc_nick *nick, int is_away) +{ + if ((cfg_irc_away_check > 0) + && ((cfg_irc_away_check_max_nicks == 0) || + (channel->nicks_count <= cfg_irc_away_check_max_nicks))) + { + if (((is_away) && (!(nick->flags & IRC_NICK_AWAY))) || + ((!is_away) && (nick->flags & IRC_NICK_AWAY))) + { + IRC_NICK_SET_FLAG(nick, is_away, IRC_NICK_AWAY); + gui_nicklist_draw (channel->buffer, 0, 0); + } + } +} + +/* + * irc_nick_print_log: print nick infos in log (usually for crash dump) + */ + +void +irc_nick_print_log (t_irc_nick *nick) +{ + weechat_log_printf ("=> nick %s (addr:0x%X)]\n", nick->nick, nick); + weechat_log_printf (" host . . . . . : %s\n", nick->host); + weechat_log_printf (" flags. . . . . : %d\n", nick->flags); + weechat_log_printf (" color. . . . . : %d\n", nick->color); + weechat_log_printf (" prev_nick. . . : 0x%X\n", nick->prev_nick); + weechat_log_printf (" next_nick. . . : 0x%X\n", nick->next_nick); +} diff --git a/src/plugins/irc/irc-recv.c b/src/plugins/irc/irc-recv.c new file mode 100644 index 000000000..4e0d195ad --- /dev/null +++ b/src/plugins/irc/irc-recv.c @@ -0,0 +1,5058 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-recv.c: implementation of IRC commands (server to client), + according to RFC 1459,2810,2811,2812 */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <wctype.h> +#include <sys/time.h> +#include <time.h> +#include <sys/utsname.h> +#include <regex.h> + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/alias.h" +#include "../../common/command.h" +#include "../../common/hotlist.h" +#include "../../common/utf8.h" +#include "../../common/util.h" +#include "../../common/weeconfig.h" +#include "../../gui/gui.h" + +#ifdef PLUGINS +#include "../../plugins/plugins.h" +#endif + + +char *irc_last_command_received = NULL; +int command_ignored, command_force_highlight; + + +/* + * irc_recv_is_word_char: return 1 if given character is a "word character" + */ + +int +irc_recv_is_word_char (char *str) +{ + wint_t c = utf8_get_wc (str); + + if (c == WEOF) + return 0; + + if (iswalnum (c)) + return 1; + + switch (c) + { + case '-': + case '_': + case '|': + return 1; + } + + /* not a 'word char' */ + return 0; +} + +/* + * irc_recv_command_is_numeric: return 1 if given string is 100% numeric + */ + +int +irc_recv_command_is_numeric (char *str) +{ + while (str && str[0]) + { + if (!isdigit (str[0])) + return 0; + str++; + } + return 1; +} + +/* + * irc_recv_is_highlight: return 1 if given message contains highlight (with given nick + * or at least one of string in "irc_higlight" setting) + */ + +int +irc_recv_is_highlight (char *message, char *nick) +{ + char *msg, *highlight, *match, *match_pre, *match_post, *msg_pos, *pos, *pos_end; + int end, length, startswith, endswith, wildcard_start, wildcard_end; + + /* empty message ? */ + if (!message || !message[0]) + return 0; + + /* highlight asked by a plugin */ + if (command_force_highlight) + return 1; + + /* highlight by nickname */ + match = strstr (message, nick); + if (match) + { + match_pre = utf8_prev_char (message, match); + if (!match_pre) + match_pre = match - 1; + match_post = match + strlen(nick); + startswith = ((match == message) || (!irc_recv_is_word_char (match_pre))); + endswith = ((!match_post[0]) || (!irc_recv_is_word_char (match_post))); + if (startswith && endswith) + return 1; + } + + /* no highlight by nickname and "irc_highlight" is empty */ + if (!cfg_irc_highlight || !cfg_irc_highlight[0]) + return 0; + + /* convert both strings to lower case */ + if ((msg = strdup (message)) == NULL) + return 0; + if ((highlight = strdup (cfg_irc_highlight)) == NULL) + { + free (msg); + return 0; + } + pos = msg; + while (pos[0]) + { + pos[0] = tolower (pos[0]); + pos++; + } + pos = highlight; + while (pos[0]) + { + pos[0] = tolower (pos[0]); + pos++; + } + + /* look in "irc_highlight" for highlight */ + pos = highlight; + end = 0; + while (!end) + { + pos_end = strchr (pos, ','); + if (!pos_end) + { + pos_end = strchr (pos, '\0'); + end = 1; + } + /* error parsing string! */ + if (!pos_end) + { + free (msg); + free (highlight); + return 0; + } + + length = pos_end - pos; + pos_end[0] = '\0'; + if (length > 0) + { + if ((wildcard_start = (pos[0] == '*'))) + { + pos++; + length--; + } + if ((wildcard_end = (*(pos_end - 1) == '*'))) + { + *(pos_end - 1) = '\0'; + length--; + } + } + + if (length > 0) + { + msg_pos = msg; + /* highlight found! */ + while ((match = strstr (msg_pos, pos)) != NULL) + { + match_pre = match - 1; + match_pre = utf8_prev_char (msg, match); + if (!match_pre) + match_pre = match - 1; + match_post = match + length; + startswith = ((match == msg) || (!irc_recv_is_word_char (match_pre))); + endswith = ((!match_post[0]) || (!irc_recv_is_word_char (match_post))); + if ((wildcard_start && wildcard_end) || + (!wildcard_start && !wildcard_end && + startswith && endswith) || + (wildcard_start && endswith) || + (wildcard_end && startswith)) + { + free (msg); + free (highlight); + return 1; + } + msg_pos = match_post; + } + } + + if (!end) + pos = pos_end + 1; + } + + /* no highlight found with "irc_highlight" list */ + free (msg); + free (highlight); + return 0; +} + +/* + * irc_recv_command: executes action when receiving IRC command + * returns: 0 = all ok, command executed + * -1 = command failed + * -2 = no command to execute + * -3 = command not found + */ + +int +irc_recv_command (t_irc_server *server, char *entire_line, + char *host, char *command, char *arguments) +{ + int i, cmd_found, return_code; + char *pos, *nick; + char *dup_entire_line, *dup_host, *dup_arguments; + t_irc_recv_func *cmd_recv_func; + char *cmd_name; + + if (!command) + return -2; + + /* look for IRC command */ + cmd_found = -1; + for (i = 0; irc_commands[i].command_name; i++) + { + if (ascii_strcasecmp (irc_commands[i].command_name, command) == 0) + { + cmd_found = i; + break; + } + } + + /* command not found */ + if (cmd_found < 0) + { + /* for numeric commands, we use default recv function (irc_recv_server_msg) */ + if (irc_recv_command_is_numeric (command)) + { + cmd_name = command; + cmd_recv_func = irc_recv_cmd_server_msg; + } + else + return -3; + } + else + { + cmd_name = irc_commands[cmd_found].command_name; + cmd_recv_func = irc_commands[cmd_found].recv_function; + } + + if (cmd_recv_func != NULL) + { + dup_entire_line = (entire_line) ? strdup (entire_line) : NULL; + dup_host = (host) ? strdup (host) : NULL; + dup_arguments = (arguments) ? strdup (arguments) : NULL; + + command_ignored = irc_ignore_check (dup_host, + cmd_name, + NULL, + server->name); + command_force_highlight = 0; +#ifdef PLUGINS + return_code = plugin_msg_handler_exec (server->name, + cmd_name, + dup_entire_line); + /* plugin handler choosed to discard message for WeeChat, + so we ignore this message in standard handler */ + if (return_code & PLUGIN_RC_OK_IGNORE_WEECHAT) + command_ignored = 1; + /* plugin asked for highlight ? */ + if (return_code & PLUGIN_RC_OK_WITH_HIGHLIGHT) + command_force_highlight = 1; +#endif + pos = (dup_host) ? strchr (dup_host, '!') : NULL; + if (pos) + pos[0] = '\0'; + nick = (dup_host) ? strdup (dup_host) : NULL; + if (pos) + pos[0] = '!'; + irc_last_command_received = strdup (dup_entire_line); + return_code = (int) (cmd_recv_func) (server, dup_host, nick, + dup_arguments); + if (irc_last_command_received) + free (irc_last_command_received); + if (nick) + free (nick); + if (dup_entire_line) + free (dup_entire_line); + if (dup_host) + free (dup_host); + if (dup_arguments) + free (dup_arguments); + return return_code; + } + + return 0; +} + +/* + * irc_recv_cmd_error: error received from server + */ + +int +irc_recv_cmd_error (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + int first; + t_gui_buffer *ptr_buffer; + t_irc_channel *ptr_channel; + + /* make C compiler happy */ + (void) host; + (void) nick; + + first = 1; + ptr_buffer = server->buffer; + + while (arguments && arguments[0]) + { + while (arguments[0] == ' ') + arguments++; + + if (arguments[0] == ':') + { + arguments++; + if (first) + irc_display_prefix (server, ptr_buffer, GUI_PREFIX_ERROR); + gui_printf (ptr_buffer, "%s%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (first) ? "" : ": ", + arguments); + if (strncmp (arguments, "Closing Link", 12) == 0) + irc_server_disconnect (server, 1); + arguments = NULL; + } + else + { + pos = strchr (arguments, ' '); + if (pos) + pos[0] = '\0'; + if (strcasecmp (arguments, server->nick) != 0) + { + if (first) + { + ptr_channel = irc_channel_search (server, arguments); + if (ptr_channel) + ptr_buffer = ptr_channel->buffer; + irc_display_prefix (server, ptr_buffer, GUI_PREFIX_ERROR); + } + gui_printf (ptr_buffer, "%s%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + (first) ? "" : " ", + arguments); + first = 0; + } + if (pos) + arguments = pos + 1; + else + arguments = NULL; + } + } + + return 0; +} + +/* + * irc_recv_cmd_invite: 'invite' message received + */ + +int +irc_recv_cmd_invite (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel; + + pos_channel = strchr (arguments, ' '); + if (pos_channel) + { + pos_channel[0] = '\0'; + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + if (pos_channel[0] == ':') + pos_channel++; + + command_ignored |= irc_ignore_check (host, "invite", pos_channel, server->name); + + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, _("You have been invited to %s%s%s by %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + if (gui_add_hotlist + && ((server->buffer->num_displayed == 0) || (gui_buffer_is_scrolled (server->buffer)))) + { + hotlist_add (HOTLIST_HIGHLIGHT, NULL, server, server->buffer, 0); + gui_status_draw (gui_current_window->buffer, 1); + } + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s channel \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, "", "invite"); + return -1; + } + return 0; +} + + +/* + * irc_recv_cmd_join: 'join' message received + */ + +int +irc_recv_cmd_join (t_irc_server *server, char *host, char *nick, char *arguments) +{ + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + char *pos; + + /* no host => we can't identify sender of message! */ + if (!host) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command received without host\n"), + WEECHAT_ERROR, "join"); + return -1; + } + + if (arguments[0] == ':') + arguments++; + + command_ignored |= irc_ignore_check (host, "join", arguments, server->name); + + ptr_channel = irc_channel_search (server, arguments); + if (!ptr_channel) + { + ptr_channel = irc_channel_new (server, IRC_CHANNEL_TYPE_CHANNEL, arguments); + if (!ptr_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot create new channel \"%s\"\n"), + WEECHAT_ERROR, arguments); + return -1; + } + gui_buffer_new (gui_current_window, server, ptr_channel, + GUI_BUFFER_TYPE_STANDARD, 1); + } + + pos = strchr (host, '!'); + if (!command_ignored) + { + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_JOIN); + gui_printf (ptr_channel->buffer, + _("%s%s %s(%s%s%s)%s has joined %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + (pos) ? pos + 1 : host, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + arguments); + } + + /* remove topic and display channel creation date if joining new channel */ + if (!ptr_channel->nicks) + { + if (ptr_channel->topic) + { + free (ptr_channel->topic); + ptr_channel->topic = NULL; + gui_chat_draw_title (ptr_channel->buffer, 1); + } + ptr_channel->display_creation_date = 1; + } + + /* add nick in channel */ + ptr_nick = irc_nick_new (server, ptr_channel, nick, 0, 0, 0, 0, 0, 0, 0); + if (ptr_nick) + ptr_nick->host = strdup ((pos) ? pos + 1 : host); + + /* redraw nicklist and status bar */ + gui_nicklist_draw (ptr_channel->buffer, 1, 1); + gui_status_draw (ptr_channel->buffer, 1); + return 0; +} + +/* + * irc_recv_cmd_kick: 'kick' message received + */ + +int +irc_recv_cmd_kick (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_comment; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + pos_nick[0] = '\0'; + pos_nick++; + while (pos_nick[0] == ' ') + pos_nick++; + + pos_comment = strchr (pos_nick, ' '); + if (pos_comment) + { + pos_comment[0] = '\0'; + pos_comment++; + while (pos_comment[0] == ' ') + pos_comment++; + if (pos_comment[0] == ':') + pos_comment++; + } + + command_ignored |= irc_ignore_check (host, "kick", arguments, server->name); + + ptr_channel = irc_channel_search (server, arguments); + if (!ptr_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s channel \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, arguments, "kick"); + return -1; + } + + if (!command_ignored) + { + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_PART); + gui_printf (ptr_channel->buffer, _("%s%s%s has kicked %s%s%s from %s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + arguments); + if (pos_comment) + gui_printf (ptr_channel->buffer, " %s(%s%s%s)\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_comment, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + else + gui_printf (ptr_channel->buffer, "\n"); + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s nick \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, "", "kick"); + return -1; + } + if (strcmp (pos_nick, server->nick) == 0) + { + /* my nick was kicked => free all nicks, channel is not active any more */ + irc_nick_free_all (ptr_channel); + gui_nicklist_draw (ptr_channel->buffer, 1, 1); + gui_status_draw (ptr_channel->buffer, 1); + if (server->autorejoin) + irc_send_cmd_join (server, NULL, ptr_channel->name); + } + { + /* someone was kicked from channel (but not me) => remove only this nick */ + ptr_nick = irc_nick_search (ptr_channel, pos_nick); + if (ptr_nick) + { + irc_nick_free (ptr_channel, ptr_nick); + gui_nicklist_draw (ptr_channel->buffer, 1, 1); + gui_status_draw (ptr_channel->buffer, 1); + } + } + return 0; +} + +/* + * irc_recv_cmd_kill: 'kill' message received + */ + +int +irc_recv_cmd_kill (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_host2, *pos_comment; + t_irc_channel *ptr_channel; + + pos_host2 = strchr (arguments, ' '); + if (pos_host2) + { + pos_host2[0] = '\0'; + pos_host2++; + while (pos_host2[0] == ' ') + pos_host2++; + + if (pos_host2[0] == ':') + pos_comment = pos_host2 + 1; + else + { + pos_comment = strchr (pos_host2, ' '); + if (pos_comment) + { + pos_comment[0] = '\0'; + pos_comment++; + while (pos_comment[0] == ' ') + pos_comment++; + if (pos_comment[0] == ':') + pos_comment++; + } + } + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (!command_ignored + && !irc_ignore_check (host, "kill", ptr_channel->name, server->name)) + { + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_PART); + gui_printf (ptr_channel->buffer, _("%s%s%s has killed %s%s%s from server"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + if (pos_comment) + gui_printf (ptr_channel->buffer, " %s(%s%s%s)\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_comment, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + else + gui_printf (ptr_channel->buffer, "\n"); + } + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s host not found for \"%s\" command\n"), + WEECHAT_ERROR, "kill"); + return -1; + } + return 0; +} + +/* + * irc_recv_cmd_mode: 'mode' message received + */ + +int +irc_recv_cmd_mode (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_modes, *pos; + t_irc_channel *ptr_channel; + + /* no host => we can't identify sender of message! */ + if (!host) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command received without host\n"), + WEECHAT_ERROR, "mode"); + return -1; + } + + pos_modes = strchr (arguments, ' '); + if (!pos_modes) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command received without channel or nickname\n"), + WEECHAT_ERROR, "mode"); + return -1; + } + pos_modes[0] = '\0'; + pos_modes++; + while (pos_modes[0] == ' ') + pos_modes++; + if (pos_modes[0] == ':') + pos_modes++; + + /* remove spaces after modes */ + pos = pos_modes + strlen (pos_modes) - 1; + while ((pos >= pos_modes) && (pos[0] == ' ')) + { + pos[0] = '\0'; + pos--; + } + + if (irc_channel_is_channel (arguments)) + { + ptr_channel = irc_channel_search (server, arguments); + if (ptr_channel) + { + command_ignored |= irc_ignore_check (host, "mode", + ptr_channel->name, server->name); + if (!command_ignored) + { + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_INFO); + gui_printf (ptr_channel->buffer, + _("Mode %s%s %s[%s%s%s]%s by %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_channel->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_modes, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + } + irc_mode_channel_set (server, ptr_channel, pos_modes); + irc_server_sendf (server, "MODE %s", ptr_channel->name); + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s channel \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, arguments, "mode"); + return -1; + } + } + else + { + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("User mode %s[%s%s%s]%s by %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_modes, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + } + irc_mode_user_set (server, pos_modes); + } + return 0; +} + +/* + * irc_recv_cmd_nick: 'nick' message received + */ + +int +irc_recv_cmd_nick (t_irc_server *server, char *host, char *nick, char *arguments) +{ + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + int nick_is_me; + t_gui_window *ptr_win; + t_gui_buffer *ptr_buffer; + + /* no host => we can't identify sender of message! */ + if (!host) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command received without host\n"), + WEECHAT_ERROR, "nick"); + return -1; + } + + if (arguments[0] == ':') + arguments++; + + /* change nickname in any opened private window */ + for (ptr_buffer = gui_buffers; ptr_buffer; + ptr_buffer = ptr_buffer->next_buffer) + { + if ((GUI_SERVER(ptr_buffer) == server) && GUI_BUFFER_IS_PRIVATE(ptr_buffer)) + { + if ((GUI_CHANNEL(ptr_buffer)->name) + && (ascii_strcasecmp (nick, GUI_CHANNEL(ptr_buffer)->name) == 0)) + { + ptr_channel = irc_channel_search_any (server, arguments); + if (!ptr_channel) + { + free (GUI_CHANNEL(ptr_buffer)->name); + GUI_CHANNEL(ptr_buffer)->name = strdup (arguments); + } + } + } + } + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + ptr_nick = irc_nick_search (ptr_channel, nick); + if (ptr_nick) + { + nick_is_me = (strcmp (ptr_nick->nick, server->nick) == 0) ? 1 : 0; + if (nick_is_me) + gui_add_hotlist = 0; + irc_nick_change (ptr_channel, ptr_nick, arguments); + if (!command_ignored + && !irc_ignore_check (host, "nick", ptr_channel->name, server->name)) + { + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_INFO); + if (nick_is_me) + gui_printf (ptr_channel->buffer, _("You are now known as %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments); + else + gui_printf (ptr_channel->buffer, _("%s%s%s is now known as %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments); + } + gui_nicklist_draw (ptr_channel->buffer, 1, 1); + gui_add_hotlist = 1; + } + } + + if (strcmp (server->nick, nick) == 0) + { + free (server->nick); + server->nick = strdup (arguments); + gui_status_draw (gui_current_window->buffer, 1); + for (ptr_win = gui_windows; ptr_win; ptr_win = ptr_win->next_window) + { + if (ptr_win->buffer->server == server) + gui_input_draw (ptr_win->buffer, 1); + } + } + else + { + gui_status_draw (gui_current_window->buffer, 1); + gui_input_draw (gui_current_window->buffer, 1); + } + + return 0; +} + +/* + * irc_recv_cmd_notice: 'notice' message received + */ + +int +irc_recv_cmd_notice (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *host2, *pos, *pos2, *pos_usec; + struct timeval tv; + long sec1, usec1, sec2, usec2, difftime; + t_irc_channel *ptr_channel; + int highlight; + + host2 = NULL; + if (host) + { + pos = strchr (host, '!'); + if (pos) + host2 = pos + 1; + } + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + if (pos[0] == ':') + pos++; + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s nickname not found for \"%s\" command\n"), + WEECHAT_ERROR, "notice"); + return -1; + } + + if (!command_ignored) + { + if (strncmp (pos, "\01VERSION", 8) == 0) + { + pos += 9; + pos2 = strchr (pos, '\01'); + if (pos2) + pos2[0] = '\0'; + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, _("CTCP %sVERSION%s reply from %s%s%s: %s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos); + } + else + { + if (strncmp (pos, "\01PING", 5) == 0) + { + pos += 5; + while (pos[0] == ' ') + pos++; + pos_usec = strchr (pos, ' '); + if (pos_usec) + { + pos_usec[0] = '\0'; + pos_usec++; + pos2 = strchr (pos_usec, '\01'); + if (pos2) + { + pos2[0] = '\0'; + + gettimeofday (&tv, NULL); + sec1 = atol (pos); + usec1 = atol (pos_usec); + sec2 = tv.tv_sec; + usec2 = tv.tv_usec; + + difftime = ((sec2 * 1000000) + usec2) - ((sec1 * 1000000) + usec1); + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, _("CTCP %sPING%s reply from %s%s%s: %ld.%ld seconds\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + difftime / 1000000, + (difftime % 1000000) / 1000); + } + } + } + else + { + if (nick && nick[0] && cfg_irc_notice_as_pv) + { + ptr_channel = irc_channel_search (server, nick); + if (!ptr_channel) + { + ptr_channel = irc_channel_new (server, IRC_CHANNEL_TYPE_PRIVATE, nick); + if (!ptr_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot create new private window \"%s\"\n"), + WEECHAT_ERROR, nick); + return -1; + } + gui_buffer_new (gui_current_window, server, ptr_channel, + GUI_BUFFER_TYPE_STANDARD, 0); + } + if (!ptr_channel->topic) + { + ptr_channel->topic = strdup ((host2) ? host2 : ""); + gui_chat_draw_title (ptr_channel->buffer, 1); + } + + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_NICK, + "%s<", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + if (irc_recv_is_highlight (pos, server->nick)) + { + gui_printf_type (ptr_channel->buffer, + GUI_MSG_TYPE_NICK | GUI_MSG_TYPE_HIGHLIGHT, + "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_HIGHLIGHT), + nick); + if ( (cfg_look_infobar_delay_highlight > 0) + && (ptr_channel->buffer != gui_current_window->buffer) ) + gui_infobar_printf (cfg_look_infobar_delay_highlight, + GUI_COLOR_WIN_INFOBAR_HIGHLIGHT, + _("Private %s> %s"), + nick, pos); + highlight = 1; + } + else + { + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_NICK, + "%s%s", + GUI_COLOR(GUI_COLOR_WIN_NICK_PRIVATE), + nick); + highlight = 0; + } + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_NICK, + "%s> ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, + "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos); +#ifdef PLUGINS + if (highlight) + (void) plugin_msg_handler_exec (server->name, + "weechat_highlight", + irc_last_command_received); +#endif + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + if (host) + { + gui_printf (server->buffer, "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), nick); + if (host2) + gui_printf (server->buffer, " %s(%s%s%s)", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + host2, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + gui_printf (server->buffer, "%s: ", + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + gui_printf (server->buffer, "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos); + if ((nick) && (ascii_strcasecmp (nick, "nickserv") != 0) && + (ascii_strcasecmp (nick, "chanserv") != 0) && + (ascii_strcasecmp (nick, "memoserv") != 0)) + { + if (gui_add_hotlist + && ((server->buffer->num_displayed == 0) || (gui_buffer_is_scrolled (server->buffer)))) + { + hotlist_add (HOTLIST_PRIVATE, NULL, server, server->buffer, 0); + gui_status_draw (gui_current_window->buffer, 1); + } + } + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_part: 'part' message received + */ + +int +irc_recv_cmd_part (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos_args, *join_string; + int join_length; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + + /* no host => we can't identify sender of message! */ + if (!host || !arguments) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command received without host or channel\n"), + WEECHAT_ERROR, "part"); + return -1; + } + + if (arguments[0] == ':') + arguments++; + + pos_args = strchr (arguments, ' '); + if (pos_args) + { + pos_args[0] = '\0'; + pos_args++; + while (pos_args[0] == ' ') + pos_args++; + if (pos_args[0] == ':') + pos_args++; + } + + ptr_channel = irc_channel_search (server, arguments); + if (ptr_channel) + { + command_ignored |= irc_ignore_check (host, "part", ptr_channel->name, server->name); + ptr_nick = irc_nick_search (ptr_channel, nick); + if (ptr_nick) + { + /* display part message */ + if (!command_ignored) + { + pos = strchr (host, '!'); + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_PART); + gui_printf (ptr_channel->buffer, _("%s%s %s(%s%s%s)%s has left %s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + (pos) ? pos + 1 : "", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_channel->name); + if (pos_args && pos_args[0]) + gui_printf (ptr_channel->buffer, " %s(%s%s%s)\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_args, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + else + gui_printf (ptr_channel->buffer, "\n"); + } + + /* part request was issued by local client ? */ + if (strcmp (ptr_nick->nick, server->nick) == 0) + { + irc_nick_free_all (ptr_channel); + + /* cycling ? => rejoin channel immediately */ + if (ptr_channel->cycle) + { + ptr_channel->cycle = 0; + if (ptr_channel->key) + { + join_length = strlen (ptr_channel->name) + 1 + + strlen (ptr_channel->key) + 1; + join_string = (char *)malloc (join_length); + if (join_string) + { + snprintf (join_string, join_length, "%s %s", + ptr_channel->name, + ptr_channel->key); + irc_send_cmd_join(server, ptr_channel, join_string); + free (join_string); + } + else + irc_send_cmd_join(server, ptr_channel, ptr_channel->name); + } + else + irc_send_cmd_join(server, ptr_channel, ptr_channel->name); + } + if (ptr_channel->close) + { + gui_buffer_free (ptr_channel->buffer, 1); + irc_channel_free (server, ptr_channel); + ptr_channel = NULL; + } + } + else + irc_nick_free (ptr_channel, ptr_nick); + + if (ptr_channel) + { + gui_nicklist_draw (ptr_channel->buffer, 1, 1); + gui_status_draw (ptr_channel->buffer, 1); + } + gui_input_draw (gui_current_window->buffer, 1); + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s channel \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, arguments, "part"); + return -1; + } + + return 0; +} + +/* + * irc_recv_cmd_ping: 'ping' command received + */ + +int +irc_recv_cmd_ping (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (arguments[0] == ':') + arguments++; + + pos = strrchr (arguments, ' '); + if (pos) + pos[0] = '\0'; + + irc_server_sendf (server, "PONG :%s", arguments); + + return 0; +} + +/* + * irc_recv_cmd_pong: 'pong' command received + */ + +int +irc_recv_cmd_pong (t_irc_server *server, char *host, char *nick, char *arguments) +{ + struct timeval tv; + int old_lag; + + /* make C compiler happy */ + (void) host; + (void) nick; + (void) arguments; + + if (server->lag_check_time.tv_sec != 0) + { + /* calculate lag (time diff with lag check) */ + old_lag = server->lag; + gettimeofday (&tv, NULL); + server->lag = (int) get_timeval_diff (&(server->lag_check_time), &tv); + if (old_lag != server->lag) + gui_status_draw (gui_current_window->buffer, 1); + + /* schedule next lag check */ + server->lag_check_time.tv_sec = 0; + server->lag_check_time.tv_usec = 0; + server->lag_next_check = time (NULL) + cfg_irc_lag_check; + } + return 0; +} + +/* + * irc_cmd_reply_version: send version in reply to "CTCP VERSION" request + */ + +void +irc_cmd_reply_version (t_irc_server *server, t_irc_channel *channel, + char *host, char *nick, char *message) +{ + char *pos; + struct utsname *buf; + t_gui_buffer *ptr_buffer; + + ptr_buffer = (channel) ? channel->buffer : server->buffer; + + command_ignored |= irc_ignore_check (host, "ctcp", NULL, server->name); + if (!command_ignored) + { + pos = strchr (message, ' '); + if (pos) + { + while (pos[0] == ' ') + pos++; + if (pos[0] == '\01') + pos = NULL; + else if (!pos[0]) + pos = NULL; + } + + buf = (struct utsname *) malloc (sizeof (struct utsname)); + if (buf && (uname (buf) >= 0)) + { + irc_server_sendf (server, + "NOTICE %s :%sVERSION %s v%s" + " compiled on %s, running " + "%s %s / %s%s", + nick, "\01", PACKAGE_NAME, PACKAGE_VERSION, __DATE__, + &buf->sysname, + &buf->release, &buf->machine, "\01"); + free (buf); + } + else + irc_server_sendf (server, + "NOTICE %s :%sVERSION %s v%s" + " compiled on %s%s", + nick, "\01", PACKAGE_NAME, PACKAGE_VERSION, __DATE__, + "\01"); + irc_display_prefix (server, ptr_buffer, GUI_PREFIX_SERVER); + gui_printf (ptr_buffer, + _("CTCP %sVERSION%s received from %s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + if (pos) + gui_printf (ptr_buffer, "%s: %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos); + else + gui_printf (ptr_buffer, "\n"); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_ctcp", + irc_last_command_received); +#endif + } +} + +/* + * irc_recv_cmd_privmsg: 'privmsg' command received + */ + +int +irc_recv_cmd_privmsg (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos2, *host2; + char *pos_file, *pos_addr, *pos_port, *pos_size, *pos_start_resume; /* for DCC */ + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + int highlight; + + /* no host => we can't identify sender of message! */ + if (!host) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command received without host\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + + pos = strchr (host, '!'); + if (pos) + host2 = pos + 1; + else + host2 = host; + + /* receiver is a channel ? */ + if (irc_channel_is_channel (arguments)) + { + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + if (pos[0] == ':') + pos++; + + ptr_channel = irc_channel_search (server, arguments); + if (ptr_channel) + { + if (strncmp (pos, "\01ACTION ", 8) == 0) + { + command_ignored |= irc_ignore_check (host, "action", ptr_channel->name, server->name); + pos += 8; + pos2 = strchr (pos, '\01'); + if (pos2) + pos2[0] = '\0'; + if (!command_ignored) + { + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_ACTION_ME); + if (irc_recv_is_highlight (pos, server->nick)) + { + gui_printf_type (ptr_channel->buffer, + GUI_MSG_TYPE_MSG | GUI_MSG_TYPE_HIGHLIGHT, + "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_HIGHLIGHT), + nick); + if ( (cfg_look_infobar) + && (cfg_look_infobar_delay_highlight > 0) + && (ptr_channel->buffer != gui_current_window->buffer) ) + gui_infobar_printf (cfg_look_infobar_delay_highlight, + GUI_COLOR_WIN_INFOBAR_HIGHLIGHT, + _("Channel %s: * %s %s"), + ptr_channel->name, nick, pos); + gui_printf (ptr_channel->buffer, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), pos); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_highlight", + irc_last_command_received); +#endif + } + else + { + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, + "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + gui_printf (ptr_channel->buffer, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), pos); + } + irc_channel_add_nick_speaking (ptr_channel, nick); + } + return 0; + } + if (strncmp (pos, "\01SOUND ", 7) == 0) + { + command_ignored |= irc_ignore_check (host, "ctcp", ptr_channel->name, server->name); + pos += 7; + pos2 = strchr (pos, '\01'); + if (pos2) + pos2[0] = '\0'; + if (!command_ignored) + { + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_SERVER); + gui_printf (ptr_channel->buffer, + _("Received a CTCP %sSOUND%s \"%s\" from %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos, + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + } + return 0; + } + if (strncmp (pos, "\01PING", 5) == 0) + { + command_ignored |= irc_ignore_check (host, "ctcp", ptr_channel->name, server->name); + pos += 5; + while (pos[0] == ' ') + pos++; + pos2 = strchr (pos, '\01'); + if (pos2) + pos2[0] = '\0'; + else + pos = NULL; + if (pos && !pos[0]) + pos = NULL; + if (pos) + irc_server_sendf (server, "NOTICE %s :\01PING %s\01", + nick, pos); + else + irc_server_sendf (server, "NOTICE %s :\01PING\01", + nick); + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_SERVER); + gui_printf (ptr_channel->buffer, + _("CTCP %sPING%s received from %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + return 0; + } + if (strncmp (pos, "\01VERSION", 8) == 0) + { + irc_cmd_reply_version (server, ptr_channel, host, nick, pos); + return 0; + } + + /* unknown CTCP ? */ + pos2 = strchr (pos + 1, '\01'); + if ((pos[0] == '\01') && pos2 && (pos2[1] == '\0')) + { + command_ignored |= irc_ignore_check (host, "ctcp", ptr_channel->name, server->name); + pos++; + pos2[0] = '\0'; + pos2 = strchr (pos, ' '); + if (pos2) + { + pos2[0] = '\0'; + pos2++; + while (pos2[0] == ' ') + pos2++; + if (!pos2[0]) + pos2 = NULL; + } + if (!command_ignored) + { + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_SERVER); + gui_printf (ptr_channel->buffer, + _("Unknown CTCP %s%s%s received from %s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + if (pos2) + gui_printf (ptr_channel->buffer, "%s: %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos2); + else + gui_printf (ptr_channel->buffer, "\n"); + } + return 0; + } + + /* other message */ + command_ignored |= irc_ignore_check (host, "privmsg", ptr_channel->name, server->name); + if (!command_ignored) + { + ptr_nick = irc_nick_search (ptr_channel, nick); + if (irc_recv_is_highlight (pos, server->nick)) + { + irc_display_nick (ptr_channel->buffer, ptr_nick, + (ptr_nick) ? NULL : nick, + GUI_MSG_TYPE_NICK | GUI_MSG_TYPE_HIGHLIGHT, + 1, GUI_COLOR_WIN_CHAT_HIGHLIGHT, 0); + if ( (cfg_look_infobar) + && (cfg_look_infobar_delay_highlight > 0) + && (ptr_channel->buffer != gui_current_window->buffer) ) + gui_infobar_printf (cfg_look_infobar_delay_highlight, + GUI_COLOR_WIN_INFOBAR_HIGHLIGHT, + _("Channel %s: %s> %s"), + ptr_channel->name, nick, pos); + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, + "%s\n", pos); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_highlight", + irc_last_command_received); +#endif + } + else + { + irc_display_nick (ptr_channel->buffer, ptr_nick, + (ptr_nick) ? NULL : nick, + GUI_MSG_TYPE_NICK, 1, -1, 0); + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, + "%s\n", pos); + } + irc_channel_add_nick_speaking (ptr_channel, nick); + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s channel \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, arguments, "privmsg"); + return -1; + } + } + } + else + { + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + if (pos[0] == ':') + pos++; + + /* version asked by another user => answer with WeeChat version */ + if (strncmp (pos, "\01VERSION", 8) == 0) + { + irc_cmd_reply_version (server, NULL, host, nick, pos); + return 0; + } + + /* ping request from another user => answer */ + if (strncmp (pos, "\01PING", 5) == 0) + { + command_ignored |= irc_ignore_check (host, "ctcp", NULL, server->name); + if (!command_ignored) + { + pos += 5; + while (pos[0] == ' ') + pos++; + pos2 = strchr (pos, '\01'); + if (pos2) + pos2[0] = '\0'; + else + pos = NULL; + if (pos && !pos[0]) + pos = NULL; + if (pos) + irc_server_sendf (server, "NOTICE %s :\01PING %s\01", + nick, pos); + else + irc_server_sendf (server, "NOTICE %s :\01PING\01", + nick); + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, + _("CTCP %sPING%s received from %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_ctcp", + irc_last_command_received); +#endif + } + return 0; + } + + /* incoming DCC file */ + if (strncmp (pos, "\01DCC SEND", 9) == 0) + { + /* check if DCC SEND is ok, i.e. with 0x01 at end */ + pos2 = strchr (pos + 1, '\01'); + if (!pos2) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2[0] = '\0'; + + command_ignored |= irc_ignore_check (host, "dcc", NULL, server->name); + + if (!command_ignored) + { + /* DCC filename */ + pos_file = pos + 9; + while (pos_file[0] == ' ') + pos_file++; + + /* look for file size */ + pos_size = strrchr (pos_file, ' '); + if (!pos_size) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2 = pos_size; + pos_size++; + while (pos2[0] == ' ') + pos2--; + pos2[1] = '\0'; + + /* look for DCC port */ + pos_port = strrchr (pos_file, ' '); + if (!pos_port) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2 = pos_port; + pos_port++; + while (pos2[0] == ' ') + pos2--; + pos2[1] = '\0'; + + /* look for DCC IP address */ + pos_addr = strrchr (pos_file, ' '); + if (!pos_addr) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2 = pos_addr; + pos_addr++; + while (pos2[0] == ' ') + pos2--; + pos2[1] = '\0'; + + irc_dcc_add (server, IRC_DCC_FILE_RECV, + strtoul (pos_addr, NULL, 10), + atoi (pos_port), nick, -1, pos_file, NULL, + strtoul (pos_size, NULL, 10)); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_dcc", + irc_last_command_received); +#endif + } + return 0; + } + + /* incoming DCC RESUME (asked by receiver) */ + if (strncmp (pos, "\01DCC RESUME", 11) == 0) + { + /* check if DCC RESUME is ok, i.e. with 0x01 at end */ + pos2 = strchr (pos + 1, '\01'); + if (!pos2) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2[0] = '\0'; + + command_ignored |= irc_ignore_check (host, "dcc", NULL, server->name); + + if (!command_ignored) + { + /* DCC filename */ + pos_file = pos + 11; + while (pos_file[0] == ' ') + pos_file++; + + /* look for resume start position */ + pos_start_resume = strrchr (pos_file, ' '); + if (!pos_start_resume) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2 = pos_start_resume; + pos_start_resume++; + while (pos2[0] == ' ') + pos2--; + pos2[1] = '\0'; + + /* look for DCC port */ + pos_port = strrchr (pos_file, ' '); + if (!pos_port) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2 = pos_port; + pos_port++; + while (pos2[0] == ' ') + pos2--; + pos2[1] = '\0'; + + irc_dcc_accept_resume (server, pos_file, atoi (pos_port), + strtoul (pos_start_resume, NULL, 10)); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_dcc", + irc_last_command_received); +#endif + } + return 0; + } + + /* incoming DCC ACCEPT (resume accepted by sender) */ + if (strncmp (pos, "\01DCC ACCEPT", 11) == 0) + { + /* check if DCC ACCEPT is ok, i.e. with 0x01 at end */ + pos2 = strchr (pos + 1, '\01'); + if (!pos2) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2[0] = '\0'; + + command_ignored |= irc_ignore_check (host, "dcc", NULL, server->name); + + if (!command_ignored) + { + /* DCC filename */ + pos_file = pos + 11; + while (pos_file[0] == ' ') + pos_file++; + + /* look for resume start position */ + pos_start_resume = strrchr (pos_file, ' '); + if (!pos_start_resume) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2 = pos_start_resume; + pos_start_resume++; + while (pos2[0] == ' ') + pos2--; + pos2[1] = '\0'; + + /* look for DCC port */ + pos_port = strrchr (pos_file, ' '); + if (!pos_port) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2 = pos_port; + pos_port++; + while (pos2[0] == ' ') + pos2--; + pos2[1] = '\0'; + + irc_dcc_start_resume (server, pos_file, atoi (pos_port), + strtoul (pos_start_resume, NULL, 10)); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_dcc", + irc_last_command_received); +#endif + } + return 0; + } + + /* incoming DCC CHAT */ + if (strncmp (pos, "\01DCC CHAT", 9) == 0) + { + /* check if DCC CHAT is ok, i.e. with 0x01 at end */ + pos2 = strchr (pos + 1, '\01'); + if (!pos2) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos2[0] = '\0'; + + command_ignored |= irc_ignore_check (host, "dcc", NULL, server->name); + + if (!command_ignored) + { + /* CHAT type */ + pos_file = pos + 9; + while (pos_file[0] == ' ') + pos_file++; + + /* DCC IP address */ + pos_addr = strchr (pos_file, ' '); + if (!pos_addr) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos_addr[0] = '\0'; + pos_addr++; + while (pos_addr[0] == ' ') + pos_addr++; + + /* look for DCC port */ + pos_port = strchr (pos_addr, ' '); + if (!pos_port) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + pos_port[0] = '\0'; + pos_port++; + while (pos_port[0] == ' ') + pos_port++; + + if (ascii_strcasecmp (pos_file, "chat") != 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s unknown DCC CHAT type received from "), + WEECHAT_ERROR); + gui_printf (server->buffer, "%s%s%s: \"%s\"\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_file); + return -1; + } + + irc_dcc_add (server, IRC_DCC_CHAT_RECV, + strtoul (pos_addr, NULL, 10), + atoi (pos_port), nick, -1, NULL, NULL, 0); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_dcc", + irc_last_command_received); +#endif + } + return 0; + } + + /* private message received => display it */ + ptr_channel = irc_channel_search (server, nick); + + if (strncmp (pos, "\01ACTION ", 8) == 0) + { + command_ignored |= irc_ignore_check (host, "action", NULL, server->name); + command_ignored |= irc_ignore_check (host, "pv", NULL, server->name); + + if (!command_ignored) + { + if (!ptr_channel) + { + ptr_channel = irc_channel_new (server, + IRC_CHANNEL_TYPE_PRIVATE, + nick); + if (!ptr_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot create new private window \"%s\"\n"), + WEECHAT_ERROR, nick); + return -1; + } + gui_buffer_new (gui_current_window, server, ptr_channel, + GUI_BUFFER_TYPE_STANDARD, 0); + } + if (!ptr_channel->topic) + { + ptr_channel->topic = strdup (host2); + gui_chat_draw_title (ptr_channel->buffer, 1); + } + + pos += 8; + pos2 = strchr (pos, '\01'); + if (pos2) + pos2[0] = '\0'; + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_ACTION_ME); + if (irc_recv_is_highlight (pos, server->nick)) + { + gui_printf_type (ptr_channel->buffer, + GUI_MSG_TYPE_MSG | GUI_MSG_TYPE_HIGHLIGHT, + "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_HIGHLIGHT), + nick); + if ( (cfg_look_infobar) + && (cfg_look_infobar_delay_highlight > 0) + && (ptr_channel->buffer != gui_current_window->buffer) ) + gui_infobar_printf (cfg_look_infobar_delay_highlight, + GUI_COLOR_WIN_INFOBAR_HIGHLIGHT, + _("Channel %s: * %s %s"), + ptr_channel->name, nick, pos); + gui_printf (ptr_channel->buffer, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), pos); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_highlight", + irc_last_command_received); +#endif + } + else + { + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, + "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + gui_printf (ptr_channel->buffer, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), pos); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_pv", + irc_last_command_received); +#endif + } + } + } + else + { + /* unknown CTCP ? */ + pos2 = strchr (pos + 1, '\01'); + if ((pos[0] == '\01') && pos2 && (pos2[1] == '\0')) + { + command_ignored |= irc_ignore_check (host, "ctcp", NULL, server->name); + + if (!command_ignored) + { + pos++; + pos2[0] = '\0'; + pos2 = strchr (pos, ' '); + if (pos2) + { + pos2[0] = '\0'; + pos2++; + while (pos2[0] == ' ') + pos2++; + if (!pos2[0]) + pos2 = NULL; + } + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, + _("Unknown CTCP %s%s%s received from %s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick); + if (pos2) + gui_printf (server->buffer, "%s: %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos2); + else + gui_printf (server->buffer, "\n"); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_ctcp", + irc_last_command_received); +#endif + } + return 0; + } + else + { + command_ignored |= irc_ignore_check (host, "pv", NULL, server->name); + + if (!command_ignored) + { + if (!ptr_channel) + { + ptr_channel = irc_channel_new (server, + IRC_CHANNEL_TYPE_PRIVATE, + nick); + if (!ptr_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot create new private window \"%s\"\n"), + WEECHAT_ERROR, nick); + return -1; + } + gui_buffer_new (gui_current_window, server, ptr_channel, + GUI_BUFFER_TYPE_STANDARD, 0); + } + if (!ptr_channel->topic) + { + ptr_channel->topic = strdup (host2); + gui_chat_draw_title (ptr_channel->buffer, 1); + } + + if (irc_recv_is_highlight (pos, server->nick)) + { + irc_display_nick (ptr_channel->buffer, NULL, nick, + GUI_MSG_TYPE_NICK | GUI_MSG_TYPE_HIGHLIGHT, 1, + GUI_COLOR_WIN_CHAT_HIGHLIGHT, 0); + if ((cfg_look_infobar_delay_highlight > 0) + && (ptr_channel->buffer != gui_current_window->buffer)) + gui_infobar_printf (cfg_look_infobar_delay_highlight, + GUI_COLOR_WIN_INFOBAR_HIGHLIGHT, + _("Private %s> %s"), + nick, pos); + highlight = 1; + } + else + { + irc_display_nick (ptr_channel->buffer, NULL, nick, + GUI_MSG_TYPE_NICK, 1, GUI_COLOR_WIN_NICK_PRIVATE, 0); + highlight = 0; + } + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, + "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos); +#ifdef PLUGINS + (void) plugin_msg_handler_exec (server->name, + "weechat_pv", + irc_last_command_received); + if (highlight) + (void) plugin_msg_handler_exec (server->name, + "weechat_highlight", + irc_last_command_received); +#endif + } + } + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "privmsg"); + return -1; + } + } + return 0; +} + +/* + * irc_recv_cmd_quit: 'quit' command received + */ + +int +irc_recv_cmd_quit (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + + /* no host => we can't identify sender of message! */ + if (!host) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command received without host\n"), + WEECHAT_ERROR, "quit"); + return -1; + } + + if (arguments[0] == ':') + arguments++; + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if ((ptr_channel->type == IRC_CHANNEL_TYPE_PRIVATE) + || (ptr_channel->type == IRC_CHANNEL_TYPE_DCC_CHAT)) + ptr_nick = NULL; + else + ptr_nick = irc_nick_search (ptr_channel, nick); + + if (ptr_nick || (strcmp (ptr_channel->name, nick) == 0)) + { + if (ptr_nick) + irc_nick_free (ptr_channel, ptr_nick); + if (!command_ignored + && !irc_ignore_check (host, "quit", ptr_channel->name, server->name)) + { + pos = strchr (host, '!'); + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_QUIT); + gui_printf (ptr_channel->buffer, + _("%s%s %s(%s%s%s)%s has quit"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + (pos) ? pos + 1 : "", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + gui_printf (ptr_channel->buffer, + " %s(%s%s%s)\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + gui_nicklist_draw (ptr_channel->buffer, 1, 1); + gui_status_draw (ptr_channel->buffer, 1); + } + } + + return 0; +} + +/* + * irc_recv_cmd_server_mode_reason: command received from server (numeric), + * format: "mode :reason" + */ + +int +irc_recv_cmd_server_mode_reason (t_irc_server *server, char *host, + char *nick, char *arguments) +{ + char *ptr_msg; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + /* skip nickname if at beginning of server message */ + if (strncmp (server->nick, arguments, strlen (server->nick)) == 0) + { + arguments += strlen (server->nick) + 1; + while (arguments[0] == ' ') + arguments++; + } + + ptr_msg = strchr (arguments, ' '); + if (ptr_msg) + { + ptr_msg[0] = '\0'; + ptr_msg++; + while (ptr_msg[0] == ' ') + ptr_msg++; + if (ptr_msg[0] == ':') + ptr_msg++; + } + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s%s: %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), arguments, + (ptr_msg) ? ptr_msg : ""); + } + return 0; +} + +/* + * irc_recv_cmd_server_msg: command received from server (numeric) + */ + +int +irc_recv_cmd_server_msg (t_irc_server *server, char *host, char *nick, char *arguments) +{ + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + while (arguments[0] == ' ') + arguments++; + + /* skip nickname if at beginning of server message */ + if (strncasecmp (server->nick, arguments, strlen (server->nick)) == 0) + { + arguments += strlen (server->nick) + 1; + while (arguments[0] == ' ') + arguments++; + } + + if (arguments[0] == ':') + arguments++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), arguments); + } + return 0; +} + +/* + * irc_recv_cmd_server_reply: server reply + */ + +int +irc_recv_cmd_server_reply (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos2; + int first; + + /* make C compiler happy */ + (void) server; + (void) host; + (void) nick; + + if (!command_ignored) + { + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + } + else + pos = arguments; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + first = 1; + + while (pos && pos[0]) + { + pos2 = strchr (pos, ' '); + if ((pos[0] == ':') || (!pos2)) + { + if (pos[0] == ':') + pos++; + gui_printf (server->buffer, "%s%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (first) ? "" : ": ", + pos); + pos = NULL; + } + else + { + pos2[0] = '\0'; + gui_printf (server->buffer, "%s%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + (first) ? "" : " ", + pos); + first = 0; + pos = pos2 + 1; + } + } + } + return 0; +} + +/* + * irc_recv_cmd_topic: 'topic' command received + */ + +int +irc_recv_cmd_topic (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + t_irc_channel *ptr_channel; + t_gui_buffer *buffer; + + if (!irc_channel_is_channel (arguments)) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command received without channel\n"), + WEECHAT_ERROR, "topic"); + return -1; + } + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + if (pos[0] == ':') + pos++; + if (!pos[0]) + pos = NULL; + } + + command_ignored |= irc_ignore_check (host, "topic", arguments, server->name); + + ptr_channel = irc_channel_search (server, arguments); + buffer = (ptr_channel) ? ptr_channel->buffer : server->buffer; + + if (!command_ignored) + { + irc_display_prefix (server, buffer, GUI_PREFIX_INFO); + if (pos) + { + gui_printf (buffer, + _("%s%s%s has changed topic for %s%s%s to:"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + gui_printf (buffer, " \"%s%s\"\n", pos, GUI_NO_COLOR); + } + else + gui_printf (buffer, + _("%s%s%s has unset topic for %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + arguments); + } + + if (ptr_channel) + { + if (ptr_channel->topic) + free (ptr_channel->topic); + if (pos) + ptr_channel->topic = strdup (pos); + else + ptr_channel->topic = strdup (""); + gui_chat_draw_title (ptr_channel->buffer, 1); + } + + return 0; +} + +/* + * irc_recv_cmd_wallops: 'wallops' command received + */ + +int +irc_recv_cmd_wallops (t_irc_server *server, char *host, char *nick, char *arguments) +{ + command_ignored |= irc_ignore_check (host, "wallops", arguments, server->name); + + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + if (arguments[0] == ':') + arguments++; + gui_printf (server->buffer, + _("WALLOPS from %s%s%s: %s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + arguments); + } + + return 0; +} + +/* + * irc_recv_cmd_001: '001' command (connected to irc server) + */ + +int +irc_recv_cmd_001 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + char **commands, **ptr, *vars_replaced; + char *away_msg; + + pos = strchr (arguments, ' '); + if (pos) + pos[0] = '\0'; + if (strcmp (server->nick, arguments) != 0) + { + free (server->nick); + server->nick = strdup (arguments); + } + + irc_recv_cmd_server_msg (server, host, nick, arguments); + + /* connection to IRC server is ok! */ + server->is_connected = 1; + server->lag_next_check = time (NULL) + cfg_irc_lag_check; + + /* set away message if user was away (before disconnection for example) */ + if (server->away_message && server->away_message[0]) + { + away_msg = strdup (server->away_message); + if (away_msg) + { + irc_send_away (server, away_msg); + free (away_msg); + } + } + + /* execute command when connected */ + if (server->command && server->command[0]) + { + /* splitting command on ';' which can be escaped with '\;' */ + commands = split_multi_command (server->command, ';'); + if (commands) + { + for (ptr = commands; *ptr; ptr++) + { + vars_replaced = alias_replace_vars (server, NULL, *ptr); + user_command (server, NULL, + (vars_replaced) ? vars_replaced : *ptr, 0); + if (vars_replaced) + free (vars_replaced); + } + free_multi_command (commands); + } + + if (server->command_delay > 0) + server->command_time = time (NULL) + 1; + else + irc_server_autojoin_channels (server); + } + else + irc_server_autojoin_channels (server); + + gui_status_draw (server->buffer, 1); + gui_input_draw (server->buffer, 1); + + return 0; +} + +/* + * irc_recv_cmd_005: '005' command (some infos from server) + */ + +int +irc_recv_cmd_005 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos2; + + irc_recv_cmd_server_msg (server, host, nick, arguments); + + pos = strstr (arguments, "PREFIX="); + if (pos) + { + pos += 7; + if (pos[0] == '(') + { + pos2 = strchr (pos, ')'); + if (!pos2) + return 0; + pos = pos2 + 1; + } + pos2 = strchr (pos, ' '); + if (pos2) + pos2[0] = '\0'; + if (server->prefix) + free (server->prefix); + server->prefix = strdup (pos); + if (pos2) + pos2[0] = ' '; + } + + return 0; +} + +/* + * irc_recv_cmd_221: '221' command (user mode string) + */ + +int +irc_recv_cmd_221 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_mode; + + /* make C compiler happy */ + (void) server; + (void) host; + (void) nick; + + pos_mode = strchr (arguments, ' '); + if (pos_mode) + { + pos_mode[0] = '\0'; + pos_mode++; + while (pos_mode[0] == ' ') + pos_mode++; + + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, + _("User mode for %s%s%s is %s[%s%s%s]\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_mode, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "221"); + return -1; + } + + return 0; +} + +/* + * irc_recv_cmd_301: '301' command (away message) + */ + +int +irc_recv_cmd_301 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_message; + t_irc_channel *ptr_channel; + t_gui_buffer *ptr_buffer; + + /* make C compiler happy */ + (void) server; + (void) host; + (void) nick; + + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_message = strchr (pos_nick, ' '); + if (pos_message) + { + pos_message[0] = '\0'; + pos_message++; + while (pos_message[0] == ' ') + pos_message++; + if (pos_message[0] == ':') + pos_message++; + + if (!command_ignored) + { + /* look for private buffer to display message */ + ptr_channel = irc_channel_search (server, pos_nick); + if (!cfg_irc_show_away_once || !ptr_channel || + !(ptr_channel->away_message) || + (strcmp (ptr_channel->away_message, pos_message) != 0)) + { + ptr_buffer = (ptr_channel) ? ptr_channel->buffer : server->buffer; + irc_display_prefix (server, ptr_buffer, GUI_PREFIX_INFO); + gui_printf (ptr_buffer, + _("%s%s%s is away: %s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_message); + if (ptr_channel) + { + if (ptr_channel->away_message) + free (ptr_channel->away_message); + ptr_channel->away_message = strdup (pos_message); + } + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_302: '302' command (userhost) + */ + +int +irc_recv_cmd_302 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_host, *ptr_next; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + arguments = strchr (arguments, ' '); + if (arguments) + { + while (arguments[0] == ' ') + arguments++; + if (arguments[0] == ':') + arguments++; + while (arguments) + { + pos_host = strchr (arguments, '='); + if (pos_host) + { + pos_host[0] = '\0'; + pos_host++; + + ptr_next = strchr (pos_host, ' '); + if (ptr_next) + { + ptr_next[0] = '\0'; + ptr_next++; + while (ptr_next[0] == ' ') + ptr_next++; + } + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s%s%s=%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_host); + } + else + ptr_next = NULL; + arguments = ptr_next; + if (arguments && !arguments[0]) + arguments = NULL; + } + } + } + return 0; +} + +/* + * irc_recv_cmd_303: '303' command (ison) + */ + +int +irc_recv_cmd_303 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *ptr_next; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, _("Users online: ")); + + arguments = strchr (arguments, ' '); + if (arguments) + { + while (arguments[0] == ' ') + arguments++; + if (arguments[0] == ':') + arguments++; + while (arguments) + { + ptr_next = strchr (arguments, ' '); + if (ptr_next) + { + ptr_next[0] = '\0'; + ptr_next++; + while (ptr_next[0] == ' ') + ptr_next++; + } + gui_printf (server->buffer, "%s%s ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments); + arguments = ptr_next; + if (arguments && !arguments[0]) + arguments = NULL; + } + } + gui_printf (server->buffer, "\n"); + } + return 0; +} + +/* + * irc_recv_cmd_305: '305' command (unaway) + */ + +int +irc_recv_cmd_305 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + t_gui_window *ptr_win; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + arguments = strchr (arguments, ' '); + if (arguments) + { + while (arguments[0] == ' ') + arguments++; + if (arguments[0] == ':') + arguments++; + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s\n", arguments); + } + } + server->is_away = 0; + server->away_time = 0; + for (ptr_win = gui_windows; ptr_win; ptr_win = ptr_win->next_window) + { + if (GUI_SERVER(ptr_win->buffer) == server) + gui_status_draw (ptr_win->buffer, 1); + } + return 0; +} + +/* + * irc_recv_cmd_306: '306' command (now away) + */ + +int +irc_recv_cmd_306 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + t_gui_window *ptr_win; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + arguments = strchr (arguments, ' '); + if (arguments) + { + while (arguments[0] == ' ') + arguments++; + if (arguments[0] == ':') + arguments++; + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s\n", arguments); + } + } + server->is_away = 1; + server->away_time = time (NULL); + for (ptr_win = gui_windows; ptr_win; ptr_win = ptr_win->next_window) + { + if (GUI_SERVER(ptr_win->buffer) == server) + gui_status_draw (ptr_win->buffer, 1); + if (GUI_SERVER(ptr_win->buffer) == server) + ptr_win->buffer->last_read_line = + ptr_win->buffer->last_line; + } + return 0; +} + +/* + * irc_recv_cmd_whois_nick_msg: a whois command with nick and message + */ + +int +irc_recv_cmd_whois_nick_msg (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_msg; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_msg = strchr (pos_nick, ' '); + if (pos_msg) + { + pos_msg[0] = '\0'; + pos_msg++; + while (pos_msg[0] == ' ') + pos_msg++; + if (pos_msg[0] == ':') + pos_msg++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s[%s%s%s] %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_msg); + } + } + } + return 0; +} + +/* + * irc_recv_cmd_310: '310' command (whois, help mode) + */ + +int +irc_recv_cmd_310 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, _("%s[%s%s%s]%s help mode (+h)\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + } + return 0; +} + +/* + * irc_recv_cmd_311: '311' command (whois, user) + */ + +int +irc_recv_cmd_311 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_user, *pos_host, *pos_realname; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_user = strchr (pos_nick, ' '); + if (pos_user) + { + pos_user[0] = '\0'; + pos_user++; + while (pos_user[0] == ' ') + pos_user++; + pos_host = strchr (pos_user, ' '); + if (pos_host) + { + pos_host[0] = '\0'; + pos_host++; + while (pos_host[0] == ' ') + pos_host++; + pos_realname = strchr (pos_host, ' '); + if (pos_realname) + { + pos_realname[0] = '\0'; + pos_realname++; + while (pos_realname[0] == ' ') + pos_realname++; + if (pos_realname[0] == '*') + pos_realname++; + while (pos_realname[0] == ' ') + pos_realname++; + if (pos_realname[0] == ':') + pos_realname++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, + "%s[%s%s%s] (%s%s@%s%s)%s: %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_user, + pos_host, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_realname); + } + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_312: '312' command (whois, server) + */ + +int +irc_recv_cmd_312 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_server, *pos_serverinfo; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_server = strchr (pos_nick, ' '); + if (pos_server) + { + pos_server[0] = '\0'; + pos_server++; + while (pos_server[0] == ' ') + pos_server++; + pos_serverinfo = strchr (pos_server, ' '); + if (pos_serverinfo) + { + pos_serverinfo[0] = '\0'; + pos_serverinfo++; + while (pos_serverinfo[0] == ' ') + pos_serverinfo++; + if (pos_serverinfo[0] == ':') + pos_serverinfo++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, + "%s[%s%s%s] %s%s %s(%s%s%s)\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_server, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_serverinfo, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_314: '314' command (whowas) + */ + +int +irc_recv_cmd_314 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_user, *pos_host, *pos_realname; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_user = strchr (pos_nick, ' '); + if (pos_user) + { + pos_user[0] = '\0'; + pos_user++; + while (pos_user[0] == ' ') + pos_user++; + pos_host = strchr (pos_user, ' '); + if (pos_host) + { + pos_host[0] = '\0'; + pos_host++; + while (pos_host[0] == ' ') + pos_host++; + pos_realname = strchr (pos_host, ' '); + if (pos_realname) + { + pos_realname[0] = '\0'; + pos_realname++; + while (pos_realname[0] == ' ') + pos_realname++; + pos_realname = strchr (pos_realname, ' '); + if (pos_realname) + { + pos_realname[0] = '\0'; + pos_realname++; + while (pos_realname[0] == ' ') + pos_realname++; + if (pos_realname[0] == ':') + pos_realname++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, + _("%s%s %s(%s%s@%s%s)%s was %s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_user, + pos_host, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_realname); + } + } + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_315: '315' command (end of /who) + */ + +int +irc_recv_cmd_315 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + t_irc_channel *ptr_channel; + + /* make C compiler happy */ + (void) host; + (void) nick; + + /* skip nickname if at beginning of server message */ + if (strncmp (server->nick, arguments, strlen (server->nick)) == 0) + { + arguments += strlen (server->nick) + 1; + while (arguments[0] == ' ') + arguments++; + } + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + ptr_channel = irc_channel_search (server, arguments); + if (ptr_channel && (ptr_channel->checking_away > 0)) + { + ptr_channel->checking_away--; + return 0; + } + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s%s %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos); + } + } + else + { + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s\n", arguments); + } + } + return 0; +} + +/* + * irc_recv_cmd_317: '317' command (whois, idle) + */ + +int +irc_recv_cmd_317 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_idle, *pos_signon, *pos_message; + int idle_time, day, hour, min, sec; + time_t datetime; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_idle = strchr (pos_nick, ' '); + if (pos_idle) + { + pos_idle[0] = '\0'; + pos_idle++; + while (pos_idle[0] == ' ') + pos_idle++; + pos_signon = strchr (pos_idle, ' '); + if (pos_signon) + { + pos_signon[0] = '\0'; + pos_signon++; + while (pos_signon[0] == ' ') + pos_signon++; + pos_message = strchr (pos_signon, ' '); + if (pos_message) + { + pos_message[0] = '\0'; + + idle_time = atoi (pos_idle); + day = idle_time / (60 * 60 * 24); + hour = (idle_time % (60 * 60 * 24)) / (60 * 60); + min = ((idle_time % (60 * 60 * 24)) % (60 * 60)) / 60; + sec = ((idle_time % (60 * 60 * 24)) % (60 * 60)) % 60; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, _("%s[%s%s%s]%s idle: "), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + + if (day > 0) + gui_printf (server->buffer, "%s%d %s%s, ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + day, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (day > 1) ? _("days") : _("day")); + + datetime = (time_t)(atol (pos_signon)); + gui_printf (server->buffer, + _("%s%02d %s%s %s%02d %s%s %s%02d %s%s, signon at: %s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + hour, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (hour > 1) ? _("hours") : _("hour"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + min, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (min > 1) ? _("minutes") : _("minute"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + sec, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (sec > 1) ? _("seconds") : _("second"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ctime (&datetime)); + } + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_319: '319' command (whois, channels) + */ + +int +irc_recv_cmd_319 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_channel, *pos; + int color; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_channel = strchr (pos_nick, ' '); + if (pos_channel) + { + pos_channel[0] = '\0'; + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + if (pos_channel[0] == ':') + pos_channel++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s[%s%s%s]%s Channels: ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + while (pos_channel && pos_channel[0]) + { + if (irc_mode_nick_prefix_allowed (server, pos_channel[0])) + { + switch (pos_channel[0]) + { + case '@': /* op */ + color = GUI_COLOR_WIN_NICK_OP; + break; + case '%': /* half-op */ + color = GUI_COLOR_WIN_NICK_HALFOP; + break; + case '+': /* voice */ + color = GUI_COLOR_WIN_NICK_VOICE; + break; + case '~': /* channel owner */ + color = GUI_COLOR_WIN_NICK_CHANOWNER; + break; + case '&': /* channel admin */ + color = GUI_COLOR_WIN_NICK_CHANADMIN; + break; + case '!': /* channel admin (2) */ + color = GUI_COLOR_WIN_NICK_CHANADMIN; + break; + case '-': /* channel user */ + color = GUI_COLOR_WIN_NICK_CHANUSER; + break; + default: + color = GUI_COLOR_WIN_CHAT; + break; + } + gui_printf (server->buffer, "%s%c", + GUI_COLOR(GUI_COLOR_WIN_NICK_OP), + pos_channel[0]); + pos_channel++; + } + + pos = strchr (pos_channel, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + } + gui_printf (server->buffer, "%s%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + (pos && pos[0]) ? " " : "\n"); + pos_channel = pos; + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_321: '321' command (/list start) + */ + +int +irc_recv_cmd_321 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + } + else + pos = arguments; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s\n", pos); + } + return 0; +} + +/* + * irc_recv_cmd_322: '322' command (channel for /list) + */ + +int +irc_recv_cmd_322 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + } + else + pos = arguments; + + if (server->cmd_list_regexp) + { + if (regexec (server->cmd_list_regexp, pos, 0, NULL, 0) == 0) { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s\n", pos); + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s\n", pos); + } + } + return 0; +} + +/* + * irc_recv_cmd_323: '323' command (/list end) + */ + +int +irc_recv_cmd_323 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + } + else + pos = arguments; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s\n", pos); + } + return 0; +} + +/* + * irc_recv_cmd_324: '324' command (channel mode) + */ + +int +irc_recv_cmd_324 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_modes, *pos; + t_irc_channel *ptr_channel; + + /* make C compiler happy */ + (void) host; + (void) nick; + + pos_channel = strchr (arguments, ' '); + if (pos_channel) + { + pos_channel[0] = '\0'; + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + + pos_modes = strchr (pos_channel, ' '); + if (pos_modes) + { + pos_modes[0] = '\0'; + pos_modes++; + while (pos_modes[0] == ' ') + pos_modes++; + + /* remove spaces after modes */ + pos = pos_modes + strlen (pos_modes) - 1; + while ((pos >= pos_modes) && (pos[0] == ' ')) + { + pos[0] = '\0'; + pos--; + } + + /* search channel */ + ptr_channel = irc_channel_search (server, pos_channel); + if (ptr_channel) + { + if (pos_modes[0]) + { + if (ptr_channel->modes) + ptr_channel->modes = (char *) realloc (ptr_channel->modes, + strlen (pos_modes) + 1); + else + ptr_channel->modes = (char *) malloc (strlen (pos_modes) + 1); + strcpy (ptr_channel->modes, pos_modes); + irc_mode_channel_set (server, ptr_channel, pos_modes); + } + else + { + if (ptr_channel->modes) + { + free (ptr_channel->modes); + ptr_channel->modes = NULL; + } + } + gui_status_draw (ptr_channel->buffer, 1); + } + } + } + return 0; +} + +/* + * irc_recv_cmd_327: '327' command (whois, host) + */ + +int +irc_recv_cmd_327 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_host1, *pos_host2, *pos_other; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_host1 = strchr (pos_nick, ' '); + if (pos_host1) + { + pos_host1[0] = '\0'; + pos_host1++; + while (pos_host1[0] == ' ') + pos_host1++; + pos_host2 = strchr (pos_host1, ' '); + if (pos_host2) + { + pos_host2[0] = '\0'; + pos_host2++; + while (pos_host2[0] == ' ') + pos_host2++; + + pos_other = strchr (pos_host2, ' '); + if (pos_other) + { + pos_other[0] = '\0'; + pos_other++; + while (pos_other[0] == ' ') + pos_other++; + } + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, + "%s[%s%s%s] %s%s %s %s%s%s%s%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_host1, + pos_host2, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + (pos_other) ? "(" : "", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (pos_other) ? pos_other : "", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + (pos_other) ? ")" : ""); + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_329: '329' command received (channel creation date) + */ + +int +irc_recv_cmd_329 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_date; + t_irc_channel *ptr_channel; + time_t datetime; + + /* make C compiler happy */ + (void) nick; + + pos_channel = strchr (arguments, ' '); + if (pos_channel) + { + while (pos_channel[0] == ' ') + pos_channel++; + pos_date = strchr (pos_channel, ' '); + if (pos_date) + { + pos_date[0] = '\0'; + pos_date++; + while (pos_date[0] == ' ') + pos_date++; + + ptr_channel = irc_channel_search (server, pos_channel); + if (!ptr_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s channel \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, pos_channel, "329"); + return -1; + } + + command_ignored |= irc_ignore_check (host, "329", + pos_channel, server->name); + if (!command_ignored && (ptr_channel->display_creation_date)) + { + datetime = (time_t)(atol (pos_date)); + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_INFO); + gui_printf (ptr_channel->buffer, _("Channel created on %s"), + ctime (&datetime)); + } + ptr_channel->display_creation_date = 0; + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot identify date/time for \"%s\" command\n"), + WEECHAT_ERROR, "329"); + return -1; + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot identify channel for \"%s\" command\n"), + WEECHAT_ERROR, "329"); + return -1; + } + return 0; +} + +/* + * irc_recv_cmd_331: '331' command received (no topic for channel) + */ + +int +irc_recv_cmd_331 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos; + t_irc_channel *ptr_channel; + + /* make C compiler happy */ + (void) server; + (void) nick; + + if (!command_ignored) + { + pos_channel = strchr (arguments, ' '); + if (pos_channel) + { + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + pos = strchr (pos_channel, ' '); + if (pos) + pos[0] = '\0'; + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s channel \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, "", "331"); + return -1; + } + + ptr_channel = irc_channel_search (server, pos_channel); + command_ignored |= irc_ignore_check (host, "331", + (ptr_channel) ? ptr_channel->name : NULL, + server->name); + if (!command_ignored) + { + irc_display_prefix (server, + (ptr_channel) ? ptr_channel->buffer : NULL, + GUI_PREFIX_INFO); + gui_printf ((ptr_channel) ? ptr_channel->buffer : NULL, + _("No topic set for %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel); + } + } + return 0; +} + +/* + * irc_recv_cmd_332: '332' command received (topic of channel) + */ + +int +irc_recv_cmd_332 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos2; + t_irc_channel *ptr_channel; + t_gui_buffer *ptr_buffer; + + /* make C compiler happy */ + (void) nick; + + pos = strchr (arguments, ' '); + if (pos) + { + while (pos[0] == ' ') + pos++; + pos2 = strchr (pos, ' '); + if (pos2) + { + pos2[0] = '\0'; + ptr_channel = irc_channel_search (server, pos); + ptr_buffer = (ptr_channel) ? + ptr_channel->buffer : server->buffer; + pos2++; + while (pos2[0] == ' ') + pos2++; + if (pos2[0] == ':') + pos2++; + if (ptr_channel) + { + if (ptr_channel->topic) + free (ptr_channel->topic); + ptr_channel->topic = strdup (pos2); + } + + command_ignored |= irc_ignore_check (host, "332", pos, server->name); + if (!command_ignored) + { + irc_display_prefix (server, ptr_buffer, GUI_PREFIX_INFO); + gui_printf (ptr_buffer, _("Topic for %s%s%s is: "), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + gui_printf (ptr_buffer, "\"%s%s\"\n", pos2, GUI_NO_COLOR); + } + + if (ptr_channel) + gui_chat_draw_title (ptr_buffer, 1); + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot identify channel for \"%s\" command\n"), + WEECHAT_ERROR, "332"); + return -1; + } + return 0; +} + +/* + * irc_recv_cmd_333: '333' command received (infos about topic (nick / date)) + */ + +int +irc_recv_cmd_333 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_nick, *pos_date; + t_irc_channel *ptr_channel; + t_gui_buffer *ptr_buffer; + time_t datetime; + + /* make C compiler happy */ + (void) nick; + + pos_channel = strchr (arguments, ' '); + if (pos_channel) + { + while (pos_channel[0] == ' ') + pos_channel++; + pos_nick = strchr (pos_channel, ' '); + if (pos_nick) + { + pos_nick[0] = '\0'; + pos_nick++; + while (pos_nick[0] == ' ') + pos_nick++; + pos_date = strchr (pos_nick, ' '); + if (pos_date) + { + pos_date[0] = '\0'; + pos_date++; + while (pos_date[0] == ' ') + pos_date++; + + ptr_channel = irc_channel_search (server, pos_channel); + ptr_buffer = (ptr_channel) ? + ptr_channel->buffer : server->buffer; + + command_ignored |= irc_ignore_check (host, "333", + pos_channel, server->name); + if (!command_ignored) + { + datetime = (time_t)(atol (pos_date)); + irc_display_prefix (server, ptr_buffer, GUI_PREFIX_INFO); + gui_printf (ptr_buffer, _("Topic set by %s%s%s, %s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ctime (&datetime)); + } + } + else + return 0; + } + else + return 0; + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot identify channel for \"%s\" command\n"), + WEECHAT_ERROR, "333"); + return -1; + } + return 0; +} + +/* + * irc_recv_cmd_338: '338' command (whois, host) + */ + +int +irc_recv_cmd_338 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_host, *pos_message; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_host = strchr (pos_nick, ' '); + if (pos_host) + { + pos_host[0] = '\0'; + pos_host++; + while (pos_host[0] == ' ') + pos_host++; + pos_message = strchr (pos_host, ' '); + if (pos_message) + { + pos_message[0] = '\0'; + pos_message++; + while (pos_message[0] == ' ') + pos_message++; + if (pos_message[0] == ':') + pos_message++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s[%s%s%s] %s%s %s%s %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_message, + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_host); + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_341: '341' command received (inviting) + */ + +int +irc_recv_cmd_341 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_channel; + + /* make C compiler happy */ + (void) host; + (void) nick; + + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + pos_nick[0] = '\0'; + pos_nick++; + while (pos_nick[0] == ' ') + pos_nick++; + + pos_channel = strchr (pos_nick, ' '); + if (pos_channel) + { + pos_channel[0] = '\0'; + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + if (pos_channel[0] == ':') + pos_channel++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, + _("%s%s%s has invited %s%s%s on %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel); + gui_status_draw (gui_current_window->buffer, 1); + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot identify channel for \"%s\" command\n"), + WEECHAT_ERROR, "341"); + return -1; + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot identify nickname for \"%s\" command\n"), + WEECHAT_ERROR, "341"); + return -1; + } + return 0; +} + +/* + * irc_recv_cmd_344: '344' command (channel reop) + */ + +int +irc_recv_cmd_344 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_host; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_channel = strchr (arguments, ' '); + if (pos_channel) + { + while (pos_channel[0] == ' ') + pos_channel++; + pos_host = strchr (pos_channel, ' '); + if (pos_host) + { + pos_host[0] = '\0'; + pos_host++; + while (pos_host[0] == ' ') + pos_host++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, _("Channel reop %s%s%s: %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_host); + } + } + } + return 0; +} + +/* + * irc_recv_cmd_345: '345' command (end of channel reop) + */ + +int +irc_recv_cmd_345 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos; + + /* make C compiler happy */ + (void) host; + (void) nick; + + /* skip nickname if at beginning of server message */ + if (strncmp (server->nick, arguments, strlen (server->nick)) == 0) + { + arguments += strlen (server->nick) + 1; + while (arguments[0] == ' ') + arguments++; + } + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s%s %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos); + } + } + else + { + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s\n", arguments); + } + } + return 0; +} + +/* + * irc_recv_cmd_348: '348' command received (channel exception list) + */ + +int +irc_recv_cmd_348 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_exception, *pos_user, *pos_date, *pos; + t_irc_channel *ptr_channel; + t_gui_buffer *buffer; + time_t datetime; + + /* make C compiler happy */ + (void) nick; + + /* look for channel */ + pos_channel = strchr (arguments, ' '); + if (!pos_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "348"); + return -1; + } + pos_channel[0] = '\0'; + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + + /* look for exception mask */ + pos_exception = strchr (pos_channel, ' '); + if (!pos_exception) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "348"); + return -1; + } + pos_exception[0] = '\0'; + pos_exception++; + while (pos_exception[0] == ' ') + pos_exception++; + + /* look for user who set exception */ + pos_user = strchr (pos_exception, ' '); + if (pos_user) + { + pos_user[0] = '\0'; + pos_user++; + while (pos_user[0] == ' ') + pos_user++; + + /* look for date/time */ + pos_date = strchr (pos_user, ' '); + if (pos_date) + { + pos_date[0] = '\0'; + pos_date++; + while (pos_date[0] == ' ') + pos_date++; + } + } + else + pos_date = NULL; + + ptr_channel = irc_channel_search (server, pos_channel); + buffer = (ptr_channel) ? ptr_channel->buffer : server->buffer; + + command_ignored |= irc_ignore_check (host, "348", pos_channel, server->name); + + if (!command_ignored) + { + irc_display_prefix (server, buffer, GUI_PREFIX_INFO); + gui_printf (buffer, "%s[%s%s%s]%s exception %s%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_exception, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + if (pos_user) + { + pos = strchr (pos_user, '!'); + if (pos) + { + pos[0] = '\0'; + gui_printf (buffer, _(" by %s%s %s(%s%s%s)"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_user, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos + 1, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + else + gui_printf (buffer, _(" by %s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_user); + } + if (pos_date) + { + datetime = (time_t)(atol (pos_date)); + gui_printf_nolog (buffer, "%s, %s", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ctime (&datetime)); + } + else + gui_printf_nolog (buffer, "\n"); + } + return 0; +} + +/* + * irc_recv_cmd_349: '349' command received (end of channel exception list) + */ + +int +irc_recv_cmd_349 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_msg; + t_irc_channel *ptr_channel; + t_gui_buffer *buffer; + + /* make C compiler happy */ + (void) nick; + + pos_channel = strchr (arguments, ' '); + if (!pos_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "349"); + return -1; + } + pos_channel[0] = '\0'; + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + + pos_msg = strchr (pos_channel, ' '); + if (!pos_msg) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "349"); + return -1; + } + pos_msg[0] = '\0'; + pos_msg++; + while (pos_msg[0] == ' ') + pos_msg++; + if (pos_msg[0] == ':') + pos_msg++; + + ptr_channel = irc_channel_search (server, pos_channel); + buffer = (ptr_channel) ? ptr_channel->buffer : server->buffer; + + command_ignored |= irc_ignore_check (host, "349", pos_channel, server->name); + + if (!command_ignored) + { + irc_display_prefix (server, buffer, GUI_PREFIX_INFO); + gui_printf (buffer, "%s[%s%s%s] %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_msg); + } + return 0; +} + +/* + * irc_recv_cmd_351: '351' command received (server version) + */ + +int +irc_recv_cmd_351 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos2; + + /* make C compiler happy */ + (void) server; + (void) host; + (void) nick; + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + } + else + pos = arguments; + + pos2 = strstr (pos, " :"); + if (pos2) + { + pos2[0] = '\0'; + pos2 += 2; + } + + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + if (pos2) + gui_printf (server->buffer, "%s %s\n", pos, pos2); + else + gui_printf (server->buffer, "%s\n", pos); + } + return 0; +} + +/* + * irc_recv_cmd_352: '352' command (who) + */ + +int +irc_recv_cmd_352 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_user, *pos_host, *pos_server, *pos_nick; + char *pos_attr, *pos_hopcount, *pos_realname; + int length; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + + /* make C compiler happy */ + (void) nick; + + pos_channel = strchr (arguments, ' '); + if (pos_channel) + { + while (pos_channel[0] == ' ') + pos_channel++; + pos_user = strchr (pos_channel, ' '); + if (pos_user) + { + pos_user[0] = '\0'; + pos_user++; + while (pos_user[0] == ' ') + pos_user++; + pos_host = strchr (pos_user, ' '); + if (pos_host) + { + pos_host[0] = '\0'; + pos_host++; + while (pos_host[0] == ' ') + pos_host++; + pos_server = strchr (pos_host, ' '); + if (pos_server) + { + pos_server[0] = '\0'; + pos_server++; + while (pos_server[0] == ' ') + pos_server++; + pos_nick = strchr (pos_server, ' '); + if (pos_nick) + { + pos_nick[0] = '\0'; + pos_nick++; + while (pos_nick[0] == ' ') + pos_nick++; + pos_attr = strchr (pos_nick, ' '); + if (pos_attr) + { + pos_attr[0] = '\0'; + pos_attr++; + while (pos_attr[0] == ' ') + pos_attr++; + pos_hopcount = strchr (pos_attr, ' '); + if (pos_hopcount) + { + pos_hopcount[0] = '\0'; + pos_hopcount++; + while (pos_hopcount[0] == ' ') + pos_hopcount++; + if (pos_hopcount[0] == ':') + pos_hopcount++; + pos_realname = strchr (pos_hopcount, ' '); + if (pos_realname) + { + pos_realname[0] = '\0'; + pos_realname++; + while (pos_realname[0] == ' ') + pos_realname++; + + command_ignored |= irc_ignore_check (host, "352", pos_channel, server->name); + + ptr_channel = irc_channel_search (server, pos_channel); + if (ptr_channel && (ptr_channel->checking_away > 0)) + { + ptr_nick = irc_nick_search (ptr_channel, pos_nick); + if (ptr_nick) + { + if (ptr_nick->host) + free (ptr_nick->host); + length = strlen (pos_user) + 1 + strlen (pos_host) + 1; + ptr_nick->host = (char *) malloc (length); + if (ptr_nick->host) + snprintf (ptr_nick->host, length, "%s@%s", pos_user, pos_host); + irc_nick_set_away (ptr_channel, ptr_nick, + (pos_attr[0] == 'G') ? 1 : 0); + } + return 0; + } + + if (!command_ignored) + { + irc_display_prefix (server, server->buffer, + GUI_PREFIX_SERVER); + gui_printf (server->buffer, + "%s%s%s on %s%s%s %s %s %s%s@%s %s(%s%s%s)\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_attr, + pos_hopcount, + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_user, + pos_host, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_realname, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + } + } + } + } + } + } + } + } + return 0; +} + +/* + * irc_recv_cmd_353: '353' command received (list of users on a channel) + */ + +int +irc_recv_cmd_353 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos_nick; + int is_chanowner, is_chanadmin, is_chanadmin2, is_op, is_halfop; + int has_voice, is_chanuser; + int prefix_found, color; + t_irc_channel *ptr_channel; + t_gui_buffer *ptr_buffer; + + /* make C compiler happy */ + (void) nick; + + pos = strstr (arguments, " = "); + if (pos) + arguments = pos + 3; + else + { + pos = strstr (arguments, " * "); + if (pos) + arguments = pos + 3; + else + { + pos = strstr (arguments, " @ "); + if (pos) + arguments = pos + 3; + } + } + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + + ptr_channel = irc_channel_search (server, arguments); + if (ptr_channel) + ptr_buffer = ptr_channel->buffer; + else + ptr_buffer = server->buffer; + + pos++; + while (pos[0] == ' ') + pos++; + if (pos[0] != ':') + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "353"); + return -1; + } + + command_ignored |= irc_ignore_check (host, "353", + (ptr_channel) ? ptr_channel->name : NULL, + server->name); + + /* channel is not joined => display users on server buffer */ + if (!command_ignored && !ptr_channel) + { + /* display users on channel */ + irc_display_prefix (server, ptr_buffer, GUI_PREFIX_SERVER); + gui_printf (ptr_buffer, _("Nicks %s%s%s: %s["), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + + pos++; + if (pos[0]) + { + while (pos && pos[0]) + { + is_chanowner = 0; + is_chanadmin = 0; + is_chanadmin2 = 0; + is_op = 0; + is_halfop = 0; + has_voice = 0; + is_chanuser = 0; + prefix_found = 1; + + while (prefix_found) + { + prefix_found = 0; + + if (irc_mode_nick_prefix_allowed (server, pos[0])) + { + prefix_found = 1; + switch (pos[0]) + { + case '@': /* op */ + is_op = 1; + color = GUI_COLOR_WIN_NICK_OP; + break; + case '%': /* half-op */ + is_halfop = 1; + color = GUI_COLOR_WIN_NICK_HALFOP; + break; + case '+': /* voice */ + has_voice = 1; + color = GUI_COLOR_WIN_NICK_VOICE; + break; + case '~': /* channel owner */ + is_chanowner = 1; + color = GUI_COLOR_WIN_NICK_CHANOWNER; + break; + case '&': /* channel admin */ + is_chanadmin = 1; + color = GUI_COLOR_WIN_NICK_CHANADMIN; + break; + case '!': /* channel admin (2) */ + is_chanadmin2 = 1; + color = GUI_COLOR_WIN_NICK_CHANADMIN; + break; + case '-': /* channel user */ + is_chanuser = 1; + color = GUI_COLOR_WIN_NICK_CHANUSER; + break; + default: + color = GUI_COLOR_WIN_CHAT; + break; + } + if (!command_ignored && !ptr_channel) + gui_printf (ptr_buffer, "%s%c", + GUI_COLOR(color), + pos[0]); + } + if (prefix_found) + pos++; + } + pos_nick = pos; + pos = strchr (pos, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + } + if (ptr_channel) + { + if (!irc_nick_new (server, ptr_channel, pos_nick, + is_chanowner, is_chanadmin, is_chanadmin2, + is_op, is_halfop, has_voice, is_chanuser)) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot create nick \"%s\" for channel \"%s\"\n"), + WEECHAT_ERROR, pos_nick, ptr_channel->name); + } + } + else + { + if (!command_ignored) + { + gui_printf (ptr_buffer, "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT), pos_nick); + if (pos && pos[0]) + gui_printf (ptr_buffer, " "); + } + } + } + } + if (ptr_channel) + { + gui_nicklist_draw (ptr_channel->buffer, 1, 1); + gui_status_draw (ptr_channel->buffer, 1); + } + else + { + if (!command_ignored) + gui_printf (ptr_buffer, "%s]\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "353"); + return -1; + } + return 0; +} + +/* + * irc_recv_cmd_366: '366' command received (end of /names list) + */ + +int +irc_recv_cmd_366 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos2; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + int num_nicks, num_op, num_halfop, num_voice, num_normal; + + /* make C compiler happy */ + (void) nick; + + pos = strchr (arguments, ' '); + if (pos) + { + while (pos[0] == ' ') + pos++; + pos2 = strchr (pos, ' '); + if (pos2) + { + pos2[0] = '\0'; + pos2++; + while (pos2[0] == ' ') + pos2++; + if (pos2[0] == ':') + pos2++; + + ptr_channel = irc_channel_search (server, pos); + if (ptr_channel) + { + command_ignored |= irc_ignore_check (host, "366", ptr_channel->name, server->name); + + if (!command_ignored) + { + /* display users on channel */ + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_SERVER); + gui_printf (ptr_channel->buffer, _("Nicks %s%s%s: %s["), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_channel->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + + for (ptr_nick = ptr_channel->nicks; ptr_nick; ptr_nick = ptr_nick->next_nick) + { + irc_display_nick (ptr_channel->buffer, ptr_nick, NULL, + GUI_MSG_TYPE_MSG, 0, GUI_COLOR_WIN_CHAT, 1); + if (ptr_nick != ptr_channel->last_nick) + gui_printf (ptr_channel->buffer, " "); + } + gui_printf (ptr_channel->buffer, "%s]\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + + /* display number of nicks, ops, halfops & voices on the channel */ + irc_nick_count (ptr_channel, &num_nicks, &num_op, &num_halfop, &num_voice, + &num_normal); + irc_display_prefix (server, ptr_channel->buffer, GUI_PREFIX_INFO); + gui_printf (ptr_channel->buffer, + _("Channel %s%s%s: %s%d%s %s %s(%s%d%s %s, " + "%s%d%s %s, %s%d%s %s, %s%d%s %s%s)\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_channel->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + num_nicks, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (num_nicks > 1) ? _("nicks") : _("nick"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + num_op, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (num_op > 1) ? _("ops") : _("op"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + num_halfop, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (num_halfop > 1) ? _("halfops") : _("halfop"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + num_voice, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (num_voice > 1) ? _("voices") : _("voice"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + num_normal, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + _("normal"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + irc_send_cmd_mode (server, NULL, ptr_channel->name); + irc_channel_check_away (server, ptr_channel, 1); + } + else + { + if (!command_ignored) + { + irc_display_prefix (server, gui_current_window->buffer, GUI_PREFIX_INFO); + gui_printf (gui_current_window->buffer, "%s%s%s: %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos2); + } + return 0; + } + } + } + return 0; +} + +/* + * irc_recv_cmd_367: '367' command received (banlist) + */ + +int +irc_recv_cmd_367 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_ban, *pos_user, *pos_date, *pos; + t_irc_channel *ptr_channel; + t_gui_buffer *buffer; + time_t datetime; + + /* make C compiler happy */ + (void) nick; + + /* look for channel */ + pos_channel = strchr (arguments, ' '); + if (!pos_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "367"); + return -1; + } + pos_channel[0] = '\0'; + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + + /* look for ban mask */ + pos_ban = strchr (pos_channel, ' '); + if (!pos_ban) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "367"); + return -1; + } + pos_ban[0] = '\0'; + pos_ban++; + while (pos_ban[0] == ' ') + pos_ban++; + + /* look for user who set ban */ + pos_date = NULL; + pos_user = strchr (pos_ban, ' '); + if (pos_user) + { + pos_user[0] = '\0'; + pos_user++; + while (pos_user[0] == ' ') + pos_user++; + + /* look for date/time */ + pos_date = strchr (pos_user, ' '); + if (pos_date) + { + pos_date[0] = '\0'; + pos_date++; + while (pos_date[0] == ' ') + pos_date++; + } + } + + ptr_channel = irc_channel_search (server, pos_channel); + buffer = (ptr_channel) ? ptr_channel->buffer : server->buffer; + + command_ignored |= irc_ignore_check (host, "367", pos_channel, server->name); + + if (!command_ignored) + { + irc_display_prefix (server, buffer, GUI_PREFIX_INFO); + if (pos_user) + { + gui_printf_nolog (buffer, _("%s[%s%s%s] %s%s%s banned by "), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_ban, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + pos = strchr (pos_user, '!'); + if (pos) + { + pos[0] = '\0'; + gui_printf (buffer, "%s%s %s(%s%s%s)", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_user, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos + 1, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + else + gui_printf (buffer, "%s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_user); + if (pos_date) + { + datetime = (time_t)(atol (pos_date)); + gui_printf (buffer, "%s, %s", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ctime (&datetime)); + } + else + gui_printf (buffer, "\n"); + } + else + gui_printf_nolog (buffer, _("%s[%s%s%s] %s%s%s banned\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + pos_ban, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + return 0; +} + +/* + * irc_recv_cmd_368: '368' command received (end of banlist) + */ + +int +irc_recv_cmd_368 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_channel, *pos_msg; + t_irc_channel *ptr_channel; + t_gui_buffer *buffer; + + /* make C compiler happy */ + (void) nick; + + pos_channel = strchr (arguments, ' '); + if (!pos_channel) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "368"); + return -1; + } + pos_channel[0] = '\0'; + pos_channel++; + while (pos_channel[0] == ' ') + pos_channel++; + + pos_msg = strchr (pos_channel, ' '); + if (!pos_msg) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot parse \"%s\" command\n"), + WEECHAT_ERROR, "368"); + return -1; + } + pos_msg[0] = '\0'; + pos_msg++; + while (pos_msg[0] == ' ') + pos_msg++; + if (pos_msg[0] == ':') + pos_msg++; + + ptr_channel = irc_channel_search (server, pos_channel); + buffer = (ptr_channel) ? ptr_channel->buffer : server->buffer; + + command_ignored |= irc_ignore_check (host, "368", pos_channel, server->name); + + if (!command_ignored) + { + irc_display_prefix (server, buffer, GUI_PREFIX_INFO); + gui_printf_nolog (buffer, "%s[%s%s%s] %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_channel, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_msg); + } + return 0; +} + +/* + * irc_recv_cmd_432: '432' command received (erroneous nickname) + */ + +int +irc_recv_cmd_432 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + /* Note: this IRC command can not be ignored */ + + irc_recv_cmd_error (server, host, nick, arguments); + + if (!server->is_connected) + { + if (strcmp (server->nick, server->nick1) == 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("%s: trying 2nd nickname \"%s\"\n"), + PACKAGE_NAME, server->nick2); + free (server->nick); + server->nick = strdup (server->nick2); + } + else + { + if (strcmp (server->nick, server->nick2) == 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("%s: trying 3rd nickname \"%s\"\n"), + PACKAGE_NAME, server->nick3); + free (server->nick); + server->nick = strdup (server->nick3); + } + else + { + if (strcmp (server->nick, server->nick3) == 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("%s: all declared nicknames are already in " + "use or invalid, closing connection with " + "server!\n"), + PACKAGE_NAME); + irc_server_disconnect (server, 1); + return 0; + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("%s: trying 1st nickname \"%s\"\n"), + PACKAGE_NAME, server->nick1); + free (server->nick); + server->nick = strdup (server->nick1); + } + } + } + irc_server_sendf (server, "NICK %s", server->nick); + } + return 0; +} + +/* + * irc_recv_cmd_433: '433' command received (nickname already in use) + */ + +int +irc_recv_cmd_433 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + /* Note: this IRC command can not be ignored */ + + if (!server->is_connected) + { + if (strcmp (server->nick, server->nick1) == 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("%s: nickname \"%s\" is already in use, " + "trying 2nd nickname \"%s\"\n"), + PACKAGE_NAME, server->nick, server->nick2); + free (server->nick); + server->nick = strdup (server->nick2); + } + else + { + if (strcmp (server->nick, server->nick2) == 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("%s: nickname \"%s\" is already in use, " + "trying 3rd nickname \"%s\"\n"), + PACKAGE_NAME, server->nick, server->nick3); + free (server->nick); + server->nick = strdup (server->nick3); + } + else + { + if (strcmp (server->nick, server->nick3) == 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("%s: all declared nicknames are already in use, " + "closing connection with server!\n"), + PACKAGE_NAME); + irc_server_disconnect (server, 1); + return 0; + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("%s: nickname \"%s\" is already in use, " + "trying 1st nickname \"%s\"\n"), + PACKAGE_NAME, server->nick, server->nick1); + free (server->nick); + server->nick = strdup (server->nick1); + } + } + } + irc_server_sendf (server, "NICK %s", server->nick); + } + else + return irc_recv_cmd_error (server, host, nick, arguments); + + return 0; +} + +/* + * irc_recv_cmd_438: '438' command received (not authorized to change nickname) + */ + +int +irc_recv_cmd_438 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos, *pos2; + + /* make C compiler happy */ + (void) server; + (void) host; + (void) nick; + + if (!command_ignored) + { + pos = strchr (arguments, ' '); + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + if (pos) + { + pos[0] = '\0'; + pos++; + + pos2 = strstr (pos, " :"); + if (pos2) + { + pos2[0] = '\0'; + pos2 += 2; + gui_printf (server->buffer, "%s (%s => %s)\n", pos2, arguments, pos); + } + else + gui_printf (server->buffer, "%s (%s)\n", pos, arguments); + } + else + gui_printf (server->buffer, "%s\n", arguments); + } + return 0; +} + +/* + * irc_recv_cmd_671: '671' command (whois, secure connection) + */ + +int +irc_recv_cmd_671 (t_irc_server *server, char *host, char *nick, char *arguments) +{ + char *pos_nick, *pos_message; + + /* make C compiler happy */ + (void) host; + (void) nick; + + if (!command_ignored) + { + pos_nick = strchr (arguments, ' '); + if (pos_nick) + { + while (pos_nick[0] == ' ') + pos_nick++; + pos_message = strchr (pos_nick, ' '); + if (pos_message) + { + pos_message[0] = '\0'; + pos_message++; + while (pos_message[0] == ' ') + pos_message++; + if (pos_message[0] == ':') + pos_message++; + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "%s[%s%s%s] %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + pos_nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_message); + } + } + } + return 0; +} diff --git a/src/plugins/irc/irc-send.c b/src/plugins/irc/irc-send.c new file mode 100644 index 000000000..a4d71d498 --- /dev/null +++ b/src/plugins/irc/irc-send.c @@ -0,0 +1,2281 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-send.c: implementation of IRC commands (client to server), + according to RFC 1459,2810,2811,2812 */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <sys/time.h> +#include <netdb.h> +#include <time.h> +#include <sys/utsname.h> +#include <regex.h> + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/command.h" +#include "../../common/util.h" +#include "../../common/weeconfig.h" +#include "../../gui/gui.h" + + +/* + * irc_send_login: login to irc server + */ + +void +irc_send_login (t_irc_server *server) +{ + if ((server->password) && (server->password[0])) + irc_server_sendf (server, "PASS %s", server->password); + + if (!server->nick) + server->nick = strdup (server->nick1); + irc_server_sendf (server, + "NICK %s\n" + "USER %s %s %s :%s", + server->nick, server->username, server->username, + server->address, server->realname); + gui_input_draw (gui_current_window->buffer, 1); +} + +/* + * irc_send_cmd_admin: find information about the administrator of the server + */ + +int +irc_send_cmd_admin (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "ADMIN %s", arguments); + else + irc_server_sendf (server, "ADMIN"); + return 0; +} + +/* + * irc_send_me: send a ctcp action to a channel + */ + +int +irc_send_me (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char *string; + + irc_server_sendf (server, "PRIVMSG %s :\01ACTION %s\01", + channel->name, + (arguments && arguments[0]) ? arguments : ""); + irc_display_prefix (NULL, channel->buffer, GUI_PREFIX_ACTION_ME); + string = (arguments && arguments[0]) ? + (char *)gui_color_decode ((unsigned char *)arguments, 1, 0) : NULL; + gui_printf (channel->buffer, "%s%s %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + server->nick, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (string) ? string : ""); + if (string) + free (string); + return 0; +} + +/* + * irc_send_me_all_channels: send a ctcp action to all channels of a server + */ + +int +irc_send_me_all_channels (t_irc_server *server, char *arguments) +{ + t_irc_channel *ptr_channel; + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + irc_send_me (server, ptr_channel, arguments); + } + return 0; +} + +/* + * irc_send_cmd_ame: send a ctcp action to all channels of all connected servers + */ + +int +irc_send_cmd_ame (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + + /* make C compiler happy */ + (void) server; + (void) channel; + + gui_add_hotlist = 0; + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server->is_connected) + { + for (ptr_channel = ptr_server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + irc_send_me (ptr_server, ptr_channel, arguments); + } + } + } + gui_add_hotlist = 1; + return 0; +} + +/* + * irc_send_cmd_amsg: send message to all channels of all connected servers + */ + +int +irc_send_cmd_amsg (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + char *string; + + /* make C compiler happy */ + (void) server; + (void) channel; + + if (arguments) + { + gui_add_hotlist = 0; + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server->is_connected) + { + for (ptr_channel = ptr_server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + { + irc_server_sendf (ptr_server, "PRIVMSG %s :%s", + ptr_channel->name, arguments); + ptr_nick = irc_nick_search (ptr_channel, ptr_server->nick); + if (ptr_nick) + { + irc_display_nick (ptr_channel->buffer, ptr_nick, NULL, + GUI_MSG_TYPE_NICK, 1, -1, 0); + string = (char *)gui_color_decode ((unsigned char *)arguments, 1, 0); + gui_printf (ptr_channel->buffer, "%s\n", (string) ? string : arguments); + if (string) + free (string); + } + else + { + irc_display_prefix (ptr_server, ptr_server->buffer, GUI_PREFIX_ERROR); + gui_printf (ptr_server->buffer, + _("%s cannot find nick for sending message\n"), + WEECHAT_ERROR); + } + } + } + } + } + gui_add_hotlist = 1; + } + else + return -1; + return 0; +} + +/* + * irc_send_away: toggle away status for one server + */ + +void +irc_send_away (t_irc_server *server, char *arguments) +{ + char *string, buffer[4096]; + t_gui_window *ptr_window; + time_t time_now, elapsed; + + if (!server) + return; + + if (arguments) + { + if (server->away_message) + free (server->away_message); + server->away_message = (char *) malloc (strlen (arguments) + 1); + if (server->away_message) + strcpy (server->away_message, arguments); + + /* if server is connected, send away command now */ + if (server->is_connected) + { + server->is_away = 1; + server->away_time = time (NULL); + irc_server_sendf (server, "AWAY :%s", arguments); + if (cfg_irc_display_away != CFG_IRC_DISPLAY_AWAY_OFF) + { + string = (char *)gui_color_decode ((unsigned char *)arguments, 1, 0); + if (cfg_irc_display_away == CFG_IRC_DISPLAY_AWAY_LOCAL) + irc_display_away (server, "away", (string) ? string : arguments); + else + { + snprintf (buffer, sizeof (buffer), "is away: %s", (string) ? string : arguments); + irc_send_me_all_channels (server, buffer); + } + if (string) + free (string); + } + irc_server_set_away (server, server->nick, 1); + for (ptr_window = gui_windows; ptr_window; + ptr_window = ptr_window->next_window) + { + if (GUI_SERVER(ptr_window->buffer) == server) + ptr_window->buffer->last_read_line = + ptr_window->buffer->last_line; + } + } + else + { + /* server not connected, store away for future usage + (when connecting to server) */ + string = (char *)gui_color_decode ((unsigned char *)arguments, 1, 0); + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_INFO); + gui_printf_nolog (server->buffer, + _("Future away on %s%s%s: %s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + server->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (string) ? string : arguments); + if (string) + free (string); + } + } + else + { + if (server->away_message) + { + free (server->away_message); + server->away_message = NULL; + } + + /* if server is connected, send away command now */ + if (server->is_connected) + { + irc_server_sendf (server, "AWAY"); + server->is_away = 0; + if (server->away_time != 0) + { + time_now = time (NULL); + elapsed = (time_now >= server->away_time) ? + time_now - server->away_time : 0; + server->away_time = 0; + if (cfg_irc_display_away != CFG_IRC_DISPLAY_AWAY_OFF) + { + if (cfg_irc_display_away == CFG_IRC_DISPLAY_AWAY_LOCAL) + { + snprintf (buffer, sizeof (buffer), + "gone %.2ld:%.2ld:%.2ld", + (long int)(elapsed / 3600), + (long int)((elapsed / 60) % 60), + (long int)(elapsed % 60)); + irc_display_away (server, "back", buffer); + } + else + { + snprintf (buffer, sizeof (buffer), + "is back (gone %.2ld:%.2ld:%.2ld)", + (long int)(elapsed / 3600), + (long int)((elapsed / 60) % 60), + (long int)(elapsed % 60)); + irc_send_me_all_channels (server, buffer); + } + } + } + irc_server_set_away (server, server->nick, 0); + } + else + { + /* server not connected, remove away message but do not send anything */ + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_INFO); + gui_printf_nolog (server->buffer, + _("Future away on %s%s%s removed.\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + server->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + } +} + +/* + * irc_send_cmd_away: toggle away status + */ + +int +irc_send_cmd_away (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *pos; + t_irc_server *ptr_server; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + /* make C compiler happy */ + (void) channel; + + gui_add_hotlist = 0; + if (arguments && (strncmp (arguments, "-all", 4) == 0)) + { + pos = arguments + 4; + while (pos[0] == ' ') + pos++; + if (!pos[0]) + pos = NULL; + + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server->is_connected) + irc_send_away (ptr_server, pos); + } + } + else + irc_send_away (server, arguments); + + gui_status_draw (buffer, 1); + gui_add_hotlist = 1; + return 0; +} + +/* + * irc_send_cmd_ban: bans nicks or hosts + */ + +int +irc_send_cmd_ban (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *pos_channel, *pos, *pos2; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (arguments) + { + pos_channel = NULL; + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + + if (irc_channel_is_channel (arguments)) + { + pos_channel = arguments; + pos++; + while (pos[0] == ' ') + pos++; + } + else + { + pos[0] = ' '; + pos = arguments; + } + } + else + pos = arguments; + + /* channel not given, use default buffer */ + if (!pos_channel) + { + if (!GUI_BUFFER_IS_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "ban"); + return -1; + } + pos_channel = GUI_CHANNEL(buffer)->name; + } + + /* loop on users */ + while (pos && pos[0]) + { + pos2 = strchr (pos, ' '); + if (pos2) + { + pos2[0] = '\0'; + pos2++; + while (pos2[0] == ' ') + pos2++; + } + irc_server_sendf (server, "MODE %s +b %s", pos_channel, pos); + pos = pos2; + } + } + else + { + if (!GUI_BUFFER_IS_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "ban"); + return -1; + } + irc_server_sendf (server, "MODE %s +b", GUI_CHANNEL(buffer)->name); + } + + return 0; +} + +/* + * irc_send_cmd_ctcp: send a ctcp message + */ + +int +irc_send_cmd_ctcp (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char *pos_type, *pos_args, *pos; + struct timeval tv; + + /* make C compiler happy */ + (void) channel; + + pos_type = strchr (arguments, ' '); + if (pos_type) + { + pos_type[0] = '\0'; + pos_type++; + while (pos_type[0] == ' ') + pos_type++; + pos_args = strchr (pos_type, ' '); + if (pos_args) + { + pos_args[0] = '\0'; + pos_args++; + while (pos_args[0] == ' ') + pos_args++; + } + else + pos_args = NULL; + + pos = pos_type; + while (pos[0]) + { + pos[0] = toupper (pos[0]); + pos++; + } + + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "CTCP%s(%s%s%s)%s: %s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + pos_type); + + if ((ascii_strcasecmp (pos_type, "ping") == 0) && (!pos_args)) + { + gettimeofday (&tv, NULL); + irc_server_sendf (server, "PRIVMSG %s :\01PING %d %d\01", + arguments, tv.tv_sec, tv.tv_usec); + gui_printf (server->buffer, " %s%d %d\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + tv.tv_sec, tv.tv_usec); + } + else + { + if (pos_args) + { + irc_server_sendf (server, "PRIVMSG %s :\01%s %s\01", + arguments, pos_type, pos_args); + gui_printf (server->buffer, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + pos_args); + } + else + { + irc_server_sendf (server, "PRIVMSG %s :\01%s\01", + arguments, pos_type); + gui_printf (server->buffer, "\n"); + } + } + } + return 0; +} + +/* + * irc_send_cmd_cycle: leave and rejoin a channel + */ + +int +irc_send_cmd_cycle (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *channel_name, *pos_args, *ptr_arg, *buf; + t_irc_channel *ptr_channel; + char **channels; + int i, argc; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (arguments) + { + if (irc_channel_is_channel (arguments)) + { + channel_name = arguments; + pos_args = strchr (arguments, ' '); + if (pos_args) + { + pos_args[0] = '\0'; + pos_args++; + while (pos_args[0] == ' ') + pos_args++; + } + channels = explode_string (channel_name, ",", 0, &argc); + if (channels) + { + for (i = 0; i < argc; i++) + { + ptr_channel = irc_channel_search (server, channels[i]); + /* mark channal as cycling */ + if (ptr_channel && + (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL)) + ptr_channel->cycle = 1; + } + free_exploded_string (channels); + } + } + else + { + if (GUI_BUFFER_IS_SERVER(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can not be executed on a server buffer\n"), + WEECHAT_ERROR, "cycle"); + return -1; + } + + /* does nothing on private buffer (cycle has no sense!) */ + if (GUI_BUFFER_IS_PRIVATE(buffer)) + return 0; + + channel_name = GUI_CHANNEL(buffer)->name; + pos_args = arguments; + GUI_CHANNEL(buffer)->cycle = 1; + } + } + else + { + if (GUI_BUFFER_IS_SERVER(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can not be executed on a server buffer\n"), + WEECHAT_ERROR, "part"); + return -1; + } + + /* does nothing on private buffer (cycle has no sense!) */ + if (GUI_BUFFER_IS_PRIVATE(buffer)) + return 0; + + channel_name = GUI_CHANNEL(buffer)->name; + pos_args = NULL; + GUI_CHANNEL(buffer)->cycle = 1; + } + + ptr_arg = (pos_args) ? pos_args : + (cfg_irc_default_msg_part && cfg_irc_default_msg_part[0]) ? + cfg_irc_default_msg_part : NULL; + + if (ptr_arg) + { + buf = weechat_strreplace (ptr_arg, "%v", PACKAGE_VERSION); + irc_server_sendf (server, "PART %s :%s", channel_name, + (buf) ? buf : ptr_arg); + if (buf) + free (buf); + } + else + irc_server_sendf (server, "PART %s", channel_name); + + return 0; +} + +/* + * irc_send_cmd_dehalfop: remove half operator privileges from nickname(s) + */ + +int +irc_send_cmd_dehalfop (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (GUI_BUFFER_IS_CHANNEL(buffer)) + { + if (argc == 0) + irc_server_sendf (server, "MODE %s -h %s", + GUI_CHANNEL(buffer)->name, + server->nick); + else + irc_send_mode_nicks (server, GUI_CHANNEL(buffer)->name, + "-", "h", argc, argv); + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "dehalfop"); + } + return 0; +} + +/* + * irc_send_cmd_deop: remove operator privileges from nickname(s) + */ + +int +irc_send_cmd_deop (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (GUI_BUFFER_IS_CHANNEL(buffer)) + { + if (argc == 0) + irc_server_sendf (server, "MODE %s -o %s", + GUI_CHANNEL(buffer)->name, + server->nick); + else + irc_send_mode_nicks (server, GUI_CHANNEL(buffer)->name, + "-", "o", argc, argv); + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "deop"); + } + return 0; +} + +/* + * irc_send_cmd_devoice: remove voice from nickname(s) + */ + +int +irc_send_cmd_devoice (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (GUI_BUFFER_IS_CHANNEL(buffer)) + { + if (argc == 0) + irc_server_sendf (server, "MODE %s -v %s", + GUI_CHANNEL(buffer)->name, + server->nick); + else + irc_send_mode_nicks (server, GUI_CHANNEL(buffer)->name, + "-", "v", argc, argv); + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "devoice"); + return -1; + } + return 0; +} + +/* + * irc_send_cmd_die: shotdown the server + */ + +int +irc_send_cmd_die (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + (void) arguments; + + irc_server_sendf (server, "DIE"); + return 0; +} + +/* + * irc_send_cmd_halfop: give half operator privileges to nickname(s) + */ + +int +irc_send_cmd_halfop (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (GUI_BUFFER_IS_CHANNEL(buffer)) + { + if (argc == 0) + irc_server_sendf (server, "MODE %s +h %s", + GUI_CHANNEL(buffer)->name, + server->nick); + else + irc_send_mode_nicks (server, GUI_CHANNEL(buffer)->name, + "+", "h", argc, argv); + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "halfop"); + return -1; + } + return 0; +} + +/* + * irc_send_cmd_info: get information describing the server + */ + +int +irc_send_cmd_info (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "INFO %s", arguments); + else + irc_server_sendf (server, "INFO"); + return 0; +} + +/* + * irc_send_cmd_invite: invite a nick on a channel + */ + +int +irc_send_cmd_invite (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (argc == 2) + irc_server_sendf (server, "INVITE %s %s", argv[0], argv[1]); + else + { + if (!GUI_BUFFER_IS_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "invite"); + return -1; + } + irc_server_sendf (server, "INVITE %s %s", + argv[0], GUI_CHANNEL(buffer)->name); + } + return 0; +} + +/* + * irc_send_cmd_ison: check if a nickname is currently on IRC + */ + +int +irc_send_cmd_ison (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "ISON %s", arguments); + return 0; +} + +/* + * irc_send_cmd_join: join a new channel + */ + +int +irc_send_cmd_join (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (irc_channel_is_channel (arguments)) + irc_server_sendf (server, "JOIN %s", arguments); + else + irc_server_sendf (server, "JOIN #%s", arguments); + return 0; +} + +/* + * irc_send_cmd_kick: forcibly remove a user from a channel + */ + +int +irc_send_cmd_kick (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *pos_channel, *pos_nick, *pos_comment; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (irc_channel_is_channel (arguments)) + { + pos_channel = arguments; + pos_nick = strchr (arguments, ' '); + if (!pos_nick) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong arguments for \"%s\" command\n"), + WEECHAT_ERROR, "kick"); + return -1; + } + pos_nick[0] = '\0'; + pos_nick++; + while (pos_nick[0] == ' ') + pos_nick++; + } + else + { + if (!GUI_BUFFER_IS_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "kick"); + return -1; + } + pos_channel = GUI_CHANNEL(buffer)->name; + pos_nick = arguments; + } + + pos_comment = strchr (pos_nick, ' '); + if (pos_comment) + { + pos_comment[0] = '\0'; + pos_comment++; + while (pos_comment[0] == ' ') + pos_comment++; + } + + if (pos_comment) + irc_server_sendf (server, "KICK %s %s :%s", pos_channel, pos_nick, pos_comment); + else + irc_server_sendf (server, "KICK %s %s", pos_channel, pos_nick); + + return 0; +} + +/* + * irc_send_cmd_kickban: forcibly remove a user from a channel and ban it + */ + +int +irc_send_cmd_kickban (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *pos_channel, *pos_nick, *pos_comment; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (irc_channel_is_channel (arguments)) + { + pos_channel = arguments; + pos_nick = strchr (arguments, ' '); + if (!pos_nick) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong arguments for \"%s\" command\n"), + WEECHAT_ERROR, "kickban"); + return -1; + } + pos_nick[0] = '\0'; + pos_nick++; + while (pos_nick[0] == ' ') + pos_nick++; + } + else + { + if (!GUI_BUFFER_IS_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "kickban"); + return -1; + } + pos_channel = GUI_CHANNEL(buffer)->name; + pos_nick = arguments; + } + + pos_comment = strchr (pos_nick, ' '); + if (pos_comment) + { + pos_comment[0] = '\0'; + pos_comment++; + while (pos_comment[0] == ' ') + pos_comment++; + } + + irc_server_sendf (server, "MODE %s +b %s", pos_channel, pos_nick); + if (pos_comment) + irc_server_sendf (server, "KICK %s %s :%s", pos_channel, pos_nick, pos_comment); + else + irc_server_sendf (server, "KICK %s %s", pos_channel, pos_nick); + + return 0; +} + +/* + * irc_send_cmd_kill: close client-server connection + */ + +int +irc_send_cmd_kill (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char *pos; + + /* make C compiler happy */ + (void) channel; + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + irc_server_sendf (server, "KILL %s :%s", arguments, pos); + } + else + irc_server_sendf (server, "KILL %s", arguments); + return 0; +} + +/* + * irc_send_cmd_links: list all servernames which are known by the server + * answering the query + */ + +int +irc_send_cmd_links (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "LINKS %s", arguments); + else + irc_server_sendf (server, "LINKS"); + return 0; +} + +/* + * irc_send_cmd_list: close client-server connection + */ + +int +irc_send_cmd_list (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char buffer[512]; + int ret; + /* make C compiler happy */ + (void) channel; + + if (server->cmd_list_regexp) + { + regfree (server->cmd_list_regexp); + free (server->cmd_list_regexp); + server->cmd_list_regexp = NULL; + } + + if (arguments) + { + server->cmd_list_regexp = (regex_t *) malloc (sizeof (regex_t)); + if (server->cmd_list_regexp) + { + if ((ret = regcomp (server->cmd_list_regexp, arguments, REG_NOSUB | REG_ICASE)) != 0) + { + regerror (ret, server->cmd_list_regexp, buffer, sizeof(buffer)); + gui_printf (server->buffer, + _("%s \"%s\" is not a valid regular expression (%s)\n"), + WEECHAT_ERROR, arguments, buffer); + } + else + irc_server_sendf (server, "LIST"); + } + else + { + gui_printf (server->buffer, + _("%s not enough memory for regular expression\n"), + WEECHAT_ERROR); + } + } + else + irc_server_sendf (server, "LIST"); + + return 0; +} + +/* + * irc_send_cmd_lusers: get statistics about ths size of the IRC network + */ + +int +irc_send_cmd_lusers (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "LUSERS %s", arguments); + else + irc_server_sendf (server, "LUSERS"); + return 0; +} + +/* + * irc_send_cmd_me: send a ctcp action to the current channel + */ + +int +irc_send_cmd_me (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (GUI_BUFFER_IS_SERVER(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can not be executed on a server buffer\n"), + WEECHAT_ERROR, "me"); + return -1; + } + irc_send_me (server, GUI_CHANNEL(buffer), arguments); + return 0; +} + +/* + * irc_send_cmd_mode: change mode for channel/nickname + */ + +int +irc_send_cmd_mode (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "MODE %s", arguments); + return 0; +} + +/* + * irc_send_mode_nicks: send mode change for many nicks on a channel + */ + +void +irc_send_mode_nicks (t_irc_server *server, char *channel, + char *set, char *mode, int argc, char **argv) +{ + int i, length; + char *command; + + length = 0; + for (i = 0; i < argc; i++) + length += strlen (argv[i]) + 1; + length += strlen (channel) + (argc * strlen (mode)) + 32; + command = (char *)malloc (length); + if (command) + { + snprintf (command, length, "MODE %s %s", channel, set); + for (i = 0; i < argc; i++) + strcat (command, mode); + for (i = 0; i < argc; i++) + { + strcat (command, " "); + strcat (command, argv[i]); + } + irc_server_sendf (server, "%s", command); + free (command); + } +} + +/* + * irc_send_cmd_motd: get the "Message Of The Day" + */ + +int +irc_send_cmd_motd (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "MOTD %s", arguments); + else + irc_server_sendf (server, "MOTD"); + return 0; +} + +/* + * irc_send_cmd_msg: send a message to a nick or channel + */ + +int +irc_send_cmd_msg (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_window *window; + t_gui_buffer *buffer; + char *pos, *pos_comma; + char *msg_pwd_hidden; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + char *string; + + gui_buffer_find_context (server, channel, &window, &buffer); + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + + while (arguments && arguments[0]) + { + pos_comma = strchr (arguments, ','); + if (pos_comma) + { + pos_comma[0] = '\0'; + pos_comma++; + } + if (strcmp (arguments, "*") == 0) + { + if (!GUI_BUFFER_IS_CHANNEL(buffer) && + !GUI_BUFFER_IS_PRIVATE(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel or private buffer\n"), + WEECHAT_ERROR, "msg *"); + return -1; + } + ptr_channel = GUI_CHANNEL(buffer); + if (GUI_BUFFER_IS_CHANNEL(buffer)) + ptr_nick = irc_nick_search (ptr_channel, server->nick); + else + ptr_nick = NULL; + irc_display_nick (buffer, ptr_nick, + (ptr_nick) ? NULL : server->nick, + GUI_MSG_TYPE_NICK, 1, -1, 0); + string = (char *)gui_color_decode ((unsigned char *)pos, 1, 0); + gui_printf_type (buffer, GUI_MSG_TYPE_MSG, "%s\n", + (string) ? string : ""); + if (string) + free (string); + + irc_server_sendf (server, "PRIVMSG %s :%s", ptr_channel->name, pos); + } + else + { + if (irc_channel_is_channel (arguments)) + { + ptr_channel = irc_channel_search (server, arguments); + if (ptr_channel) + { + ptr_nick = irc_nick_search (ptr_channel, server->nick); + if (ptr_nick) + { + irc_display_nick (ptr_channel->buffer, ptr_nick, NULL, + GUI_MSG_TYPE_NICK, 1, -1, 0); + string = (char *)gui_color_decode ((unsigned char *)pos, 1, 0); + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, "%s\n", + (string) ? string : ""); + if (string) + free (string); + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s nick \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, server->nick, "msg"); + } + } + irc_server_sendf (server, "PRIVMSG %s :%s", arguments, pos); + } + else + { + /* message to nickserv with identify ? */ + if (strcmp (arguments, "nickserv") == 0) + { + msg_pwd_hidden = strdup (pos); + if (cfg_log_hide_nickserv_pwd) + irc_display_hide_password (msg_pwd_hidden, 0); + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf_type (server->buffer, GUI_MSG_TYPE_NICK, + "%s-%s%s%s- ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + string = (char *)gui_color_decode ((unsigned char *)msg_pwd_hidden, 1, 0); + gui_printf (server->buffer, "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (string) ? string : ""); + if (string) + free (string); + irc_server_sendf (server, "PRIVMSG %s :%s", arguments, pos); + free (msg_pwd_hidden); + return 0; + } + + string = (char *)gui_color_decode ((unsigned char *)pos, 1, 0); + ptr_channel = irc_channel_search (server, arguments); + if (ptr_channel) + { + irc_display_nick (ptr_channel->buffer, NULL, server->nick, + GUI_MSG_TYPE_NICK, 1, GUI_COLOR_WIN_NICK_SELF, 0); + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, + "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (string) ? string : ""); + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + gui_printf (server->buffer, "MSG%s(%s%s%s)%s: ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + gui_printf_type (server->buffer, GUI_MSG_TYPE_MSG, + "%s\n", + (string) ? string : pos); + } + if (string) + free (string); + irc_server_sendf (server, "PRIVMSG %s :%s", arguments, pos); + } + } + arguments = pos_comma; + } + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong argument count for \"%s\" command\n"), + WEECHAT_ERROR, "msg"); + return -1; + } + return 0; +} + +/* + * irc_send_cmd_names: list nicknames on channels + */ + +int +irc_send_cmd_names (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (arguments) + irc_server_sendf (server, "NAMES %s", arguments); + else + { + if (!GUI_BUFFER_IS_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "names"); + return -1; + } + else + irc_server_sendf (server, "NAMES %s", + GUI_CHANNEL(buffer)->name); + } + return 0; +} + +/* + * irc_send_cmd_nick_server: change nickname on a server + */ + +void +irc_send_cmd_nick_server (t_irc_server *server, char *nickname) +{ + t_irc_channel *ptr_channel; + + if (server->is_connected) + irc_server_sendf (server, "NICK %s", nickname); + else + { + if (server->nick) + free (server->nick); + server->nick = strdup (nickname); + gui_input_draw (server->buffer, 1); + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + gui_input_draw (ptr_channel->buffer, 1); + } + } +} + +/* + * irc_send_cmd_nick: change nickname + */ + +int +irc_send_cmd_nick (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_irc_server *ptr_server; + + /* make C compiler happy */ + (void) channel; + + if (!server) + return 0; + + if (argc == 2) + { + if (strncmp (argv[0], "-all", 4) != 0) + return -1; + + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + irc_send_cmd_nick_server (ptr_server, argv[1]); + } + } + else + { + if (argc == 1) + irc_send_cmd_nick_server (server, argv[0]); + else + return -1; + } + + return 0; +} + +/* + * irc_send_cmd_notice: send notice message + */ + +int +irc_send_cmd_notice (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char *pos, *string; + + /* make C compiler happy */ + (void) channel; + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + irc_display_prefix (server, server->buffer, GUI_PREFIX_SERVER); + string = (char *)gui_color_decode ((unsigned char *)pos, 1, 0); + gui_printf (server->buffer, "notice%s(%s%s%s)%s: %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + arguments, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (string) ? string : ""); + if (string) + free (string); + irc_server_sendf (server, "NOTICE %s :%s", arguments, pos); + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong argument count for \"%s\" command\n"), + WEECHAT_ERROR, "notice"); + return -1; + } + return 0; +} + +/* + * irc_send_cmd_op: give operator privileges to nickname(s) + */ + +int +irc_send_cmd_op (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (GUI_BUFFER_IS_CHANNEL(buffer)) + { + if (argc == 0) + irc_server_sendf (server, "MODE %s +o %s", + GUI_CHANNEL(buffer)->name, + server->nick); + else + irc_send_mode_nicks (server, GUI_CHANNEL(buffer)->name, + "+", "o", argc, argv); + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "op"); + return -1; + } + return 0; +} + +/* + * irc_send_cmd_oper: get oper privileges + */ + +int +irc_send_cmd_oper (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "OPER %s", arguments); + return 0; +} + +/* + * irc_send_cmd_part: leave a channel or close a private window + */ + +int +irc_send_cmd_part (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *channel_name, *pos_args, *ptr_arg, *buf; + t_irc_channel *ptr_channel; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (arguments) + { + if (irc_channel_is_channel (arguments)) + { + channel_name = arguments; + pos_args = strchr (arguments, ' '); + if (pos_args) + { + pos_args[0] = '\0'; + pos_args++; + while (pos_args[0] == ' ') + pos_args++; + } + } + else + { + if (!GUI_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel or private buffer\n"), + WEECHAT_ERROR, "part"); + return -1; + } + channel_name = GUI_CHANNEL(buffer)->name; + pos_args = arguments; + } + } + else + { + if (!GUI_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel or private buffer\n"), + WEECHAT_ERROR, "part"); + return -1; + } + if (GUI_BUFFER_IS_PRIVATE(buffer)) + { + ptr_channel = GUI_CHANNEL(buffer); + gui_buffer_free (ptr_channel->buffer, 1); + irc_channel_free (server, ptr_channel); + gui_status_draw (buffer, 1); + gui_input_draw (buffer, 1); + return 0; + } + channel_name = GUI_CHANNEL(buffer)->name; + pos_args = NULL; + } + + ptr_arg = (pos_args) ? pos_args : + (cfg_irc_default_msg_part && cfg_irc_default_msg_part[0]) ? + cfg_irc_default_msg_part : NULL; + + if (ptr_arg) + { + buf = weechat_strreplace (ptr_arg, "%v", PACKAGE_VERSION); + irc_server_sendf (server, "PART %s :%s", channel_name, + (buf) ? buf : ptr_arg); + if (buf) + free (buf); + } + else + irc_server_sendf (server, "PART %s", channel_name); + + return 0; +} + +/* + * irc_send_cmd_ping: ping a server + */ + +int +irc_send_cmd_ping (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "PING %s", arguments); + return 0; +} + +/* + * irc_send_cmd_pong: send pong answer to a daemon + */ + +int +irc_send_cmd_pong (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "PONG %s", arguments); + return 0; +} + +/* + * irc_send_cmd_query: start private conversation with a nick + */ + +int +irc_send_cmd_query (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_window *window; + t_gui_buffer *buffer; + char *pos, *string; + t_irc_channel *ptr_channel; + t_gui_buffer *ptr_buffer; + + gui_buffer_find_context (server, channel, &window, &buffer); + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + if (!pos[0]) + pos = NULL; + } + + /* create private window if not already opened */ + ptr_channel = irc_channel_search (server, arguments); + if (!ptr_channel) + { + ptr_channel = irc_channel_new (server, IRC_CHANNEL_TYPE_PRIVATE, arguments); + if (!ptr_channel) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s cannot create new private buffer \"%s\"\n"), + WEECHAT_ERROR, arguments); + return -1; + } + gui_buffer_new (window, server, ptr_channel, + GUI_BUFFER_TYPE_STANDARD, 1); + gui_chat_draw_title (ptr_channel->buffer, 1); + } + else + { + for (ptr_buffer = gui_buffers; ptr_buffer; ptr_buffer = ptr_buffer->next_buffer) + { + if (ptr_buffer->channel == ptr_channel) + { + gui_window_switch_to_buffer (window, ptr_buffer); + gui_window_redraw_buffer (ptr_buffer); + break; + } + } + } + + /* display text if given */ + if (pos) + { + irc_display_nick (ptr_channel->buffer, NULL, server->nick, + GUI_MSG_TYPE_NICK, 1, GUI_COLOR_WIN_NICK_SELF, 0); + string = (char *)gui_color_decode ((unsigned char *)pos, 1, 0); + gui_printf_type (ptr_channel->buffer, GUI_MSG_TYPE_MSG, + "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (string) ? string : ""); + if (string) + free (string); + irc_server_sendf (server, "PRIVMSG %s :%s", arguments, pos); + } + return 0; +} + +/* + * irc_send_quit_server: send QUIT to a server + */ + +void +irc_send_quit_server (t_irc_server *server, char *arguments) +{ + char *ptr_arg, *buf; + + if (server->is_connected) + { + ptr_arg = (arguments) ? arguments : + (cfg_irc_default_msg_quit && cfg_irc_default_msg_quit[0]) ? + cfg_irc_default_msg_quit : NULL; + + if (ptr_arg) + { + buf = weechat_strreplace (ptr_arg, "%v", PACKAGE_VERSION); + irc_server_sendf (server, "QUIT :%s", + (buf) ? buf : ptr_arg); + if (buf) + free (buf); + } + else + irc_server_sendf (server, "QUIT"); + } +} + +/* + * irc_send_cmd_quit: disconnect from all servers and quit WeeChat + */ + +int +irc_send_cmd_quit (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_irc_server *ptr_server; + + /* make C compiler happy */ + (void) server; + (void) channel; + + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + irc_send_quit_server (ptr_server, arguments); + } + quit_weechat = 1; + return 0; +} + +/* + * irc_send_cmd_quote: send raw data to server + */ + +int +irc_send_cmd_quote (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (!server || server->sock < 0) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf_nolog (NULL, + _("%s command \"%s\" needs a server connection!\n"), + WEECHAT_ERROR, "quote"); + return -1; + } + irc_server_sendf (server, "%s", arguments); + return 0; +} + +/* + * irc_send_cmd_rehash: tell the server to reload its config file + */ + +int +irc_send_cmd_rehash (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + (void) arguments; + + irc_server_sendf (server, "REHASH"); + return 0; +} + +/* + * irc_send_cmd_restart: tell the server to restart itself + */ + +int +irc_send_cmd_restart (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + (void) arguments; + + irc_server_sendf (server, "RESTART"); + return 0; +} + +/* + * irc_send_cmd_service: register a new service + */ + +int +irc_send_cmd_service (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "SERVICE %s", arguments); + return 0; +} + +/* + * irc_send_cmd_servlist: list services currently connected to the network + */ + +int +irc_send_cmd_servlist (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "SERVLIST %s", arguments); + else + irc_server_sendf (server, "SERVLIST"); + return 0; +} + +/* + * irc_send_cmd_squery: deliver a message to a service + */ + +int +irc_send_cmd_squery (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char *pos; + + /* make C compiler happy */ + (void) channel; + + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + { + pos++; + } + irc_server_sendf (server, "SQUERY %s :%s", arguments, pos); + } + else + irc_server_sendf (server, "SQUERY %s", arguments); + + return 0; +} + +/* + * irc_send_cmd_squit: disconnect server links + */ + +int +irc_send_cmd_squit (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "SQUIT %s", arguments); + return 0; +} + +/* + * irc_send_cmd_stats: query statistics about server + */ + +int +irc_send_cmd_stats (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "STATS %s", arguments); + else + irc_server_sendf (server, "STATS"); + return 0; +} + +/* + * irc_send_cmd_summon: give users who are on a host running an IRC server + * a message asking them to please join IRC + */ + +int +irc_send_cmd_summon (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "SUMMON %s", arguments); + return 0; +} + +/* + * irc_send_cmd_time: query local time from server + */ + +int +irc_send_cmd_time (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "TIME %s", arguments); + else + irc_server_sendf (server, "TIME"); + return 0; +} + +/* + * irc_send_cmd_topic: get/set topic for a channel + */ + +int +irc_send_cmd_topic (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *channel_name, *new_topic, *pos; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + channel_name = NULL; + new_topic = NULL; + + if (arguments) + { + if (irc_channel_is_channel (arguments)) + { + channel_name = arguments; + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + new_topic = (pos[0]) ? pos : NULL; + } + } + else + new_topic = arguments; + } + + /* look for current channel if not specified */ + if (!channel_name) + { + if (GUI_BUFFER_IS_SERVER(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can not be executed on a server buffer\n"), + WEECHAT_ERROR, "topic"); + return -1; + } + channel_name = GUI_CHANNEL(buffer)->name; + } + + if (new_topic) + { + if (strcmp (new_topic, "-delete") == 0) + irc_server_sendf (server, "TOPIC %s :", channel_name); + else + irc_server_sendf (server, "TOPIC %s :%s", channel_name, new_topic); + } + else + irc_server_sendf (server, "TOPIC %s", channel_name); + + return 0; +} + +/* + * irc_send_cmd_trace: find the route to specific server + */ + +int +irc_send_cmd_trace (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "TRACE %s", arguments); + else + irc_server_sendf (server, "TRACE"); + return 0; +} + +/* + * irc_send_cmd_unban: unbans nicks or hosts + */ + +int +irc_send_cmd_unban (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *pos_channel, *pos, *pos2; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (arguments) + { + pos_channel = NULL; + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + + if (irc_channel_is_channel (arguments)) + { + pos_channel = arguments; + pos++; + while (pos[0] == ' ') + pos++; + } + else + { + pos[0] = ' '; + pos = arguments; + } + } + else + pos = arguments; + + /* channel not given, use default buffer */ + if (!pos_channel) + { + if (!GUI_BUFFER_IS_CHANNEL(buffer)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "unban"); + return -1; + } + pos_channel = GUI_CHANNEL(buffer)->name; + } + + /* loop on users */ + while (pos && pos[0]) + { + pos2 = strchr (pos, ' '); + if (pos2) + { + pos2[0] = '\0'; + pos2++; + while (pos2[0] == ' ') + pos2++; + } + irc_server_sendf (server, "MODE %s -b %s", pos_channel, pos); + pos = pos2; + } + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong argument count for \"%s\" command\n"), + WEECHAT_ERROR, "unban"); + return -1; + } + return 0; +} + +/* + * irc_send_cmd_userhost: return a list of information about nicknames + */ + +int +irc_send_cmd_userhost (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "USERHOST %s", arguments); + return 0; +} + +/* + * irc_send_cmd_users: list of users logged into the server + */ + +int +irc_send_cmd_users (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "USERS %s", arguments); + else + irc_server_sendf (server, "USERS"); + return 0; +} + +/* + * irc_send_cmd_version: gives the version info of nick or server (current or specified) + */ + +int +irc_send_cmd_version (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (arguments) + { + if (GUI_BUFFER_IS_CHANNEL(buffer) && + irc_nick_search (GUI_CHANNEL(buffer), arguments)) + irc_server_sendf (server, "PRIVMSG %s :\01VERSION\01", + arguments); + else + irc_server_sendf (server, "VERSION %s", + arguments); + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, _("%s, compiled on %s %s\n"), + PACKAGE_STRING, + __DATE__, __TIME__); + irc_server_sendf (server, "VERSION"); + } + return 0; +} + +/* + * irc_send_cmd_voice: give voice to nickname(s) + */ + +int +irc_send_cmd_voice (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (GUI_BUFFER_IS_CHANNEL(buffer)) + { + if (argc == 0) + irc_server_sendf (server, "MODE %s +v %s", + GUI_CHANNEL(buffer)->name, + server->nick); + else + irc_send_mode_nicks (server, GUI_CHANNEL(buffer)->name, + "+", "v", argc, argv); + } + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s \"%s\" command can only be executed in a channel buffer\n"), + WEECHAT_ERROR, "voice"); + return -1; + } + return 0; +} + +/* + * irc_send_cmd_wallops: send a message to all currently connected users who + * have set the 'w' user mode for themselves + */ + +int +irc_send_cmd_wallops (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "WALLOPS :%s", arguments); + return 0; +} + +/* + * irc_send_cmd_who: generate a query which returns a list of information + */ + +int +irc_send_cmd_who (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + if (arguments) + irc_server_sendf (server, "WHO %s", arguments); + else + irc_server_sendf (server, "WHO"); + return 0; +} + +/* + * irc_send_cmd_whois: query information about user(s) + */ + +int +irc_send_cmd_whois (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "WHOIS %s", arguments); + return 0; +} + +/* + * irc_send_cmd_whowas: ask for information about a nickname which no longer exists + */ + +int +irc_send_cmd_whowas (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + /* make C compiler happy */ + (void) channel; + + irc_server_sendf (server, "WHOWAS %s", arguments); + return 0; +} diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c new file mode 100644 index 000000000..0c119ceb1 --- /dev/null +++ b/src/plugins/irc/irc-server.c @@ -0,0 +1,2415 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + +/* irc-server.c: connection and communication with IRC server */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <pwd.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + +#include "../../common/weechat.h" +#include "irc.h" +#include "../../common/log.h" +#include "../../common/util.h" +#include "../../common/weeconfig.h" +#include "../../gui/gui.h" + +#ifdef PLUGINS +#include "../../plugins/plugins.h" +#endif + + +t_irc_server *irc_servers = NULL; +t_irc_server *last_irc_server = NULL; + +t_irc_message *irc_recv_msgq = NULL; +t_irc_message *irc_msgq_last_msg = NULL; + +int irc_check_away = 0; + +#ifdef HAVE_GNUTLS +const int gnutls_cert_type_prio[] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 }; +#if LIBGNUTLS_VERSION_NUMBER >= 0x010700 + const int gnutls_prot_prio[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, + GNUTLS_TLS1_0, GNUTLS_SSL3, 0 }; +#else + const int gnutls_prot_prio[] = { GNUTLS_TLS1_1, GNUTLS_TLS1_0, + GNUTLS_SSL3, 0 }; +#endif +#endif + + +/* + * irc_server_init: init server struct with default values + */ + +void +irc_server_init (t_irc_server *server) +{ + /* user choices */ + server->name = NULL; + server->autoconnect = 0; + server->autoreconnect = 1; + server->autoreconnect_delay = 30; + server->temp_server = 0; + server->address = NULL; + server->port = -1; + server->ipv6 = 0; + server->ssl = 0; + server->password = NULL; + server->nick1 = NULL; + server->nick2 = NULL; + server->nick3 = NULL; + server->username = NULL; + server->realname = NULL; + server->hostname = NULL; + server->command = NULL; + server->command_delay = 1; + server->autojoin = NULL; + server->autorejoin = 0; + server->notify_levels = NULL; + + /* internal vars */ + server->child_pid = 0; + server->child_read = -1; + server->child_write = -1; + server->sock = -1; + server->is_connected = 0; + server->ssl_connected = 0; + server->unterminated_message = NULL; + server->nick = NULL; + server->nick_modes = NULL; + server->prefix = NULL; + server->reconnect_start = 0; + server->command_time = 0; + server->reconnect_join = 0; + server->disable_autojoin = 0; + server->is_away = 0; + server->away_message = NULL; + server->away_time = 0; + server->lag = 0; + server->lag_check_time.tv_sec = 0; + server->lag_check_time.tv_usec = 0; + server->lag_next_check = time (NULL) + cfg_irc_lag_check; + server->cmd_list_regexp = NULL; + server->queue_msg = 0; + server->last_user_message = 0; + server->outqueue = NULL; + server->last_outqueue = NULL; + server->buffer = NULL; + server->saved_buffer = NULL; + server->channels = NULL; + server->last_channel = NULL; +} + +/* + * irc_server_init_with_url: init a server with url of this form: + * irc://nick:pass@irc.toto.org:6667 + * returns: 0 = ok + * -1 = invalid syntax + */ + +int +irc_server_init_with_url (char *irc_url, t_irc_server *server) +{ + char *url, *pos_server, *pos_channel, *pos, *pos2; + int ipv6, ssl; + struct passwd *my_passwd; + + irc_server_init (server); + ipv6 = 0; + ssl = 0; + if (strncasecmp (irc_url, "irc6://", 7) == 0) + { + pos = irc_url + 7; + ipv6 = 1; + } + else if (strncasecmp (irc_url, "ircs://", 7) == 0) + { + pos = irc_url + 7; + ssl = 1; + } + else if ((strncasecmp (irc_url, "irc6s://", 8) == 0) + || (strncasecmp (irc_url, "ircs6://", 8) == 0)) + { + pos = irc_url + 8; + ipv6 = 1; + ssl = 1; + } + else if (strncasecmp (irc_url, "irc://", 6) == 0) + { + pos = irc_url + 6; + } + else + return -1; + + url = strdup (pos); + pos_server = strchr (url, '@'); + if (pos_server) + { + pos_server[0] = '\0'; + pos_server++; + if (!pos[0]) + { + free (url); + return -1; + } + pos2 = strchr (url, ':'); + if (pos2) + { + pos2[0] = '\0'; + server->password = strdup (pos2 + 1); + } + server->nick1 = strdup (url); + } + else + { + if ((my_passwd = getpwuid (geteuid ())) != NULL) + server->nick1 = strdup (my_passwd->pw_name); + else + { + weechat_iconv_fprintf (stderr, "%s: %s (%s).", + WEECHAT_WARNING, + _("Unable to get user's name"), + strerror (errno)); + free (url); + return -1; + } + pos_server = url; + } + if (!pos_server[0]) + { + free (url); + return -1; + } + pos_channel = strchr (pos_server, '/'); + if (pos_channel) + { + pos_channel[0] = '\0'; + pos_channel++; + } + pos = strchr (pos_server, ':'); + if (pos) + { + pos[0] = '\0'; + server->port = atoi (pos + 1); + } + server->name = strdup (pos_server); + server->address = strdup (pos_server); + if (pos_channel && pos_channel[0]) + { + if (irc_channel_is_channel (pos_channel)) + server->autojoin = strdup (pos_channel); + else + { + server->autojoin = (char *) malloc (strlen (pos_channel) + 2); + strcpy (server->autojoin, "#"); + strcat (server->autojoin, pos_channel); + } + } + + free (url); + + server->ipv6 = ipv6; + server->ssl = ssl; + + /* some default values */ + if (server->port < 0) + server->port = IRC_DEFAULT_PORT; + server->nick2 = (char *) malloc (strlen (server->nick1) + 2); + strcpy (server->nick2, server->nick1); + server->nick2 = strcat (server->nick2, "1"); + server->nick3 = (char *) malloc (strlen (server->nick1) + 2); + strcpy (server->nick3, server->nick1); + server->nick3 = strcat (server->nick3, "2"); + + return 0; +} + +/* + * irc_server_alloc: allocate a new server and add it to the servers queue + */ + +t_irc_server * +irc_server_alloc () +{ + t_irc_server *new_server; + + /* alloc memory for new server */ + if ((new_server = (t_irc_server *) malloc (sizeof (t_irc_server))) == NULL) + { + weechat_iconv_fprintf (stderr, + _("%s cannot allocate new server\n"), + WEECHAT_ERROR); + return NULL; + } + + /* initialize new server */ + irc_server_init (new_server); + + /* add new server to queue */ + new_server->prev_server = last_irc_server; + new_server->next_server = NULL; + if (irc_servers) + last_irc_server->next_server = new_server; + else + irc_servers = new_server; + last_irc_server = new_server; + + /* all is ok, return address of new server */ + return new_server; +} + +/* + * irc_server_outqueue_add: add a message in out queue + */ + +void +irc_server_outqueue_add (t_irc_server *server, char *msg1, char *msg2, + int modified) +{ + t_irc_outqueue *new_outqueue; + + new_outqueue = (t_irc_outqueue *)malloc (sizeof (t_irc_outqueue)); + if (new_outqueue) + { + new_outqueue->message_before_mod = (msg1) ? strdup (msg1) : NULL; + new_outqueue->message_after_mod = (msg2) ? strdup (msg2) : NULL; + new_outqueue->modified = modified; + + new_outqueue->prev_outqueue = server->last_outqueue; + new_outqueue->next_outqueue = NULL; + if (server->outqueue) + server->last_outqueue->next_outqueue = new_outqueue; + else + server->outqueue = new_outqueue; + server->last_outqueue = new_outqueue; + } +} + +/* + * irc_server_outqueue_free: free a message in out queue + */ + +void +irc_server_outqueue_free (t_irc_server *server, t_irc_outqueue *outqueue) +{ + t_irc_outqueue *new_outqueue; + + /* remove outqueue message */ + if (server->last_outqueue == outqueue) + server->last_outqueue = outqueue->prev_outqueue; + if (outqueue->prev_outqueue) + { + (outqueue->prev_outqueue)->next_outqueue = outqueue->next_outqueue; + new_outqueue = server->outqueue; + } + else + new_outqueue = outqueue->next_outqueue; + + if (outqueue->next_outqueue) + (outqueue->next_outqueue)->prev_outqueue = outqueue->prev_outqueue; + + if (outqueue->message_before_mod) + free (outqueue->message_before_mod); + if (outqueue->message_after_mod) + free (outqueue->message_after_mod); + free (outqueue); + server->outqueue = new_outqueue; +} + +/* + * irc_server_outqueue_free_all: free all outqueued messages + */ + +void +irc_server_outqueue_free_all (t_irc_server *server) +{ + while (server->outqueue) + irc_server_outqueue_free (server, server->outqueue); +} + +/* + * irc_server_destroy: free server data (not struct himself) + */ + +void +irc_server_destroy (t_irc_server *server) +{ + if (!server) + return; + + /* free data */ + if (server->name) + free (server->name); + if (server->address) + free (server->address); + if (server->password) + free (server->password); + if (server->nick1) + free (server->nick1); + if (server->nick2) + free (server->nick2); + if (server->nick3) + free (server->nick3); + if (server->username) + free (server->username); + if (server->realname) + free (server->realname); + if (server->hostname) + free (server->hostname); + if (server->command) + free (server->command); + if (server->autojoin) + free (server->autojoin); + if (server->notify_levels) + free (server->notify_levels); + if (server->unterminated_message) + free (server->unterminated_message); + if (server->nick) + free (server->nick); + if (server->nick_modes) + free (server->nick_modes); + if (server->prefix) + free (server->prefix); + if (server->away_message) + free (server->away_message); + if (server->outqueue) + irc_server_outqueue_free_all (server); + if (server->channels) + irc_channel_free_all (server); +} + +/* + * irc_server_free: free a server and remove it from servers queue + */ + +void +irc_server_free (t_irc_server *server) +{ + t_irc_server *new_irc_servers; + + if (!server) + return; + + /* close any opened channel/private */ + while (server->channels) + irc_channel_free (server, server->channels); + + /* remove server from queue */ + if (last_irc_server == server) + last_irc_server = server->prev_server; + if (server->prev_server) + { + (server->prev_server)->next_server = server->next_server; + new_irc_servers = irc_servers; + } + else + new_irc_servers = server->next_server; + + if (server->next_server) + (server->next_server)->prev_server = server->prev_server; + + irc_server_destroy (server); + free (server); + irc_servers = new_irc_servers; +} + +/* + * irc_server_free_all: free all allocated servers + */ + +void +irc_server_free_all () +{ + /* for each server in memory, remove it */ + while (irc_servers) + irc_server_free (irc_servers); +} + +/* + * irc_server_new: creates a new server, and initialize it + */ + +t_irc_server * +irc_server_new (char *name, int autoconnect, int autoreconnect, + int autoreconnect_delay, int temp_server, char *address, + int port, int ipv6, int ssl, char *password, + char *nick1, char *nick2, char *nick3, char *username, + char *realname, char *hostname, char *command, int command_delay, + char *autojoin, int autorejoin, char *notify_levels) +{ + t_irc_server *new_server; + + if (!name || !address || (port < 0)) + return NULL; + +#ifdef DEBUG + weechat_log_printf ("Creating new server (name:%s, address:%s, port:%d, pwd:%s, " + "nick1:%s, nick2:%s, nick3:%s, username:%s, realname:%s, " + "hostname: %s, command:%s, autojoin:%s, autorejoin:%s, " + "notify_levels:%s)\n", + name, address, port, (password) ? password : "", + (nick1) ? nick1 : "", (nick2) ? nick2 : "", (nick3) ? nick3 : "", + (username) ? username : "", (realname) ? realname : "", + (hostname) ? hostname : "", (command) ? command : "", + (autojoin) ? autojoin : "", (autorejoin) ? "on" : "off", + (notify_levels) ? notify_levels : ""); +#endif + + if ((new_server = irc_server_alloc ())) + { + new_server->name = strdup (name); + new_server->autoconnect = autoconnect; + new_server->autoreconnect = autoreconnect; + new_server->autoreconnect_delay = autoreconnect_delay; + new_server->temp_server = temp_server; + new_server->address = strdup (address); + new_server->port = port; + new_server->ipv6 = ipv6; + new_server->ssl = ssl; + new_server->password = (password) ? strdup (password) : strdup (""); + new_server->nick1 = (nick1) ? strdup (nick1) : strdup ("weechat_user"); + new_server->nick2 = (nick2) ? strdup (nick2) : strdup ("weechat2"); + new_server->nick3 = (nick3) ? strdup (nick3) : strdup ("weechat3"); + new_server->username = + (username) ? strdup (username) : strdup ("weechat"); + new_server->realname = + (realname) ? strdup (realname) : strdup ("realname"); + new_server->hostname = + (hostname) ? strdup (hostname) : NULL; + new_server->command = + (command) ? strdup (command) : NULL; + new_server->command_delay = command_delay; + new_server->autojoin = + (autojoin) ? strdup (autojoin) : NULL; + new_server->autorejoin = autorejoin; + new_server->notify_levels = + (notify_levels) ? strdup (notify_levels) : NULL; + } + else + return NULL; + return new_server; +} + +/* + * irc_server_duplicate: duplicate a server + * return: pointer to new server, NULL if error + */ + +t_irc_server * +irc_server_duplicate (t_irc_server *server, char *new_name) +{ + t_irc_server *new_server; + + /* check if another server exists with this name */ + if (irc_server_search (new_name)) + return 0; + + /* duplicate server */ + new_server = irc_server_new (new_name, + server->autoconnect, + server->autoreconnect, + server->autoreconnect_delay, + server->temp_server, + server->address, + server->port, + server->ipv6, + server->ssl, + server->password, + server->nick1, + server->nick2, + server->nick3, + server->username, + server->realname, + server->hostname, + server->command, + server->command_delay, + server->autojoin, + server->autorejoin, + server->notify_levels); + + return new_server; +} + +/* + * irc_server_rename: rename server (internal name) + * return: 1 if ok, 0 if error + */ + +int +irc_server_rename (t_irc_server *server, char *new_name) +{ + char *str; + + /* check if another server exists with this name */ + if (irc_server_search (new_name)) + return 0; + + /* rename server */ + str = strdup (new_name); + if (str) + { + if (server->name) + free (server->name); + server->name = str; + return 1; + } + return 0; +} + +/* + * irc_server_send: send data to IRC server + * return number of bytes sent, -1 if error + */ + +int +irc_server_send (t_irc_server *server, char *buffer, int size_buf) +{ + if (!server) + return -1; + +#ifdef HAVE_GNUTLS + if (server->ssl_connected) + return gnutls_record_send (server->gnutls_sess, buffer, size_buf); + else +#endif + return send (server->sock, buffer, size_buf, 0); +} + +/* + * irc_server_outqueue_send: send a message from outqueue + */ + +void +irc_server_outqueue_send (t_irc_server *server) +{ + time_t time_now; + char *pos; + + if (server->outqueue) + { + time_now = time (NULL); + if (time_now >= server->last_user_message + cfg_irc_anti_flood) + { + if (server->outqueue->message_before_mod) + { + pos = strchr (server->outqueue->message_before_mod, '\r'); + if (pos) + pos[0] = '\0'; + gui_printf_raw_data (server, 1, 0, + server->outqueue->message_before_mod); + if (pos) + pos[0] = '\r'; + } + if (server->outqueue->message_after_mod) + { + pos = strchr (server->outqueue->message_after_mod, '\r'); + if (pos) + pos[0] = '\0'; + gui_printf_raw_data (server, 1, server->outqueue->modified, + server->outqueue->message_after_mod); + if (pos) + pos[0] = '\r'; + } + if (irc_server_send (server, server->outqueue->message_after_mod, + strlen (server->outqueue->message_after_mod)) <= 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, _("%s error sending data to IRC server\n"), + WEECHAT_ERROR); + } + server->last_user_message = time_now; + irc_server_outqueue_free (server, server->outqueue); + } + } +} + +/* + * irc_server_send_one_msg: send one message to IRC server + */ + +int +irc_server_send_one_msg (t_irc_server *server, char *message) +{ + static char buffer[4096]; + char *new_msg, *ptr_msg, *pos; + int rc, queue, first_message; + time_t time_now; + + rc = 1; + +#ifdef DEBUG + gui_printf (server->buffer, "[DEBUG] Sending to server >>> %s\n", message); +#endif +#ifdef PLUGINS + new_msg = plugin_modifier_exec (PLUGIN_MODIFIER_IRC_OUT, + server->name, + message); +#else + new_msg = NULL; +#endif + + /* no changes in new message */ + if (new_msg && (strcmp (buffer, new_msg) == 0)) + { + free (new_msg); + new_msg = NULL; + } + + /* message not dropped? */ + if (!new_msg || new_msg[0]) + { + first_message = 1; + ptr_msg = (new_msg) ? new_msg : message; + + while (rc && ptr_msg && ptr_msg[0]) + { + pos = strchr (ptr_msg, '\n'); + if (pos) + pos[0] = '\0'; + + snprintf (buffer, sizeof (buffer) - 1, "%s\r\n", ptr_msg); + + /* anti-flood: look whether we should queue outgoing message or not */ + time_now = time (NULL); + queue = 0; + if ((server->queue_msg) + && ((server->outqueue) + || ((cfg_irc_anti_flood > 0) + && (time_now - server->last_user_message < cfg_irc_anti_flood)))) + queue = 1; + + /* if queue, then only queue message and send nothing now */ + if (queue) + { + irc_server_outqueue_add (server, + (new_msg && first_message) ? message : NULL, + buffer, + (new_msg) ? 1 : 0); + } + else + { + if (first_message) + gui_printf_raw_data (server, 1, 0, message); + if (new_msg) + gui_printf_raw_data (server, 1, 1, ptr_msg); + if (irc_server_send (server, buffer, strlen (buffer)) <= 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, _("%s error sending data to IRC server\n"), + WEECHAT_ERROR); + rc = 0; + } + else + { + if (server->queue_msg) + server->last_user_message = time_now; + } + } + if (pos) + { + pos[0] = '\n'; + ptr_msg = pos + 1; + } + else + ptr_msg = NULL; + + first_message = 0; + } + } + else + gui_printf_raw_data (server, 1, 1, _("(message dropped)")); + if (new_msg) + free (new_msg); + + return rc; +} + +/* + * irc_server_sendf: send formatted data to IRC server + * many messages may be sent, separated by '\n' + */ + +void +irc_server_sendf (t_irc_server *server, char *fmt, ...) +{ + va_list args; + static char buffer[4096]; + char *ptr_buf, *pos; + int rc; + + if (!server) + return; + + va_start (args, fmt); + vsnprintf (buffer, sizeof (buffer) - 1, fmt, args); + va_end (args); + + ptr_buf = buffer; + while (ptr_buf && ptr_buf[0]) + { + pos = strchr (ptr_buf, '\n'); + if (pos) + pos[0] = '\0'; + + rc = irc_server_send_one_msg (server, ptr_buf); + + if (pos) + { + pos[0] = '\n'; + ptr_buf = pos + 1; + } + else + ptr_buf = NULL; + + if (!rc) + ptr_buf = NULL; + } +} + +/* + * irc_server_parse_message: parse IRC message and return pointer to + * host, command and arguments (if any) + */ + +void +irc_server_parse_message (char *message, char **host, char **command, char **args) +{ + char *pos, *pos2; + + *host = NULL; + *command = NULL; + *args = NULL; + + if (message[0] == ':') + { + pos = strchr (message, ' '); + if (pos) + { + *host = strndup (message + 1, pos - (message + 1)); + pos++; + } + else + pos = message; + } + else + pos = message; + + if (pos && pos[0]) + { + while (pos[0] == ' ') + pos++; + pos2 = strchr (pos, ' '); + if (pos2) + { + *command = strndup (pos, pos2 - pos); + pos2++; + while (pos2[0] == ' ') + pos2++; + *args = strdup (pos2); + } + } +} + +/* + * irc_server_msgq_add_msg: add a message to received messages queue (at the end) + */ + +void +irc_server_msgq_add_msg (t_irc_server *server, char *msg) +{ + t_irc_message *message; + + if (!server->unterminated_message && !msg[0]) + return; + + message = (t_irc_message *) malloc (sizeof (t_irc_message)); + if (!message) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s not enough memory for received IRC message\n"), + WEECHAT_ERROR); + return; + } + message->server = server; + if (server->unterminated_message) + { + message->data = (char *) malloc (strlen (server->unterminated_message) + + strlen (msg) + 1); + if (!message->data) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s not enough memory for received IRC message\n"), + WEECHAT_ERROR); + } + else + { + strcpy (message->data, server->unterminated_message); + strcat (message->data, msg); + } + free (server->unterminated_message); + server->unterminated_message = NULL; + } + else + message->data = strdup (msg); + message->next_message = NULL; + + if (irc_msgq_last_msg) + { + irc_msgq_last_msg->next_message = message; + irc_msgq_last_msg = message; + } + else + { + irc_recv_msgq = message; + irc_msgq_last_msg = message; + } +} + +/* + * irc_server_msgq_add_unterminated: add an unterminated message to queue + */ + +void +irc_server_msgq_add_unterminated (t_irc_server *server, char *string) +{ + if (!string[0]) + return; + + if (server->unterminated_message) + { + server->unterminated_message = + (char *) realloc (server->unterminated_message, + strlen (server->unterminated_message) + + strlen (string) + 1); + if (!server->unterminated_message) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s not enough memory for received IRC message\n"), + WEECHAT_ERROR); + } + else + strcat (server->unterminated_message, string); + } + else + { + server->unterminated_message = strdup (string); + if (!server->unterminated_message) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s not enough memory for received IRC message\n"), + WEECHAT_ERROR); + } + } +} + +/* + * irc_server_msgq_add_buffer: explode received buffer, creating queued messages + */ + +void +irc_server_msgq_add_buffer (t_irc_server *server, char *buffer) +{ + char *pos_cr, *pos_lf; + + while (buffer[0]) + { + pos_cr = strchr (buffer, '\r'); + pos_lf = strchr (buffer, '\n'); + + if (!pos_cr && !pos_lf) + { + /* no CR/LF found => add to unterminated and return */ + irc_server_msgq_add_unterminated (server, buffer); + return; + } + + if (pos_cr && ((!pos_lf) || (pos_lf > pos_cr))) + { + /* found '\r' first => ignore this char */ + pos_cr[0] = '\0'; + irc_server_msgq_add_unterminated (server, buffer); + buffer = pos_cr + 1; + } + else + { + /* found: '\n' first => terminate message */ + pos_lf[0] = '\0'; + irc_server_msgq_add_msg (server, buffer); + buffer = pos_lf + 1; + } + } +} + +/* + * irc_server_msgq_flush: flush message queue + */ + +void +irc_server_msgq_flush () +{ + t_irc_message *next; + char *ptr_data, *new_msg, *ptr_msg, *pos; + char *host, *command, *args; + + while (irc_recv_msgq) + { + if (irc_recv_msgq->data) + { +#ifdef DEBUG + gui_printf (gui_current_window->buffer, "[DEBUG] %s\n", irc_recv_msgq->data); +#endif + ptr_data = irc_recv_msgq->data; + while (ptr_data[0] == ' ') + ptr_data++; + + if (ptr_data[0]) + { + gui_printf_raw_data (irc_recv_msgq->server, 0, 0, ptr_data); +#ifdef DEBUG + gui_printf (NULL, "[DEBUG] data received from server: %s\n", ptr_data); +#endif +#ifdef PLUGINS + new_msg = plugin_modifier_exec (PLUGIN_MODIFIER_IRC_IN, + irc_recv_msgq->server->name, + ptr_data); +#else + new_msg = NULL; +#endif + /* no changes in new message */ + if (new_msg && (strcmp (ptr_data, new_msg) == 0)) + { + free (new_msg); + new_msg = NULL; + } + + /* message not dropped? */ + if (!new_msg || new_msg[0]) + { + /* use new message (returned by plugin) */ + ptr_msg = (new_msg) ? new_msg : ptr_data; + + while (ptr_msg && ptr_msg[0]) + { + pos = strchr (ptr_msg, '\n'); + if (pos) + pos[0] = '\0'; + + if (new_msg) + gui_printf_raw_data (irc_recv_msgq->server, 0, 1, ptr_msg); + + irc_server_parse_message (ptr_msg, &host, &command, &args); + + switch (irc_recv_command (irc_recv_msgq->server, ptr_msg, host, command, args)) + { + case -1: + irc_display_prefix (irc_recv_msgq->server, + irc_recv_msgq->server->buffer, GUI_PREFIX_ERROR); + gui_printf (irc_recv_msgq->server->buffer, + _("%s Command \"%s\" failed!\n"), WEECHAT_ERROR, command); + break; + case -2: + irc_display_prefix (irc_recv_msgq->server, + irc_recv_msgq->server->buffer, GUI_PREFIX_ERROR); + gui_printf (irc_recv_msgq->server->buffer, + _("%s No command to execute!\n"), WEECHAT_ERROR); + break; + case -3: + irc_display_prefix (irc_recv_msgq->server, + irc_recv_msgq->server->buffer, GUI_PREFIX_ERROR); + gui_printf (irc_recv_msgq->server->buffer, + _("%s Unknown command: cmd=\"%s\", host=\"%s\", args=\"%s\"\n"), + WEECHAT_WARNING, command, host, args); + break; + } + if (host) + free (host); + if (command) + free (command); + if (args) + free (args); + + if (pos) + { + pos[0] = '\n'; + ptr_msg = pos + 1; + } + else + ptr_msg = NULL; + } + } + else + gui_printf_raw_data (irc_recv_msgq->server, 0, 1, _("(message dropped)")); + if (new_msg) + free (new_msg); + } + free (irc_recv_msgq->data); + } + + next = irc_recv_msgq->next_message; + free (irc_recv_msgq); + irc_recv_msgq = next; + if (irc_recv_msgq == NULL) + irc_msgq_last_msg = NULL; + } +} + +/* + * irc_server_recv: receive data from an irc server + */ + +void +irc_server_recv (t_irc_server *server) +{ + static char buffer[4096 + 2]; + int num_read; + + if (!server) + return; + +#ifdef HAVE_GNUTLS + if (server->ssl_connected) + num_read = gnutls_record_recv (server->gnutls_sess, buffer, sizeof (buffer) - 2); + else +#endif + num_read = recv (server->sock, buffer, sizeof (buffer) - 2, 0); + + if (num_read > 0) + { + buffer[num_read] = '\0'; + irc_server_msgq_add_buffer (server, buffer); + irc_server_msgq_flush (); + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot read data from socket, disconnecting from server...\n"), + WEECHAT_ERROR); + irc_server_disconnect (server, 1); + } +} + +/* + * irc_server_child_kill: kill child process and close pipe + */ + +void +irc_server_child_kill (t_irc_server *server) +{ + /* kill process */ + if (server->child_pid > 0) + { + kill (server->child_pid, SIGKILL); + waitpid (server->child_pid, NULL, 0); + server->child_pid = 0; + } + + /* close pipe used with child */ + if (server->child_read != -1) + { + close (server->child_read); + server->child_read = -1; + } + if (server->child_write != -1) + { + close (server->child_write); + server->child_write = -1; + } +} + +/* + * irc_server_close_connection: close server connection + * (kill child, close socket/pipes) + */ + +void +irc_server_close_connection (t_irc_server *server) +{ + irc_server_child_kill (server); + + /* close network socket */ + if (server->sock != -1) + { +#ifdef HAVE_GNUTLS + if (server->ssl_connected) + gnutls_bye (server->gnutls_sess, GNUTLS_SHUT_WR); +#endif + close (server->sock); + server->sock = -1; +#ifdef HAVE_GNUTLS + if (server->ssl_connected) + gnutls_deinit (server->gnutls_sess); +#endif + } + + /* free any pending message */ + if (server->unterminated_message) + { + free (server->unterminated_message); + server->unterminated_message = NULL; + } + irc_server_outqueue_free_all (server); + + /* server is now disconnected */ + server->is_connected = 0; + server->ssl_connected = 0; +} + +/* + * irc_server_reconnect_schedule: schedule reconnect for a server + */ + +void +irc_server_reconnect_schedule (t_irc_server *server) +{ + if (server->autoreconnect) + { + server->reconnect_start = time (NULL); + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, _("%s: Reconnecting to server in %d seconds\n"), + PACKAGE_NAME, server->autoreconnect_delay); + } + else + server->reconnect_start = 0; +} + +/* + * irc_server_child_read: read connection progress from child process + */ + +void +irc_server_child_read (t_irc_server *server) +{ + char buffer[1]; + int num_read; + + num_read = read (server->child_read, buffer, sizeof (buffer)); + if (num_read > 0) + { + switch (buffer[0]) + { + /* connection OK */ + case '0': + /* enable SSL if asked */ +#ifdef HAVE_GNUTLS + if (server->ssl_connected) + { + gnutls_transport_set_ptr (server->gnutls_sess, + (gnutls_transport_ptr) ((unsigned long) server->sock)); + if (gnutls_handshake (server->gnutls_sess) < 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s gnutls handshake failed\n"), + WEECHAT_ERROR); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + return; + } + } +#endif + /* kill child and login to server */ + irc_server_child_kill (server); + irc_send_login (server); + break; + /* adress not found */ + case '1': + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + if (cfg_proxy_use) + gui_printf (server->buffer, + _("%s proxy address \"%s\" not found\n"), + WEECHAT_ERROR, server->address); + else + gui_printf (server->buffer, + _("%s address \"%s\" not found\n"), + WEECHAT_ERROR, server->address); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + break; + /* IP address not found */ + case '2': + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + if (cfg_proxy_use) + gui_printf (server->buffer, + _("%s proxy IP address not found\n"), WEECHAT_ERROR); + else + gui_printf (server->buffer, + _("%s IP address not found\n"), WEECHAT_ERROR); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + break; + /* connection refused */ + case '3': + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + if (cfg_proxy_use) + gui_printf (server->buffer, + _("%s proxy connection refused\n"), WEECHAT_ERROR); + else + gui_printf (server->buffer, + _("%s connection refused\n"), WEECHAT_ERROR); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + break; + /* proxy fails to connect to server */ + case '4': + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s proxy fails to establish connection to " + "server (check username/password if used)\n"), + WEECHAT_ERROR); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + break; + /* fails to set local hostname/IP */ + case '5': + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s unable to set local hostname/IP\n"), + WEECHAT_ERROR); + irc_server_close_connection (server); + irc_server_reconnect_schedule (server); + break; + } + } +} + +/* + * irc_server_convbase64_8x3_to_6x4 : convert 3 bytes of 8 bits in 4 bytes of 6 bits + */ + +void +irc_server_convbase64_8x3_to_6x4 (char *from, char* to) +{ + unsigned char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + to[0] = base64_table [ (from[0] & 0xfc) >> 2 ]; + to[1] = base64_table [ ((from[0] & 0x03) << 4) + ((from[1] & 0xf0) >> 4) ]; + to[2] = base64_table [ ((from[1] & 0x0f) << 2) + ((from[2] & 0xc0) >> 6) ]; + to[3] = base64_table [ from[2] & 0x3f ]; +} + +/* + * irc_server_base64encode: encode a string in base64 + */ + +void +irc_server_base64encode (char *from, char *to) +{ + char *f, *t; + int from_len; + + from_len = strlen(from); + + f = from; + t = to; + + while (from_len >= 3) + { + irc_server_convbase64_8x3_to_6x4 (f, t); + f += 3 * sizeof (*f); + t += 4 * sizeof (*t); + from_len -= 3; + } + + if (from_len > 0) + { + char rest[3] = { 0, 0, 0 }; + switch (from_len) + { + case 1 : + rest[0] = f[0]; + irc_server_convbase64_8x3_to_6x4 (rest, t); + t[2] = t[3] = '='; + break; + case 2 : + rest[0] = f[0]; + rest[1] = f[1]; + irc_server_convbase64_8x3_to_6x4 (rest, t); + t[3] = '='; + break; + } + t[4] = 0; + } +} + +/* + * irc_server_pass_httpproxy: establish connection/authentification to an + * http proxy + * return : + * - 0 if connexion throw proxy was successful + * - 1 if connexion fails + */ + +int +irc_server_pass_httpproxy (int sock, char *address, int port) +{ + + char buffer[256]; + char authbuf[128]; + char authbuf_base64[196]; + int n, m; + + if (cfg_proxy_username && cfg_proxy_username[0]) + { + /* authentification */ + snprintf (authbuf, sizeof (authbuf), "%s:%s", + cfg_proxy_username, cfg_proxy_password); + irc_server_base64encode (authbuf, authbuf_base64); + n = snprintf (buffer, sizeof (buffer), + "CONNECT %s:%d HTTP/1.0\r\nProxy-Authorization: Basic %s\r\n\r\n", + address, port, authbuf_base64); + } + else + { + /* no authentification */ + n = snprintf (buffer, sizeof (buffer), + "CONNECT %s:%d HTTP/1.0\r\n\r\n", address, port); + } + + m = send (sock, buffer, n, 0); + if (n != m) + return 1; + + n = recv (sock, buffer, sizeof (buffer), 0); + + /* success result must be like: "HTTP/1.0 200 OK" */ + if (n < 12) + return 1; + + if (memcmp (buffer, "HTTP/", 5) || memcmp (buffer + 9, "200", 3)) + return 1; + + return 0; +} + +/* + * irc_server_resolve: resolve hostname on its IP address + * (works with ipv4 and ipv6) + * return : + * - 0 if resolution was successful + * - 1 if resolution fails + */ + +int +irc_server_resolve (char *hostname, char *ip, int *version) +{ + char ipbuffer[NI_MAXHOST]; + struct addrinfo *res; + + if (version != NULL) + *version = 0; + + res = NULL; + + if (getaddrinfo (hostname, NULL, NULL, &res) != 0) + return 1; + + if (!res) + return 1; + + if (getnameinfo (res->ai_addr, res->ai_addrlen, ipbuffer, sizeof(ipbuffer), NULL, 0, NI_NUMERICHOST) != 0) + { + freeaddrinfo (res); + return 1; + } + + if ((res->ai_family == AF_INET) && (version != NULL)) + *version = 4; + if ((res->ai_family == AF_INET6) && (version != NULL)) + *version = 6; + + strcpy (ip, ipbuffer); + + freeaddrinfo (res); + + return 0; +} + +/* + * irc_server_pass_socks4proxy: establish connection/authentification thru a + * socks4 proxy + * return : + * - 0 if connexion thru proxy was successful + * - 1 if connexion fails + */ + +int +irc_server_pass_socks4proxy (int sock, char *address, int port, char *username) +{ + /* + * socks4 protocol is explained here: + * http://archive.socks.permeo.com/protocol/socks4.protocol + * + */ + + struct s_socks4 + { + char version; /* 1 byte */ /* socks version : 4 or 5 */ + char method; /* 1 byte */ /* socks method : connect (1) or bind (2) */ + unsigned short port; /* 2 bytes */ /* destination port */ + unsigned long address; /* 4 bytes */ /* destination address */ + char user[64]; /* username (64 characters seems to be enought) */ + } socks4; + unsigned char buffer[24]; + char ip_addr[NI_MAXHOST]; + + socks4.version = 4; + socks4.method = 1; + socks4.port = htons (port); + irc_server_resolve (address, ip_addr, NULL); + socks4.address = inet_addr (ip_addr); + strncpy (socks4.user, username, sizeof (socks4.user) - 1); + + send (sock, (char *) &socks4, 8 + strlen (socks4.user) + 1, 0); + recv (sock, buffer, sizeof (buffer), 0); + + if (buffer[0] == 0 && buffer[1] == 90) + return 0; + + return 1; +} + +/* + * irc_server_pass_socks5proxy: establish connection/authentification thru a + * socks5 proxy + * return : + * - 0 if connexion thru proxy was successful + * - 1 if connexion fails + */ + +int +irc_server_pass_socks5proxy (int sock, char *address, int port) +{ + /* + * socks5 protocol is explained in RFC 1928 + * socks5 authentication with username/pass is explained in RFC 1929 + */ + + struct s_sock5 + { + char version; /* 1 byte */ /* socks version : 4 or 5 */ + char nmethods; /* 1 byte */ /* size in byte(s) of field 'method', here 1 byte */ + char method; /* 1-255 bytes */ /* socks method : noauth (0), auth(user/pass) (2), ... */ + } socks5; + unsigned char buffer[288]; + int username_len, password_len, addr_len, addr_buffer_len; + unsigned char *addr_buffer; + + socks5.version = 5; + socks5.nmethods = 1; + + if (cfg_proxy_username && cfg_proxy_username[0]) + socks5.method = 2; /* with authentication */ + else + socks5.method = 0; /* without authentication */ + + send (sock, (char *) &socks5, sizeof(socks5), 0); + /* server socks5 must respond with 2 bytes */ + if (recv (sock, buffer, 2, 0) != 2) + return 1; + + if (cfg_proxy_username && cfg_proxy_username[0]) + { + /* with authentication */ + /* -> socks server must respond with : + * - socks version (buffer[0]) = 5 => socks5 + * - socks method (buffer[1]) = 2 => authentication + */ + + if (buffer[0] != 5 || buffer[1] != 2) + return 1; + + /* authentication as in RFC 1929 */ + username_len = strlen(cfg_proxy_username); + password_len = strlen(cfg_proxy_password); + + /* make username/password buffer */ + buffer[0] = 1; + buffer[1] = (unsigned char) username_len; + memcpy(buffer + 2, cfg_proxy_username, username_len); + buffer[2 + username_len] = (unsigned char) password_len; + memcpy (buffer + 3 + username_len, cfg_proxy_password, password_len); + + send (sock, buffer, 3 + username_len + password_len, 0); + + /* server socks5 must respond with 2 bytes */ + if (recv (sock, buffer, 2, 0) != 2) + return 1; + + /* buffer[1] = auth state, must be 0 for success */ + if (buffer[1] != 0) + return 1; + } + else + { + /* without authentication */ + /* -> socks server must respond with : + * - socks version (buffer[0]) = 5 => socks5 + * - socks method (buffer[1]) = 0 => no authentication + */ + if (!(buffer[0] == 5 && buffer[1] == 0)) + return 1; + } + + /* authentication successful then giving address/port to connect */ + addr_len = strlen(address); + addr_buffer_len = 4 + 1 + addr_len + 2; + addr_buffer = (unsigned char *) malloc (addr_buffer_len * sizeof(*addr_buffer)); + if (!addr_buffer) + return 1; + addr_buffer[0] = 5; /* version 5 */ + addr_buffer[1] = 1; /* command: 1 for connect */ + addr_buffer[2] = 0; /* reserved */ + addr_buffer[3] = 3; /* address type : ipv4 (1), domainname (3), ipv6 (4) */ + addr_buffer[4] = (unsigned char) addr_len; + memcpy (addr_buffer + 5, address, addr_len); /* server address */ + *((unsigned short *) (addr_buffer + 5 + addr_len)) = htons (port); /* server port */ + + send (sock, addr_buffer, addr_buffer_len, 0); + free (addr_buffer); + + /* dialog with proxy server */ + if (recv (sock, buffer, 4, 0) != 4) + return 1; + + if (!(buffer[0] == 5 && buffer[1] == 0)) + return 1; + + /* buffer[3] = address type */ + switch(buffer[3]) + { + case 1 : + /* ipv4 + * server socks return server bound address and port + * address of 4 bytes and port of 2 bytes (= 6 bytes) + */ + if (recv (sock, buffer, 6, 0) != 6) + return 1; + break; + case 3: + /* domainname + * server socks return server bound address and port + */ + /* reading address length */ + if (recv (sock, buffer, 1, 0) != 1) + return 1; + addr_len = buffer[0]; + /* reading address + port = addr_len + 2 */ + if (recv (sock, buffer, addr_len + 2, 0) != (addr_len + 2)) + return 1; + break; + case 4 : + /* ipv6 + * server socks return server bound address and port + * address of 16 bytes and port of 2 bytes (= 18 bytes) + */ + if (recv (sock, buffer, 18, 0) != 18) + return 1; + break; + default: + return 1; + } + + return 0; +} + +/* + * irc_server_pass_proxy: establish connection/authentification to a proxy + * return : + * - 0 if connexion throw proxy was successful + * - 1 if connexion fails + */ + +int +irc_server_pass_proxy (int sock, char *address, int port, char *username) +{ + if (strcmp (cfg_proxy_type_values[cfg_proxy_type], "http") == 0) + return irc_server_pass_httpproxy (sock, address, port); + if (strcmp (cfg_proxy_type_values[cfg_proxy_type], "socks4") == 0) + return irc_server_pass_socks4proxy (sock, address, port, username); + if (strcmp (cfg_proxy_type_values[cfg_proxy_type], "socks5") == 0) + return irc_server_pass_socks5proxy (sock, address, port); + + return 1; +} + +/* + * irc_server_child: child process trying to connect to server + */ + +int +irc_server_child (t_irc_server *server) +{ + struct addrinfo hints, *res, *res_local; + int rc; + + res = NULL; + res_local = NULL; + + if (cfg_proxy_use) + { + /* get info about server */ + memset (&hints, 0, sizeof (hints)); + hints.ai_family = (cfg_proxy_ipv6) ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo (cfg_proxy_address, NULL, &hints, &res) !=0) + { + write(server->child_write, "1", 1); + return 0; + } + if (!res) + { + write(server->child_write, "1", 1); + return 0; + } + if ((cfg_proxy_ipv6 && (res->ai_family != AF_INET6)) + || ((!cfg_proxy_ipv6 && (res->ai_family != AF_INET)))) + { + write (server->child_write, "2", 1); + freeaddrinfo (res); + return 0; + } + + if (cfg_proxy_ipv6) + ((struct sockaddr_in6 *)(res->ai_addr))->sin6_port = htons (cfg_proxy_port); + else + ((struct sockaddr_in *)(res->ai_addr))->sin_port = htons (cfg_proxy_port); + + /* connect to server */ + if (connect (server->sock, res->ai_addr, res->ai_addrlen) != 0) + { + write(server->child_write, "3", 1); + freeaddrinfo (res); + return 0; + } + + if (irc_server_pass_proxy (server->sock, server->address, server->port, server->username)) + { + write(server->child_write, "4", 1); + freeaddrinfo (res); + return 0; + } + } + else + { + /* set local hostname/IP if asked by user */ + if (server->hostname && server->hostname[0]) + { + memset (&hints, 0, sizeof(hints)); + hints.ai_family = (server->ipv6) ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_STREAM; + rc = getaddrinfo (server->hostname, NULL, &hints, &res_local); + if ((rc != 0) || !res_local + || (server->ipv6 && (res_local->ai_family != AF_INET6)) + || ((!server->ipv6 && (res_local->ai_family != AF_INET)))) + { + write (server->child_write, "5", 1); + if (res_local) + freeaddrinfo (res_local); + return 0; + } + if (bind (server->sock, res_local->ai_addr, res_local->ai_addrlen) < 0) + { + write (server->child_write, "5", 1); + if (res_local) + freeaddrinfo (res_local); + return 0; + } + } + + /* get info about server */ + memset (&hints, 0, sizeof(hints)); + hints.ai_family = (server->ipv6) ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_STREAM; + rc = getaddrinfo (server->address, NULL, &hints, &res); + if ((rc != 0) || !res) + { + write (server->child_write, "1", 1); + if (res) + freeaddrinfo (res); + return 0; + } + if ((server->ipv6 && (res->ai_family != AF_INET6)) + || ((!server->ipv6 && (res->ai_family != AF_INET)))) + { + write (server->child_write, "2", 1); + if (res) + freeaddrinfo (res); + if (res_local) + freeaddrinfo (res_local); + return 0; + } + + /* connect to server */ + if (server->ipv6) + ((struct sockaddr_in6 *)(res->ai_addr))->sin6_port = htons (server->port); + else + ((struct sockaddr_in *)(res->ai_addr))->sin_port = htons (server->port); + + if (connect (server->sock, res->ai_addr, res->ai_addrlen) != 0) + { + write (server->child_write, "3", 1); + if (res) + freeaddrinfo (res); + if (res_local) + freeaddrinfo (res_local); + return 0; + } + } + + write (server->child_write, "0", 1); + if (res) + freeaddrinfo (res); + if (res_local) + freeaddrinfo (res_local); + return 0; +} + +/* + * irc_server_connect: connect to an IRC server + */ + +int +irc_server_connect (t_irc_server *server, int disable_autojoin) +{ + int child_pipe[2], set; +#ifndef __CYGWIN__ + pid_t pid; +#endif + +#ifndef HAVE_GNUTLS + if (server->ssl) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot connect with SSL since WeeChat was not built " + "with GNUtls support\n"), WEECHAT_ERROR); + return 0; + } +#endif + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + if (cfg_proxy_use) + { + gui_printf (server->buffer, + _("%s: connecting to server %s:%d%s%s via %s proxy %s:%d%s...\n"), + PACKAGE_NAME, server->address, server->port, + (server->ipv6) ? " (IPv6)" : "", + (server->ssl) ? " (SSL)" : "", + cfg_proxy_type_values[cfg_proxy_type], cfg_proxy_address, cfg_proxy_port, + (cfg_proxy_ipv6) ? " (IPv6)" : ""); + weechat_log_printf (_("Connecting to server %s:%d%s%s via %s proxy %s:%d%s...\n"), + server->address, server->port, + (server->ipv6) ? " (IPv6)" : "", + (server->ssl) ? " (SSL)" : "", + cfg_proxy_type_values[cfg_proxy_type], cfg_proxy_address, cfg_proxy_port, + (cfg_proxy_ipv6) ? " (IPv6)" : ""); + } + else + { + gui_printf (server->buffer, + _("%s: connecting to server %s:%d%s%s...\n"), + PACKAGE_NAME, server->address, server->port, + (server->ipv6) ? " (IPv6)" : "", + (server->ssl) ? " (SSL)" : ""); + weechat_log_printf (_("Connecting to server %s:%d%s%s...\n"), + server->address, server->port, + (server->ipv6) ? " (IPv6)" : "", + (server->ssl) ? " (SSL)" : ""); + } + + /* close any opened connection and kill child process if running */ + irc_server_close_connection (server); + + /* init SSL if asked */ + server->ssl_connected = 0; +#ifdef HAVE_GNUTLS + if (server->ssl) + { + if (gnutls_init (&server->gnutls_sess, GNUTLS_CLIENT) != 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s gnutls init error\n"), WEECHAT_ERROR); + return 0; + } + gnutls_set_default_priority (server->gnutls_sess); + gnutls_certificate_type_set_priority (server->gnutls_sess, gnutls_cert_type_prio); + gnutls_protocol_set_priority (server->gnutls_sess, gnutls_prot_prio); + gnutls_credentials_set (server->gnutls_sess, GNUTLS_CRD_CERTIFICATE, gnutls_xcred); + server->ssl_connected = 1; + } +#endif + + /* create pipe for child process */ + if (pipe (child_pipe) < 0) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot create pipe\n"), WEECHAT_ERROR); + return 0; + } + server->child_read = child_pipe[0]; + server->child_write = child_pipe[1]; + + /* create socket and set options */ + if (cfg_proxy_use) + server->sock = socket ((cfg_proxy_ipv6) ? AF_INET6 : AF_INET, SOCK_STREAM, 0); + else + server->sock = socket ((server->ipv6) ? AF_INET6 : AF_INET, SOCK_STREAM, 0); + if (server->sock == -1) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot create socket\n"), WEECHAT_ERROR); + return 0; + } + + /* set SO_REUSEADDR option for socket */ + set = 1; + if (setsockopt (server->sock, SOL_SOCKET, SO_REUSEADDR, + (void *) &set, sizeof (set)) == -1) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot set socket option \"SO_REUSEADDR\"\n"), + WEECHAT_WARNING); + } + + /* set SO_KEEPALIVE option for socket */ + set = 1; + if (setsockopt (server->sock, SOL_SOCKET, SO_KEEPALIVE, + (void *) &set, sizeof (set)) == -1) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot set socket option \"SO_KEEPALIVE\"\n"), + WEECHAT_WARNING); + } + +#ifdef __CYGWIN__ + /* connection may block under Cygwin, there's no other known way + to do better today, since connect() in child process seems not to work + any suggestion is welcome to improve that! + */ + irc_server_child (server); + server->child_pid = 0; + irc_server_child_read (server); +#else + switch (pid = fork ()) + { + /* fork failed */ + case -1: + irc_server_close_connection (server); + return 0; + /* child process */ + case 0: + setuid (getuid ()); + irc_server_child (server); + _exit (EXIT_SUCCESS); + } + /* parent process */ + server->child_pid = pid; +#endif + + server->disable_autojoin = disable_autojoin; + + return 1; +} + +/* + * irc_server_reconnect: reconnect to a server (after disconnection) + */ + +void +irc_server_reconnect (t_irc_server *server) +{ + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, _("%s: Reconnecting to server...\n"), + PACKAGE_NAME); + server->reconnect_start = 0; + + if (irc_server_connect (server, 0)) + server->reconnect_join = 1; + else + irc_server_reconnect_schedule (server); +} + +/* + * irc_server_auto_connect: auto-connect to servers (called at startup) + */ + +void +irc_server_auto_connect (int auto_connect, int temp_server) +{ + t_irc_server *ptr_server; + + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if ( ((temp_server) && (ptr_server->temp_server)) + || ((!temp_server) && (auto_connect) && (ptr_server->autoconnect)) ) + { + (void) gui_buffer_new (gui_current_window, ptr_server, NULL, + GUI_BUFFER_TYPE_STANDARD, 1); + gui_window_redraw_buffer (gui_current_window->buffer); + if (!irc_server_connect (ptr_server, 0)) + irc_server_reconnect_schedule (ptr_server); + } + } +} + +/* + * irc_server_disconnect: disconnect from an irc server + */ + +void +irc_server_disconnect (t_irc_server *server, int reconnect) +{ + t_irc_channel *ptr_channel; + + if (server->is_connected) + { + /* write disconnection message on each channel/private buffer */ + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + irc_nick_free_all (ptr_channel); + irc_display_prefix (NULL, ptr_channel->buffer, GUI_PREFIX_INFO); + gui_printf (ptr_channel->buffer, _("Disconnected from server!\n")); + gui_nicklist_draw (ptr_channel->buffer, 1, 1); + gui_status_draw (ptr_channel->buffer, 1); + } + } + + irc_server_close_connection (server); + + if (server->buffer) + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, _("Disconnected from server!\n")); + } + + if (server->nick_modes) + { + free (server->nick_modes); + server->nick_modes = NULL; + } + if (server->prefix) + { + free (server->prefix); + server->prefix = NULL; + } + server->is_away = 0; + server->away_time = 0; + server->lag = 0; + server->lag_check_time.tv_sec = 0; + server->lag_check_time.tv_usec = 0; + server->lag_next_check = time (NULL) + cfg_irc_lag_check; + + if ((reconnect) && (server->autoreconnect)) + irc_server_reconnect_schedule (server); + else + server->reconnect_start = 0; + + /* discard current nick if no reconnection asked */ + if (!reconnect && server->nick) + { + free (server->nick); + server->nick = NULL; + } + + gui_window_redraw_buffer (gui_current_window->buffer); +} + +/* + * irc_server_disconnect_all: disconnect from all irc servers + */ + +void +irc_server_disconnect_all () +{ + t_irc_server *ptr_server; + + for (ptr_server = irc_servers; ptr_server; ptr_server = ptr_server->next_server) + irc_server_disconnect (ptr_server, 0); +} + +/* + * irc_server_autojoin_channels: autojoin (or rejoin) channels + */ + +void +irc_server_autojoin_channels (t_irc_server *server) +{ + t_irc_channel *ptr_channel; + + /* auto-join after disconnection (only rejoins opened channels) */ + if (!server->disable_autojoin && server->reconnect_join && server->channels) + { + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + { + if (ptr_channel->key) + irc_server_sendf (server, "JOIN %s %s", + ptr_channel->name, ptr_channel->key); + else + irc_server_sendf (server, "JOIN %s", + ptr_channel->name); + } + } + server->reconnect_join = 0; + } + else + { + /* auto-join when connecting to server for first time */ + if (!server->disable_autojoin && server->autojoin && server->autojoin[0]) + irc_send_cmd_join (server, NULL, server->autojoin); + } + + server->disable_autojoin = 0; +} + +/* + * irc_server_search: return pointer on a server with a name + */ + +t_irc_server * +irc_server_search (char *servername) +{ + t_irc_server *ptr_server; + + if (!servername) + return NULL; + + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (strcmp (ptr_server->name, servername) == 0) + return ptr_server; + } + return NULL; +} + +/* + * irc_server_get_number_connected: returns number of connected server + */ + +int +irc_server_get_number_connected () +{ + t_irc_server *ptr_server; + int number; + + number = 0; + for (ptr_server = irc_servers; ptr_server; ptr_server = ptr_server->next_server) + { + if (ptr_server->is_connected) + number++; + } + return number; +} + +/* + * irc_server_get_number_buffer: returns position of a server and total number of + * buffers with a buffer + */ + +void +irc_server_get_number_buffer (t_irc_server *server, + int *server_pos, int *server_total) +{ + t_irc_server *ptr_server; + + *server_pos = 0; + *server_total = 0; + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server->buffer) + { + (*server_total)++; + if (ptr_server == server) + *server_pos = *server_total; + } + } +} + +/* + * irc_server_name_already_exists: return 1 if server name already exists + * otherwise return 0 + */ + +int +irc_server_name_already_exists (char *name) +{ + t_irc_server *ptr_server; + + if (!name) + return 0; + + for (ptr_server = irc_servers; ptr_server; ptr_server = ptr_server->next_server) + { + if (strcmp (ptr_server->name, name) == 0) + return 1; + } + return 0; +} + +/* + * irc_server_get_channel_count: return number of channels for server + */ + +int +irc_server_get_channel_count (t_irc_server *server) +{ + int count; + t_irc_channel *ptr_channel; + + count = 0; + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + count++; + } + return count; +} + +/* + * irc_server_get_pv_count: return number of pv for server + */ + +int +irc_server_get_pv_count (t_irc_server *server) +{ + int count; + t_irc_channel *ptr_channel; + + count = 0; + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type != IRC_CHANNEL_TYPE_CHANNEL) + count++; + } + return count; +} + +/* + * irc_server_remove_away: remove away for all chans/nicks (for all servers) + */ + +void +irc_server_remove_away () +{ + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + + for (ptr_server = irc_servers; ptr_server; ptr_server = ptr_server->next_server) + { + if (ptr_server->is_connected) + { + for (ptr_channel = ptr_server->channels; ptr_channel; ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + irc_channel_remove_away (ptr_channel); + } + } + } +} + +/* + * irc_server_check_away: check for away on all channels (for all servers) + */ + +void +irc_server_check_away () +{ + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + + for (ptr_server = irc_servers; ptr_server; ptr_server = ptr_server->next_server) + { + if (ptr_server->is_connected) + { + for (ptr_channel = ptr_server->channels; ptr_channel; ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + irc_channel_check_away (ptr_server, ptr_channel, 0); + } + } + } +} + +/* + * irc_server_set_away: set/unset away status for a server (all channels) + */ + +void +irc_server_set_away (t_irc_server *server, char *nick, int is_away) +{ + t_irc_channel *ptr_channel; + + for (ptr_channel = server->channels; ptr_channel; ptr_channel = ptr_channel->next_channel) + { + if (server->is_connected) + { + if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL) + irc_channel_set_away (ptr_channel, nick, is_away); + } + } +} + +/* + * irc_server_get_default_notify_level: get default notify level for server + */ + +int +irc_server_get_default_notify_level (t_irc_server *server) +{ + int notify, value; + char *pos; + + notify = GUI_NOTIFY_LEVEL_DEFAULT; + + if (!server || !server->notify_levels) + return notify; + + pos = strstr (server->notify_levels, "*:"); + if (pos) + { + pos += 2; + if (pos[0]) + { + value = (int)(pos[0] - '0'); + if ((value >= GUI_NOTIFY_LEVEL_MIN) + && (value <= GUI_NOTIFY_LEVEL_MAX)) + notify = value; + } + } + + return notify; +} + +/* + * irc_server_set_default_notify_level: set default notify level for server + */ + +void +irc_server_set_default_notify_level (t_irc_server *server, int notify) +{ + char level_string[2]; + + if (server) + { + level_string[0] = notify + '0'; + level_string[1] = '\0'; + config_option_list_set (&(server->notify_levels), "*", level_string); + } +} + +/* + * irc_server_print_log: print server infos in log (usually for crash dump) + */ + +void +irc_server_print_log (t_irc_server *server) +{ + weechat_log_printf ("[server %s (addr:0x%X)]\n", server->name, server); + weechat_log_printf (" autoconnect . . . . : %d\n", server->autoconnect); + weechat_log_printf (" autoreconnect . . . : %d\n", server->autoreconnect); + weechat_log_printf (" autoreconnect_delay : %d\n", server->autoreconnect_delay); + weechat_log_printf (" temp_server . . . . : %d\n", server->temp_server); + weechat_log_printf (" address . . . . . . : '%s'\n", server->address); + weechat_log_printf (" port. . . . . . . . : %d\n", server->port); + weechat_log_printf (" ipv6. . . . . . . . : %d\n", server->ipv6); + weechat_log_printf (" ssl . . . . . . . . : %d\n", server->ssl); + weechat_log_printf (" password. . . . . . : '%s'\n", + (server->password && server->password[0]) ? + "(hidden)" : server->password); + weechat_log_printf (" nick1 . . . . . . . : '%s'\n", server->nick1); + weechat_log_printf (" nick2 . . . . . . . : '%s'\n", server->nick2); + weechat_log_printf (" nick3 . . . . . . . : '%s'\n", server->nick3); + weechat_log_printf (" username. . . . . . : '%s'\n", server->username); + weechat_log_printf (" realname. . . . . . : '%s'\n", server->realname); + weechat_log_printf (" command . . . . . . : '%s'\n", + (server->command && server->command[0]) ? + "(hidden)" : server->command); + weechat_log_printf (" command_delay . . . : %d\n", server->command_delay); + weechat_log_printf (" autojoin. . . . . . : '%s'\n", server->autojoin); + weechat_log_printf (" autorejoin. . . . . : %d\n", server->autorejoin); + weechat_log_printf (" notify_levels . . . : %s\n", server->notify_levels); + weechat_log_printf (" child_pid . . . . . : %d\n", server->child_pid); + weechat_log_printf (" child_read . . . . : %d\n", server->child_read); + weechat_log_printf (" child_write . . . . : %d\n", server->child_write); + weechat_log_printf (" sock. . . . . . . . : %d\n", server->sock); + weechat_log_printf (" is_connected. . . . : %d\n", server->is_connected); + weechat_log_printf (" ssl_connected . . . : %d\n", server->ssl_connected); + weechat_log_printf (" unterminated_message: '%s'\n", server->unterminated_message); + weechat_log_printf (" nick. . . . . . . . : '%s'\n", server->nick); + weechat_log_printf (" nick_modes. . . . . : '%s'\n", server->nick_modes); + weechat_log_printf (" prefix. . . . . . . : '%s'\n", server->prefix); + weechat_log_printf (" reconnect_start . . : %ld\n", server->reconnect_start); + weechat_log_printf (" command_time. . . . : %ld\n", server->command_time); + weechat_log_printf (" reconnect_join. . . : %d\n", server->reconnect_join); + weechat_log_printf (" disable_autojoin. . : %d\n", server->disable_autojoin); + weechat_log_printf (" is_away . . . . . . : %d\n", server->is_away); + weechat_log_printf (" away_message. . . . : '%s'\n", server->away_message); + weechat_log_printf (" away_time . . . . . : %ld\n", server->away_time); + weechat_log_printf (" lag . . . . . . . . : %d\n", server->lag); + weechat_log_printf (" lag_check_time. . . : tv_sec:%d, tv_usec:%d\n", + server->lag_check_time.tv_sec, + server->lag_check_time.tv_usec); + weechat_log_printf (" lag_next_check. . . : %ld\n", server->lag_next_check); + weechat_log_printf (" last_user_message . : %ld\n", server->last_user_message); + weechat_log_printf (" outqueue. . . . . . : 0x%X\n", server->outqueue); + weechat_log_printf (" last_outqueue . . . : 0x%X\n", server->last_outqueue); + weechat_log_printf (" buffer. . . . . . . : 0x%X\n", server->buffer); + weechat_log_printf (" channels. . . . . . : 0x%X\n", server->channels); + weechat_log_printf (" last_channel. . . . : 0x%X\n", server->last_channel); + weechat_log_printf (" prev_server . . . . : 0x%X\n", server->prev_server); + weechat_log_printf (" next_server . . . . : 0x%X\n", server->next_server); +} diff --git a/src/plugins/irc/irc.h b/src/plugins/irc/irc.h new file mode 100644 index 000000000..9a94570c3 --- /dev/null +++ b/src/plugins/irc/irc.h @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_IRC_H +#define __WEECHAT_IRC_H 1 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <regex.h> + +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + +#include "../../gui/gui.h" + +#ifndef NI_MAXHOST +#define NI_MAXHOST 256 +#endif + +#define IRC_DEFAULT_PORT 6667 +#define IRC_DEFAULT_PREFIXES_LIST "@%+~&!-" + +/* nick types */ + +#define IRC_NICK_CHANOWNER 1 +#define IRC_NICK_CHANADMIN 2 +#define IRC_NICK_OP 4 +#define IRC_NICK_HALFOP 8 +#define IRC_NICK_VOICE 16 +#define IRC_NICK_AWAY 32 +#define IRC_NICK_CHANADMIN2 64 +#define IRC_NICK_CHANUSER 128 +#define IRC_NICK_SET_FLAG(nick, set, flag) \ + if (set) \ + nick->flags |= flag; \ + else \ + nick->flags &= 0xFFFF - flag; + +#define irc_server_sendf_queued(server, fmt, argz...) \ + if (server) \ + { \ + server->queue_msg = 1; \ + irc_server_sendf (server, fmt, ##argz); \ + server->queue_msg = 0; \ + } + +typedef struct t_irc_nick t_irc_nick; + +struct t_irc_nick +{ + char *nick; /* nickname */ + char *host; /* full hostname */ + int flags; /* chanowner/chanadmin (unrealircd), */ + /* op, halfop, voice, away */ + int color; /* color for nickname in chat window */ + t_irc_nick *prev_nick; /* link to previous nick on the channel */ + t_irc_nick *next_nick; /* link to next nick on the channel */ +}; + +#define IRC_CHANNEL_PREFIX "#&+!" + +/* channel types */ +#define IRC_CHANNEL_TYPE_UNKNOWN -1 +#define IRC_CHANNEL_TYPE_CHANNEL 0 +#define IRC_CHANNEL_TYPE_PRIVATE 1 +#define IRC_CHANNEL_TYPE_DCC_CHAT 2 + +#define IRC_CHANNEL_NICKS_SPEAKING_LIMIT 32 + +typedef struct t_irc_channel t_irc_channel; + +struct t_irc_channel +{ + int type; /* channel type */ + void *dcc_chat; /* DCC CHAT pointer (NULL if not DCC) */ + char *name; /* name of channel (exemple: "#abc") */ + char *topic; /* topic of channel (host for private) */ + char *modes; /* channel modes */ + int limit; /* user limit (0 is limit not set) */ + char *key; /* channel key (NULL if no key is set) */ + int nicks_count; /* # nicks on channel (0 if dcc/pv) */ + int checking_away; /* = 1 if checking away with WHO cmd */ + char *away_message; /* to display away only once in private */ + int cycle; /* currently cycling (/part then /join) */ + int close; /* close request (/buffer close) */ + int display_creation_date; /* 1 if creation date should be displayed */ + int nick_completion_reset; /* 1 if nick completion should be rebuilt */ + /* there was some join/part on channel */ + t_irc_nick *nicks; /* nicks on the channel */ + t_irc_nick *last_nick; /* last nick on the channel */ + t_weelist *nicks_speaking; /* nicks speaking (for smart completion) */ + t_weelist *last_nick_speaking; /* last nick speaking */ + t_gui_buffer *buffer; /* GUI buffer allocated for channel */ + t_irc_channel *prev_channel; /* link to previous channel */ + t_irc_channel *next_channel; /* link to next channel */ +}; + +/* server types */ + +typedef struct t_irc_outqueue t_irc_outqueue; + +struct t_irc_outqueue +{ + char *message_before_mod; /* message before any modifier */ + char *message_after_mod; /* message after modifier(s) */ + int modified; /* message was modified by modifier(s) */ + t_irc_outqueue *next_outqueue; /* pointer to next message in queue */ + t_irc_outqueue *prev_outqueue; /* pointer to previous message in queue */ +}; + +typedef struct t_irc_server t_irc_server; + +struct t_irc_server +{ + /* user choices */ + char *name; /* internal name of server */ + int autoconnect; /* = 1 if auto connect at startup */ + int autoreconnect; /* = 1 if auto reco when disconnected */ + int autoreconnect_delay; /* delay before trying again reconnect */ + int temp_server; /* server is temporary (will not be saved)*/ + char *address; /* address of server (IP or name) */ + int port; /* port for server (6667 by default) */ + int ipv6; /* use IPv6 protocol */ + int ssl; /* SSL protocol */ + char *password; /* password for server */ + char *nick1; /* first nickname for the server */ + char *nick2; /* alternate nickname */ + char *nick3; /* 2nd alternate nickname */ + char *username; /* user name */ + char *realname; /* real name */ + char *hostname; /* custom hostname */ + char *command; /* command to run once connected */ + int command_delay; /* delay after execution of command */ + char *autojoin; /* channels to automatically join */ + int autorejoin; /* auto rejoin channels when kicked */ + char *notify_levels; /* channels notify levels */ + + /* internal vars */ + pid_t child_pid; /* pid of child process (connecting) */ + int child_read; /* to read into child pipe */ + int child_write; /* to write into child pipe */ + int sock; /* socket for server (IPv4 or IPv6) */ + int is_connected; /* 1 if WeeChat is connected to server */ + int ssl_connected; /* = 1 if connected with SSL */ +#ifdef HAVE_GNUTLS + gnutls_session gnutls_sess; /* gnutls session (only if SSL is used) */ +#endif + char *unterminated_message; /* beginning of a message in input buf */ + char *nick; /* current nickname */ + char *nick_modes; /* nick modes */ + char *prefix; /* nick prefix allowed (from msg 005) */ + time_t reconnect_start; /* this time + delay = reconnect time */ + time_t command_time; /* this time + command_delay = time to */ + /* autojoin channels */ + int reconnect_join; /* 1 if channels opened to rejoin */ + int disable_autojoin; /* 1 if user asked to not autojoin chans */ + int is_away; /* 1 is user is marked as away */ + char *away_message; /* away message, NULL if not away */ + time_t away_time; /* time() when user marking as away */ + int lag; /* lag (in milliseconds) */ + struct timeval lag_check_time; /* last time lag was checked (ping sent) */ + time_t lag_next_check; /* time for next check */ + regex_t *cmd_list_regexp; /* compiled Regular Expression for /list */ + int queue_msg; /* set to 1 when queue (out) is required */ + time_t last_user_message; /* time of last user message (anti flood) */ + t_irc_outqueue *outqueue; /* queue for outgoing user messages */ + t_irc_outqueue *last_outqueue; /* last outgoing user message */ + t_gui_buffer *buffer; /* GUI buffer allocated for server */ + t_gui_buffer *saved_buffer; /* channel before jumping to next server */ + t_irc_channel *channels; /* opened channels on server */ + t_irc_channel *last_channel; /* last opened channal on server */ + t_irc_server *prev_server; /* link to previous server */ + t_irc_server *next_server; /* link to next server */ +}; + +/* irc commands */ + +typedef int (t_irc_recv_func)(t_irc_server *, char *, char *, char *); + +typedef struct t_irc_command t_irc_command; + +struct t_irc_command +{ + char *command_name; /* IRC command name */ + char *command_description; /* command description (for /help) */ + char *arguments; /* command arguments (for /help) */ + char *arguments_description; /* arguments description (for /help) */ + char *completion_template; /* template for completion */ + /* NULL=no completion, ""=default (nick) */ + int min_arg, max_arg; /* min & max number of arguments */ + int conversion; /* = 1 if cmd args are converted (charset */ + /* and color) before sending to server */ + int needs_connection; /* = 1 if cmd needs server connection */ + int (*cmd_function_args)(t_irc_server *, t_irc_channel *, int, char **); + /* function called when user enters cmd */ + int (*cmd_function_1arg)(t_irc_server *, t_irc_channel *, char *); + /* function called when user enters cmd */ + t_irc_recv_func *recv_function; /* function called when cmd is received */ +}; + +/* irc messages */ + +typedef struct t_irc_message t_irc_message; + +struct t_irc_message +{ + t_irc_server *server; /* server pointer for received msg */ + char *data; /* message content */ + t_irc_message *next_message; /* link to next message */ +}; + +/* DCC types */ + +#define IRC_DCC_CHAT_RECV 0 /* receiving DCC chat */ +#define IRC_DCC_CHAT_SEND 1 /* sending DCC chat */ +#define IRC_DCC_FILE_RECV 2 /* incoming DCC file */ +#define IRC_DCC_FILE_SEND 3 /* sending DCC file */ + +/* DCC status */ + +#define IRC_DCC_WAITING 0 /* waiting for host answer */ +#define IRC_DCC_CONNECTING 1 /* connecting to host */ +#define IRC_DCC_ACTIVE 2 /* sending/receiving data */ +#define IRC_DCC_DONE 3 /* transfer done */ +#define IRC_DCC_FAILED 4 /* DCC failed */ +#define IRC_DCC_ABORTED 5 /* DCC aborted by user */ + +/* DCC blocksize (for file) */ + +#define IRC_DCC_MIN_BLOCKSIZE 1024 /* min DCC block size when sending file */ +#define IRC_DCC_MAX_BLOCKSIZE 102400 /* max DCC block size when sending file */ + +/* DCC errors (for file) */ + +#define IRC_DCC_NO_ERROR 0 /* no error to report, all ok! */ +#define IRC_DCC_ERROR_READ_LOCAL 1 /* unable to read local file */ +#define IRC_DCC_ERROR_SEND_BLOCK 2 /* unable to send block to receiver */ +#define IRC_DCC_ERROR_READ_ACK 3 /* unable to read ACK from receiver */ +#define IRC_DCC_ERROR_CONNECT_SENDER 4 /* unable to connect to sender */ +#define IRC_DCC_ERROR_RECV_BLOCK 5 /* unable to recv block from sender */ +#define IRC_DCC_ERROR_WRITE_LOCAL 6 /* unable to write to local file */ + +/* DCC macros for type */ + +#define IRC_DCC_IS_CHAT(type) ((type == IRC_DCC_CHAT_RECV) || \ + (type == IRC_DCC_CHAT_SEND)) +#define IRC_DCC_IS_FILE(type) ((type == IRC_DCC_FILE_RECV) || \ + (type == IRC_DCC_FILE_SEND)) +#define IRC_DCC_IS_RECV(type) ((type == IRC_DCC_CHAT_RECV) || \ + (type == IRC_DCC_FILE_RECV)) +#define IRC_DCC_IS_SEND(type) ((type == IRC_DCC_CHAT_SEND) || \ + (type == IRC_DCC_FILE_SEND)) + +/* DCC macro for status */ + +#define IRC_DCC_ENDED(status) ((status == IRC_DCC_DONE) || \ + (status == IRC_DCC_FAILED) || \ + (status == IRC_DCC_ABORTED)) + +typedef struct t_irc_dcc t_irc_dcc; + +struct t_irc_dcc +{ + t_irc_server *server; /* irc server */ + t_irc_channel *channel; /* irc channel (for DCC chat only) */ + int type; /* DCC type (file/chat, send/receive) */ + int status; /* DCC status (waiting, sending, ..) */ + time_t start_time; /* the time when DCC started */ + time_t start_transfer; /* the time when DCC transfer started */ + unsigned long addr; /* IP address */ + int port; /* port */ + char *nick; /* remote nick */ + int sock; /* socket for connection */ + pid_t child_pid; /* pid of child process (sending/recving) */ + int child_read; /* to read into child pipe */ + int child_write; /* to write into child pipe */ + char *unterminated_message; /* beginning of a message in input buf */ + int fast_send; /* fase send for files: does not wait ACK */ + int file; /* local file (for reading or writing) */ + char *filename; /* filename (given by sender) */ + char *local_filename; /* local filename (with path) */ + int filename_suffix; /* suffix (.1 for ex) if renaming file */ + int blocksize; /* block size for sending file */ + unsigned long size; /* file size */ + unsigned long pos; /* number of bytes received/sent */ + unsigned long ack; /* number of bytes received OK */ + unsigned long start_resume; /* start of resume (in bytes) */ + time_t last_check_time; /* last time we looked at bytes sent/recv */ + unsigned long last_check_pos; /* bytes sent/recv at last check */ + time_t last_activity; /* time of last byte received/sent */ + unsigned long bytes_per_sec; /* bytes per second */ + unsigned long eta; /* estimated time of arrival */ + t_irc_dcc *prev_dcc; /* link to previous dcc file/chat */ + t_irc_dcc *next_dcc; /* link to next dcc file/chat */ +}; + +/* ignore types */ + +/* pre-defined ignore types, all other types are made with IRC commands */ +/* for example: part join quit notice invite ... */ + +#define IRC_IGNORE_ACTION "action" +#define IRC_IGNORE_CTCP "ctcp" +#define IRC_IGNORE_DCC "dcc" +#define IRC_IGNORE_PRIVATE "pv" + +typedef struct t_irc_ignore t_irc_ignore; + +struct t_irc_ignore +{ + char *mask; /* nickname or mask */ + char *type; /* type of ignore */ + char *channel_name; /* name of channel, "*" == all */ + char *server_name; /* name of server, "*" == all */ + t_irc_ignore *prev_ignore; /* pointer to previous ignore */ + t_irc_ignore *next_ignore; /* pointer to next ignore */ +}; + +/* variables */ + +extern t_irc_command irc_commands[]; +extern t_irc_server *irc_servers; +#ifdef HAVE_GNUTLS +extern const int gnutls_cert_type_prio[]; +extern const int gnutls_prot_prio[]; +#endif +extern t_irc_message *irc_recv_msgq, *irc_msgq_last_msg; +extern int irc_check_away; +extern t_irc_dcc *irc_dcc_list; +extern t_irc_dcc *irc_last_dcc; +extern char *irc_dcc_status_string[6]; +extern t_irc_ignore *irc_ignore; +extern t_irc_ignore *irc_last_ignore; + +/* server functions (irc-server.c) */ + +extern void irc_server_init (t_irc_server *); +extern int irc_server_init_with_url (char *, t_irc_server *); +extern t_irc_server *irc_server_alloc (); +extern void irc_server_outqueue_free_all (t_irc_server *); +extern void irc_server_destroy (t_irc_server *); +extern void irc_server_free (t_irc_server *); +extern void irc_server_free_all (); +extern t_irc_server *irc_server_new (char *, int, int, int, int, char *, int, int, int, + char *, char *, char *, char *, char *, char *, + char *, char *, int, char *, int, char *); +extern t_irc_server *irc_server_duplicate (t_irc_server *, char *); +extern int irc_server_rename (t_irc_server *, char *); +extern int irc_server_send (t_irc_server *, char *, int); +extern void irc_server_outqueue_send (t_irc_server *); +extern void irc_server_sendf (t_irc_server *, char *, ...); +extern void irc_server_parse_message (char *, char **, char **, char **); +extern void irc_server_recv (t_irc_server *); +extern void irc_server_child_read (t_irc_server *); +extern void irc_server_convbase64_8x3_to_6x4 (char *, char*); +extern void irc_server_base64encode (char *, char *); +extern int irc_server_pass_httpproxy (int, char*, int); +extern int irc_server_resolve (char *, char *, int *); +extern int irc_server_pass_socks4proxy (int, char*, int, char*); +extern int irc_server_pass_socks5proxy (int, char*, int); +extern int irc_server_pass_proxy (int, char*, int, char*); +extern int irc_server_connect (t_irc_server *, int); +extern void irc_server_reconnect (t_irc_server *); +extern void irc_server_auto_connect (int, int); +extern void irc_server_disconnect (t_irc_server *, int); +extern void irc_server_disconnect_all (); +extern void irc_server_autojoin_channels (); +extern t_irc_server *irc_server_search (char *); +extern int irc_server_get_number_connected (); +extern void irc_server_get_number_buffer (t_irc_server *, int *, int *); +extern int irc_server_name_already_exists (char *); +extern int irc_server_get_channel_count (t_irc_server *); +extern int irc_server_get_pv_count (t_irc_server *); +extern void irc_server_remove_away (); +extern void irc_server_check_away (); +extern void irc_server_set_away (t_irc_server *, char *, int); +extern int irc_server_get_default_notify_level (t_irc_server *); +extern void irc_server_set_default_notify_level (t_irc_server *, int); +extern void irc_server_print_log (t_irc_server *); + +/* channel functions (irc-channel.c) */ + +extern t_irc_channel *irc_channel_new (t_irc_server *, int, char *); +extern void irc_channel_free (t_irc_server *, t_irc_channel *); +extern void irc_channel_free_all (t_irc_server *); +extern t_irc_channel *irc_channel_search (t_irc_server *, char *); +extern t_irc_channel *irc_channel_search_any (t_irc_server *, char *); +extern t_irc_channel *irc_channel_search_any_without_buffer (t_irc_server *, char *); +extern t_irc_channel *irc_channel_search_dcc (t_irc_server *, char *); +extern int irc_channel_is_channel (char *); +extern void irc_channel_remove_away (t_irc_channel *); +extern void irc_channel_check_away (t_irc_server *, t_irc_channel *, int); +extern void irc_channel_set_away (t_irc_channel *, char *, int); +extern int irc_channel_create_dcc (t_irc_dcc *); +extern int irc_channel_get_notify_level (t_irc_server *, t_irc_channel *); +extern void irc_channel_set_notify_level (t_irc_server *, t_irc_channel *, int); +extern void irc_channel_add_nick_speaking (t_irc_channel *, char *); +extern void irc_channel_print_log (t_irc_channel *); + +/* nick functions (irc-nick.c) */ + +extern int irc_nick_find_color (t_irc_nick *); +extern t_irc_nick *irc_nick_new (t_irc_server *, t_irc_channel *, char *, + int, int, int, int, int, int, int); +extern void irc_nick_resort (t_irc_channel *, t_irc_nick *); +extern void irc_nick_change (t_irc_channel *, t_irc_nick *, char *); +extern void irc_nick_free (t_irc_channel *, t_irc_nick *); +extern void irc_nick_free_all (t_irc_channel *); +extern t_irc_nick *irc_nick_search (t_irc_channel *, char *); +extern void irc_nick_count (t_irc_channel *, int *, int *, int *, int *, int *); +extern int irc_nick_get_max_length (t_irc_channel *); +extern void irc_nick_set_away (t_irc_channel *, t_irc_nick *, int); +extern void irc_nick_print_log (t_irc_nick *); + +/* mode functions (irc-mode.c) */ + +extern void irc_mode_channel_set (t_irc_server *, t_irc_channel *, char *); +extern void irc_mode_user_set (t_irc_server *, char *); +extern int irc_mode_nick_prefix_allowed (t_irc_server *, char); + +/* DCC functions (irc-dcc.c) */ + +extern void irc_dcc_redraw (int); +extern void irc_dcc_free (t_irc_dcc *); +extern void irc_dcc_close (t_irc_dcc *, int); +extern void irc_dcc_chat_remove_channel (t_irc_channel *); +extern void irc_dcc_accept (t_irc_dcc *); +extern void irc_dcc_accept_resume (t_irc_server *, char *, int, unsigned long); +extern void irc_dcc_start_resume (t_irc_server *, char *, int, unsigned long); +extern t_irc_dcc *irc_dcc_alloc (); +extern t_irc_dcc *irc_dcc_add (t_irc_server *, int, unsigned long, int, char *, int, + char *, char *, unsigned long); +extern void irc_dcc_send_request (t_irc_server *, int, char *, char *); +extern void irc_dcc_chat_sendf (t_irc_dcc *, char *, ...); +extern void irc_dcc_file_send_fork (t_irc_dcc *); +extern void irc_dcc_file_recv_fork (t_irc_dcc *); +extern void irc_dcc_handle (); +extern void irc_dcc_end (); +extern void irc_dcc_print_log (); + +/* IRC display (irc-diplay.c) */ + +extern void irc_display_hide_password (char *, int); +extern void irc_display_prefix (t_irc_server *, t_gui_buffer *, char *); +extern void irc_display_nick (t_gui_buffer *, t_irc_nick *, char *, int, + int, int, int); +extern void irc_display_away (t_irc_server *, char *, char *); +extern void irc_display_mode (t_irc_server *, t_gui_buffer *, char *, char *, + char, char *, char *, char *, char *); +extern void irc_display_server (t_irc_server *ptr_server, int); + +/* IRC commands issued by user (irc-send.c) */ + +extern void irc_send_login (t_irc_server *); +extern int irc_send_cmd_admin (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_ame (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_amsg (t_irc_server *, t_irc_channel *, char *); +extern void irc_send_away (t_irc_server *, char *); +extern int irc_send_cmd_away (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_ban (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_ctcp (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_cycle (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_dehalfop (t_irc_server *, t_irc_channel *, int, char **); +extern int irc_send_cmd_deop (t_irc_server *, t_irc_channel *, int, char **); +extern int irc_send_cmd_devoice (t_irc_server *, t_irc_channel *, int, char **); +extern int irc_send_cmd_die (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_halfop (t_irc_server *, t_irc_channel *, int, char **); +extern int irc_send_cmd_info (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_invite (t_irc_server *, t_irc_channel *, int, char **); +extern int irc_send_cmd_ison (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_join (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_kick (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_kickban (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_kill (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_links (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_list (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_lusers (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_me (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_mode (t_irc_server *, t_irc_channel *, char *); +extern void irc_send_mode_nicks (t_irc_server *, char *, char *, char *, int, char **); +extern int irc_send_cmd_motd (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_msg (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_names (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_nick (t_irc_server *, t_irc_channel *, int, char **); +extern int irc_send_cmd_notice (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_op (t_irc_server *, t_irc_channel *, int, char **); +extern int irc_send_cmd_oper (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_part (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_ping (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_pong (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_query (t_irc_server *, t_irc_channel *, char *); +extern void irc_send_quit_server (t_irc_server *, char *); +extern int irc_send_cmd_quit (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_quote (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_rehash (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_restart (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_service (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_servlist (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_squery (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_squit (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_stats (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_summon (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_time (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_topic (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_trace (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_unban (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_userhost (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_users (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_version (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_voice (t_irc_server *, t_irc_channel *, int, char **); +extern int irc_send_cmd_wallops (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_who (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_whois (t_irc_server *, t_irc_channel *, char *); +extern int irc_send_cmd_whowas (t_irc_server *, t_irc_channel *, char *); + +/* IRC commands executed when received from server (irc-recv.c) */ + +extern int irc_recv_is_highlight (char *, char *); +extern int irc_recv_command (t_irc_server *, char *, char *, char *, char *); +extern int irc_recv_cmd_error (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_invite (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_join (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_kick (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_kill (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_mode (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_nick (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_notice (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_part (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_ping (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_pong (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_privmsg (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_quit (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_server_mode_reason (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_server_msg (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_server_reply (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_topic (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_wallops (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_001 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_005 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_221 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_301 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_302 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_303 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_305 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_306 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_whois_nick_msg (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_310 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_311 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_312 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_314 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_315 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_317 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_319 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_321 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_322 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_323 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_324 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_327 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_329 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_331 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_332 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_333 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_338 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_341 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_344 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_345 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_348 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_349 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_351 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_352 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_353 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_365 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_366 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_367 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_368 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_432 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_433 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_438 (t_irc_server *, char *, char *, char *); +extern int irc_recv_cmd_671 (t_irc_server *, char *, char *, char *); + +/* ignore functions (irc-ignore.c) */ + +extern int irc_ignore_check (char *, char *, char *, char *); +extern t_irc_ignore *irc_ignore_add (char *, char *, char *, char *); +extern t_irc_ignore *irc_ignore_add_from_config (char *); +extern void irc_ignore_free_all (); +extern int irc_ignore_search_free (char *, char *, char *, char *); +extern int irc_ignore_search_free_by_number (int); +extern void irc_ignore_print_log (); + +#endif /* irc.h */ |