weechat/src/plugins/irc/irc-server.c

6510 lines
227 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* irc-server.c - I/O communication with IRC servers
*
* Copyright (C) 2003-2020 Sébastien Helleu <flashcode@flashtux.org>
* Copyright (C) 2005-2010 Emmanuel Bouthenot <kolter@openics.org>
* Copyright (C) 2012 Simon Arlott
*
* This file is part of WeeChat, the extensible chat client.
*
* WeeChat is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* WeeChat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#ifdef _WIN32
#include <winsock.h>
#else
#include <sys/socket.h>
#include <sys/time.h>
#endif /* _WIN32 */
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#endif /* HAVE_GNUTLS */
#include "../weechat-plugin.h"
#include "irc.h"
#include "irc-server.h"
#include "irc-bar-item.h"
#include "irc-buffer.h"
#include "irc-channel.h"
#include "irc-color.h"
#include "irc-command.h"
#include "irc-config.h"
#include "irc-input.h"
#include "irc-message.h"
#include "irc-nick.h"
#include "irc-notify.h"
#include "irc-protocol.h"
#include "irc-raw.h"
#include "irc-redirect.h"
#include "irc-sasl.h"
struct t_irc_server *irc_servers = NULL;
struct t_irc_server *last_irc_server = NULL;
struct t_irc_message *irc_recv_msgq = NULL;
struct t_irc_message *irc_msgq_last_msg = NULL;
char *irc_server_sasl_fail_string[IRC_SERVER_NUM_SASL_FAIL] =
{ "continue", "reconnect", "disconnect" };
char *irc_server_options[IRC_SERVER_NUM_OPTIONS][2] =
{ { "addresses", "" },
{ "proxy", "" },
{ "ipv6", "on" },
{ "ssl", "off" },
{ "ssl_cert", "" },
{ "ssl_password", "" },
{ "ssl_priorities", "NORMAL:-VERS-SSL3.0" },
{ "ssl_dhkey_size", "2048" },
{ "ssl_fingerprint", "" },
{ "ssl_verify", "on" },
{ "password", "" },
{ "capabilities", "" },
{ "sasl_mechanism", "plain" },
{ "sasl_username", "" },
{ "sasl_password", "" },
{ "sasl_key", "", },
{ "sasl_timeout", "15" },
{ "sasl_fail", "continue" },
{ "autoconnect", "off" },
{ "autoreconnect", "on" },
{ "autoreconnect_delay", "10" },
{ "nicks", "" },
{ "nicks_alternate", "on" },
{ "username", "" },
{ "realname", "" },
{ "local_hostname", "" },
{ "usermode", "" },
{ "command", "" },
{ "command_delay", "0" },
{ "autojoin", "" },
{ "autorejoin", "off" },
{ "autorejoin_delay", "30" },
{ "connection_timeout", "60" },
{ "anti_flood_prio_high", "2" },
{ "anti_flood_prio_low", "2" },
{ "away_check", "0" },
{ "away_check_max_nicks", "25" },
{ "msg_kick", "" },
{ "msg_part", "WeeChat ${info:version}" },
{ "msg_quit", "WeeChat ${info:version}" },
{ "notify", "" },
{ "split_msg_max_length", "512" },
{ "charset_message", "message" },
};
char *irc_server_casemapping_string[IRC_SERVER_NUM_CASEMAPPING] =
{ "rfc1459", "strict-rfc1459", "ascii" };
char *irc_server_prefix_modes_default = "ov";
char *irc_server_prefix_chars_default = "@+";
char *irc_server_chanmodes_default = "beI,k,l";
const char *irc_server_send_default_tags = NULL; /* default tags when */
/* sending a message */
#ifdef HAVE_GNUTLS
gnutls_digest_algorithm_t irc_fingerprint_digest_algos[IRC_FINGERPRINT_NUM_ALGOS] =
{ GNUTLS_DIG_SHA1, GNUTLS_DIG_SHA256, GNUTLS_DIG_SHA512 };
char *irc_fingerprint_digest_algos_name[IRC_FINGERPRINT_NUM_ALGOS] =
{ "SHA-1", "SHA-256", "SHA-512" };
int irc_fingerprint_digest_algos_size[IRC_FINGERPRINT_NUM_ALGOS] =
{ 160, 256, 512 };
#endif /* HAVE_GNUTLS */
void irc_server_reconnect (struct t_irc_server *server);
void irc_server_free_data (struct t_irc_server *server);
void irc_server_autojoin_create_buffers (struct t_irc_server *server);
/*
* Checks if a server pointer is valid.
*
* Returns:
* 1: server exists
* 0: server does not exist
*/
int
irc_server_valid (struct t_irc_server *server)
{
struct t_irc_server *ptr_server;
if (!server)
return 0;
for (ptr_server = irc_servers; ptr_server;
ptr_server = ptr_server->next_server)
{
if (ptr_server == server)
return 1;
}
/* server not found */
return 0;
}
/*
* Searches for a server by name.
*
* Returns pointer to server found, NULL if not found.
*/
struct t_irc_server *
irc_server_search (const char *server_name)
{
struct t_irc_server *ptr_server;
if (!server_name)
return NULL;
for (ptr_server = irc_servers; ptr_server;
ptr_server = ptr_server->next_server)
{
if (strcmp (ptr_server->name, server_name) == 0)
return ptr_server;
}
/* server not found */
return NULL;
}
/*
* Searches for a server by name (case insensitive).
*
* Returns pointer to server found, NULL if not found.
*/
struct t_irc_server *
irc_server_casesearch (const char *server_name)
{
struct t_irc_server *ptr_server;
if (!server_name)
return NULL;
for (ptr_server = irc_servers; ptr_server;
ptr_server = ptr_server->next_server)
{
if (weechat_strcasecmp (ptr_server->name, server_name) == 0)
return ptr_server;
}
/* server not found */
return NULL;
}
/*
* Searches for a server option name.
*
* Returns index of option in array "irc_server_option_string", -1 if not found.
*/
int
irc_server_search_option (const char *option_name)
{
int i;
if (!option_name)
return -1;
for (i = 0; i < IRC_SERVER_NUM_OPTIONS; i++)
{
if (weechat_strcasecmp (irc_server_options[i][0], option_name) == 0)
return i;
}
/* server option not found */
return -1;
}
/*
* Searches for a casemapping.
*
* Returns index of casemapping in array "irc_server_casemapping_string", -1 if
* not found.
*/
int
irc_server_search_casemapping (const char *casemapping)
{
int i;
for (i = 0; i < IRC_SERVER_NUM_CASEMAPPING; i++)
{
if (weechat_strcasecmp (irc_server_casemapping_string[i], casemapping) == 0)
return i;
}
/* casemapping not found */
return -1;
}
/*
* Compares two strings on server (case insensitive, depends on casemapping).
*
* Returns:
* < 0: string1 < string2
* 0: string1 == string2
* > 0: string1 > string2
*/
int
irc_server_strcasecmp (struct t_irc_server *server,
const char *string1, const char *string2)
{
int casemapping, rc;
casemapping = (server) ? server->casemapping : IRC_SERVER_CASEMAPPING_RFC1459;
switch (casemapping)
{
case IRC_SERVER_CASEMAPPING_RFC1459:
rc = weechat_strcasecmp_range (string1, string2, 30);
break;
case IRC_SERVER_CASEMAPPING_STRICT_RFC1459:
rc = weechat_strcasecmp_range (string1, string2, 29);
break;
case IRC_SERVER_CASEMAPPING_ASCII:
rc = weechat_strcasecmp (string1, string2);
break;
default:
rc = weechat_strcasecmp_range (string1, string2, 30);
break;
}
return rc;
}
/*
* Compares two strings on server (case insensitive, depends on casemapping) for
* max chars.
*
* Returns:
* < 0: string1 < string2
* 0: string1 == string2
* > 0: string1 > string2
*/
int
irc_server_strncasecmp (struct t_irc_server *server,
const char *string1, const char *string2, int max)
{
int casemapping, rc;
casemapping = (server) ? server->casemapping : IRC_SERVER_CASEMAPPING_RFC1459;
switch (casemapping)
{
case IRC_SERVER_CASEMAPPING_RFC1459:
rc = weechat_strncasecmp_range (string1, string2, max, 30);
break;
case IRC_SERVER_CASEMAPPING_STRICT_RFC1459:
rc = weechat_strncasecmp_range (string1, string2, max, 29);
break;
case IRC_SERVER_CASEMAPPING_ASCII:
rc = weechat_strncasecmp (string1, string2, max);
break;
default:
rc = weechat_strncasecmp_range (string1, string2, max, 30);
break;
}
return rc;
}
/*
* Evaluates a string using the server as context:
* ${irc_server.xxx} and ${server} are replaced by a server option and the
* server name.
*
* Returns the evaluated string.
*
* Note: result must be freed after use.
*/
char *
irc_server_eval_expression (struct t_irc_server *server, const char *string)
{
struct t_hashtable *pointers, *extra_vars;
char *value;
pointers = weechat_hashtable_new (
32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_POINTER,
NULL, NULL);
extra_vars = weechat_hashtable_new (
32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL, NULL);
if (server)
{
if (pointers)
weechat_hashtable_set (pointers, "irc_server", server);
if (extra_vars)
weechat_hashtable_set (extra_vars, "server", server->name);
}
value = weechat_string_eval_expression (string,
pointers, extra_vars, NULL);
if (pointers)
weechat_hashtable_free (pointers);
if (extra_vars)
weechat_hashtable_free (extra_vars);
return value;
}
/*
* Evaluates and returns the fingerprint.
*
* Returns the evaluated fingerprint, NULL if the fingerprint option is
* invalid.
*
* Note: result must be freed after use.
*/
char *
irc_server_eval_fingerprint (struct t_irc_server *server)
{
#ifdef HAVE_GNUTLS
const char *ptr_fingerprint;
char *fingerprint_eval, **fingerprints, *str_sizes;
int i, j, rc, algo, length;
ptr_fingerprint = IRC_SERVER_OPTION_STRING(server,
IRC_SERVER_OPTION_SSL_FINGERPRINT);
/* empty fingerprint is just ignored (considered OK) */
if (!ptr_fingerprint || !ptr_fingerprint[0])
return strdup ("");
/* evaluate fingerprint */
fingerprint_eval = irc_server_eval_expression (server, ptr_fingerprint);
if (!fingerprint_eval || !fingerprint_eval[0])
{
weechat_printf (
server->buffer,
_("%s%s: the evaluated fingerprint for server \"%s\" must not be "
"empty"),
weechat_prefix ("error"),
IRC_PLUGIN_NAME,
server->name);
if (fingerprint_eval)
free (fingerprint_eval);
return NULL;
}
/* split fingerprint */
fingerprints = weechat_string_split (fingerprint_eval, ",", NULL,
WEECHAT_STRING_SPLIT_STRIP_LEFT
| WEECHAT_STRING_SPLIT_STRIP_RIGHT
| WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
0, NULL);
if (!fingerprints)
return fingerprint_eval;
rc = 0;
for (i = 0; fingerprints[i]; i++)
{
length = strlen (fingerprints[i]);
algo = irc_server_fingerprint_search_algo_with_size (length * 4);
if (algo < 0)
{
rc = -1;
break;
}
for (j = 0; j < length; j++)
{
if (!isxdigit ((unsigned char)fingerprints[i][j]))
{
rc = -2;
break;
}
}
if (rc < 0)
break;
}
weechat_string_free_split (fingerprints);
switch (rc)
{
case -1: /* invalid size */
str_sizes = irc_server_fingerprint_str_sizes ();
weechat_printf (
server->buffer,
_("%s%s: invalid fingerprint size for server \"%s\", the "
"number of hexadecimal digits must be "
"one of: %s"),
weechat_prefix ("error"),
IRC_PLUGIN_NAME,
server->name,
(str_sizes) ? str_sizes : "?");
if (str_sizes)
free (str_sizes);
free (fingerprint_eval);
return NULL;
case -2: /* invalid content */
weechat_printf (
server->buffer,
_("%s%s: invalid fingerprint for server \"%s\", it must "
"contain only hexadecimal digits (0-9, "
"a-f)"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, server->name);
free (fingerprint_eval);
return NULL;
}
return fingerprint_eval;
#else
/* make C compiler happy */
(void) server;
return strdup ("");
#endif /* HAVE_GNUTLS */
}
/*
* Checks if SASL is enabled on server.
*
* Returns:
* 1: SASL is enabled
* 0: SASL is disabled
*/
int
irc_server_sasl_enabled (struct t_irc_server *server)
{
int sasl_mechanism, rc;
char *sasl_username, *sasl_password;
const char *sasl_key;
sasl_mechanism = IRC_SERVER_OPTION_INTEGER(
server, IRC_SERVER_OPTION_SASL_MECHANISM);
sasl_username = irc_server_eval_expression (
server,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_USERNAME));
sasl_password = irc_server_eval_expression (
server,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_PASSWORD));
sasl_key = IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_KEY);
/*
* SASL is enabled if one of these conditions is true:
* - mechanism is "external"
* - mechanism is "ecdsa-nist256p-challenge" with username/key set
* - another mechanism with username/password set
*/
rc = ((sasl_mechanism == IRC_SASL_MECHANISM_EXTERNAL)
|| ((sasl_mechanism == IRC_SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE)
&& sasl_username && sasl_username[0]
&& sasl_key && sasl_key[0])
|| (sasl_username && sasl_username[0]
&& sasl_password && sasl_password[0])) ? 1 : 0;
if (sasl_username)
free (sasl_username);
if (sasl_password)
free (sasl_password);
return rc;
}
/*
* Gets name of server without port (ends before first '/' if found).
*
* Note: result must be freed after use.
*/
char *
irc_server_get_name_without_port (const char *name)
{
char *pos;
if (!name)
return NULL;
pos = strchr (name, '/');
if (pos && (pos != name))
return weechat_strndup (name, pos - name);
return strdup (name);
}
/*
* Sets addresses for server.
*
* Returns:
* 1: addresses have been set (changed)
* 0: nothing set (addresses unchanged)
*/
int
irc_server_set_addresses (struct t_irc_server *server, const char *addresses)
{
int i;
char *pos, *error, *addresses_eval;
const char *ptr_addresses;
long number;
addresses_eval = NULL;
ptr_addresses = addresses;
if (ptr_addresses && (strncmp (ptr_addresses, "fake:", 5) == 0))
{
server->fake_server = 1;
ptr_addresses += 5;
}
else
{
server->fake_server = 0;
}
if (ptr_addresses && ptr_addresses[0])
{
addresses_eval = irc_server_eval_expression (server, ptr_addresses);
if (server->addresses_eval
&& (strcmp (server->addresses_eval, addresses_eval) == 0))
{
free (addresses_eval);
return 0;
}
}
/* free data */
if (server->addresses_eval)
{
free (server->addresses_eval);
server->addresses_eval = NULL;
}
server->addresses_count = 0;
if (server->addresses_array)
{
weechat_string_free_split (server->addresses_array);
server->addresses_array = NULL;
}
if (server->ports_array)
{
free (server->ports_array);
server->ports_array = NULL;
}
if (server->retry_array)
{
free (server->retry_array);
server->retry_array = NULL;
}
/* set new addresses/ports */
server->addresses_eval = addresses_eval;
if (!addresses_eval)
return 1;
server->addresses_array = weechat_string_split (
addresses_eval,
",",
" ",
WEECHAT_STRING_SPLIT_STRIP_LEFT
| WEECHAT_STRING_SPLIT_STRIP_RIGHT
| WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
0,
&server->addresses_count);
server->ports_array = malloc (
server->addresses_count * sizeof (server->ports_array[0]));
server->retry_array = malloc (
server->addresses_count * sizeof (server->retry_array[0]));
for (i = 0; i < server->addresses_count; i++)
{
pos = strchr (server->addresses_array[i], '/');
if (pos)
{
pos[0] = 0;
pos++;
error = NULL;
number = strtol (pos, &error, 10);
server->ports_array[i] = (error && !error[0]) ?
number : IRC_SERVER_DEFAULT_PORT;
}
else
{
server->ports_array[i] = IRC_SERVER_DEFAULT_PORT;
}
server->retry_array[i] = 0;
}
return 1;
}
/*
* Sets index of current address for server.
*/
void
irc_server_set_index_current_address (struct t_irc_server *server, int index)
{
int addresses_changed;
addresses_changed = irc_server_set_addresses (
server,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_ADDRESSES));
if (addresses_changed)
{
/* if the addresses have changed, reset the index to 0 */
index = 0;
}
if (server->current_address)
{
free (server->current_address);
server->current_address = NULL;
/* copy current retry value before loading next server */
if (!addresses_changed
&& server->index_current_address < server->addresses_count)
{
server->retry_array[server->index_current_address] = server->current_retry;
}
}
server->current_port = 0;
server->current_retry = 0;
if (server->addresses_count > 0)
{
index %= server->addresses_count;
server->index_current_address = index;
server->current_address = strdup (server->addresses_array[index]);
server->current_port = server->ports_array[index];
server->current_retry = server->retry_array[index];
}
}
/*
* Sets nicks for server.
*/
void
irc_server_set_nicks (struct t_irc_server *server, const char *nicks)
{
char *nicks2;
/* free data */
server->nicks_count = 0;
if (server->nicks_array)
{
weechat_string_free_split (server->nicks_array);
server->nicks_array = NULL;
}
/* evaluate value */
nicks2 = irc_server_eval_expression (server, nicks);
/* set new nicks */
server->nicks_array = weechat_string_split (
(nicks2) ? nicks2 : IRC_SERVER_DEFAULT_NICKS,
",",
NULL,
WEECHAT_STRING_SPLIT_STRIP_LEFT
| WEECHAT_STRING_SPLIT_STRIP_RIGHT
| WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
0,
&server->nicks_count);
if (nicks2)
free (nicks2);
}
/*
* Sets nickname for server.
*/
void
irc_server_set_nick (struct t_irc_server *server, const char *nick)
{
struct t_irc_channel *ptr_channel;
/* if nick is the same, just return */
if ((!server->nick && !nick)
|| (server->nick && nick && strcmp (server->nick, nick) == 0))
{
return;
}
/* update the nick in server */
if (server->nick)
free (server->nick);
server->nick = (nick) ? strdup (nick) : NULL;
/* set local variable "nick" for server and all channels/pv */
weechat_buffer_set (server->buffer, "localvar_set_nick", nick);
for (ptr_channel = server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
weechat_buffer_set (ptr_channel->buffer, "localvar_set_nick", nick);
}
weechat_bar_item_update ("input_prompt");
weechat_bar_item_update ("irc_nick");
weechat_bar_item_update ("irc_nick_host");
}
/*
* Sets host for server.
*/
void
irc_server_set_host (struct t_irc_server *server, const char *host)
{
struct t_irc_channel *ptr_channel;
/* if host is the same, just return */
if ((!server->host && !host)
|| (server->host && host && strcmp (server->host, host) == 0))
{
return;
}
/* update the nick host in server */
if (server->host)
free (server->host);
server->host = (host) ? strdup (host) : NULL;
/* set local variable "host" for server and all channels/pv */
weechat_buffer_set (server->buffer, "localvar_set_host", host);
for (ptr_channel = server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
weechat_buffer_set (ptr_channel->buffer,
"localvar_set_host", host);
}
weechat_bar_item_update ("irc_host");
weechat_bar_item_update ("irc_nick_host");
}
/*
* Gets index of nick in array "nicks_array".
*
* Returns index of nick in array, -1 if nick is not set or not found in
* "nicks_array".
*/
int
irc_server_get_nick_index (struct t_irc_server *server)
{
int i;
if (!server->nick)
return -1;
for (i = 0; i < server->nicks_count; i++)
{
if (strcmp (server->nick, server->nicks_array[i]) == 0)
{
return i;
}
}
/* nick not found */
return -1;
}
/*
* Gets an alternate nick when the nick is already used on server.
*
* First tries all declared nicks, then builds nicks by adding "_", until
* length of 9.
*
* If all nicks are still used, builds 99 alternate nicks by using number at the
* end.
*
* Example: nicks = "abcde,fghi,jkl"
* => nicks tried: abcde
* fghi
* jkl
* abcde_
* abcde__
* abcde___
* abcde____
* abcde___1
* abcde___2
* ...
* abcde__99
*
* Returns NULL if no more alternate nick is available.
*/
const char *
irc_server_get_alternate_nick (struct t_irc_server *server)
{
static char nick[64];
char str_number[64];
int nick_index, length_nick, length_number;
nick[0] = '\0';
/* we are still trying nicks from option "nicks" */
if (server->nick_alternate_number < 0)
{
nick_index = irc_server_get_nick_index (server);
if (nick_index < 0)
nick_index = 0;
else
{
nick_index = (nick_index + 1) % server->nicks_count;
/* stop loop if first nick tried was not in the list of nicks */
if ((nick_index == 0) && (server->nick_first_tried < 0))
server->nick_first_tried = 0;
}
if (nick_index != server->nick_first_tried)
{
snprintf (nick, sizeof (nick),
"%s", server->nicks_array[nick_index]);
return nick;
}
/* now we have tried all nicks in list */
/* if alternate nicks are disabled, just return NULL */
if (!IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_NICKS_ALTERNATE))
return NULL;
/* use main nick and we will add "_" and then number if needed */
server->nick_alternate_number = 0;
snprintf (nick, sizeof (nick), "%s", server->nicks_array[0]);
}
else
snprintf (nick, sizeof (nick), "%s", server->nick);
/* if length is < 9, just add a "_" */
if (strlen (nick) < 9)
{
strcat (nick, "_");
return nick;
}
server->nick_alternate_number++;
/* number is max 99 */
if (server->nick_alternate_number > 99)
return NULL;
/* be sure the nick has 9 chars max */
nick[9] = '\0';
/* generate number */
snprintf (str_number, sizeof (str_number),
"%d", server->nick_alternate_number);
/* copy number in nick */
length_nick = strlen (nick);
length_number = strlen (str_number);
if (length_number > length_nick)
return NULL;
memcpy (nick + length_nick - length_number, str_number, length_number);
/* return alternate nick */
return nick;
}
/*
* Gets value of a feature item in "isupport" (copy of IRC message 005).
*
* Returns value of feature (empty string if feature has no value, NULL if
* feature is not found).
*/
const char *
irc_server_get_isupport_value (struct t_irc_server *server, const char *feature)
{
char feature2[64], *pos_feature, *pos_equal, *pos_space;
int length;
static char value[256];
if (!server || !server->isupport || !feature)
return NULL;
/* search feature with value */
snprintf (feature2, sizeof (feature2), " %s=", feature);
pos_feature = strstr (server->isupport, feature2);
if (pos_feature)
{
/* feature found with value, return value */
pos_feature++;
pos_equal = strchr (pos_feature, '=');
pos_space = strchr (pos_feature, ' ');
if (pos_space)
length = pos_space - pos_equal - 1;
else
length = strlen (pos_equal) + 1;
if (length > (int)sizeof (value) - 1)
length = (int)sizeof (value) - 1;
memcpy (value, pos_equal + 1, length);
value[length] = '\0';
return value;
}
/* search feature without value */
feature2[strlen (feature2) - 1] = ' ';
pos_feature = strstr (server->isupport, feature2);
if (pos_feature)
{
value[0] = '\0';
return value;
}
/* feature not found in isupport */
return NULL;
}
/*
* Sets "prefix_modes" and "prefix_chars" in server using value of PREFIX in IRC
* message 005.
*
* For example, if prefix is "(ohv)@%+":
* prefix_modes is set to "ohv"
* prefix_chars is set to "@%+".
*/
void
irc_server_set_prefix_modes_chars (struct t_irc_server *server,
const char *prefix)
{
char *pos;
int i, old_length_chars, length_modes, length_chars;
if (!server || !prefix)
return;
old_length_chars = (server->prefix_chars) ?
strlen (server->prefix_chars) :
strlen (irc_server_prefix_chars_default);
/* free previous values */
if (server->prefix_modes)
{
free (server->prefix_modes);
server->prefix_modes = NULL;
}
if (server->prefix_chars)
{
free (server->prefix_chars);
server->prefix_chars = NULL;
}
/* assign new values */
pos = strchr (prefix, ')');
if (pos)
{
server->prefix_modes = weechat_strndup (prefix + 1,
pos - prefix - 1);
if (server->prefix_modes)
{
pos++;
length_modes = strlen (server->prefix_modes);
length_chars = strlen (pos);
server->prefix_chars = malloc (length_modes + 1);
if (server->prefix_chars)
{
for (i = 0; i < length_modes; i++)
{
server->prefix_chars[i] = (i < length_chars) ? pos[i] : ' ';
}
server->prefix_chars[length_modes] = '\0';
}
else
{
free (server->prefix_modes);
server->prefix_modes = NULL;
}
}
}
length_chars = (server->prefix_chars) ?
strlen (server->prefix_chars) :
strlen (irc_server_prefix_chars_default);
if (length_chars != old_length_chars)
irc_nick_realloc_prefixes (server, old_length_chars, length_chars);
}
/*
* Sets lag in server buffer (local variable), update bar item "lag"
* and send signal "irc_server_lag_changed" for the server.
*/
void
irc_server_set_lag (struct t_irc_server *server)
{
char str_lag[32];
if (server->lag >= weechat_config_integer (irc_config_network_lag_min_show))
{
snprintf (str_lag, sizeof (str_lag),
((server->lag_check_time.tv_sec == 0) || (server->lag < 1000)) ?
"%.3f" : "%.0f",
((float)(server->lag)) / 1000);
weechat_buffer_set (server->buffer, "localvar_set_lag", str_lag);
}
else
{
weechat_buffer_set (server->buffer, "localvar_del_lag", "");
}
weechat_hook_signal_send ("irc_server_lag_changed",
WEECHAT_HOOK_SIGNAL_STRING,
server->name);
weechat_bar_item_update ("lag");
}
/*
* Gets prefix_modes for server (for example: "ohv").
*
* Returns default modes if prefix_modes is not set in server.
*/
const char *
irc_server_get_prefix_modes (struct t_irc_server *server)
{
return (server && server->prefix_modes) ?
server->prefix_modes : irc_server_prefix_modes_default;
}
/*
* Gets prefix_chars for server (for example: "@%+").
*
* Returns default chars if prefix_chars is not set in server.
*/
const char *
irc_server_get_prefix_chars (struct t_irc_server *server)
{
return (server && server->prefix_chars) ?
server->prefix_chars : irc_server_prefix_chars_default;
}
/*
* Gets index of mode in prefix_modes.
*
* The mode is for example 'o' or 'v'.
*
* Returns -1 if mode does not exist in server.
*/
int
irc_server_get_prefix_mode_index (struct t_irc_server *server, char mode)
{
const char *prefix_modes;
char *pos;
if (server)
{
prefix_modes = irc_server_get_prefix_modes (server);
pos = strchr (prefix_modes, mode);
if (pos)
return pos - prefix_modes;
}
return -1;
}
/*
* Gets index of prefix_char in prefix_chars.
*
* The prefix char is for example '@' or '+'.
*
* Returns -1 if prefix_char does not exist in server.
*/
int
irc_server_get_prefix_char_index (struct t_irc_server *server,
char prefix_char)
{
const char *prefix_chars;
char *pos;
if (server)
{
prefix_chars = irc_server_get_prefix_chars (server);
pos = strchr (prefix_chars, prefix_char);
if (pos)
return pos - prefix_chars;
}
return -1;
}
/*
* Gets mode for prefix char.
*
* For example prefix_char '@' can return 'o'.
*
* Returns ' ' (space) if prefix char is not found.
*/
char
irc_server_get_prefix_mode_for_char (struct t_irc_server *server,
char prefix_char)
{
const char *prefix_modes;
int index;
if (server)
{
prefix_modes = irc_server_get_prefix_modes (server);
index = irc_server_get_prefix_char_index (server, prefix_char);
if (index >= 0)
return prefix_modes[index];
}
return ' ';
}
/*
* Gets prefix char for mode.
*
* For example mode 'o' can return '@'.
*
* Returns a space if mode is not found.
*/
char
irc_server_get_prefix_char_for_mode (struct t_irc_server *server, char mode)
{
const char *prefix_chars;
int index;
if (server)
{
prefix_chars = irc_server_get_prefix_chars (server);
index = irc_server_get_prefix_mode_index (server, mode);
if (index >= 0)
return prefix_chars[index];
}
return ' ';
}
/*
* Gets chanmodes for server (for example: "eIb,k,l,imnpstS").
*
* Returns default chanmodes if chanmodes is not set in server.
*/
const char *
irc_server_get_chanmodes (struct t_irc_server *server)
{
return (server && server->chanmodes) ?
server->chanmodes : irc_server_chanmodes_default;
}
/*
* Checks if a prefix char is valid for a status message
* (message sent for example to ops/voiced).
*
* The prefix (for example '@' or '+') must be in STATUSMSG,
* or in "prefix_chars" if STATUSMSG is not defined.
*
* Returns:
* 1: prefix is valid for a status message
* 0: prefix is NOT valid for a status message
*/
int
irc_server_prefix_char_statusmsg (struct t_irc_server *server,
char prefix_char)
{
const char *support_statusmsg;
support_statusmsg = irc_server_get_isupport_value (server, "STATUSMSG");
if (support_statusmsg)
return (strchr (support_statusmsg, prefix_char)) ? 1 : 0;
return (irc_server_get_prefix_char_index (server, prefix_char) >= 0) ?
1 : 0;
}
/*
* Get max modes supported in one command by the server
* (in isupport value, with the format: "MODES=4").
*
* Default is 4 if the info is not given by the server.
*/
int
irc_server_get_max_modes (struct t_irc_server *server)
{
const char *support_modes;
char *error;
long number;
int max_modes;
max_modes = 4;
support_modes = irc_server_get_isupport_value (server, "MODES");
if (support_modes)
{
error = NULL;
number = strtol (support_modes, &error, 10);
if (error && !error[0])
{
max_modes = number;
if (max_modes < 1)
max_modes = 1;
if (max_modes > 128)
max_modes = 128;
}
}
return max_modes;
}
/*
* Gets an evaluated default_msg server option: replaces "%v" by WeeChat
* version if there's no ${...} in string, or just evaluates the string.
*
* Note: result must be freed after use.
*/
char *
irc_server_get_default_msg (const char *default_msg,
struct t_irc_server *server,
const char *channel_name)
{
char *version;
struct t_hashtable *extra_vars;
char *msg, *res;
/*
* "%v" for version is deprecated since WeeChat 1.6, where
* an expression ${info:version} is preferred, so we replace
* the "%v" with version only if there's no "${...}" in string
*/
if (strstr (default_msg, "%v") && !strstr (default_msg, "${"))
{
version = weechat_info_get ("version", "");
res = weechat_string_replace (default_msg, "%v",
(version) ? version : "");
if (version)
free (version);
return res;
}
extra_vars = weechat_hashtable_new (32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL,
NULL);
if (extra_vars)
{
weechat_hashtable_set (extra_vars, "server", server->name);
weechat_hashtable_set (extra_vars, "channel",
(channel_name) ? channel_name : "");
weechat_hashtable_set (extra_vars, "nick", server->nick);
}
msg = weechat_string_eval_expression (default_msg, NULL, extra_vars, NULL);
if (extra_vars)
weechat_hashtable_free (extra_vars);
return msg;
}
/*
* Allocates a new server and adds it to the servers queue.
*
* Returns pointer to new server, NULL if error.
*/
struct t_irc_server *
irc_server_alloc (const char *name)
{
struct t_irc_server *new_server;
int i, length;
char *option_name;
if (irc_server_casesearch (name))
return NULL;
/* alloc memory for new server */
new_server = malloc (sizeof (*new_server));
if (!new_server)
{
weechat_printf (NULL,
_("%s%s: error when allocating new server"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
return NULL;
}
/* add new server to queue */
new_server->prev_server = last_irc_server;
new_server->next_server = NULL;
if (last_irc_server)
last_irc_server->next_server = new_server;
else
irc_servers = new_server;
last_irc_server = new_server;
/* set name */
new_server->name = strdup (name);
/* internal vars */
new_server->temp_server = 0;
new_server->fake_server = 0;
new_server->reloading_from_config = 0;
new_server->reloaded_from_config = 0;
new_server->addresses_eval = NULL;
new_server->addresses_count = 0;
new_server->addresses_array = NULL;
new_server->ports_array = NULL;
new_server->retry_array = NULL;
new_server->index_current_address = 0;
new_server->current_address = NULL;
new_server->current_ip = NULL;
new_server->current_port = 0;
new_server->current_retry = 0;
new_server->sock = -1;
new_server->hook_connect = NULL;
new_server->hook_fd = NULL;
new_server->hook_timer_connection = NULL;
new_server->hook_timer_sasl = NULL;
new_server->is_connected = 0;
new_server->ssl_connected = 0;
new_server->disconnected = 0;
new_server->unterminated_message = NULL;
new_server->nicks_count = 0;
new_server->nicks_array = NULL;
new_server->nick_first_tried = 0;
new_server->nick_alternate_number = -1;
new_server->nick = NULL;
new_server->nick_modes = NULL;
new_server->host = NULL;
new_server->checking_cap_ls = 0;
new_server->cap_ls = weechat_hashtable_new (32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL,
NULL);
new_server->checking_cap_list = 0;
new_server->cap_list = weechat_hashtable_new (32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL,
NULL);
new_server->isupport = NULL;
new_server->prefix_modes = NULL;
new_server->prefix_chars = NULL;
new_server->nick_max_length = 0;
new_server->user_max_length = 0;
new_server->host_max_length = 0;
new_server->casemapping = IRC_SERVER_CASEMAPPING_RFC1459;
new_server->chantypes = NULL;
new_server->chanmodes = NULL;
new_server->monitor = 0;
new_server->monitor_time = 0;
new_server->reconnect_delay = 0;
new_server->reconnect_start = 0;
new_server->command_time = 0;
new_server->reconnect_join = 0;
new_server->disable_autojoin = 0;
new_server->is_away = 0;
new_server->away_message = NULL;
new_server->away_time = 0;
new_server->lag = 0;
new_server->lag_displayed = -1;
new_server->lag_check_time.tv_sec = 0;
new_server->lag_check_time.tv_usec = 0;
new_server->lag_next_check = time (NULL) +
weechat_config_integer (irc_config_network_lag_check);
new_server->lag_last_refresh = 0;
new_server->cmd_list_regexp = NULL;
new_server->last_user_message = 0;
new_server->last_away_check = 0;
new_server->last_data_purge = 0;
for (i = 0; i < IRC_SERVER_NUM_OUTQUEUES_PRIO; i++)
{
new_server->outqueue[i] = NULL;
new_server->last_outqueue[i] = NULL;
}
new_server->redirects = NULL;
new_server->last_redirect = NULL;
new_server->notify_list = NULL;
new_server->last_notify = NULL;
new_server->notify_count = 0;
new_server->join_manual = weechat_hashtable_new (
32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_TIME,
NULL, NULL);
new_server->join_channel_key = weechat_hashtable_new (
32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL, NULL);
new_server->join_noswitch = weechat_hashtable_new (
32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_TIME,
NULL, NULL);
new_server->buffer = NULL;
new_server->buffer_as_string = NULL;
new_server->channels = NULL;
new_server->last_channel = NULL;
/* create options with null value */
for (i = 0; i < IRC_SERVER_NUM_OPTIONS; i++)
{
length = strlen (new_server->name) + 1 +
strlen (irc_server_options[i][0]) +
512 + /* inherited option name (irc.server_default.xxx) */
1;
option_name = malloc (length);
if (option_name)
{
snprintf (option_name, length, "%s.%s << irc.server_default.%s",
new_server->name,
irc_server_options[i][0],
irc_server_options[i][0]);
new_server->options[i] = irc_config_server_new_option (
irc_config_file,
irc_config_section_server,
i,
option_name,
NULL,
NULL,
1,
&irc_config_server_check_value_cb,
irc_server_options[i][0],
NULL,
&irc_config_server_change_cb,
irc_server_options[i][0],
NULL);
irc_config_server_change_cb (irc_server_options[i][0], NULL,
new_server->options[i]);
free (option_name);
}
}
return new_server;
}
/*
* Initializes a server with URL of this form: irc://nick:pass@irc.toto.org:6667
*
* Returns pointer to new server, NULL if error.
*/
struct t_irc_server *
irc_server_alloc_with_url (const char *irc_url)
{
char *irc_url2, *pos_server, *pos_nick, *pos_password;
char *pos_address, *pos_port, *pos_channel, *pos;
char *server_address, *server_nicks, *server_autojoin;
char default_port[16];
int ipv6, ssl, length;
struct t_irc_server *ptr_server;
irc_url2 = strdup (irc_url);
if (!irc_url2)
return NULL;
pos_server = NULL;
pos_nick = NULL;
pos_password = NULL;
pos_address = NULL;
pos_port = NULL;
pos_channel = NULL;
ipv6 = 0;
ssl = 0;
snprintf (default_port, sizeof (default_port),
"%d", IRC_SERVER_DEFAULT_PORT);
pos_server = strstr (irc_url2, "://");
if (!pos_server || !pos_server[3])
{
free (irc_url2);
return NULL;
}
pos_server[0] = '\0';
pos_server += 3;
pos_channel = strstr (pos_server, "/");
if (pos_channel)
{
pos_channel[0] = '\0';
pos_channel++;
while (pos_channel[0] == '/')
{
pos_channel++;
}
}
/* check for SSL / IPv6 */
if (weechat_strcasecmp (irc_url2, "irc6") == 0)
{
ipv6 = 1;
}
else if (weechat_strcasecmp (irc_url2, "ircs") == 0)
{
ssl = 1;
}
else if ((weechat_strcasecmp (irc_url2, "irc6s") == 0)
|| (weechat_strcasecmp (irc_url2, "ircs6") == 0))
{
ipv6 = 1;
ssl = 1;
}
if (ssl)
{
snprintf (default_port, sizeof (default_port),
"%d", IRC_SERVER_DEFAULT_PORT_SSL);
}
/* search for nick, password, address+port */
pos_address = strchr (pos_server, '@');
if (pos_address)
{
pos_address[0] = '\0';
pos_address++;
pos_nick = pos_server;
pos_password = strchr (pos_server, ':');
if (pos_password)
{
pos_password[0] = '\0';
pos_password++;
}
}
else
pos_address = pos_server;
/*
* search for port in address, and skip optional [ ] around address
* (can be used to indicate IPv6 port, after ']')
*/
if (pos_address[0] == '[')
{
pos_address++;
pos = strchr (pos_address, ']');
if (!pos)
{
free (irc_url2);
return NULL;
}
pos[0] = '\0';
pos++;
pos_port = strchr (pos, ':');
if (pos_port)
{
pos_port[0] = '\0';
pos_port++;
}
}
else
{
pos_port = strchr (pos_address, ':');
if (pos_port)
{
pos_port[0] = '\0';
pos_port++;
}
}
ptr_server = irc_server_alloc (pos_address);
if (ptr_server)
{
ptr_server->temp_server = 1;
if (pos_address && pos_address[0])
{
length = strlen (pos_address) + 1 +
((pos_port) ? strlen (pos_port) : 16) + 1;
server_address = malloc (length);
if (server_address)
{
snprintf (server_address, length,
"%s/%s",
pos_address,
(pos_port && pos_port[0]) ? pos_port : default_port);
weechat_config_option_set (
ptr_server->options[IRC_SERVER_OPTION_ADDRESSES],
server_address,
1);
free (server_address);
}
}
weechat_config_option_set (ptr_server->options[IRC_SERVER_OPTION_IPV6],
(ipv6) ? "on" : "off",
1);
weechat_config_option_set (ptr_server->options[IRC_SERVER_OPTION_SSL],
(ssl) ? "on" : "off",
1);
if (pos_nick && pos_nick[0])
{
length = ((strlen (pos_nick) + 2) * 5) + 1;
server_nicks = malloc (length);
if (server_nicks)
{
snprintf (server_nicks, length,
"%s,%s1,%s2,%s3,%s4",
pos_nick, pos_nick, pos_nick, pos_nick, pos_nick);
weechat_config_option_set (
ptr_server->options[IRC_SERVER_OPTION_NICKS],
server_nicks,
1);
free (server_nicks);
}
}
if (pos_password && pos_password[0])
{
weechat_config_option_set (
ptr_server->options[IRC_SERVER_OPTION_PASSWORD],
pos_password,
1);
}
weechat_config_option_set (
ptr_server->options[IRC_SERVER_OPTION_AUTOCONNECT],
"on",
1);
/* autojoin */
if (pos_channel && pos_channel[0])
{
if (irc_channel_is_channel (ptr_server, pos_channel))
server_autojoin = strdup (pos_channel);
else
{
server_autojoin = malloc (strlen (pos_channel) + 2);
if (server_autojoin)
{
strcpy (server_autojoin, "#");
strcat (server_autojoin, pos_channel);
}
}
if (server_autojoin)
{
weechat_config_option_set (
ptr_server->options[IRC_SERVER_OPTION_AUTOJOIN],
server_autojoin,
1);
free (server_autojoin);
}
}
}
free (irc_url2);
return ptr_server;
}
/*
* Applies command line options to a server.
*
* For example: -ssl -nossl -password=test -proxy=myproxy
*/
void
irc_server_apply_command_line_options (struct t_irc_server *server,
int argc, char **argv)
{
int i, index_option;
char *pos, *option_name, *ptr_value, *value_boolean[2] = { "off", "on" };
for (i = 0; i < argc; i++)
{
if (argv[i][0] == '-')
{
pos = strchr (argv[i], '=');
if (pos)
{
option_name = weechat_strndup (argv[i] + 1, pos - argv[i] - 1);
ptr_value = pos + 1;
}
else
{
option_name = strdup (argv[i] + 1);
ptr_value = value_boolean[1];
}
if (option_name)
{
if (weechat_strcasecmp (option_name, "temp") == 0)
{
/* temporary server, not saved */
server->temp_server = 1;
}
else
{
index_option = irc_server_search_option (option_name);
if (index_option < 0)
{
/* look if option is negative, like "-noxxx" */
if (weechat_strncasecmp (argv[i], "-no", 3) == 0)
{
free (option_name);
option_name = strdup (argv[i] + 3);
index_option = irc_server_search_option (option_name);
ptr_value = value_boolean[0];
}
}
if (index_option >= 0)
{
weechat_config_option_set (server->options[index_option],
ptr_value, 1);
}
}
free (option_name);
}
}
}
}
/*
* Adds a message in out queue.
*/
void
irc_server_outqueue_add (struct t_irc_server *server, int priority,
const char *command, const char *msg1,
const char *msg2, int modified, const char *tags,
struct t_irc_redirect *redirect)
{
struct t_irc_outqueue *new_outqueue;
new_outqueue = malloc (sizeof (*new_outqueue));
if (new_outqueue)
{
new_outqueue->command = (command) ? strdup (command) : strdup ("unknown");
new_outqueue->message_before_mod = (msg1) ? strdup (msg1) : NULL;
new_outqueue->message_after_mod = (msg2) ? strdup (msg2) : NULL;
new_outqueue->modified = modified;
new_outqueue->tags = (tags) ? strdup (tags) : NULL;
new_outqueue->redirect = redirect;
new_outqueue->prev_outqueue = server->last_outqueue[priority];
new_outqueue->next_outqueue = NULL;
if (server->last_outqueue[priority])
server->last_outqueue[priority]->next_outqueue = new_outqueue;
else
server->outqueue[priority] = new_outqueue;
server->last_outqueue[priority] = new_outqueue;
}
}
/*
* Frees a message in out queue.
*/
void
irc_server_outqueue_free (struct t_irc_server *server,
int priority,
struct t_irc_outqueue *outqueue)
{
struct t_irc_outqueue *new_outqueue;
if (!server || !outqueue)
return;
/* remove outqueue message */
if (server->last_outqueue[priority] == outqueue)
server->last_outqueue[priority] = outqueue->prev_outqueue;
if (outqueue->prev_outqueue)
{
(outqueue->prev_outqueue)->next_outqueue = outqueue->next_outqueue;
new_outqueue = server->outqueue[priority];
}
else
new_outqueue = outqueue->next_outqueue;
if (outqueue->next_outqueue)
(outqueue->next_outqueue)->prev_outqueue = outqueue->prev_outqueue;
/* free data */
if (outqueue->command)
free (outqueue->command);
if (outqueue->message_before_mod)
free (outqueue->message_before_mod);
if (outqueue->message_after_mod)
free (outqueue->message_after_mod);
if (outqueue->tags)
free (outqueue->tags);
free (outqueue);
/* set new head */
server->outqueue[priority] = new_outqueue;
}
/*
* Frees all messages in out queue.
*/
void
irc_server_outqueue_free_all (struct t_irc_server *server, int priority)
{
while (server->outqueue[priority])
{
irc_server_outqueue_free (server, priority,
server->outqueue[priority]);
}
}
/*
* Frees server data.
*/
void
irc_server_free_data (struct t_irc_server *server)
{
int i;
if (!server)
return;
/* free linked lists */
for (i = 0; i < IRC_SERVER_NUM_OUTQUEUES_PRIO; i++)
{
irc_server_outqueue_free_all (server, i);
}
irc_redirect_free_all (server);
irc_notify_free_all (server);
irc_channel_free_all (server);
/* free hashtables */
weechat_hashtable_free (server->join_manual);
weechat_hashtable_free (server->join_channel_key);
weechat_hashtable_free (server->join_noswitch);
/* free server data */
for (i = 0; i < IRC_SERVER_NUM_OPTIONS; i++)
{
if (server->options[i])
weechat_config_option_free (server->options[i]);
}
if (server->name)
free (server->name);
if (server->addresses_eval)
free (server->addresses_eval);
if (server->addresses_array)
weechat_string_free_split (server->addresses_array);
if (server->ports_array)
free (server->ports_array);
if (server->retry_array)
free (server->retry_array);
if (server->current_address)
free (server->current_address);
if (server->current_ip)
free (server->current_ip);
if (server->hook_connect)
weechat_unhook (server->hook_connect);
if (server->hook_fd)
weechat_unhook (server->hook_fd);
if (server->hook_timer_connection)
weechat_unhook (server->hook_timer_connection);
if (server->hook_timer_sasl)
weechat_unhook (server->hook_timer_sasl);
if (server->unterminated_message)
free (server->unterminated_message);
if (server->nicks_array)
weechat_string_free_split (server->nicks_array);
if (server->nick)
free (server->nick);
if (server->nick_modes)
free (server->nick_modes);
if (server->host)
free (server->host);
if (server->cap_ls)
weechat_hashtable_free (server->cap_ls);
if (server->cap_list)
weechat_hashtable_free (server->cap_list);
if (server->isupport)
free (server->isupport);
if (server->prefix_modes)
free (server->prefix_modes);
if (server->prefix_chars)
free (server->prefix_chars);
if (server->chantypes)
free (server->chantypes);
if (server->chanmodes)
free (server->chanmodes);
if (server->away_message)
free (server->away_message);
if (server->cmd_list_regexp)
{
regfree (server->cmd_list_regexp);
free (server->cmd_list_regexp);
}
if (server->buffer_as_string)
free (server->buffer_as_string);
}
/*
* Frees a server and remove it from list of servers.
*/
void
irc_server_free (struct t_irc_server *server)
{
struct t_irc_server *new_irc_servers;
if (!server)
return;
/*
* close server buffer (and all channels/privates)
* (only if we are not in a /upgrade, because during upgrade we want to
* keep connections and closing server buffer would disconnect from server)
*/
if (server->buffer && !irc_signal_upgrade_received)
weechat_buffer_close (server->buffer);
/* 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_free_data (server);
free (server);
irc_servers = new_irc_servers;
}
/*
* Frees all servers.
*/
void
irc_server_free_all ()
{
/* for each server in memory, remove it */
while (irc_servers)
{
irc_server_free (irc_servers);
}
}
/*
* Copies a server.
*
* Returns pointer to new server, NULL if error.
*/
struct t_irc_server *
irc_server_copy (struct t_irc_server *server, const char *new_name)
{
struct t_irc_server *new_server;
struct t_infolist *infolist;
char *mask, *pos;
const char *option_name;
int length, index_option;
/* check if another server exists with this name */
if (irc_server_casesearch (new_name))
return NULL;
new_server = irc_server_alloc (new_name);
if (!new_server)
return NULL;
/* duplicate temporary/fake server flags */
new_server->temp_server = server->temp_server;
new_server->fake_server = server->fake_server;
/* duplicate options */
length = 32 + strlen (server->name) + 1;
mask = malloc (length);
if (!mask)
return 0;
snprintf (mask, length, "irc.server.%s.*", server->name);
infolist = weechat_infolist_get ("option", NULL, mask);
free (mask);
if (infolist)
{
while (weechat_infolist_next (infolist))
{
if (!weechat_infolist_integer (infolist, "value_is_null"))
{
option_name = weechat_infolist_string (infolist,
"option_name");
pos = strrchr (option_name, '.');
if (pos)
{
index_option = irc_server_search_option (pos + 1);
if (index_option >= 0)
{
weechat_config_option_set (
new_server->options[index_option],
weechat_infolist_string (infolist, "value"),
1);
}
}
}
}
weechat_infolist_free (infolist);
}
return new_server;
}
/*
* Renames a server (internal name).
*
* Returns:
* 1: OK
* 0: error
*/
int
irc_server_rename (struct t_irc_server *server, const char *new_name)
{
int length;
char *mask, *pos_option, *new_option_name, charset_modifier[256];
const char *buffer_name, *option_name;
struct t_infolist *infolist;
struct t_config_option *ptr_option;
struct t_irc_channel *ptr_channel;
/* check if another server exists with this name */
if (irc_server_casesearch (new_name))
return 0;
/* rename options */
length = 32 + strlen (server->name) + 1;
mask = malloc (length);
if (!mask)
return 0;
snprintf (mask, length, "irc.server.%s.*", server->name);
infolist = weechat_infolist_get ("option", NULL, mask);
free (mask);
if (infolist)
{
while (weechat_infolist_next (infolist))
{
ptr_option = weechat_config_get (
weechat_infolist_string (infolist, "full_name"));
if (ptr_option)
{
option_name = weechat_infolist_string (infolist, "option_name");
if (option_name)
{
pos_option = strrchr (option_name, '.');
if (pos_option)
{
pos_option++;
length = strlen (new_name) + 1 + strlen (pos_option) + 1;
new_option_name = malloc (length);
if (new_option_name)
{
snprintf (new_option_name, length,
"%s.%s", new_name, pos_option);
weechat_config_option_rename (ptr_option, new_option_name);
free (new_option_name);
}
}
}
}
}
weechat_infolist_free (infolist);
}
/* rename server */
if (server->name)
free (server->name);
server->name = strdup (new_name);
/* change name and local variables on buffers */
for (ptr_channel = server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
if (ptr_channel->buffer)
{
buffer_name = irc_buffer_build_name (server->name,
ptr_channel->name);
weechat_buffer_set (ptr_channel->buffer, "name", buffer_name);
weechat_buffer_set (ptr_channel->buffer, "localvar_set_server",
server->name);
}
}
if (server->buffer)
{
buffer_name = irc_buffer_build_name (server->name, NULL);
weechat_buffer_set (server->buffer, "name", buffer_name);
weechat_buffer_set (server->buffer, "short_name", server->name);
weechat_buffer_set (server->buffer, "localvar_set_server",
server->name);
weechat_buffer_set (server->buffer, "localvar_set_channel",
server->name);
snprintf (charset_modifier, sizeof (charset_modifier),
"irc.%s", server->name);
weechat_buffer_set (server->buffer, "localvar_set_charset_modifier",
charset_modifier);
}
return 1;
}
/*
* Reorders list of servers.
*
* Returns the number of servers moved in the list (>= 0).
*/
int
irc_server_reorder (const char **servers, int num_servers)
{
struct t_irc_server *ptr_server, *ptr_server2;
int i, num_moved;
ptr_server = irc_servers;
num_moved = 0;
for (i = 0; ptr_server && (i < num_servers); i++)
{
for (ptr_server2 = ptr_server; ptr_server2;
ptr_server2 = ptr_server2->next_server)
{
if (strcmp (ptr_server2->name, servers[i]) == 0)
break;
}
if (ptr_server2 == ptr_server)
{
ptr_server = ptr_server->next_server;
}
else if (ptr_server2)
{
/* extract server from list */
if (ptr_server2 == irc_servers)
irc_servers = ptr_server2->next_server;
if (ptr_server2 == last_irc_server)
last_irc_server = ptr_server2->prev_server;
if (ptr_server2->prev_server)
(ptr_server2->prev_server)->next_server = ptr_server2->next_server;
if (ptr_server2->next_server)
(ptr_server2->next_server)->prev_server = ptr_server2->prev_server;
/* set pointers in ptr_server2 */
ptr_server2->prev_server = ptr_server->prev_server;
ptr_server2->next_server = ptr_server;
/* insert ptr_server2 before ptr_server */
if (ptr_server->prev_server)
(ptr_server->prev_server)->next_server = ptr_server2;
ptr_server->prev_server = ptr_server2;
/* adjust list of servers if needed */
if (ptr_server == irc_servers)
irc_servers = ptr_server2;
num_moved++;
}
}
return num_moved;
}
/*
* Sends a signal for an IRC message (received or sent).
*/
void
irc_server_send_signal (struct t_irc_server *server, const char *signal,
const char *command, const char *full_message,
const char *tags)
{
int length;
char *str_signal, *full_message_tags;
length = strlen (server->name) + 1 + strlen (signal) + 1 + strlen (command) + 1;
str_signal = malloc (length);
if (str_signal)
{
snprintf (str_signal, length,
"%s,%s_%s", server->name, signal, command);
if (tags)
{
length = strlen (tags) + 1 + strlen (full_message) + 1;
full_message_tags = malloc (length);
if (full_message_tags)
{
snprintf (full_message_tags, length,
"%s;%s", tags, full_message);
(void) weechat_hook_signal_send (str_signal,
WEECHAT_HOOK_SIGNAL_STRING,
(void *)full_message_tags);
free (full_message_tags);
}
}
else
{
(void) weechat_hook_signal_send (str_signal,
WEECHAT_HOOK_SIGNAL_STRING,
(void *)full_message);
}
free (str_signal);
}
}
/*
* Sends data to IRC server.
*
* Returns number of bytes sent, -1 if error.
*/
int
irc_server_send (struct t_irc_server *server, const char *buffer, int size_buf)
{
int rc;
if (server->fake_server)
return size_buf;
if (!server)
{
weechat_printf (
NULL,
_("%s%s: sending data to server: null pointer (please report "
"problem to developers)"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
return 0;
}
if (size_buf <= 0)
{
weechat_printf (
server->buffer,
_("%s%s: sending data to server: empty buffer (please report "
"problem to developers)"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
return 0;
}
#ifdef HAVE_GNUTLS
if (server->ssl_connected)
rc = gnutls_record_send (server->gnutls_sess, buffer, size_buf);
else
#endif /* HAVE_GNUTLS */
rc = send (server->sock, buffer, size_buf, 0);
if (rc < 0)
{
#ifdef HAVE_GNUTLS
if (server->ssl_connected)
{
weechat_printf (
server->buffer,
_("%s%s: sending data to server: error %d %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
rc, gnutls_strerror (rc));
}
else
#endif /* HAVE_GNUTLS */
{
weechat_printf (
server->buffer,
_("%s%s: sending data to server: error %d %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
errno, strerror (errno));
}
}
return rc;
}
/*
* Sets default tags used when sending message.
*/
void
irc_server_set_send_default_tags (const char *tags)
{
irc_server_send_default_tags = tags;
}
/*
* Gets tags to send by concatenation of tags and irc_server_send_default_tags
* (if set).
*
* Note: result must be freed after use.
*/
char *
irc_server_get_tags_to_send (const char *tags)
{
int length;
char *buf;
if (!tags && !irc_server_send_default_tags)
return NULL;
if (!tags)
return strdup (irc_server_send_default_tags);
if (!irc_server_send_default_tags)
return strdup (tags);
/* concatenate tags and irc_server_send_default_tags */
length = strlen (tags) + 1 + strlen (irc_server_send_default_tags) + 1;
buf = malloc (length);
if (buf)
snprintf (buf, length, "%s,%s", tags, irc_server_send_default_tags);
return buf;
}
/*
* Sends a message from out queue.
*/
void
irc_server_outqueue_send (struct t_irc_server *server)
{
time_t time_now;
char *pos, *tags_to_send;
int priority, anti_flood;
time_now = time (NULL);
/* detect if system clock has been changed (now lower than before) */
if (server->last_user_message > time_now)
server->last_user_message = time_now;
for (priority = 0; priority < IRC_SERVER_NUM_OUTQUEUES_PRIO; priority++)
{
switch (priority)
{
case 0:
anti_flood = IRC_SERVER_OPTION_INTEGER(
server, IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_HIGH);
break;
default:
anti_flood = IRC_SERVER_OPTION_INTEGER(
server, IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_LOW);
break;
}
if (server->outqueue[priority]
&& (time_now >= server->last_user_message + anti_flood))
{
if (server->outqueue[priority]->message_before_mod)
{
pos = strchr (server->outqueue[priority]->message_before_mod,
'\r');
if (pos)
pos[0] = '\0';
irc_raw_print (server, IRC_RAW_FLAG_SEND,
server->outqueue[priority]->message_before_mod);
if (pos)
pos[0] = '\r';
}
if (server->outqueue[priority]->message_after_mod)
{
pos = strchr (server->outqueue[priority]->message_after_mod,
'\r');
if (pos)
pos[0] = '\0';
irc_raw_print (server, IRC_RAW_FLAG_SEND |
((server->outqueue[priority]->modified) ? IRC_RAW_FLAG_MODIFIED : 0),
server->outqueue[priority]->message_after_mod);
if (pos)
pos[0] = '\r';
/* send signal with command that will be sent to server */
irc_server_send_signal (
server, "irc_out",
server->outqueue[priority]->command,
server->outqueue[priority]->message_after_mod,
NULL);
tags_to_send = irc_server_get_tags_to_send (
server->outqueue[priority]->tags);
irc_server_send_signal (
server, "irc_outtags",
server->outqueue[priority]->command,
server->outqueue[priority]->message_after_mod,
(tags_to_send) ? tags_to_send : "");
if (tags_to_send)
free (tags_to_send);
/* send command */
irc_server_send (
server, server->outqueue[priority]->message_after_mod,
strlen (server->outqueue[priority]->message_after_mod));
server->last_user_message = time_now;
/* start redirection if redirect is set */
if (server->outqueue[priority]->redirect)
{
irc_redirect_init_command (
server->outqueue[priority]->redirect,
server->outqueue[priority]->message_after_mod);
}
}
irc_server_outqueue_free (server, priority,
server->outqueue[priority]);
break;
}
}
}
/*
* Sends one message to IRC server.
*
* If flag contains outqueue priority value, then messages are in a queue and
* sent slowly (to be sure there will not be any "excess flood"), value of
* queue_msg is priority:
* 1 = higher priority, for user messages
* 2 = lower priority, for other messages (like auto reply to CTCP queries)
*
* Returns:
* 1: OK
* 0: error
*/
int
irc_server_send_one_msg (struct t_irc_server *server, int flags,
const char *message, const char *nick,
const char *command, const char *channel,
const char *tags)
{
static char buffer[4096];
const char *ptr_msg, *ptr_chan_nick;
char *new_msg, *pos, *tags_to_send, *msg_encoded;
char str_modifier[128], modifier_data[256];
int rc, queue_msg, add_to_queue, first_message, anti_flood;
int pos_channel, pos_text, pos_encode;
time_t time_now;
struct t_irc_redirect *ptr_redirect;
rc = 1;
/* run modifier "irc_out_xxx" */
snprintf (str_modifier, sizeof (str_modifier),
"irc_out_%s",
(command) ? command : "unknown");
new_msg = weechat_hook_modifier_exec (str_modifier,
server->name,
message);
/* no changes in new message */
if (new_msg && (strcmp (message, 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;
msg_encoded = NULL;
irc_message_parse (server, ptr_msg, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, &pos_channel,
&pos_text);
switch (IRC_SERVER_OPTION_INTEGER(server,
IRC_SERVER_OPTION_CHARSET_MESSAGE))
{
case IRC_SERVER_CHARSET_MESSAGE_MESSAGE:
pos_encode = 0;
break;
case IRC_SERVER_CHARSET_MESSAGE_CHANNEL:
pos_encode = (pos_channel >= 0) ? pos_channel : pos_text;
break;
case IRC_SERVER_CHARSET_MESSAGE_TEXT:
pos_encode = pos_text;
break;
default:
pos_encode = 0;
break;
}
if (pos_encode >= 0)
{
ptr_chan_nick = (channel) ? channel : nick;
if (ptr_chan_nick)
{
snprintf (modifier_data, sizeof (modifier_data),
"%s.%s.%s",
weechat_plugin->name,
server->name,
ptr_chan_nick);
}
else
{
snprintf (modifier_data, sizeof (modifier_data),
"%s.%s",
weechat_plugin->name,
server->name);
}
msg_encoded = irc_message_convert_charset (ptr_msg, pos_encode,
"charset_encode",
modifier_data);
}
if (msg_encoded)
ptr_msg = msg_encoded;
while (rc && ptr_msg && ptr_msg[0])
{
pos = strchr (ptr_msg, '\n');
if (pos)
pos[0] = '\0';
snprintf (buffer, sizeof (buffer), "%s\r\n", ptr_msg);
/* anti-flood: look whether we should queue outgoing message or not */
time_now = time (NULL);
/* detect if system clock has been changed (now lower than before) */
if (server->last_user_message > time_now)
server->last_user_message = time_now;
/* get queue from flags */
queue_msg = 0;
if (flags & IRC_SERVER_SEND_OUTQ_PRIO_HIGH)
queue_msg = 1;
else if (flags & IRC_SERVER_SEND_OUTQ_PRIO_LOW)
queue_msg = 2;
switch (queue_msg - 1)
{
case 0:
anti_flood = IRC_SERVER_OPTION_INTEGER(
server, IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_HIGH);
break;
default:
anti_flood = IRC_SERVER_OPTION_INTEGER(
server, IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_LOW);
break;
}
add_to_queue = 0;
if ((queue_msg > 0)
&& (server->outqueue[queue_msg - 1]
|| ((anti_flood > 0)
&& (time_now - server->last_user_message < anti_flood))))
{
add_to_queue = queue_msg;
}
tags_to_send = irc_server_get_tags_to_send (tags);
ptr_redirect = irc_redirect_search_available (server);
if (add_to_queue > 0)
{
/* queue message (do not send anything now) */
irc_server_outqueue_add (server, add_to_queue - 1, command,
(new_msg && first_message) ? message : NULL,
buffer,
(new_msg) ? 1 : 0,
tags_to_send,
ptr_redirect);
/* mark redirect as "used" */
if (ptr_redirect)
ptr_redirect->assigned_to_command = 1;
}
else
{
if (first_message)
{
irc_raw_print (server, IRC_RAW_FLAG_SEND, message);
}
if (new_msg)
{
irc_raw_print (server,
IRC_RAW_FLAG_SEND | IRC_RAW_FLAG_MODIFIED,
ptr_msg);
}
/* send signal with command that will be sent to server */
irc_server_send_signal (server, "irc_out",
(command) ? command : "unknown",
ptr_msg,
NULL);
irc_server_send_signal (server, "irc_outtags",
(command) ? command : "unknown",
ptr_msg,
(tags_to_send) ? tags_to_send : "");
if (irc_server_send (server, buffer, strlen (buffer)) <= 0)
rc = 0;
else
{
if (queue_msg > 0)
server->last_user_message = time_now;
}
if (ptr_redirect)
irc_redirect_init_command (ptr_redirect, buffer);
}
if (tags_to_send)
free (tags_to_send);
if (pos)
{
pos[0] = '\n';
ptr_msg = pos + 1;
}
else
ptr_msg = NULL;
first_message = 0;
}
if (msg_encoded)
free (msg_encoded);
}
else
{
irc_raw_print (server, IRC_RAW_FLAG_SEND | IRC_RAW_FLAG_MODIFIED,
_("(message dropped)"));
}
if (new_msg)
free (new_msg);
return rc;
}
/*
* Sends formatted data to IRC server.
*
* Many messages may be sent, separated by '\n'.
*
* If flags contains "IRC_SERVER_SEND_RETURN_HASHTABLE", then a hashtable with
* split of message is returned (see function irc_message_split() in
* irc-message.c)
*
* Note: hashtable must be freed after use.
*/
struct t_hashtable *
irc_server_sendf (struct t_irc_server *server, int flags, const char *tags,
const char *format, ...)
{
char **items, hash_key[32], value[32], *nick, *command, *channel, *new_msg;
char str_modifier[128];
const char *str_message, *str_args;
int i, items_count, number, ret_number, rc;
struct t_hashtable *hashtable, *ret_hashtable;
if (!server)
return NULL;
weechat_va_format (format);
if (!vbuffer)
return NULL;
ret_hashtable = NULL;
ret_number = 1;
if (flags & IRC_SERVER_SEND_RETURN_HASHTABLE)
{
ret_hashtable = weechat_hashtable_new (32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL, NULL);
}
rc = 1;
items = weechat_string_split (vbuffer, "\n", NULL,
WEECHAT_STRING_SPLIT_STRIP_LEFT
| WEECHAT_STRING_SPLIT_STRIP_RIGHT
| WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
0, &items_count);
for (i = 0; i < items_count; i++)
{
/* run modifier "irc_out1_xxx" (like "irc_out_xxx", but before split) */
irc_message_parse (server, items[i], NULL, NULL,
&nick, NULL, NULL, &command, &channel, NULL, NULL,
NULL, NULL, NULL, NULL);
snprintf (str_modifier, sizeof (str_modifier),
"irc_out1_%s",
(command) ? command : "unknown");
new_msg = weechat_hook_modifier_exec (str_modifier,
server->name,
items[i]);
/* no changes in new message */
if (new_msg && (strcmp (items[i], new_msg) == 0))
{
free (new_msg);
new_msg = NULL;
}
/* message not dropped? */
if (!new_msg || new_msg[0])
{
/* send signal with command that will be sent to server (before split) */
irc_server_send_signal (server, "irc_out1",
(command) ? command : "unknown",
(new_msg) ? new_msg : items[i],
NULL);
/*
* split message if needed (max is 512 bytes by default,
* including the final "\r\n")
*/
hashtable = irc_message_split (server,
(new_msg) ? new_msg : items[i]);
if (hashtable)
{
number = 1;
while (1)
{
snprintf (hash_key, sizeof (hash_key), "msg%d", number);
str_message = weechat_hashtable_get (hashtable, hash_key);
if (!str_message)
break;
snprintf (hash_key, sizeof (hash_key), "args%d", number);
str_args = weechat_hashtable_get (hashtable, hash_key);
rc = irc_server_send_one_msg (server, flags, str_message,
nick, command, channel, tags);
if (!rc)
break;
if (ret_hashtable)
{
snprintf (hash_key, sizeof (hash_key),
"msg%d", ret_number);
weechat_hashtable_set (ret_hashtable,
hash_key, str_message);
if (str_args)
{
snprintf (hash_key, sizeof (hash_key),
"args%d", ret_number);
weechat_hashtable_set (ret_hashtable,
hash_key, str_args);
}
ret_number++;
}
number++;
}
if (ret_hashtable)
{
snprintf (value, sizeof (value), "%d", ret_number - 1);
weechat_hashtable_set (ret_hashtable, "count", value);
}
weechat_hashtable_free (hashtable);
if (!rc)
break;
}
}
if (nick)
free (nick);
if (command)
free (command);
if (channel)
free (channel);
if (new_msg)
free (new_msg);
}
if (items)
weechat_string_free_split (items);
free (vbuffer);
return ret_hashtable;
}
/*
* Adds a message to received messages queue (at the end).
*/
void
irc_server_msgq_add_msg (struct t_irc_server *server, const char *msg)
{
struct t_irc_message *message;
if (!server->unterminated_message && !msg[0])
return;
message = malloc (sizeof (*message));
if (!message)
{
weechat_printf (server->buffer,
_("%s%s: not enough memory for received message"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
return;
}
message->server = server;
if (server->unterminated_message)
{
message->data = malloc (strlen (server->unterminated_message) +
strlen (msg) + 1);
if (!message->data)
{
weechat_printf (server->buffer,
_("%s%s: not enough memory for received message"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
}
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;
}
}
/*
* Adds an unterminated message to queue.
*/
void
irc_server_msgq_add_unterminated (struct t_irc_server *server,
const char *string)
{
char *unterminated_message2;
if (!string[0])
return;
if (server->unterminated_message)
{
unterminated_message2 =
realloc (server->unterminated_message,
(strlen (server->unterminated_message) +
strlen (string) + 1));
if (!unterminated_message2)
{
weechat_printf (server->buffer,
_("%s%s: not enough memory for received message"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
free (server->unterminated_message);
server->unterminated_message = NULL;
return;
}
server->unterminated_message = unterminated_message2;
strcat (server->unterminated_message, string);
}
else
{
server->unterminated_message = strdup (string);
if (!server->unterminated_message)
{
weechat_printf (server->buffer,
_("%s%s: not enough memory for received message"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
}
}
}
/*
* Splits received buffer, creating queued messages.
*/
void
irc_server_msgq_add_buffer (struct t_irc_server *server, const 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;
}
}
}
/*
* Flushes message queue.
*/
void
irc_server_msgq_flush ()
{
struct t_irc_message *next;
char *ptr_data, *new_msg, *new_msg2, *ptr_msg, *ptr_msg2, *pos;
char *nick, *host, *command, *channel, *arguments;
char *msg_decoded, *msg_decoded_without_color;
char str_modifier[128], modifier_data[256];
int pos_channel, pos_text, pos_decode;
while (irc_recv_msgq)
{
if (irc_recv_msgq->data)
{
/*
* read message only if connection was not lost
* (or if we are on a fake server)
*/
if ((irc_recv_msgq->server->sock != -1)
|| irc_recv_msgq->server->fake_server)
{
ptr_data = irc_recv_msgq->data;
while (ptr_data[0] == ' ')
{
ptr_data++;
}
if (ptr_data[0])
{
irc_raw_print (irc_recv_msgq->server, IRC_RAW_FLAG_RECV,
ptr_data);
irc_message_parse (irc_recv_msgq->server,
ptr_data, NULL, NULL, NULL, NULL, NULL,
&command, NULL, NULL, NULL, NULL, NULL,
NULL, NULL);
snprintf (str_modifier, sizeof (str_modifier),
"irc_in_%s",
(command) ? command : "unknown");
new_msg = weechat_hook_modifier_exec (
str_modifier,
irc_recv_msgq->server->name,
ptr_data);
if (command)
free (command);
/* 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)
{
irc_raw_print (
irc_recv_msgq->server,
IRC_RAW_FLAG_RECV | IRC_RAW_FLAG_MODIFIED,
ptr_msg);
}
irc_message_parse (irc_recv_msgq->server, ptr_msg,
NULL, NULL, &nick, NULL, &host,
&command, &channel, &arguments,
NULL, NULL, NULL,
&pos_channel, &pos_text);
msg_decoded = NULL;
switch (IRC_SERVER_OPTION_INTEGER(irc_recv_msgq->server,
IRC_SERVER_OPTION_CHARSET_MESSAGE))
{
case IRC_SERVER_CHARSET_MESSAGE_MESSAGE:
pos_decode = 0;
break;
case IRC_SERVER_CHARSET_MESSAGE_CHANNEL:
pos_decode = (pos_channel >= 0) ? pos_channel : pos_text;
break;
case IRC_SERVER_CHARSET_MESSAGE_TEXT:
pos_decode = pos_text;
break;
default:
pos_decode = 0;
break;
}
if (pos_decode >= 0)
{
/* convert charset for message */
if (channel
&& irc_channel_is_channel (irc_recv_msgq->server,
channel))
{
snprintf (modifier_data, sizeof (modifier_data),
"%s.%s.%s",
weechat_plugin->name,
irc_recv_msgq->server->name,
channel);
}
else
{
if (nick && (!host || (strcmp (nick, host) != 0)))
{
snprintf (modifier_data,
sizeof (modifier_data),
"%s.%s.%s",
weechat_plugin->name,
irc_recv_msgq->server->name,
nick);
}
else
{
snprintf (modifier_data,
sizeof (modifier_data),
"%s.%s",
weechat_plugin->name,
irc_recv_msgq->server->name);
}
}
msg_decoded = irc_message_convert_charset (
ptr_msg, pos_decode,
"charset_decode", modifier_data);
}
/* replace WeeChat internal color codes by "?" */
msg_decoded_without_color =
weechat_string_remove_color (
(msg_decoded) ? msg_decoded : ptr_msg,
"?");
/* call modifier after charset */
ptr_msg2 = (msg_decoded_without_color) ?
msg_decoded_without_color : ((msg_decoded) ? msg_decoded : ptr_msg);
snprintf (str_modifier, sizeof (str_modifier),
"irc_in2_%s",
(command) ? command : "unknown");
new_msg2 = weechat_hook_modifier_exec (
str_modifier,
irc_recv_msgq->server->name,
ptr_msg2);
if (new_msg2 && (strcmp (ptr_msg2, new_msg2) == 0))
{
free (new_msg2);
new_msg2 = NULL;
}
/* message not dropped? */
if (!new_msg2 || new_msg2[0])
{
/* use new message (returned by plugin) */
if (new_msg2)
ptr_msg2 = new_msg2;
/* parse and execute command */
if (irc_redirect_message (irc_recv_msgq->server,
ptr_msg2, command,
arguments))
{
/* message redirected, we'll not display it! */
}
else
{
/* message not redirected, display it */
irc_protocol_recv_command (
irc_recv_msgq->server,
ptr_msg2,
command,
channel);
}
}
if (new_msg2)
free (new_msg2);
if (nick)
free (nick);
if (host)
free (host);
if (command)
free (command);
if (channel)
free (channel);
if (arguments)
free (arguments);
if (msg_decoded)
free (msg_decoded);
if (msg_decoded_without_color)
free (msg_decoded_without_color);
if (pos)
{
pos[0] = '\n';
ptr_msg = pos + 1;
}
else
ptr_msg = NULL;
}
}
else
{
irc_raw_print (irc_recv_msgq->server,
IRC_RAW_FLAG_RECV | IRC_RAW_FLAG_MODIFIED,
_("(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)
irc_msgq_last_msg = NULL;
}
}
/*
* Receives data from a server.
*/
int
irc_server_recv_cb (const void *pointer, void *data, int fd)
{
struct t_irc_server *server;
static char buffer[4096 + 2];
int num_read, msgq_flush, end_recv;
/* make C compiler happy */
(void) data;
(void) fd;
server = (struct t_irc_server *)pointer;
if (!server || server->fake_server)
return WEECHAT_RC_ERROR;
msgq_flush = 0;
end_recv = 0;
while (!end_recv)
{
end_recv = 1;
#ifdef HAVE_GNUTLS
if (server->ssl_connected)
num_read = gnutls_record_recv (server->gnutls_sess, buffer,
sizeof (buffer) - 2);
else
#endif /* HAVE_GNUTLS */
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);
msgq_flush = 1; /* the flush will be done after the loop */
#ifdef HAVE_GNUTLS
if (server->ssl_connected
&& (gnutls_record_check_pending (server->gnutls_sess) > 0))
{
/*
* if there are unread data in the gnutls buffers,
* go on with recv
*/
end_recv = 0;
}
#endif /* HAVE_GNUTLS */
}
else
{
#ifdef HAVE_GNUTLS
if (server->ssl_connected)
{
if ((num_read == 0)
|| ((num_read != GNUTLS_E_AGAIN)
&& (num_read != GNUTLS_E_INTERRUPTED)))
{
weechat_printf (
server->buffer,
_("%s%s: reading data on socket: error %d %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
num_read,
(num_read == 0) ? _("(connection closed by peer)") :
gnutls_strerror (num_read));
weechat_printf (
server->buffer,
_("%s%s: disconnecting from server..."),
weechat_prefix ("network"), IRC_PLUGIN_NAME);
irc_server_disconnect (server, !server->is_connected, 1);
}
}
else
#endif /* HAVE_GNUTLS */
{
if ((num_read == 0)
|| ((errno != EAGAIN) && (errno != EWOULDBLOCK)))
{
weechat_printf (
server->buffer,
_("%s%s: reading data on socket: error %d %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
errno,
(num_read == 0) ? _("(connection closed by peer)") :
strerror (errno));
weechat_printf (
server->buffer,
_("%s%s: disconnecting from server..."),
weechat_prefix ("network"), IRC_PLUGIN_NAME);
irc_server_disconnect (server, !server->is_connected, 1);
}
}
}
}
if (msgq_flush)
irc_server_msgq_flush ();
return WEECHAT_RC_OK;
}
/*
* Callback for server connection: it is called if WeeChat is TCP-connected to
* server, but did not receive message 001.
*/
int
irc_server_timer_connection_cb (const void *pointer, void *data,
int remaining_calls)
{
struct t_irc_server *server;
/* make C compiler happy */
(void) data;
(void) remaining_calls;
server = (struct t_irc_server *)pointer;
if (!server)
return WEECHAT_RC_ERROR;
server->hook_timer_connection = NULL;
if (!server->is_connected)
{
weechat_printf (
server->buffer,
_("%s%s: connection timeout (message 001 not received)"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
irc_server_disconnect (server, !server->is_connected, 1);
}
return WEECHAT_RC_OK;
}
/*
* Callback for SASL authentication timer: it is called if there is a timeout
* with SASL authentication (if SASL authentication is OK or failed, then hook
* timer is removed before this callback is called).
*/
int
irc_server_timer_sasl_cb (const void *pointer, void *data, int remaining_calls)
{
struct t_irc_server *server;
int sasl_fail;
/* make C compiler happy */
(void) data;
(void) remaining_calls;
server = (struct t_irc_server *)pointer;
if (!server)
return WEECHAT_RC_ERROR;
server->hook_timer_sasl = NULL;
if (!server->is_connected)
{
weechat_printf (server->buffer,
_("%s%s: SASL authentication timeout"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
sasl_fail = IRC_SERVER_OPTION_INTEGER(server,
IRC_SERVER_OPTION_SASL_FAIL);
if ((sasl_fail == IRC_SERVER_SASL_FAIL_RECONNECT)
|| (sasl_fail == IRC_SERVER_SASL_FAIL_DISCONNECT))
{
irc_server_disconnect (
server, 0,
(sasl_fail == IRC_SERVER_SASL_FAIL_RECONNECT) ? 1 : 0);
}
else
irc_server_sendf (server, 0, NULL, "CAP END");
}
return WEECHAT_RC_OK;
}
/*
* Callback called for each manual join of a server: deletes old channels in the
* hashtable.
*/
void
irc_server_check_join_manual_cb (void *data,
struct t_hashtable *hashtable,
const void *key, const void *value)
{
/* make C compiler happy */
(void) data;
if (*((time_t *)value) + (60 * 10) < time (NULL))
weechat_hashtable_remove (hashtable, key);
}
/*
* Callback called for each join without switch of a server: deletes old channel
* in the hashtable.
*/
void
irc_server_check_join_noswitch_cb (void *data,
struct t_hashtable *hashtable,
const void *key, const void *value)
{
/* make C compiler happy */
(void) data;
if (*((time_t *)value) + (60 * 10) < time (NULL))
weechat_hashtable_remove (hashtable, key);
}
/*
* Callback called for each smart filtered join of a channel: deletes old
* entries in the hashtable.
*/
void
irc_server_check_join_smart_filtered_cb (void *data,
struct t_hashtable *hashtable,
const void *key, const void *value)
{
int unmask_delay;
/* make C compiler happy */
(void) data;
unmask_delay = weechat_config_integer (irc_config_look_smart_filter_join_unmask);
if ((unmask_delay == 0)
|| (*((time_t *)value) < time (NULL) - (unmask_delay * 60)))
{
weechat_hashtable_remove (hashtable, key);
}
}
/*
* Timer called each second to perform some operations on servers.
*/
int
irc_server_timer_cb (const void *pointer, void *data, int remaining_calls)
{
struct t_irc_server *ptr_server;
struct t_irc_channel *ptr_channel;
struct t_irc_redirect *ptr_redirect, *ptr_next_redirect;
time_t current_time;
static struct timeval tv;
int away_check, refresh_lag;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) remaining_calls;
current_time = time (NULL);
for (ptr_server = irc_servers; ptr_server;
ptr_server = ptr_server->next_server)
{
/* check if reconnection is pending */
if ((!ptr_server->is_connected)
&& (ptr_server->reconnect_start > 0)
&& (current_time >= (ptr_server->reconnect_start + ptr_server->reconnect_delay)))
{
irc_server_reconnect (ptr_server);
}
else
{
if (!ptr_server->is_connected)
continue;
/* send queued messages */
irc_server_outqueue_send (ptr_server);
/* check for lag */
if ((weechat_config_integer (irc_config_network_lag_check) > 0)
&& (ptr_server->lag_check_time.tv_sec == 0)
&& (current_time >= ptr_server->lag_next_check))
{
irc_server_sendf (ptr_server, 0, NULL, "PING %s",
(ptr_server->current_address) ?
ptr_server->current_address : "weechat");
gettimeofday (&(ptr_server->lag_check_time), NULL);
ptr_server->lag = 0;
ptr_server->lag_last_refresh = 0;
}
else
{
/* check away (only if lag check was not done) */
away_check = IRC_SERVER_OPTION_INTEGER(
ptr_server, IRC_SERVER_OPTION_AWAY_CHECK);
if (!weechat_hashtable_has_key (ptr_server->cap_list,
"away-notify")
&& (away_check > 0)
&& ((ptr_server->last_away_check == 0)
|| (current_time >= ptr_server->last_away_check + (away_check * 60))))
{
irc_server_check_away (ptr_server);
}
}
/* check if it's time to autojoin channels (after command delay) */
if ((ptr_server->command_time != 0)
&& (current_time >= ptr_server->command_time +
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_COMMAND_DELAY)))
{
irc_server_autojoin_channels (ptr_server);
ptr_server->command_time = 0;
}
/* check if it's time to send MONITOR command */
if ((ptr_server->monitor_time != 0)
&& (current_time >= ptr_server->monitor_time))
{
if (ptr_server->monitor > 0)
irc_notify_send_monitor (ptr_server);
ptr_server->monitor_time = 0;
}
/* compute lag */
if (ptr_server->lag_check_time.tv_sec != 0)
{
refresh_lag = 0;
gettimeofday (&tv, NULL);
ptr_server->lag = (int)(weechat_util_timeval_diff (&(ptr_server->lag_check_time),
&tv) / 1000);
/* refresh lag item if needed */
if (((ptr_server->lag_last_refresh == 0)
|| (current_time >= ptr_server->lag_last_refresh + weechat_config_integer (irc_config_network_lag_refresh_interval)))
&& (ptr_server->lag >= weechat_config_integer (irc_config_network_lag_min_show)))
{
ptr_server->lag_last_refresh = current_time;
if (ptr_server->lag != ptr_server->lag_displayed)
{
ptr_server->lag_displayed = ptr_server->lag;
refresh_lag = 1;
}
}
/* lag timeout? => disconnect */
if ((weechat_config_integer (irc_config_network_lag_reconnect) > 0)
&& (ptr_server->lag >= weechat_config_integer (irc_config_network_lag_reconnect) * 1000))
{
weechat_printf (
ptr_server->buffer,
_("%s%s: lag is high, reconnecting to server %s%s%s"),
weechat_prefix ("network"),
IRC_PLUGIN_NAME,
IRC_COLOR_CHAT_SERVER,
ptr_server->name,
IRC_COLOR_RESET);
irc_server_disconnect (ptr_server, 0, 1);
}
else
{
/* stop lag counting if max lag is reached */
if ((weechat_config_integer (irc_config_network_lag_max) > 0)
&& (ptr_server->lag >= (weechat_config_integer (irc_config_network_lag_max) * 1000)))
{
/* refresh lag item */
ptr_server->lag_last_refresh = current_time;
if (ptr_server->lag != ptr_server->lag_displayed)
{
ptr_server->lag_displayed = ptr_server->lag;
refresh_lag = 1;
}
/* schedule next lag check in 5 seconds */
ptr_server->lag_check_time.tv_sec = 0;
ptr_server->lag_check_time.tv_usec = 0;
ptr_server->lag_next_check = time (NULL) +
weechat_config_integer (irc_config_network_lag_check);
}
}
if (refresh_lag)
irc_server_set_lag (ptr_server);
}
/* remove redirects if timeout occurs */
ptr_redirect = ptr_server->redirects;
while (ptr_redirect)
{
ptr_next_redirect = ptr_redirect->next_redirect;
if ((ptr_redirect->start_time > 0)
&& (ptr_redirect->start_time + ptr_redirect->timeout < current_time))
{
irc_redirect_stop (ptr_redirect, "timeout");
}
ptr_redirect = ptr_next_redirect;
}
/* purge some data (every 10 minutes) */
if (current_time > ptr_server->last_data_purge + (60 * 10))
{
weechat_hashtable_map (ptr_server->join_manual,
&irc_server_check_join_manual_cb,
NULL);
weechat_hashtable_map (ptr_server->join_noswitch,
&irc_server_check_join_noswitch_cb,
NULL);
for (ptr_channel = ptr_server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
if (ptr_channel->join_smart_filtered)
{
weechat_hashtable_map (ptr_channel->join_smart_filtered,
&irc_server_check_join_smart_filtered_cb,
NULL);
}
}
ptr_server->last_data_purge = current_time;
}
}
}
return WEECHAT_RC_OK;
}
/*
* Closes server connection.
*/
void
irc_server_close_connection (struct t_irc_server *server)
{
int i;
if (server->hook_timer_connection)
{
weechat_unhook (server->hook_timer_connection);
server->hook_timer_connection = NULL;
}
if (server->hook_timer_sasl)
{
weechat_unhook (server->hook_timer_sasl);
server->hook_timer_sasl = NULL;
}
if (server->hook_fd)
{
weechat_unhook (server->hook_fd);
server->hook_fd = NULL;
}
if (server->hook_connect)
{
weechat_unhook (server->hook_connect);
server->hook_connect = NULL;
}
else
{
#ifdef HAVE_GNUTLS
/* close SSL connection */
if (server->ssl_connected)
{
if (server->sock != -1)
gnutls_bye (server->gnutls_sess, GNUTLS_SHUT_WR);
gnutls_deinit (server->gnutls_sess);
}
#endif /* HAVE_GNUTLS */
}
if (server->sock != -1)
{
#ifdef _WIN32
closesocket (server->sock);
#else
close (server->sock);
#endif /* _WIN32 */
server->sock = -1;
}
/* free any pending message */
if (server->unterminated_message)
{
free (server->unterminated_message);
server->unterminated_message = NULL;
}
for (i = 0; i < IRC_SERVER_NUM_OUTQUEUES_PRIO; i++)
{
irc_server_outqueue_free_all (server, i);
}
/* remove all redirects */
irc_redirect_free_all (server);
/* remove all manual joins */
weechat_hashtable_remove_all (server->join_manual);
/* remove all keys for pending joins */
weechat_hashtable_remove_all (server->join_channel_key);
/* remove all keys for joins without switch */
weechat_hashtable_remove_all (server->join_noswitch);
/* server is now disconnected */
server->is_connected = 0;
server->ssl_connected = 0;
}
/*
* Schedules reconnection on server.
*/
void
irc_server_reconnect_schedule (struct t_irc_server *server)
{
int minutes, seconds;
if (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_AUTORECONNECT))
{
/* growing reconnect delay */
if (server->reconnect_delay == 0)
server->reconnect_delay = IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_AUTORECONNECT_DELAY);
else
server->reconnect_delay = server->reconnect_delay * weechat_config_integer (irc_config_network_autoreconnect_delay_growing);
if ((weechat_config_integer (irc_config_network_autoreconnect_delay_max) > 0)
&& (server->reconnect_delay > weechat_config_integer (irc_config_network_autoreconnect_delay_max)))
server->reconnect_delay = weechat_config_integer (irc_config_network_autoreconnect_delay_max);
server->reconnect_start = time (NULL);
minutes = server->reconnect_delay / 60;
seconds = server->reconnect_delay % 60;
if ((minutes > 0) && (seconds > 0))
{
weechat_printf (
server->buffer,
_("%s%s: reconnecting to server in %d %s, %d %s"),
weechat_prefix ("network"),
IRC_PLUGIN_NAME,
minutes,
NG_("minute", "minutes", minutes),
seconds,
NG_("second", "seconds", seconds));
}
else if (minutes > 0)
{
weechat_printf (
server->buffer,
_("%s%s: reconnecting to server in %d %s"),
weechat_prefix ("network"),
IRC_PLUGIN_NAME,
minutes,
NG_("minute", "minutes", minutes));
}
else
{
weechat_printf (
server->buffer,
_("%s%s: reconnecting to server in %d %s"),
weechat_prefix ("network"),
IRC_PLUGIN_NAME,
seconds,
NG_("second", "seconds", seconds));
}
}
else
{
server->reconnect_delay = 0;
server->reconnect_start = 0;
}
}
/*
* Logins to server.
*/
void
irc_server_login (struct t_irc_server *server)
{
const char *capabilities;
char *password, *username, *realname, *username2;
password = irc_server_eval_expression (
server,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PASSWORD));
username = irc_server_eval_expression (
server,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_USERNAME));
realname = irc_server_eval_expression (
server,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_REALNAME));
capabilities = IRC_SERVER_OPTION_STRING(
server, IRC_SERVER_OPTION_CAPABILITIES);
if (password && password[0])
{
irc_server_sendf (
server, 0, NULL,
"PASS %s%s",
((password[0] == ':') || (strchr (password, ' '))) ? ":" : "",
password);
}
if (!server->nick)
{
irc_server_set_nick (server,
(server->nicks_array) ?
server->nicks_array[0] : "weechat");
server->nick_first_tried = 0;
}
else
server->nick_first_tried = irc_server_get_nick_index (server);
server->nick_alternate_number = -1;
if (irc_server_sasl_enabled (server) || (capabilities && capabilities[0]))
{
irc_server_sendf (server, 0, NULL, "CAP LS " IRC_SERVER_VERSION_CAP);
}
username2 = (username && username[0]) ?
weechat_string_replace (username, " ", "_") : strdup ("weechat");
irc_server_sendf (
server, 0, NULL,
"NICK %s%s\n"
"USER %s 0 * :%s",
(server->nick && strchr (server->nick, ':')) ? ":" : "",
server->nick,
(username2) ? username2 : "weechat",
(realname && realname[0]) ? realname : ((username2) ? username2 : "weechat"));
if (username2)
free (username2);
if (server->hook_timer_connection)
weechat_unhook (server->hook_timer_connection);
server->hook_timer_connection = weechat_hook_timer (
IRC_SERVER_OPTION_INTEGER (server, IRC_SERVER_OPTION_CONNECTION_TIMEOUT) * 1000,
0, 1,
&irc_server_timer_connection_cb,
server, NULL);
if (password)
free (password);
if (username)
free (username);
if (realname)
free (realname);
}
/*
* Switches address and tries another (called if connection failed with an
* address/port).
*/
void
irc_server_switch_address (struct t_irc_server *server, int connection)
{
if (server->addresses_count > 1)
{
irc_server_set_index_current_address (
server,
(server->index_current_address + 1) % server->addresses_count);
weechat_printf (
server->buffer,
_("%s%s: switching address to %s/%d"),
weechat_prefix ("network"),
IRC_PLUGIN_NAME,
server->current_address,
server->current_port);
if (connection)
{
if (server->index_current_address == 0)
irc_server_reconnect_schedule (server);
else
irc_server_connect (server);
}
}
else
{
if (connection)
irc_server_reconnect_schedule (server);
}
}
/*
* Reads connection status.
*/
int
irc_server_connect_cb (const void *pointer, void *data,
int status, int gnutls_rc, int sock,
const char *error, const char *ip_address)
{
struct t_irc_server *server;
const char *proxy;
/* make C compiler happy */
(void) data;
server = (struct t_irc_server *)pointer;
proxy = IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PROXY);
server->hook_connect = NULL;
server->sock = sock;
switch (status)
{
case WEECHAT_HOOK_CONNECT_OK:
/* set IP */
if (server->current_ip)
free (server->current_ip);
server->current_ip = (ip_address) ? strdup (ip_address) : NULL;
weechat_printf (
server->buffer,
_("%s%s: connected to %s/%d (%s)"),
weechat_prefix ("network"),
IRC_PLUGIN_NAME,
server->current_address,
server->current_port,
(server->current_ip) ? server->current_ip : "?");
if (!server->fake_server)
{
server->hook_fd = weechat_hook_fd (server->sock,
1, 0, 0,
&irc_server_recv_cb,
server, NULL);
}
/* login to server */
irc_server_login (server);
break;
case WEECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND:
weechat_printf (
server->buffer,
(proxy && proxy[0]) ?
_("%s%s: proxy address \"%s\" not found") :
_("%s%s: address \"%s\" not found"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
server->current_address);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
irc_server_switch_address (server, 1);
break;
case WEECHAT_HOOK_CONNECT_IP_ADDRESS_NOT_FOUND:
weechat_printf (
server->buffer,
(proxy && proxy[0]) ?
_("%s%s: proxy IP address not found") :
_("%s%s: IP address not found"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
irc_server_switch_address (server, 1);
break;
case WEECHAT_HOOK_CONNECT_CONNECTION_REFUSED:
weechat_printf (
server->buffer,
(proxy && proxy[0]) ?
_("%s%s: proxy connection refused") :
_("%s%s: connection refused"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
server->current_retry++;
irc_server_switch_address (server, 1);
break;
case WEECHAT_HOOK_CONNECT_PROXY_ERROR:
weechat_printf (
server->buffer,
_("%s%s: proxy fails to establish connection to server (check "
"username/password if used and if server address/port is "
"allowed by proxy)"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
irc_server_switch_address (server, 1);
break;
case WEECHAT_HOOK_CONNECT_LOCAL_HOSTNAME_ERROR:
weechat_printf (
server->buffer,
_("%s%s: unable to set local hostname/IP"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
irc_server_reconnect_schedule (server);
break;
case WEECHAT_HOOK_CONNECT_GNUTLS_INIT_ERROR:
weechat_printf (
server->buffer,
_("%s%s: TLS init error"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
server->current_retry++;
irc_server_reconnect_schedule (server);
break;
case WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR:
weechat_printf (
server->buffer,
_("%s%s: TLS handshake failed"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
#ifdef HAVE_GNUTLS
if (gnutls_rc == GNUTLS_E_DH_PRIME_UNACCEPTABLE)
{
weechat_printf (
server->buffer,
_("%s%s: you should play with option "
"irc.server.%s.ssl_dhkey_size (current value is %d, try "
"a lower value like %d or %d)"),
weechat_prefix ("error"),
IRC_PLUGIN_NAME,
server->name,
IRC_SERVER_OPTION_INTEGER (
server, IRC_SERVER_OPTION_SSL_DHKEY_SIZE),
IRC_SERVER_OPTION_INTEGER (
server, IRC_SERVER_OPTION_SSL_DHKEY_SIZE) / 2,
IRC_SERVER_OPTION_INTEGER (
server, IRC_SERVER_OPTION_SSL_DHKEY_SIZE) / 4);
}
#else
(void) gnutls_rc;
#endif /* HAVE_GNUTLS */
irc_server_close_connection (server);
server->current_retry++;
irc_server_switch_address (server, 1);
break;
case WEECHAT_HOOK_CONNECT_MEMORY_ERROR:
weechat_printf (
server->buffer,
_("%s%s: not enough memory (%s)"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
(error) ? error : "-");
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
irc_server_reconnect_schedule (server);
break;
case WEECHAT_HOOK_CONNECT_TIMEOUT:
weechat_printf (
server->buffer,
_("%s%s: timeout"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
server->current_retry++;
irc_server_switch_address (server, 1);
break;
case WEECHAT_HOOK_CONNECT_SOCKET_ERROR:
weechat_printf (
server->buffer,
_("%s%s: unable to create socket"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
if (error && error[0])
{
weechat_printf (
server->buffer,
_("%s%s: error: %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, error);
}
irc_server_close_connection (server);
server->current_retry++;
irc_server_reconnect_schedule (server);
break;
}
return WEECHAT_RC_OK;
}
/*
* Sets the title for a server buffer.
*/
void
irc_server_set_buffer_title (struct t_irc_server *server)
{
char *title;
int length;
if (server && server->buffer)
{
if (server->is_connected)
{
length = 16 +
((server->current_address) ? strlen (server->current_address) : 16) +
16 + ((server->current_ip) ? strlen (server->current_ip) : 16) + 1;
title = malloc (length);
if (title)
{
snprintf (title, length, "IRC: %s/%d (%s)",
server->current_address,
server->current_port,
(server->current_ip) ? server->current_ip : "");
weechat_buffer_set (server->buffer, "title", title);
free (title);
}
}
else
{
weechat_buffer_set (server->buffer, "title", "");
}
}
}
/*
* Creates a buffer for a server.
*
* Returns pointer to buffer, NULL if error.
*/
struct t_gui_buffer *
irc_server_create_buffer (struct t_irc_server *server)
{
char buffer_name[256], charset_modifier[256];
struct t_gui_buffer *ptr_buffer_for_merge;
ptr_buffer_for_merge = NULL;
switch (weechat_config_integer (irc_config_look_server_buffer))
{
case IRC_CONFIG_LOOK_SERVER_BUFFER_MERGE_WITH_CORE:
/* merge with WeeChat core buffer */
ptr_buffer_for_merge = weechat_buffer_search_main ();
break;
case IRC_CONFIG_LOOK_SERVER_BUFFER_MERGE_WITHOUT_CORE:
/* find buffer used to merge all IRC server buffers */
ptr_buffer_for_merge = irc_buffer_search_server_lowest_number ();
break;
}
snprintf (buffer_name, sizeof (buffer_name),
"server.%s", server->name);
server->buffer = weechat_buffer_new (buffer_name,
&irc_input_data_cb, NULL, NULL,
&irc_buffer_close_cb, NULL, NULL);
if (!server->buffer)
return NULL;
if (!weechat_buffer_get_integer (server->buffer, "short_name_is_set"))
weechat_buffer_set (server->buffer, "short_name", server->name);
weechat_buffer_set (server->buffer, "localvar_set_type", "server");
weechat_buffer_set (server->buffer, "localvar_set_server", server->name);
weechat_buffer_set (server->buffer, "localvar_set_channel", server->name);
snprintf (charset_modifier, sizeof (charset_modifier),
"irc.%s", server->name);
weechat_buffer_set (server->buffer, "localvar_set_charset_modifier",
charset_modifier);
(void) weechat_hook_signal_send ("logger_backlog",
WEECHAT_HOOK_SIGNAL_POINTER,
server->buffer);
if (weechat_config_boolean (irc_config_network_send_unknown_commands))
weechat_buffer_set (server->buffer, "input_get_unknown_commands", "1");
/* set highlights settings on server buffer */
weechat_buffer_set (server->buffer, "highlight_words_add",
weechat_config_string (irc_config_look_highlight_server));
if (weechat_config_string (irc_config_look_highlight_tags_restrict)
&& weechat_config_string (irc_config_look_highlight_tags_restrict)[0])
{
weechat_buffer_set (
server->buffer, "highlight_tags_restrict",
weechat_config_string (irc_config_look_highlight_tags_restrict));
}
irc_server_set_buffer_title (server);
/*
* merge buffer if needed: if merge with(out) core set, and if no layout
* number is assigned for this buffer (if layout number is assigned, then
* buffer was already moved/merged by WeeChat core)
*/
if (ptr_buffer_for_merge
&& (weechat_buffer_get_integer (server->buffer, "layout_number") < 1))
{
weechat_buffer_merge (server->buffer, ptr_buffer_for_merge);
}
(void) weechat_hook_signal_send ("irc_server_opened",
WEECHAT_HOOK_SIGNAL_POINTER,
server->buffer);
return server->buffer;
}
/*
* Searches for a fingerprint digest algorithm with the size (in bits).
*
* Returns index of algo in enum t_irc_fingerprint_digest_algo,
* -1 if not found.
*/
#ifdef HAVE_GNUTLS
int
irc_server_fingerprint_search_algo_with_size (int size)
{
int i;
for (i = 0; i < IRC_FINGERPRINT_NUM_ALGOS; i++)
{
if (irc_fingerprint_digest_algos_size[i] == size)
return i;
}
/* digest algorithm not found */
return -1;
}
#endif /* HAVE_GNUTLS */
/*
* Returns a string with sizes of allowed fingerprint,
* in number of hexadecimal digits (== bits / 4).
*
* Example of output: "128=SHA-512, 64=SHA-256, 40=SHA-1".
*
* Note: result must be freed after use.
*/
#ifdef HAVE_GNUTLS
char *
irc_server_fingerprint_str_sizes ()
{
char str_sizes[1024], str_one_size[128];
int i;
str_sizes[0] = '\0';
for (i = IRC_FINGERPRINT_NUM_ALGOS - 1; i >= 0; i--)
{
snprintf (str_one_size, sizeof (str_one_size),
"%d=%s%s",
irc_fingerprint_digest_algos_size[i] / 4,
irc_fingerprint_digest_algos_name[i],
(i > 0) ? ", " : "");
strcat (str_sizes, str_one_size);
}
return strdup (str_sizes);
}
#endif /* HAVE_GNUTLS */
/*
* Compares two fingerprints: one hexadecimal (given by user), the second binary
* (received from IRC server).
*
* Returns:
* 0: fingerprints are the same
* -1: fingerprints are different
*/
#ifdef HAVE_GNUTLS
int
irc_server_compare_fingerprints (const char *fingerprint,
const unsigned char *fingerprint_server,
ssize_t fingerprint_size)
{
ssize_t i;
unsigned int value;
if ((ssize_t)strlen (fingerprint) != fingerprint_size * 2)
return -1;
for (i = 0; i < fingerprint_size; i++)
{
if (sscanf (&fingerprint[i * 2], "%02x", &value) != 1)
return -1;
if (value != fingerprint_server[i])
return -1;
}
/* fingerprints are the same */
return 0;
}
#endif /* HAVE_GNUTLS */
/*
* Checks if a GnuTLS session uses the certificate with a given fingerprint.
*
* Returns:
* 1: certificate has the good fingerprint
* 0: certificate does NOT have the good fingerprint
*/
#ifdef HAVE_GNUTLS
int
irc_server_check_certificate_fingerprint (struct t_irc_server *server,
gnutls_x509_crt_t certificate,
const char *good_fingerprints)
{
unsigned char *fingerprint_server[IRC_FINGERPRINT_NUM_ALGOS];
char **fingerprints;
int i, rc, algo;
size_t size_bits, size_bytes;
for (i = 0; i < IRC_FINGERPRINT_NUM_ALGOS; i++)
{
fingerprint_server[i] = NULL;
}
/* split good_fingerprints */
fingerprints = weechat_string_split (good_fingerprints, ",", NULL,
WEECHAT_STRING_SPLIT_STRIP_LEFT
| WEECHAT_STRING_SPLIT_STRIP_RIGHT
| WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
0, NULL);
if (!fingerprints)
return 0;
rc = 0;
for (i = 0; fingerprints[i]; i++)
{
size_bits = strlen (fingerprints[i]) * 4;
size_bytes = size_bits / 8;
algo = irc_server_fingerprint_search_algo_with_size (size_bits);
if (algo < 0)
continue;
if (!fingerprint_server[algo])
{
fingerprint_server[algo] = malloc (size_bytes);
if (fingerprint_server[algo])
{
/* calculate the fingerprint for the certificate */
if (gnutls_x509_crt_get_fingerprint (
certificate,
irc_fingerprint_digest_algos[algo],
fingerprint_server[algo],
&size_bytes) != GNUTLS_E_SUCCESS)
{
weechat_printf (
server->buffer,
_("%sgnutls: failed to calculate certificate "
"fingerprint (%s)"),
weechat_prefix ("error"),
irc_fingerprint_digest_algos_name[algo]);
free (fingerprint_server[algo]);
fingerprint_server[algo] = NULL;
}
}
else
{
weechat_printf (
server->buffer,
_("%s%s: not enough memory (%s)"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
"fingerprint");
}
}
if (fingerprint_server[algo])
{
/* check if the fingerprint matches */
if (irc_server_compare_fingerprints (fingerprints[i],
fingerprint_server[algo],
size_bytes) == 0)
{
rc = 1;
break;
}
}
}
weechat_string_free_split (fingerprints);
for (i = 0; i < IRC_FINGERPRINT_NUM_ALGOS; i++)
{
if (fingerprint_server[i])
free (fingerprint_server[i]);
}
return rc;
}
#endif /* HAVE_GNUTLS */
/*
* GnuTLS callback called during handshake.
*
* Returns:
* 0: certificate OK
* -1: error in certificate
*/
#ifdef HAVE_GNUTLS
int
irc_server_gnutls_callback (const void *pointer, void *data,
gnutls_session_t tls_session,
const gnutls_datum_t *req_ca, int nreq,
const gnutls_pk_algorithm_t *pk_algos,
int pk_algos_len,
#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */
gnutls_retr2_st *answer,
#else
gnutls_retr_st *answer,
#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */
int action)
{
struct t_irc_server *server;
#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */
gnutls_retr2_st tls_struct;
#else
gnutls_retr_st tls_struct;
#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */
gnutls_x509_crt_t cert_temp;
const gnutls_datum_t *cert_list;
gnutls_datum_t filedatum;
unsigned int i, cert_list_len, status;
time_t cert_time;
char *cert_path0, *cert_path1, *cert_path2, *cert_str, *fingerprint_eval;
char *weechat_dir, *ssl_password;
const char *ptr_fingerprint;
int rc, ret, fingerprint_match, hostname_match, cert_temp_init;
#if LIBGNUTLS_VERSION_NUMBER >= 0x010706 /* 1.7.6 */
gnutls_datum_t cinfo;
int rinfo;
#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010706 */
/* make C compiler happy */
(void) data;
(void) req_ca;
(void) nreq;
(void) pk_algos;
(void) pk_algos_len;
rc = 0;
if (!pointer)
return -1;
server = (struct t_irc_server *) pointer;
cert_temp_init = 0;
cert_list = NULL;
cert_list_len = 0;
fingerprint_eval = NULL;
weechat_dir = NULL;
if (action == WEECHAT_HOOK_CONNECT_GNUTLS_CB_VERIFY_CERT)
{
weechat_printf (
server->buffer,
_("%sgnutls: connected using %d-bit Diffie-Hellman shared secret "
"exchange"),
weechat_prefix ("network"),
IRC_SERVER_OPTION_INTEGER (server,
IRC_SERVER_OPTION_SSL_DHKEY_SIZE));
/* initialize the certificate structure */
if (gnutls_x509_crt_init (&cert_temp) != GNUTLS_E_SUCCESS)
{
weechat_printf (
server->buffer,
_("%sgnutls: failed to initialize certificate structure"),
weechat_prefix ("error"));
rc = -1;
goto end;
}
/* flag to do the "deinit" (at the end of function) */
cert_temp_init = 1;
/* get fingerprint option in server */
ptr_fingerprint = IRC_SERVER_OPTION_STRING(server,
IRC_SERVER_OPTION_SSL_FINGERPRINT);
fingerprint_eval = irc_server_eval_fingerprint (server);
if (!fingerprint_eval)
{
rc = -1;
goto end;
}
/* set match options */
fingerprint_match = (ptr_fingerprint && ptr_fingerprint[0]) ? 0 : 1;
hostname_match = 0;
/* get the peer's raw certificate (chain) as sent by the peer */
cert_list = gnutls_certificate_get_peers (tls_session, &cert_list_len);
if (cert_list)
{
weechat_printf (
server->buffer,
NG_("%sgnutls: receiving %d certificate",
"%sgnutls: receiving %d certificates",
cert_list_len),
weechat_prefix ("network"),
cert_list_len);
for (i = 0; i < cert_list_len; i++)
{
if (gnutls_x509_crt_import (cert_temp,
&cert_list[i],
GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS)
{
weechat_printf (
server->buffer,
_("%sgnutls: failed to import certificate[%d]"),
weechat_prefix ("error"), i + 1);
rc = -1;
goto end;
}
/* checks on first certificate received */
if (i == 0)
{
/* check if fingerprint matches the first certificate */
if (fingerprint_eval && fingerprint_eval[0])
{
fingerprint_match = irc_server_check_certificate_fingerprint (
server, cert_temp, fingerprint_eval);
}
/* check if hostname matches in the first certificate */
if (gnutls_x509_crt_check_hostname (cert_temp,
server->current_address) != 0)
{
hostname_match = 1;
}
}
#if LIBGNUTLS_VERSION_NUMBER >= 0x010706 /* 1.7.6 */
/* display infos about certificate */
#if LIBGNUTLS_VERSION_NUMBER < 0x020400 /* 2.4.0 */
rinfo = gnutls_x509_crt_print (cert_temp,
GNUTLS_X509_CRT_ONELINE, &cinfo);
#else
rinfo = gnutls_x509_crt_print (cert_temp,
GNUTLS_CRT_PRINT_ONELINE, &cinfo);
#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020400 */
if (rinfo == 0)
{
weechat_printf (
server->buffer,
_("%s - certificate[%d] info:"),
weechat_prefix ("network"), i + 1);
weechat_printf (
server->buffer,
"%s - %s",
weechat_prefix ("network"), cinfo.data);
gnutls_free (cinfo.data);
}
#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010706 */
/* check dates, only if fingerprint is not set */
if (!ptr_fingerprint || !ptr_fingerprint[0])
{
/* check expiration date */
cert_time = gnutls_x509_crt_get_expiration_time (cert_temp);
if (cert_time < time (NULL))
{
weechat_printf (
server->buffer,
_("%sgnutls: certificate has expired"),
weechat_prefix ("error"));
rc = -1;
}
/* check activation date */
cert_time = gnutls_x509_crt_get_activation_time (cert_temp);
if (cert_time > time (NULL))
{
weechat_printf (
server->buffer,
_("%sgnutls: certificate is not yet activated"),
weechat_prefix ("error"));
rc = -1;
}
}
}
/*
* if fingerprint is set, display if matches, and don't check
* anything else
*/
if (ptr_fingerprint && ptr_fingerprint[0])
{
if (fingerprint_match)
{
weechat_printf (
server->buffer,
_("%sgnutls: certificate fingerprint matches"),
weechat_prefix ("network"));
}
else
{
weechat_printf (
server->buffer,
_("%sgnutls: certificate fingerprint does NOT match "
"(check value of option "
"irc.server.%s.ssl_fingerprint)"),
weechat_prefix ("error"), server->name);
rc = -1;
}
goto end;
}
if (!hostname_match)
{
weechat_printf (
server->buffer,
_("%sgnutls: the hostname in the certificate does NOT "
"match \"%s\""),
weechat_prefix ("error"), server->current_address);
rc = -1;
}
}
/* verify the peers certificate */
if (gnutls_certificate_verify_peers2 (tls_session, &status) < 0)
{
weechat_printf (
server->buffer,
_("%sgnutls: error while checking peer's certificate"),
weechat_prefix ("error"));
rc = -1;
goto end;
}
/* check if certificate is trusted */
if (status & GNUTLS_CERT_INVALID)
{
weechat_printf (
server->buffer,
_("%sgnutls: peer's certificate is NOT trusted"),
weechat_prefix ("error"));
rc = -1;
}
else
{
weechat_printf (
server->buffer,
_("%sgnutls: peer's certificate is trusted"),
weechat_prefix ("network"));
}
/* check if certificate issuer is known */
if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
{
weechat_printf (
server->buffer,
_("%sgnutls: peer's certificate issuer is unknown"),
weechat_prefix ("error"));
rc = -1;
}
/* check that certificate is not revoked */
if (status & GNUTLS_CERT_REVOKED)
{
weechat_printf (
server->buffer,
_("%sgnutls: the certificate has been revoked"),
weechat_prefix ("error"));
rc = -1;
}
}
else if (action == WEECHAT_HOOK_CONNECT_GNUTLS_CB_SET_CERT)
{
/* using client certificate if it exists */
cert_path0 = (char *) IRC_SERVER_OPTION_STRING(
server, IRC_SERVER_OPTION_SSL_CERT);
if (cert_path0 && cert_path0[0])
{
weechat_dir = weechat_info_get ("weechat_dir", "");
cert_path1 = weechat_string_replace (cert_path0, "%h", weechat_dir);
cert_path2 = (cert_path1) ?
weechat_string_expand_home (cert_path1) : NULL;
if (cert_path2)
{
cert_str = weechat_file_get_content (cert_path2);
if (cert_str)
{
weechat_printf (
server->buffer,
_("%sgnutls: sending one certificate"),
weechat_prefix ("network"));
filedatum.data = (unsigned char *) cert_str;
filedatum.size = strlen (cert_str);
/* certificate */
gnutls_x509_crt_init (&server->tls_cert);
gnutls_x509_crt_import (server->tls_cert, &filedatum,
GNUTLS_X509_FMT_PEM);
/* key password */
ssl_password = irc_server_eval_expression (
server,
IRC_SERVER_OPTION_STRING(server,
IRC_SERVER_OPTION_SSL_PASSWORD));
/* key */
gnutls_x509_privkey_init (&server->tls_cert_key);
/*
* gnutls_x509_privkey_import2 has no "Since: ..." in GnuTLS manual but
* GnuTLS NEWS file lists it being added in 3.1.0:
* https://gitlab.com/gnutls/gnutls/blob/2b715b9564681acb3008a5574dcf25464de8b038/NEWS#L2552
*/
#if LIBGNUTLS_VERSION_NUMBER >= 0x030100 /* 3.1.0 */
ret = gnutls_x509_privkey_import2 (server->tls_cert_key,
&filedatum,
GNUTLS_X509_FMT_PEM,
ssl_password,
0);
#else
ret = gnutls_x509_privkey_import (server->tls_cert_key,
&filedatum,
GNUTLS_X509_FMT_PEM);
#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x0301000 */
if (ret < 0)
{
ret = gnutls_x509_privkey_import_pkcs8 (
server->tls_cert_key,
&filedatum,
GNUTLS_X509_FMT_PEM,
ssl_password,
GNUTLS_PKCS_PLAIN);
}
if (ret < 0)
{
weechat_printf (
server->buffer,
_("%sgnutls: invalid certificate \"%s\", error: "
"%s"),
weechat_prefix ("error"), cert_path2,
gnutls_strerror (ret));
rc = -1;
}
else
{
#if LIBGNUTLS_VERSION_NUMBER >= 0x020b00 /* 2.11.0 */
tls_struct.cert_type = GNUTLS_CRT_X509;
tls_struct.key_type = GNUTLS_PRIVKEY_X509;
#else
tls_struct.type = GNUTLS_CRT_X509;
#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020b00 */
tls_struct.ncerts = 1;
tls_struct.deinit_all = 0;
tls_struct.cert.x509 = &server->tls_cert;
tls_struct.key.x509 = server->tls_cert_key;
#if LIBGNUTLS_VERSION_NUMBER >= 0x010706 /* 1.7.6 */
/* client certificate info */
#if LIBGNUTLS_VERSION_NUMBER < 0x020400 /* 2.4.0 */
rinfo = gnutls_x509_crt_print (server->tls_cert,
GNUTLS_X509_CRT_ONELINE,
&cinfo);
#else
rinfo = gnutls_x509_crt_print (server->tls_cert,
GNUTLS_CRT_PRINT_ONELINE,
&cinfo);
#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020400 */
if (rinfo == 0)
{
weechat_printf (
server->buffer,
_("%s - client certificate info (%s):"),
weechat_prefix ("network"), cert_path2);
weechat_printf (
server->buffer, "%s - %s",
weechat_prefix ("network"), cinfo.data);
gnutls_free (cinfo.data);
}
#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010706 */
memcpy (answer, &tls_struct, sizeof (tls_struct));
free (cert_str);
}
if (ssl_password)
free (ssl_password);
}
else
{
weechat_printf (
server->buffer,
_("%sgnutls: unable to read certificate \"%s\""),
weechat_prefix ("error"), cert_path2);
}
}
if (cert_path1)
free (cert_path1);
if (cert_path2)
free (cert_path2);
}
}
end:
/* an error should stop the handshake unless the user doesn't care */
if ((rc == -1)
&& (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL_VERIFY) == 0))
{
rc = 0;
}
if (cert_temp_init)
gnutls_x509_crt_deinit (cert_temp);
if (weechat_dir)
free (weechat_dir);
if (fingerprint_eval)
free (fingerprint_eval);
return rc;
}
#endif /* HAVE_GNUTLS */
/*
* Connects to a server.
*
* Returns:
* 1: OK
* 0: error
*/
int
irc_server_connect (struct t_irc_server *server)
{
int length;
char *option_name;
struct t_config_option *proxy_type, *proxy_ipv6, *proxy_address;
struct t_config_option *proxy_port;
const char *proxy, *str_proxy_type, *str_proxy_address;
server->disconnected = 0;
if (!server->buffer)
{
if (!irc_server_create_buffer (server))
return 0;
weechat_buffer_set (server->buffer, "display", "auto");
}
irc_bar_item_update_channel ();
irc_server_set_index_current_address (server,
server->index_current_address);
if (!server->current_address)
{
weechat_printf (
server->buffer,
_("%s%s: unknown address for server \"%s\", cannot connect"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, server->name);
return 0;
}
/* free some old values (from a previous connection to server) */
if (server->isupport)
{
free (server->isupport);
server->isupport = NULL;
}
if (server->prefix_modes)
{
free (server->prefix_modes);
server->prefix_modes = NULL;
}
if (server->prefix_chars)
{
free (server->prefix_chars);
server->prefix_chars = NULL;
}
proxy_type = NULL;
proxy_ipv6 = NULL;
proxy_address = NULL;
proxy_port = NULL;
str_proxy_type = NULL;
str_proxy_address = NULL;
proxy = IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PROXY);
if (proxy && proxy[0])
{
length = 32 + strlen (proxy) + 1;
option_name = malloc (length);
if (!option_name)
{
weechat_printf (
server->buffer,
_("%s%s: not enough memory (%s)"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
"proxy");
return 0;
}
snprintf (option_name, length, "weechat.proxy.%s.type", proxy);
proxy_type = weechat_config_get (option_name);
snprintf (option_name, length, "weechat.proxy.%s.ipv6", proxy);
proxy_ipv6 = weechat_config_get (option_name);
snprintf (option_name, length, "weechat.proxy.%s.address", proxy);
proxy_address = weechat_config_get (option_name);
snprintf (option_name, length, "weechat.proxy.%s.port", proxy);
proxy_port = weechat_config_get (option_name);
free (option_name);
if (!proxy_type || !proxy_address)
{
weechat_printf (
server->buffer,
_("%s%s: proxy \"%s\" not found for server \"%s\", cannot "
"connect"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, proxy, server->name);
return 0;
}
str_proxy_type = weechat_config_string (proxy_type);
str_proxy_address = weechat_config_string (proxy_address);
if (!str_proxy_type[0] || !proxy_ipv6 || !str_proxy_address[0]
|| !proxy_port)
{
weechat_printf (
server->buffer,
_("%s%s: missing proxy settings, check options for proxy "
"\"%s\""),
weechat_prefix ("error"), IRC_PLUGIN_NAME, proxy);
return 0;
}
}
if (!server->nicks_array)
{
weechat_printf (
server->buffer,
_("%s%s: nicks not defined for server \"%s\", cannot connect"),
weechat_prefix ("error"), IRC_PLUGIN_NAME, server->name);
return 0;
}
#ifndef HAVE_GNUTLS
if (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL))
{
weechat_printf (
server->buffer,
_("%s%s: cannot connect with SSL because WeeChat was not built "
"with GnuTLS support"),
weechat_prefix ("error"), IRC_PLUGIN_NAME);
return 0;
}
#endif /* HAVE_GNUTLS */
if (proxy_type)
{
weechat_printf (
server->buffer,
_("%s%s: connecting to server %s/%d%s via %s proxy %s/%d%s..."),
weechat_prefix ("network"),
IRC_PLUGIN_NAME,
server->current_address,
server->current_port,
(IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)) ?
" (SSL)" : "",
str_proxy_type,
str_proxy_address,
weechat_config_integer (proxy_port),
(weechat_config_boolean (proxy_ipv6)) ? " (IPv6)" : "");
weechat_log_printf (
_("Connecting to server %s/%d%s via %s proxy %s/%d%s..."),
server->current_address,
server->current_port,
(IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)) ?
" (SSL)" : "",
str_proxy_type,
str_proxy_address,
weechat_config_integer (proxy_port),
(weechat_config_boolean (proxy_ipv6)) ? " (IPv6)" : "");
}
else
{
weechat_printf (
server->buffer,
_("%s%s: connecting to server %s/%d%s..."),
weechat_prefix ("network"),
IRC_PLUGIN_NAME,
server->current_address,
server->current_port,
(IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)) ?
" (SSL)" : "");
weechat_log_printf (
_("%s%s: connecting to server %s/%d%s..."),
"",
IRC_PLUGIN_NAME,
server->current_address,
server->current_port,
(IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)) ?
" (SSL)" : "");
}
/* close connection if opened */
irc_server_close_connection (server);
/* open auto-joined channels now (if needed) */
if (weechat_config_boolean (irc_config_look_buffer_open_before_autojoin)
&& !server->disable_autojoin)
{
irc_server_autojoin_create_buffers (server);
}
/* init SSL if asked and connect */
server->ssl_connected = 0;
#ifdef HAVE_GNUTLS
if (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL))
server->ssl_connected = 1;
if (!server->fake_server)
{
server->hook_connect = weechat_hook_connect (
proxy,
server->current_address,
server->current_port,
proxy_type ? weechat_config_integer (proxy_ipv6) : IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_IPV6),
server->current_retry,
(server->ssl_connected) ? &server->gnutls_sess : NULL,
(server->ssl_connected) ? &irc_server_gnutls_callback : NULL,
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_SSL_DHKEY_SIZE),
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SSL_PRIORITIES),
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_LOCAL_HOSTNAME),
&irc_server_connect_cb,
server,
NULL);
}
#else
if (!server->fake_server)
{
server->hook_connect = weechat_hook_connect (
proxy,
server->current_address,
server->current_port,
proxy_type ? weechat_config_integer (proxy_ipv6) : IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_IPV6),
server->current_retry,
NULL, NULL, 0, NULL,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_LOCAL_HOSTNAME),
&irc_server_connect_cb,
server,
NULL);
}
#endif /* HAVE_GNUTLS */
/* send signal "irc_server_connecting" with server name */
(void) weechat_hook_signal_send ("irc_server_connecting",
WEECHAT_HOOK_SIGNAL_STRING, server->name);
if (server->fake_server)
{
irc_server_connect_cb (server,
NULL, /* data */
WEECHAT_HOOK_CONNECT_OK, /* status */
0, /* gnutls_rc */
-1, /* sock */
NULL, /* error */
"1.2.3.4"); /* ip_address */
}
return 1;
}
/*
* Reconnects to a server (after disconnection).
*/
void
irc_server_reconnect (struct t_irc_server *server)
{
weechat_printf (
server->buffer,
_("%s%s: reconnecting to server..."),
weechat_prefix ("network"), IRC_PLUGIN_NAME);
server->reconnect_start = 0;
if (irc_server_connect (server))
server->reconnect_join = 1;
else
irc_server_reconnect_schedule (server);
}
/*
* Callback for auto-connect to servers (called at startup).
*/
int
irc_server_auto_connect_timer_cb (const void *pointer, void *data,
int remaining_calls)
{
struct t_irc_server *ptr_server;
int auto_connect;
/* make C compiler happy */
(void) data;
(void) remaining_calls;
auto_connect = (pointer) ? 1 : 0;
for (ptr_server = irc_servers; ptr_server;
ptr_server = ptr_server->next_server)
{
if ((auto_connect || ptr_server->temp_server)
&& (IRC_SERVER_OPTION_BOOLEAN(ptr_server, IRC_SERVER_OPTION_AUTOCONNECT)))
{
if (!irc_server_connect (ptr_server))
irc_server_reconnect_schedule (ptr_server);
}
}
return WEECHAT_RC_OK;
}
/*
* Auto-connects to servers (called at startup).
*
* If auto_connect == 1, auto-connects to all servers with flag "autoconnect".
* If auto_connect == 0, auto-connect to temporary servers only.
*/
void
irc_server_auto_connect (int auto_connect)
{
weechat_hook_timer (1, 0, 1,
&irc_server_auto_connect_timer_cb,
(auto_connect) ? (void *)1 : (void *)0,
NULL);
}
/*
* Disconnects from a server.
*/
void
irc_server_disconnect (struct t_irc_server *server, int switch_address,
int reconnect)
{
struct t_irc_channel *ptr_channel;
if (server->is_connected)
{
/*
* remove all nicks and 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 (server, ptr_channel);
if (ptr_channel->hook_autorejoin)
{
weechat_unhook (ptr_channel->hook_autorejoin);
ptr_channel->hook_autorejoin = NULL;
}
weechat_buffer_set (ptr_channel->buffer, "localvar_del_away", "");
weechat_printf (
ptr_channel->buffer,
_("%s%s: disconnected from server"),
weechat_prefix ("network"), IRC_PLUGIN_NAME);
}
/* remove away status on server buffer */
weechat_buffer_set (server->buffer, "localvar_del_away", "");
}
irc_server_close_connection (server);
if (server->buffer)
{
weechat_printf (
server->buffer,
_("%s%s: disconnected from server"),
weechat_prefix ("network"), IRC_PLUGIN_NAME);
}
server->current_retry = 0;
if (switch_address)
irc_server_switch_address (server, 0);
else
irc_server_set_index_current_address (server, 0);
if (server->nick_modes)
{
free (server->nick_modes);
server->nick_modes = NULL;
weechat_bar_item_update ("input_prompt");
weechat_bar_item_update ("irc_nick_modes");
}
if (server->host)
{
free (server->host);
server->host = NULL;
weechat_bar_item_update ("irc_host");
weechat_bar_item_update ("irc_nick_host");
}
server->checking_cap_ls = 0;
weechat_hashtable_remove_all (server->cap_ls);
server->checking_cap_list = 0;
weechat_hashtable_remove_all (server->cap_list);
server->is_away = 0;
server->away_time = 0;
server->lag = 0;
server->lag_displayed = -1;
server->lag_check_time.tv_sec = 0;
server->lag_check_time.tv_usec = 0;
server->lag_next_check = time (NULL) +
weechat_config_integer (irc_config_network_lag_check);
server->lag_last_refresh = 0;
irc_server_set_lag (server);
server->monitor = 0;
server->monitor_time = 0;
if (reconnect
&& IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_AUTORECONNECT))
irc_server_reconnect_schedule (server);
else
{
server->reconnect_delay = 0;
server->reconnect_start = 0;
}
/* discard current nick if no reconnection asked */
if (!reconnect && server->nick)
irc_server_set_nick (server, NULL);
irc_server_set_buffer_title (server);
server->disconnected = 1;
/* send signal "irc_server_disconnected" with server name */
(void) weechat_hook_signal_send ("irc_server_disconnected",
WEECHAT_HOOK_SIGNAL_STRING, server->name);
}
/*
* Disconnects from all servers.
*/
void
irc_server_disconnect_all ()
{
struct t_irc_server *ptr_server;
for (ptr_server = irc_servers; ptr_server;
ptr_server = ptr_server->next_server)
{
irc_server_disconnect (ptr_server, 0, 0);
}
}
/*
* Creates buffers for auto-joined channels on a server.
*/
void
irc_server_autojoin_create_buffers (struct t_irc_server *server)
{
const char *pos_space;
char *autojoin, *autojoin2, **channels;
int num_channels, i;
/* buffers are opened only if no channels are currently opened */
if (server->channels)
return;
/* evaluate server option "autojoin" */
autojoin = irc_server_eval_expression (
server,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_AUTOJOIN));
/* extract channel names from autojoin option */
if (autojoin && autojoin[0])
{
pos_space = strchr (autojoin, ' ');
autojoin2 = (pos_space) ?
weechat_strndup (autojoin, pos_space - autojoin) :
strdup (autojoin);
if (autojoin2)
{
channels = weechat_string_split (
autojoin2,
",",
NULL,
WEECHAT_STRING_SPLIT_STRIP_LEFT
| WEECHAT_STRING_SPLIT_STRIP_RIGHT
| WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
0,
&num_channels);
if (channels)
{
for (i = 0; i < num_channels; i++)
{
irc_channel_create_buffer (
server, IRC_CHANNEL_TYPE_CHANNEL, channels[i],
1, 1);
}
weechat_string_free_split (channels);
}
free (autojoin2);
}
}
if (autojoin)
free (autojoin);
}
/*
* Autojoins (or auto-rejoins) channels.
*/
void
irc_server_autojoin_channels (struct t_irc_server *server)
{
struct t_irc_channel *ptr_channel;
char *autojoin;
/* 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)
&& !ptr_channel->part)
{
if (ptr_channel->key)
{
irc_server_sendf (server,
IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
"JOIN %s %s",
ptr_channel->name, ptr_channel->key);
}
else
{
irc_server_sendf (server,
IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
"JOIN %s",
ptr_channel->name);
}
}
}
server->reconnect_join = 0;
}
else
{
/* auto-join when connecting to server for first time */
autojoin = irc_server_eval_expression (
server,
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_AUTOJOIN));
if (!server->disable_autojoin && autojoin && autojoin[0])
irc_command_join_server (server, autojoin, 0, 0);
if (autojoin)
free (autojoin);
}
server->disable_autojoin = 0;
}
/*
* Returns number of channels for server.
*/
int
irc_server_get_channel_count (struct t_irc_server *server)
{
int count;
struct 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;
}
/*
* Returns number of pv for server.
*/
int
irc_server_get_pv_count (struct t_irc_server *server)
{
int count;
struct 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_PRIVATE)
count++;
}
return count;
}
/*
* Removes away for all channels/nicks (for all servers).
*/
void
irc_server_remove_away (struct t_irc_server *server)
{
struct t_irc_channel *ptr_channel;
if (server->is_connected)
{
for (ptr_channel = server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL)
irc_channel_remove_away (server, ptr_channel);
}
server->last_away_check = 0;
}
}
/*
* Checks for away on all channels of a server.
*/
void
irc_server_check_away (struct t_irc_server *server)
{
struct t_irc_channel *ptr_channel;
if (server->is_connected)
{
for (ptr_channel = server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL)
irc_channel_check_whox (server, ptr_channel);
}
server->last_away_check = time (NULL);
}
}
/*
* Sets/unsets away status for a server (all channels).
*/
void
irc_server_set_away (struct t_irc_server *server, const char *nick, int is_away)
{
struct t_irc_channel *ptr_channel;
if (server->is_connected)
{
/* set/del "away" local variable on server buffer */
if (is_away)
{
weechat_buffer_set (server->buffer,
"localvar_set_away", server->away_message);
}
else
{
weechat_buffer_set (server->buffer,
"localvar_del_away", "");
}
for (ptr_channel = server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
/* set away flag for nick on channel */
if (ptr_channel->type == IRC_CHANNEL_TYPE_CHANNEL)
irc_channel_set_away (server, ptr_channel, nick, is_away);
/* set/del "away" local variable on channel buffer */
if (is_away)
{
weechat_buffer_set (ptr_channel->buffer,
"localvar_set_away", server->away_message);
}
else
{
weechat_buffer_set (ptr_channel->buffer,
"localvar_del_away", "");
}
}
}
}
/*
* Callback called when user sends (file or chat) to someone and that xfer
* plugin successfully initialized xfer and is ready for sending.
*
* In that case, irc plugin sends message to remote nick and wait for "accept"
* reply.
*/
int
irc_server_xfer_send_ready_cb (const void *pointer, void *data,
const char *signal,
const char *type_data, void *signal_data)
{
struct t_infolist *infolist;
struct t_irc_server *ptr_server;
const char *plugin_name, *plugin_id, *type, *filename, *local_address;
char converted_addr[NI_MAXHOST];
struct addrinfo *ainfo;
struct sockaddr_in *saddr;
int spaces_in_name, rc;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
infolist = (struct t_infolist *)signal_data;
if (weechat_infolist_next (infolist))
{
plugin_name = weechat_infolist_string (infolist, "plugin_name");
plugin_id = weechat_infolist_string (infolist, "plugin_id");
if (plugin_name && (strcmp (plugin_name, IRC_PLUGIN_NAME) == 0)
&& plugin_id)
{
ptr_server = irc_server_search (plugin_id);
if (ptr_server)
{
converted_addr[0] = '\0';
local_address = weechat_infolist_string (infolist,
"local_address");
if (local_address)
{
res_init ();
rc = getaddrinfo (local_address, NULL, NULL, &ainfo);
if ((rc == 0) && ainfo && ainfo->ai_addr)
{
if (ainfo->ai_family == AF_INET)
{
/* transform dotted 4 IP address to ulong string */
saddr = (struct sockaddr_in *)ainfo->ai_addr;
snprintf (converted_addr, sizeof (converted_addr),
"%lu",
(unsigned long)ntohl (saddr->sin_addr.s_addr));
}
else
{
snprintf (converted_addr, sizeof (converted_addr),
"%s", local_address);
}
}
}
type = weechat_infolist_string (infolist, "type_string");
if (type && converted_addr[0])
{
/* send DCC PRIVMSG */
if (strcmp (type, "file_send") == 0)
{
filename = weechat_infolist_string (infolist, "filename");
spaces_in_name = (strchr (filename, ' ') != NULL);
irc_server_sendf (
ptr_server,
IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
"PRIVMSG %s :\01DCC SEND %s%s%s "
"%s %d %s\01",
weechat_infolist_string (infolist, "remote_nick"),
(spaces_in_name) ? "\"" : "",
filename,
(spaces_in_name) ? "\"" : "",
converted_addr,
weechat_infolist_integer (infolist, "port"),
weechat_infolist_string (infolist, "size"));
}
else if (strcmp (type, "chat_send") == 0)
{
irc_server_sendf (
ptr_server,
IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
"PRIVMSG %s :\01DCC CHAT chat %s %d\01",
weechat_infolist_string (infolist, "remote_nick"),
converted_addr,
weechat_infolist_integer (infolist, "port"));
}
}
}
}
}
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_OK;
}
/*
* Callback called when user receives a file and that resume is possible (file
* is partially received).
*
* In that case, irc plugin sends message to remote nick with resume position.
*/
int
irc_server_xfer_resume_ready_cb (const void *pointer, void *data,
const char *signal,
const char *type_data, void *signal_data)
{
struct t_infolist *infolist;
struct t_irc_server *ptr_server;
const char *plugin_name, *plugin_id, *filename;
int spaces_in_name;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
infolist = (struct t_infolist *)signal_data;
if (weechat_infolist_next (infolist))
{
plugin_name = weechat_infolist_string (infolist, "plugin_name");
plugin_id = weechat_infolist_string (infolist, "plugin_id");
if (plugin_name && (strcmp (plugin_name, IRC_PLUGIN_NAME) == 0) && plugin_id)
{
ptr_server = irc_server_search (plugin_id);
if (ptr_server)
{
filename = weechat_infolist_string (infolist, "filename");
spaces_in_name = (strchr (filename, ' ') != NULL);
irc_server_sendf (
ptr_server,
IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
"PRIVMSG %s :\01DCC RESUME %s%s%s %d %s\01",
weechat_infolist_string (infolist, "remote_nick"),
(spaces_in_name) ? "\"" : "",
filename,
(spaces_in_name) ? "\"" : "",
weechat_infolist_integer (infolist, "port"),
weechat_infolist_string (infolist, "start_resume"));
}
}
}
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_OK;
}
/*
* Callback called when xfer plugin accepted resume request from receiver.
*
* In that case, irc plugin sends accept message to remote nick with resume
* position.
*/
int
irc_server_xfer_send_accept_resume_cb (const void *pointer, void *data,
const char *signal,
const char *type_data,
void *signal_data)
{
struct t_infolist *infolist;
struct t_irc_server *ptr_server;
const char *plugin_name, *plugin_id, *filename;
int spaces_in_name;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
infolist = (struct t_infolist *)signal_data;
if (weechat_infolist_next (infolist))
{
plugin_name = weechat_infolist_string (infolist, "plugin_name");
plugin_id = weechat_infolist_string (infolist, "plugin_id");
if (plugin_name && (strcmp (plugin_name, IRC_PLUGIN_NAME) == 0) && plugin_id)
{
ptr_server = irc_server_search (plugin_id);
if (ptr_server)
{
filename = weechat_infolist_string (infolist, "filename");
spaces_in_name = (strchr (filename, ' ') != NULL);
irc_server_sendf (
ptr_server,
IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
"PRIVMSG %s :\01DCC ACCEPT %s%s%s %d %s\01",
weechat_infolist_string (infolist, "remote_nick"),
(spaces_in_name) ? "\"" : "",
filename,
(spaces_in_name) ? "\"" : "",
weechat_infolist_integer (infolist, "port"),
weechat_infolist_string (infolist, "start_resume"));
}
}
}
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_OK;
}
/*
* Returns hdata for server.
*/
struct t_hdata *
irc_server_hdata_server_cb (const void *pointer, void *data,
const char *hdata_name)
{
struct t_hdata *hdata;
/* make C compiler happy */
(void) pointer;
(void) data;
hdata = weechat_hdata_new (hdata_name, "prev_server", "next_server",
0, 0, NULL, NULL);
if (hdata)
{
WEECHAT_HDATA_VAR(struct t_irc_server, name, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, options, POINTER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, temp_server, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, fake_server, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, reloading_from_config, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, reloaded_from_config, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, addresses_eval, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, addresses_count, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, addresses_array, STRING, 0, "addresses_count", NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, ports_array, INTEGER, 0, "addresses_count", NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, retry_array, INTEGER, 0, "addresses_count", NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, index_current_address, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, current_address, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, current_ip, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, current_port, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, current_retry, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, sock, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, hook_connect, POINTER, 0, NULL, "hook");
WEECHAT_HDATA_VAR(struct t_irc_server, hook_fd, POINTER, 0, NULL, "hook");
WEECHAT_HDATA_VAR(struct t_irc_server, hook_timer_connection, POINTER, 0, NULL, "hook");
WEECHAT_HDATA_VAR(struct t_irc_server, hook_timer_sasl, POINTER, 0, NULL, "hook");
WEECHAT_HDATA_VAR(struct t_irc_server, is_connected, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, ssl_connected, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, disconnected, INTEGER, 0, NULL, NULL);
#ifdef HAVE_GNUTLS
WEECHAT_HDATA_VAR(struct t_irc_server, gnutls_sess, OTHER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, tls_cert, OTHER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, tls_cert_key, OTHER, 0, NULL, NULL);
#endif /* HAVE_GNUTLS */
WEECHAT_HDATA_VAR(struct t_irc_server, unterminated_message, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, nicks_count, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, nicks_array, STRING, 0, "nicks_count", NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, nick_first_tried, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, nick_alternate_number, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, nick, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, nick_modes, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, host, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, checking_cap_ls, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, cap_ls, HASHTABLE, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, checking_cap_list, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, cap_list, HASHTABLE, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, isupport, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, prefix_modes, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, prefix_chars, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, nick_max_length, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, user_max_length, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, host_max_length, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, casemapping, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, chantypes, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, chanmodes, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, monitor, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, monitor_time, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, reconnect_delay, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, reconnect_start, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, command_time, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, reconnect_join, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, disable_autojoin, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, is_away, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, away_message, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, away_time, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, lag, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, lag_displayed, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, lag_check_time, OTHER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, lag_next_check, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, lag_last_refresh, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, cmd_list_regexp, POINTER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, last_user_message, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, last_away_check, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, last_data_purge, TIME, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, outqueue, POINTER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, last_outqueue, POINTER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, redirects, POINTER, 0, NULL, "irc_redirect");
WEECHAT_HDATA_VAR(struct t_irc_server, last_redirect, POINTER, 0, NULL, "irc_redirect");
WEECHAT_HDATA_VAR(struct t_irc_server, notify_list, POINTER, 0, NULL, "irc_notify");
WEECHAT_HDATA_VAR(struct t_irc_server, last_notify, POINTER, 0, NULL, "irc_notify");
WEECHAT_HDATA_VAR(struct t_irc_server, notify_count, INTEGER, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, join_manual, HASHTABLE, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, join_channel_key, HASHTABLE, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, join_noswitch, HASHTABLE, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, buffer, POINTER, 0, NULL, "buffer");
WEECHAT_HDATA_VAR(struct t_irc_server, buffer_as_string, STRING, 0, NULL, NULL);
WEECHAT_HDATA_VAR(struct t_irc_server, channels, POINTER, 0, NULL, "irc_channel");
WEECHAT_HDATA_VAR(struct t_irc_server, last_channel, POINTER, 0, NULL, "irc_channel");
WEECHAT_HDATA_VAR(struct t_irc_server, prev_server, POINTER, 0, NULL, hdata_name);
WEECHAT_HDATA_VAR(struct t_irc_server, next_server, POINTER, 0, NULL, hdata_name);
WEECHAT_HDATA_LIST(irc_servers, WEECHAT_HDATA_LIST_CHECK_POINTERS);
WEECHAT_HDATA_LIST(last_irc_server, 0);
}
return hdata;
}
/*
* Adds a server in an infolist.
*
* Returns:
* 1: OK
* 0: error
*/
int
irc_server_add_to_infolist (struct t_infolist *infolist,
struct t_irc_server *server)
{
struct t_infolist_item *ptr_item;
if (!infolist || !server)
return 0;
ptr_item = weechat_infolist_new_item (infolist);
if (!ptr_item)
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "name", server->name))
return 0;
if (!weechat_infolist_new_var_pointer (ptr_item, "buffer", server->buffer))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "buffer_name",
(server->buffer) ?
weechat_buffer_get_string (server->buffer, "name") : ""))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "buffer_short_name",
(server->buffer) ?
weechat_buffer_get_string (server->buffer, "short_name") : ""))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "addresses",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_ADDRESSES)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "proxy",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PROXY)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ipv6",
IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_IPV6)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ssl",
IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "ssl_cert",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SSL_CERT)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "ssl_password",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SSL_PASSWORD)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "ssl_priorities",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SSL_PRIORITIES)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ssl_dhkey_size",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_SSL_DHKEY_SIZE)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "ssl_fingerprint",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SSL_FINGERPRINT)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ssl_verify",
IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL_VERIFY)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "password",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PASSWORD)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "capabilities",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_CAPABILITIES)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "sasl_mechanism",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_SASL_MECHANISM)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "sasl_username",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_USERNAME)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "sasl_password",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_PASSWORD)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "sasl_key",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_KEY)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "sasl_fail",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_SASL_FAIL)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "autoconnect",
IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_AUTOCONNECT)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "autoreconnect",
IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_AUTORECONNECT)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "autoreconnect_delay",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_AUTORECONNECT_DELAY)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "nicks",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_NICKS)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "nicks_alternate",
IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_NICKS_ALTERNATE)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "username",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_USERNAME)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "realname",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_REALNAME)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "local_hostname",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_LOCAL_HOSTNAME)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "usermode",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_USERMODE)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "command",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_COMMAND)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "command_delay",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_COMMAND_DELAY)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "autojoin",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_AUTOJOIN)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "autorejoin",
IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_AUTOREJOIN)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "autorejoin_delay",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_AUTOREJOIN_DELAY)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "connection_timeout",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_CONNECTION_TIMEOUT)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "anti_flood_prio_high",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_HIGH)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "anti_flood_prio_low",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_LOW)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "away_check",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_AWAY_CHECK)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "away_check_max_nicks",
IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_AWAY_CHECK_MAX_NICKS)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "msg_kick",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_MSG_KICK)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "msg_part",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_MSG_PART)))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "msg_quit",
IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_MSG_QUIT)))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "temp_server", server->temp_server))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "fake_server", server->fake_server))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "index_current_address", server->index_current_address))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "current_address", server->current_address))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "current_ip", server->current_ip))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "current_port", server->current_port))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "current_retry", server->current_retry))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "sock", server->sock))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "is_connected", server->is_connected))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "ssl_connected", server->ssl_connected))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "disconnected", server->disconnected))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "unterminated_message", server->unterminated_message))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "nick", server->nick))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "nick_modes", server->nick_modes))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "host", server->host))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "checking_cap_ls", server->checking_cap_ls))
return 0;
if (!weechat_hashtable_add_to_infolist (server->cap_ls, ptr_item, "cap_ls"))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "checking_cap_list", server->checking_cap_list))
return 0;
if (!weechat_hashtable_add_to_infolist (server->cap_list, ptr_item, "cap_list"))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "isupport", server->isupport))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "prefix_modes", server->prefix_modes))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "prefix_chars", server->prefix_chars))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "nick_max_length", server->nick_max_length))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "user_max_length", server->user_max_length))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "host_max_length", server->host_max_length))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "casemapping", server->casemapping))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "casemapping_string", irc_server_casemapping_string[server->casemapping]))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "chantypes", server->chantypes))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "chanmodes", server->chanmodes))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "monitor", server->monitor))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "monitor_time", server->monitor_time))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "reconnect_delay", server->reconnect_delay))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "reconnect_start", server->reconnect_start))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "command_time", server->command_time))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "reconnect_join", server->reconnect_join))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "disable_autojoin", server->disable_autojoin))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "is_away", server->is_away))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "away_message", server->away_message))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "away_time", server->away_time))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "lag", server->lag))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "lag_displayed", server->lag_displayed))
return 0;
if (!weechat_infolist_new_var_buffer (ptr_item, "lag_check_time", &(server->lag_check_time), sizeof (struct timeval)))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "lag_next_check", server->lag_next_check))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "lag_last_refresh", server->lag_last_refresh))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "last_user_message", server->last_user_message))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "last_away_check", server->last_away_check))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "last_data_purge", server->last_data_purge))
return 0;
return 1;
}
/*
* Prints server infos in WeeChat log file (usually for crash dump).
*/
void
irc_server_print_log ()
{
struct t_irc_server *ptr_server;
struct t_irc_channel *ptr_channel;
int i;
for (ptr_server = irc_servers; ptr_server;
ptr_server = ptr_server->next_server)
{
weechat_log_printf ("");
weechat_log_printf ("[server %s (addr:0x%lx)]", ptr_server->name, ptr_server);
/* addresses */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_ADDRESSES]))
weechat_log_printf (" addresses. . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_ADDRESSES));
else
weechat_log_printf (" addresses. . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_ADDRESSES]));
/* proxy */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_PROXY]))
weechat_log_printf (" proxy. . . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_PROXY));
else
weechat_log_printf (" proxy. . . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_PROXY]));
/* ipv6 */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_IPV6]))
weechat_log_printf (" ipv6 . . . . . . . . : null (%s)",
(IRC_SERVER_OPTION_BOOLEAN(ptr_server, IRC_SERVER_OPTION_IPV6)) ?
"on" : "off");
else
weechat_log_printf (" ipv6 . . . . . . . . : %s",
(weechat_config_boolean (ptr_server->options[IRC_SERVER_OPTION_IPV6])) ?
"on" : "off");
/* ssl */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SSL]))
weechat_log_printf (" ssl. . . . . . . . . : null (%s)",
(IRC_SERVER_OPTION_BOOLEAN(ptr_server, IRC_SERVER_OPTION_SSL)) ?
"on" : "off");
else
weechat_log_printf (" ssl. . . . . . . . . : %s",
(weechat_config_boolean (ptr_server->options[IRC_SERVER_OPTION_SSL])) ?
"on" : "off");
/* ssl_cert */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SSL_CERT]))
weechat_log_printf (" ssl_cert . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_SSL_CERT));
else
weechat_log_printf (" ssl_cert . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_SSL_CERT]));
/* ssl_password */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SSL_PASSWORD]))
weechat_log_printf (" ssl_password . . . . : null");
else
weechat_log_printf (" ssl_password . . . . : (hidden)");
/* ssl_priorities */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SSL_PRIORITIES]))
weechat_log_printf (" ssl_priorities . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_SSL_PRIORITIES));
else
weechat_log_printf (" ssl_priorities . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_SSL_PRIORITIES]));
/* ssl_dhkey_size */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SSL_DHKEY_SIZE]))
weechat_log_printf (" ssl_dhkey_size . . . : null ('%d')",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_SSL_DHKEY_SIZE));
else
weechat_log_printf (" ssl_dhkey_size . . . : '%d'",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_SSL_DHKEY_SIZE]));
/* ssl_fingerprint */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SSL_FINGERPRINT]))
weechat_log_printf (" ssl_fingerprint. . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_SSL_FINGERPRINT));
else
weechat_log_printf (" ssl_fingerprint. . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_SSL_FINGERPRINT]));
/* ssl_verify */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SSL_VERIFY]))
weechat_log_printf (" ssl_verify . . . . . : null (%s)",
(IRC_SERVER_OPTION_BOOLEAN(ptr_server, IRC_SERVER_OPTION_SSL_VERIFY)) ?
"on" : "off");
else
weechat_log_printf (" ssl_verify . . . . . : %s",
(weechat_config_boolean (ptr_server->options[IRC_SERVER_OPTION_SSL_VERIFY])) ?
"on" : "off");
/* password */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_PASSWORD]))
weechat_log_printf (" password . . . . . . : null");
else
weechat_log_printf (" password . . . . . . : (hidden)");
/* client capabilities */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_CAPABILITIES]))
weechat_log_printf (" capabilities . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_CAPABILITIES));
else
weechat_log_printf (" capabilities . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_CAPABILITIES]));
/* sasl_mechanism */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SASL_MECHANISM]))
weechat_log_printf (" sasl_mechanism . . . : null ('%s')",
irc_sasl_mechanism_string[IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_SASL_MECHANISM)]);
else
weechat_log_printf (" sasl_mechanism . . . : '%s'",
irc_sasl_mechanism_string[weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_SASL_MECHANISM])]);
/* sasl_username */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SASL_USERNAME]))
weechat_log_printf (" sasl_username. . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_SASL_USERNAME));
else
weechat_log_printf (" sasl_username. . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_SASL_USERNAME]));
/* sasl_password */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SASL_PASSWORD]))
weechat_log_printf (" sasl_password. . . . : null");
else
weechat_log_printf (" sasl_password. . . . : (hidden)");
/* sasl_key */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SASL_KEY]))
weechat_log_printf (" sasl_key. . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_SASL_KEY));
else
weechat_log_printf (" sasl_key. . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_SASL_KEY]));
/* sasl_fail */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_SASL_FAIL]))
weechat_log_printf (" sasl_fail. . . . . . : null ('%s')",
irc_server_sasl_fail_string[IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_SASL_FAIL)]);
else
weechat_log_printf (" sasl_fail. . . . . . : '%s'",
irc_server_sasl_fail_string[weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_SASL_FAIL])]);
/* autoconnect */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_AUTOCONNECT]))
weechat_log_printf (" autoconnect. . . . . : null (%s)",
(IRC_SERVER_OPTION_BOOLEAN(ptr_server, IRC_SERVER_OPTION_AUTOCONNECT)) ?
"on" : "off");
else
weechat_log_printf (" autoconnect. . . . . : %s",
(weechat_config_boolean (ptr_server->options[IRC_SERVER_OPTION_AUTOCONNECT])) ?
"on" : "off");
/* autoreconnect */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_AUTORECONNECT]))
weechat_log_printf (" autoreconnect. . . . : null (%s)",
(IRC_SERVER_OPTION_BOOLEAN(ptr_server, IRC_SERVER_OPTION_AUTORECONNECT)) ?
"on" : "off");
else
weechat_log_printf (" autoreconnect. . . . : %s",
(weechat_config_boolean (ptr_server->options[IRC_SERVER_OPTION_AUTORECONNECT])) ?
"on" : "off");
/* autoreconnect_delay */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_AUTORECONNECT_DELAY]))
weechat_log_printf (" autoreconnect_delay. : null (%d)",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_AUTORECONNECT_DELAY));
else
weechat_log_printf (" autoreconnect_delay. : %d",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_AUTORECONNECT_DELAY]));
/* nicks */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_NICKS]))
weechat_log_printf (" nicks. . . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_NICKS));
else
weechat_log_printf (" nicks. . . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_NICKS]));
/* nicks_alternate */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_NICKS_ALTERNATE]))
weechat_log_printf (" nicks_alternate. . . : null (%s)",
(IRC_SERVER_OPTION_BOOLEAN(ptr_server, IRC_SERVER_OPTION_NICKS_ALTERNATE)) ?
"on" : "off");
else
weechat_log_printf (" nicks_alternate. . . : %s",
(weechat_config_boolean (ptr_server->options[IRC_SERVER_OPTION_NICKS_ALTERNATE])) ?
"on" : "off");
/* username */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_USERNAME]))
weechat_log_printf (" username . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_USERNAME));
else
weechat_log_printf (" username . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_USERNAME]));
/* realname */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_REALNAME]))
weechat_log_printf (" realname . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_REALNAME));
else
weechat_log_printf (" realname . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_REALNAME]));
/* local_hostname */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_LOCAL_HOSTNAME]))
weechat_log_printf (" local_hostname . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_LOCAL_HOSTNAME));
else
weechat_log_printf (" local_hostname . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_LOCAL_HOSTNAME]));
/* usermode */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_USERMODE]))
weechat_log_printf (" usermode . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_USERMODE));
else
weechat_log_printf (" usermode . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_USERMODE]));
/* command */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_COMMAND]))
weechat_log_printf (" command. . . . . . . : null");
else
weechat_log_printf (" command. . . . . . . : (hidden)");
/* command_delay */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_COMMAND_DELAY]))
weechat_log_printf (" command_delay. . . . : null (%d)",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_COMMAND_DELAY));
else
weechat_log_printf (" command_delay. . . . : %d",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_COMMAND_DELAY]));
/* autojoin */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_AUTOJOIN]))
weechat_log_printf (" autojoin . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_AUTOJOIN));
else
weechat_log_printf (" autojoin . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_AUTOJOIN]));
/* autorejoin */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_AUTOREJOIN]))
weechat_log_printf (" autorejoin . . . . . : null (%s)",
(IRC_SERVER_OPTION_BOOLEAN(ptr_server, IRC_SERVER_OPTION_AUTOREJOIN)) ?
"on" : "off");
else
weechat_log_printf (" autorejoin . . . . . : %s",
(weechat_config_boolean (ptr_server->options[IRC_SERVER_OPTION_AUTOREJOIN])) ?
"on" : "off");
/* autorejoin_delay */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_AUTOREJOIN_DELAY]))
weechat_log_printf (" autorejoin_delay . . : null (%d)",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_AUTOREJOIN_DELAY));
else
weechat_log_printf (" autorejoin_delay . . : %d",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_AUTOREJOIN_DELAY]));
/* connection_timeout */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_CONNECTION_TIMEOUT]))
weechat_log_printf (" connection_timeout . : null (%d)",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_CONNECTION_TIMEOUT));
else
weechat_log_printf (" connection_timeout . : %d",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_CONNECTION_TIMEOUT]));
/* anti_flood_prio_high */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_HIGH]))
weechat_log_printf (" anti_flood_prio_high : null (%d)",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_HIGH));
else
weechat_log_printf (" anti_flood_prio_high : %d",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_HIGH]));
/* anti_flood_prio_low */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_LOW]))
weechat_log_printf (" anti_flood_prio_low. : null (%d)",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_LOW));
else
weechat_log_printf (" anti_flood_prio_low. : %d",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_ANTI_FLOOD_PRIO_LOW]));
/* away_check */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_AWAY_CHECK]))
weechat_log_printf (" away_check . . . . . : null (%d)",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_AWAY_CHECK));
else
weechat_log_printf (" away_check . . . . . : %d",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_AWAY_CHECK]));
/* away_check_max_nicks */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_AWAY_CHECK_MAX_NICKS]))
weechat_log_printf (" away_check_max_nicks : null (%d)",
IRC_SERVER_OPTION_INTEGER(ptr_server, IRC_SERVER_OPTION_AWAY_CHECK_MAX_NICKS));
else
weechat_log_printf (" away_check_max_nicks : %d",
weechat_config_integer (ptr_server->options[IRC_SERVER_OPTION_AWAY_CHECK_MAX_NICKS]));
/* msg_kick */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_MSG_KICK]))
weechat_log_printf (" msg_kick . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_MSG_KICK));
else
weechat_log_printf (" msg_kick . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_MSG_KICK]));
/* msg_part */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_MSG_PART]))
weechat_log_printf (" msg_part . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_MSG_PART));
else
weechat_log_printf (" msg_part . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_MSG_PART]));
/* msg_quit */
if (weechat_config_option_is_null (ptr_server->options[IRC_SERVER_OPTION_MSG_QUIT]))
weechat_log_printf (" msg_quit . . . . . . : null ('%s')",
IRC_SERVER_OPTION_STRING(ptr_server, IRC_SERVER_OPTION_MSG_QUIT));
else
weechat_log_printf (" msg_quit . . . . . . : '%s'",
weechat_config_string (ptr_server->options[IRC_SERVER_OPTION_MSG_QUIT]));
/* other server variables */
weechat_log_printf (" temp_server. . . . . : %d", ptr_server->temp_server);
weechat_log_printf (" fake_server. . . . . : %d", ptr_server->fake_server);
weechat_log_printf (" reloading_from_config: %d", ptr_server->reloaded_from_config);
weechat_log_printf (" reloaded_from_config : %d", ptr_server->reloaded_from_config);
weechat_log_printf (" addresses_eval . . . : '%s'", ptr_server->addresses_eval);
weechat_log_printf (" addresses_count. . . : %d", ptr_server->addresses_count);
weechat_log_printf (" addresses_array. . . : 0x%lx", ptr_server->addresses_array);
weechat_log_printf (" ports_array. . . . . : 0x%lx", ptr_server->ports_array);
weechat_log_printf (" retry_array. . . . . : 0x%lx", ptr_server->retry_array);
weechat_log_printf (" index_current_address: %d", ptr_server->index_current_address);
weechat_log_printf (" current_address. . . : '%s'", ptr_server->current_address);
weechat_log_printf (" current_ip . . . . . : '%s'", ptr_server->current_ip);
weechat_log_printf (" current_port . . . . : %d", ptr_server->current_port);
weechat_log_printf (" current_retry. . . . : %d", ptr_server->current_retry);
weechat_log_printf (" sock . . . . . . . . : %d", ptr_server->sock);
weechat_log_printf (" hook_connect . . . . : 0x%lx", ptr_server->hook_connect);
weechat_log_printf (" hook_fd. . . . . . . : 0x%lx", ptr_server->hook_fd);
weechat_log_printf (" hook_timer_connection: 0x%lx", ptr_server->hook_timer_connection);
weechat_log_printf (" hook_timer_sasl. . . : 0x%lx", ptr_server->hook_timer_sasl);
weechat_log_printf (" is_connected . . . . : %d", ptr_server->is_connected);
weechat_log_printf (" ssl_connected. . . . : %d", ptr_server->ssl_connected);
weechat_log_printf (" disconnected . . . . : %d", ptr_server->disconnected);
#ifdef HAVE_GNUTLS
weechat_log_printf (" gnutls_sess. . . . . : 0x%lx", ptr_server->gnutls_sess);
#endif /* HAVE_GNUTLS */
weechat_log_printf (" unterminated_message : '%s'", ptr_server->unterminated_message);
weechat_log_printf (" nicks_count. . . . . : %d", ptr_server->nicks_count);
weechat_log_printf (" nicks_array. . . . . : 0x%lx", ptr_server->nicks_array);
weechat_log_printf (" nick_first_tried . . : %d", ptr_server->nick_first_tried);
weechat_log_printf (" nick_alternate_number: %d", ptr_server->nick_alternate_number);
weechat_log_printf (" nick . . . . . . . . : '%s'", ptr_server->nick);
weechat_log_printf (" nick_modes . . . . . : '%s'", ptr_server->nick_modes);
weechat_log_printf (" host . . . . . . . . : '%s'", ptr_server->host);
weechat_log_printf (" checking_cap_ls. . . : %d", ptr_server->checking_cap_ls);
weechat_log_printf (" cap_ls . . . . . . . : 0x%lx (hashtable: '%s')",
ptr_server->cap_ls,
weechat_hashtable_get_string (ptr_server->cap_ls, "keys_values"));
weechat_log_printf (" checking_cap_list. . : %d", ptr_server->checking_cap_list);
weechat_log_printf (" cap_list . . . . . . : 0x%lx (hashtable: '%s')",
ptr_server->cap_list,
weechat_hashtable_get_string (ptr_server->cap_list, "keys_values"));
weechat_log_printf (" isupport . . . . . . : '%s'", ptr_server->isupport);
weechat_log_printf (" prefix_modes . . . . : '%s'", ptr_server->prefix_modes);
weechat_log_printf (" prefix_chars . . . . : '%s'", ptr_server->prefix_chars);
weechat_log_printf (" nick_max_length. . . : %d", ptr_server->nick_max_length);
weechat_log_printf (" user_max_length. . . : %d", ptr_server->user_max_length);
weechat_log_printf (" host_max_length. . . : %d", ptr_server->host_max_length);
weechat_log_printf (" casemapping. . . . . : %d (%s)",
ptr_server->casemapping,
irc_server_casemapping_string[ptr_server->casemapping]);
weechat_log_printf (" chantypes. . . . . . : '%s'", ptr_server->chantypes);
weechat_log_printf (" chanmodes. . . . . . : '%s'", ptr_server->chanmodes);
weechat_log_printf (" monitor. . . . . . . : %d", ptr_server->monitor);
weechat_log_printf (" monitor_time . . . . : %lld", (long long)ptr_server->monitor_time);
weechat_log_printf (" reconnect_delay. . . : %d", ptr_server->reconnect_delay);
weechat_log_printf (" reconnect_start. . . : %lld", (long long)ptr_server->reconnect_start);
weechat_log_printf (" command_time . . . . : %lld", (long long)ptr_server->command_time);
weechat_log_printf (" reconnect_join . . . : %d", ptr_server->reconnect_join);
weechat_log_printf (" disable_autojoin . . : %d", ptr_server->disable_autojoin);
weechat_log_printf (" is_away. . . . . . . : %d", ptr_server->is_away);
weechat_log_printf (" away_message . . . . : '%s'", ptr_server->away_message);
weechat_log_printf (" away_time. . . . . . : %lld", (long long)ptr_server->away_time);
weechat_log_printf (" lag. . . . . . . . . : %d", ptr_server->lag);
weechat_log_printf (" lag_displayed. . . . : %d", ptr_server->lag_displayed);
weechat_log_printf (" lag_check_time . . . : tv_sec:%d, tv_usec:%d",
ptr_server->lag_check_time.tv_sec,
ptr_server->lag_check_time.tv_usec);
weechat_log_printf (" lag_next_check . . . : %lld", (long long)ptr_server->lag_next_check);
weechat_log_printf (" lag_last_refresh . . : %lld", (long long)ptr_server->lag_last_refresh);
weechat_log_printf (" cmd_list_regexp. . . : 0x%lx", ptr_server->cmd_list_regexp);
weechat_log_printf (" last_user_message. . : %lld", (long long)ptr_server->last_user_message);
weechat_log_printf (" last_away_check. . . : %lld", (long long)ptr_server->last_away_check);
weechat_log_printf (" last_data_purge. . . : %lld", (long long)ptr_server->last_data_purge);
for (i = 0; i < IRC_SERVER_NUM_OUTQUEUES_PRIO; i++)
{
weechat_log_printf (" outqueue[%02d] . . . . : 0x%lx", i, ptr_server->outqueue[i]);
weechat_log_printf (" last_outqueue[%02d]. . : 0x%lx", i, ptr_server->last_outqueue[i]);
}
weechat_log_printf (" redirects. . . . . . : 0x%lx", ptr_server->redirects);
weechat_log_printf (" last_redirect. . . . : 0x%lx", ptr_server->last_redirect);
weechat_log_printf (" notify_list. . . . . : 0x%lx", ptr_server->notify_list);
weechat_log_printf (" last_notify. . . . . : 0x%lx", ptr_server->last_notify);
weechat_log_printf (" notify_count . . . . : %d", ptr_server->notify_count);
weechat_log_printf (" join_manual. . . . . : 0x%lx (hashtable: '%s')",
ptr_server->join_manual,
weechat_hashtable_get_string (ptr_server->join_manual, "keys_values"));
weechat_log_printf (" join_channel_key . . : 0x%lx (hashtable: '%s')",
ptr_server->join_channel_key,
weechat_hashtable_get_string (ptr_server->join_channel_key, "keys_values"));
weechat_log_printf (" join_noswitch. . . . : 0x%lx (hashtable: '%s')",
ptr_server->join_noswitch,
weechat_hashtable_get_string (ptr_server->join_noswitch, "keys_values"));
weechat_log_printf (" buffer . . . . . . . : 0x%lx", ptr_server->buffer);
weechat_log_printf (" buffer_as_string . . : 0x%lx", ptr_server->buffer_as_string);
weechat_log_printf (" channels . . . . . . : 0x%lx", ptr_server->channels);
weechat_log_printf (" last_channel . . . . : 0x%lx", ptr_server->last_channel);
weechat_log_printf (" prev_server. . . . . : 0x%lx", ptr_server->prev_server);
weechat_log_printf (" next_server. . . . . : 0x%lx", ptr_server->next_server);
irc_redirect_print_log (ptr_server);
irc_notify_print_log (ptr_server);
for (ptr_channel = ptr_server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
irc_channel_print_log (ptr_channel);
}
}
}