1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-02 07:46:38 +02:00

relay: fix freeze when writing on relay socket (use non-blocking sockets in relay for irc and weechat protocols) (bug #36655)

This commit is contained in:
Sebastien Helleu
2012-07-14 22:41:52 +02:00
parent f4dc85a3cb
commit d247d773db
21 changed files with 512 additions and 252 deletions
+4 -2
View File
@@ -1183,6 +1183,8 @@ irc_server_outqueue_free (struct t_irc_server *server,
if (outqueue->tags)
free (outqueue->tags);
free (outqueue);
/* set new head */
server->outqueue[priority] = new_outqueue;
}
@@ -1561,7 +1563,7 @@ irc_server_send (struct t_irc_server *server, const char *buffer, int size_buf)
if (server->ssl_connected)
{
weechat_printf (server->buffer,
_("%s%s: sending data to server: %d %s"),
_("%s%s: sending data to server: error %d %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
rc,
gnutls_strerror (rc));
@@ -1570,7 +1572,7 @@ irc_server_send (struct t_irc_server *server, const char *buffer, int size_buf)
#endif
{
weechat_printf (server->buffer,
_("%s%s: sending data to server: %d %s"),
_("%s%s: sending data to server: error %d %s"),
weechat_prefix ("error"), IRC_PLUGIN_NAME,
errno,
strerror (errno));
+5 -22
View File
@@ -136,22 +136,20 @@ end:
* relay_irc_sendf: send formatted data to client
*/
int
void
relay_irc_sendf (struct t_relay_client *client, const char *format, ...)
{
int length, num_sent, total_sent, number;
int length, number;
char *pos, hash_key[32], *message;
const char *str_message;
struct t_hashtable *hashtable_in, *hashtable_out;
if (!client)
return 0;
return;
weechat_va_format (format);
if (!vbuffer)
return 0;
total_sent = 0;
return;
pos = strchr (vbuffer, '\r');
if (pos)
@@ -186,16 +184,7 @@ relay_irc_sendf (struct t_relay_client *client, const char *format, ...)
if (message)
{
snprintf (message, length, "%s\r\n", str_message);
num_sent = send (client->sock, message, strlen (message), 0);
if (num_sent >= 0)
total_sent += num_sent;
else
{
weechat_printf (NULL,
_("%s%s: error sending data to client: %s"),
weechat_prefix ("error"), RELAY_PLUGIN_NAME,
strerror (errno));
}
relay_client_send (client, message, strlen (message));
free (message);
}
number++;
@@ -205,13 +194,7 @@ relay_irc_sendf (struct t_relay_client *client, const char *format, ...)
weechat_hashtable_free (hashtable_in);
}
client->bytes_sent += total_sent;
free (vbuffer);
relay_buffer_refresh (NULL);
return total_sent;
}
/*
+252 -1
View File
@@ -155,7 +155,250 @@ relay_client_recv_cb (void *arg_client, int fd)
}
else
{
relay_client_set_status (client, RELAY_STATUS_DISCONNECTED);
if ((num_read == 0)
|| ((errno != EAGAIN) && (errno != EWOULDBLOCK)))
{
weechat_printf (NULL,
_("%s%s: reading data on socket for client %d: "
"error %d %s"),
weechat_prefix ("error"), RELAY_PLUGIN_NAME,
client->id,
errno,
(num_read == 0) ? _("(connection closed by peer)") :
strerror (errno));
relay_client_set_status (client, RELAY_STATUS_DISCONNECTED);
}
}
return WEECHAT_RC_OK;
}
/*
* relay_client_outqueue_add: add a message in out queue
*/
void
relay_client_outqueue_add (struct t_relay_client *client, const char *data,
int data_size)
{
struct t_relay_client_outqueue *new_outqueue;
if (!client || !data || (data_size <= 0))
return;
new_outqueue = malloc (sizeof (*new_outqueue));
if (new_outqueue)
{
new_outqueue->data = malloc (data_size);
if (!new_outqueue->data)
{
free (new_outqueue);
return;
}
memcpy (new_outqueue->data, data, data_size);
new_outqueue->data_size = data_size;
new_outqueue->prev_outqueue = client->last_outqueue;
new_outqueue->next_outqueue = NULL;
if (client->outqueue)
client->last_outqueue->next_outqueue = new_outqueue;
else
client->outqueue = new_outqueue;
client->last_outqueue = new_outqueue;
}
}
/*
* relay_client_outqueue_free: free a message in out queue
*/
void
relay_client_outqueue_free (struct t_relay_client *client,
struct t_relay_client_outqueue *outqueue)
{
struct t_relay_client_outqueue *new_outqueue;
/* remove outqueue message */
if (client->last_outqueue == outqueue)
client->last_outqueue = outqueue->prev_outqueue;
if (outqueue->prev_outqueue)
{
(outqueue->prev_outqueue)->next_outqueue = outqueue->next_outqueue;
new_outqueue = client->outqueue;
}
else
new_outqueue = outqueue->next_outqueue;
if (outqueue->next_outqueue)
(outqueue->next_outqueue)->prev_outqueue = outqueue->prev_outqueue;
/* free data */
if (outqueue->data)
free (outqueue->data);
free (outqueue);
/* set new head */
client->outqueue = new_outqueue;
}
/*
* relay_client_outqueue_free_all: free all outqueued messages
*/
void
relay_client_outqueue_free_all (struct t_relay_client *client)
{
while (client->outqueue)
{
relay_client_outqueue_free (client, client->outqueue);
}
}
/*
* relay_client_send: send data to client (add in outqueue if it's impossible
* to send now)
* return number of bytes sent to client
*/
int
relay_client_send (struct t_relay_client *client, const char *data,
int data_size)
{
int num_sent;
if (client->sock < 0)
return -1;
num_sent = -1;
/*
* if outqueue is not empty, add to outqueue
* (because message must be sent *after* messages already in outqueue)
*/
if (client->outqueue)
{
relay_client_outqueue_add (client, data, data_size);
}
else
{
num_sent = send (client->sock, data, data_size, 0);
if (num_sent >= 0)
{
if (num_sent > 0)
{
client->bytes_sent += num_sent;
relay_buffer_refresh (NULL);
}
if (num_sent < data_size)
{
/* some data was not sent, add it to outqueue */
relay_client_outqueue_add (client, data + num_sent,
data_size - num_sent);
}
}
else if (num_sent < 0)
{
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
{
/* add message to queue (will be sent later) */
relay_client_outqueue_add (client, data, data_size);
}
else
{
weechat_printf (NULL,
_("%s%s: sending data to client %d: error %d %s"),
weechat_prefix ("error"), RELAY_PLUGIN_NAME,
client->id,
errno, strerror (errno));
relay_client_set_status (client, RELAY_STATUS_DISCONNECTED);
}
}
}
return num_sent;
}
/*
* relay_client_timer_cb: timer called each second to perform some operations
* on clients
*/
int
relay_client_timer_cb (void *data, int remaining_calls)
{
struct t_relay_client *ptr_client;
int num_sent;
char *buf;
/* make C compiler happy */
(void) data;
(void) remaining_calls;
for (ptr_client = relay_clients; ptr_client;
ptr_client = ptr_client->next_client)
{
if (ptr_client->sock >= 0)
{
while (ptr_client->outqueue)
{
num_sent = send (ptr_client->sock, ptr_client->outqueue->data,
ptr_client->outqueue->data_size, 0);
if (num_sent >= 0)
{
if (num_sent > 0)
{
ptr_client->bytes_sent += num_sent;
relay_buffer_refresh (NULL);
}
if (num_sent == ptr_client->outqueue->data_size)
{
/* whole data sent, remove outqueue */
relay_client_outqueue_free (ptr_client,
ptr_client->outqueue);
}
else
{
/*
* some data was not sent, update outqueue and stop
* sending data from outqueue
*/
if (num_sent > 0)
{
buf = malloc (ptr_client->outqueue->data_size - num_sent);
if (buf)
{
memcpy (buf,
ptr_client->outqueue->data + num_sent,
ptr_client->outqueue->data_size - num_sent);
free (ptr_client->outqueue->data);
ptr_client->outqueue->data = buf;
ptr_client->outqueue->data_size = ptr_client->outqueue->data_size - num_sent;
}
}
break;
}
}
else if (num_sent < 0)
{
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
{
/* we will retry later this client's queue */
break;
}
else
{
weechat_printf (NULL,
_("%s%s: sending data to client %d: error %d %s"),
weechat_prefix ("error"),
RELAY_PLUGIN_NAME,
ptr_client->id,
errno, strerror (errno));
relay_client_set_status (ptr_client,
RELAY_STATUS_DISCONNECTED);
}
}
}
}
}
return WEECHAT_RC_OK;
@@ -200,6 +443,9 @@ relay_client_new (int sock, const char *address, struct t_relay_server *server)
break;
}
new_client->outqueue = NULL;
new_client->last_outqueue = NULL;
new_client->prev_client = NULL;
new_client->next_client = relay_clients;
if (relay_clients)
@@ -259,6 +505,8 @@ relay_client_set_status (struct t_relay_client *client,
{
client->end_time = time (NULL);
relay_client_outqueue_free_all (client);
if (client->hook_fd)
{
weechat_unhook (client->hook_fd);
@@ -360,6 +608,7 @@ relay_client_free (struct t_relay_client *client)
break;
}
}
relay_client_outqueue_free_all (client);
free (client);
@@ -525,6 +774,8 @@ relay_client_print_log ()
case RELAY_NUM_PROTOCOLS:
break;
}
weechat_log_printf (" outqueue. . . . . . . : 0x%lx", ptr_client->outqueue);
weechat_log_printf (" last_outqueue . . . . : 0x%lx", ptr_client->last_outqueue);
weechat_log_printf (" prev_client . . . . . : 0x%lx", ptr_client->prev_client);
weechat_log_printf (" next_client . . . . . : 0x%lx", ptr_client->next_client);
}
+15
View File
@@ -41,6 +41,16 @@ enum t_relay_status
((client->status == RELAY_STATUS_AUTH_FAILED) || \
(client->status == RELAY_STATUS_DISCONNECTED))
/* output queue of messages to client */
struct t_relay_client_outqueue
{
char *data; /* data to send */
int data_size; /* number of bytes */
struct t_relay_client_outqueue *next_outqueue; /* next msg in queue */
struct t_relay_client_outqueue *prev_outqueue; /* prev msg in queue */
};
/* relay client */
struct t_relay_client
@@ -60,6 +70,8 @@ struct t_relay_client
unsigned long bytes_recv; /* bytes received from client */
unsigned long bytes_sent; /* bytes sent to client */
void *protocol_data; /* data depending on protocol used */
struct t_relay_client_outqueue *outqueue; /* queue for outgoing msgs */
struct t_relay_client_outqueue *last_outqueue; /* last outgoing msg */
struct t_relay_client *prev_client;/* link to previous client */
struct t_relay_client *next_client;/* link to next client */
};
@@ -73,6 +85,9 @@ extern int relay_client_valid (struct t_relay_client *client);
extern struct t_relay_client *relay_client_search_by_number (int number);
extern struct t_relay_client *relay_client_search_by_id (int id);
extern int relay_client_recv_cb (void *arg_client, int fd);
extern int relay_client_send (struct t_relay_client *client, const char *data,
int data_size);
extern int relay_client_timer_cb (void *data, int remaining_calls);
extern struct t_relay_client *relay_client_new (int sock, const char *address,
struct t_relay_server *server);
extern void relay_client_set_status (struct t_relay_client *client,
+9 -1
View File
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -171,7 +172,7 @@ relay_server_sock_cb (void *data, int fd)
struct t_relay_server *server;
struct sockaddr_in client_addr;
socklen_t client_length;
int client_fd;
int client_fd, flags;
char ipv4_address[INET_ADDRSTRLEN + 1], *ptr_address;
/* make C compiler happy */
@@ -222,6 +223,13 @@ relay_server_sock_cb (void *data, int fd)
return WEECHAT_RC_OK;
}
/* set non-blocking mode for socket */
flags = fcntl (client_fd, F_GETFL);
if (flags == -1)
flags = 0;
fcntl (client_fd, F_SETFL, flags | O_NONBLOCK);
/* add the client */
relay_client_new (client_fd, ptr_address, server);
return WEECHAT_RC_OK;
+8
View File
@@ -50,6 +50,8 @@ int relay_signal_upgrade_received = 0; /* signal "upgrade" received ? */
char *relay_protocol_string[] = /* strings for protocols */
{ "weechat", "irc" };
struct t_hook *relay_hook_timer = NULL;
/*
* relay_protocol_search: search a protocol by name
@@ -172,6 +174,9 @@ weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[])
if (upgrading)
relay_upgrade_load ();
relay_hook_timer = weechat_hook_timer (1 * 1000, 0, 0,
&relay_client_timer_cb, NULL);
return WEECHAT_RC_OK;
}
@@ -185,6 +190,9 @@ weechat_plugin_end (struct t_weechat_plugin *plugin)
/* make C compiler happy */
(void) plugin;
if (relay_hook_timer)
weechat_unhook (relay_hook_timer);
relay_config_write ();
if (relay_signal_upgrade_received)
+14 -88
View File
@@ -24,7 +24,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -909,12 +908,10 @@ relay_weechat_msg_add_nicklist (struct t_relay_weechat_msg *msg,
void
relay_weechat_msg_send (struct t_relay_client *client,
struct t_relay_weechat_msg *msg,
int display_in_raw_buffer)
struct t_relay_weechat_msg *msg)
{
uint32_t size32;
char compression;
int num_sent;
#ifdef HAVE_ZLIB
int rc;
Bytef *dest;
@@ -942,31 +939,17 @@ relay_weechat_msg_send (struct t_relay_client *client,
memcpy (dest, &size32, 4);
dest[4] = 1;
/* send compressed data */
num_sent = send (client->sock, dest, dest_size + 5, 0);
/* display message in raw buffer */
if (display_in_raw_buffer)
{
relay_raw_print (client, RELAY_RAW_FLAG_SEND,
"obj: %d/%d bytes (%d%%, %ldms), id: %s",
(int)dest_size + 5,
msg->data_size,
100 - ((((int)dest_size + 5) * 100) / msg->data_size),
time_diff,
msg->id);
if (num_sent < 0)
{
relay_raw_print (client, RELAY_RAW_FLAG_SEND,
"error: %s", strerror (errno));
}
}
relay_raw_print (client, RELAY_RAW_FLAG_SEND,
"obj: %d/%d bytes (%d%%, %ldms), id: %s",
(int)dest_size + 5,
msg->data_size,
100 - ((((int)dest_size + 5) * 100) / msg->data_size),
time_diff,
msg->id);
if (num_sent > 0)
{
client->bytes_sent += num_sent;
relay_buffer_refresh (NULL);
}
/* send compressed data */
relay_client_send (client, (const char *)dest, dest_size + 5);
free (dest);
return;
@@ -982,26 +965,12 @@ relay_weechat_msg_send (struct t_relay_client *client,
compression = 0;
relay_weechat_msg_set_bytes (msg, 4, &compression, 1);
/* send uncompressed data */
num_sent = send (client->sock, msg->data, msg->data_size, 0);
/* display message in raw buffer */
if (display_in_raw_buffer)
{
relay_raw_print (client, RELAY_RAW_FLAG_SEND,
"obj: %d bytes", msg->data_size);
if (num_sent < 0)
{
relay_raw_print (client, RELAY_RAW_FLAG_SEND,
"error: %s", strerror (errno));
}
}
relay_raw_print (client, RELAY_RAW_FLAG_SEND,
"obj: %d bytes", msg->data_size);
if (num_sent > 0)
{
client->bytes_sent += num_sent;
relay_buffer_refresh (NULL);
}
/* send uncompressed data */
relay_client_send (client, msg->data, msg->data_size);
}
/*
@@ -1018,46 +987,3 @@ relay_weechat_msg_free (struct t_relay_weechat_msg *msg)
free (msg);
}
/*
* relay_weechat_sendf: send formatted data to client
*/
int
relay_weechat_sendf (struct t_relay_client *client, const char *format, ...)
{
char str_length[8];
int length_vbuffer, num_sent, total_sent;
if (!client)
return 0;
weechat_va_format (format);
if (!vbuffer)
return 0;
length_vbuffer = strlen (vbuffer);
total_sent = 0;
snprintf (str_length, sizeof (str_length), "%07d", length_vbuffer);
num_sent = send (client->sock, str_length, 7, 0);
client->bytes_sent += 7;
total_sent += num_sent;
if (num_sent >= 0)
{
num_sent = send (client->sock, vbuffer, length_vbuffer, 0);
client->bytes_sent += length_vbuffer;
total_sent += num_sent;
}
if (num_sent < 0)
{
weechat_printf (NULL,
_("%s%s: error sending data to client %d (%s)"),
weechat_prefix ("error"), RELAY_PLUGIN_NAME,
client->id, strerror (errno));
}
return total_sent;
}
@@ -74,8 +74,7 @@ extern void relay_weechat_msg_add_infolist (struct t_relay_weechat_msg *msg,
extern void relay_weechat_msg_add_nicklist (struct t_relay_weechat_msg *msg,
struct t_gui_buffer *buffer);
extern void relay_weechat_msg_send (struct t_relay_client *client,
struct t_relay_weechat_msg *msg,
int display_in_raw_buffer);
struct t_relay_weechat_msg *msg);
extern void relay_weechat_msg_free (struct t_relay_weechat_msg *msg);
#endif /* __WEECHAT_RELAY_WEECHAT_MSG_H */
@@ -145,7 +145,7 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(hdata)
{
relay_weechat_msg_add_hdata (msg, argv[0],
(argc > 1) ? argv_eol[1] : NULL);
relay_weechat_msg_send (client, msg, 1);
relay_weechat_msg_send (client, msg);
relay_weechat_msg_free (msg);
}
@@ -171,7 +171,7 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(info)
relay_weechat_msg_add_type (msg, RELAY_WEECHAT_MSG_OBJ_INFO);
relay_weechat_msg_add_string (msg, argv[0]);
relay_weechat_msg_add_string (msg, info);
relay_weechat_msg_send (client, msg, 1);
relay_weechat_msg_send (client, msg);
relay_weechat_msg_free (msg);
}
@@ -205,7 +205,7 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(infolist)
args = argv_eol[2];
}
relay_weechat_msg_add_infolist (msg, argv[0], (void *)value, args);
relay_weechat_msg_send (client, msg, 1);
relay_weechat_msg_send (client, msg);
relay_weechat_msg_free (msg);
}
@@ -236,7 +236,7 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(nicklist)
if (msg)
{
relay_weechat_msg_add_nicklist (msg, ptr_buffer);
relay_weechat_msg_send (client, msg, 1);
relay_weechat_msg_send (client, msg);
relay_weechat_msg_free (msg);
}
@@ -306,7 +306,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
"number,full_name,short_name,"
"nicklist,title,local_variables,"
"prev_buffer,next_buffer");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -323,7 +323,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
"buffer:0x%lx", (long unsigned int)ptr_buffer);
relay_weechat_msg_add_hdata (msg, cmd_hdata,
"number,full_name,type");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -341,7 +341,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
relay_weechat_msg_add_hdata (msg, cmd_hdata,
"number,full_name,"
"prev_buffer,next_buffer");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -360,7 +360,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
relay_weechat_msg_add_hdata (msg, cmd_hdata,
"number,full_name,"
"prev_buffer,next_buffer");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -378,7 +378,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
relay_weechat_msg_add_hdata (msg, cmd_hdata,
"number,full_name,short_name,"
"local_variables");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -395,7 +395,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
"buffer:0x%lx", (long unsigned int)ptr_buffer);
relay_weechat_msg_add_hdata (msg, cmd_hdata,
"number,full_name,title");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -412,7 +412,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
"buffer:0x%lx", (long unsigned int)ptr_buffer);
relay_weechat_msg_add_hdata (msg, cmd_hdata,
"number,full_name,local_variables");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -436,7 +436,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
ptr_buffer = weechat_hdata_pointer (ptr_hdata_line_data, ptr_line_data,
"buffer");
if (!ptr_buffer)
if (!ptr_buffer || (relay_raw_buffer && (ptr_buffer == relay_raw_buffer)))
return WEECHAT_RC_OK;
/* check if buffer is synchronized (== able to receive events) */
@@ -456,7 +456,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
"buffer,date,date_printed,"
"displayed,highlight,prefix,"
"message");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -476,7 +476,7 @@ relay_weechat_protocol_signal_buffer_cb (void *data, const char *signal,
cmd_hdata + 7);
relay_weechat_msg_add_hdata (msg, cmd_hdata,
"number,full_name");
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -523,7 +523,7 @@ relay_weechat_protocol_nicklist_map_cb (void *data,
if (msg)
{
relay_weechat_msg_add_nicklist (msg, (struct t_gui_buffer *)buffer);
relay_weechat_msg_send (ptr_client, msg, 1);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -628,7 +628,7 @@ relay_weechat_protocol_signal_upgrade_cb (void *data, const char *signal,
msg = relay_weechat_msg_new (str_signal);
if (msg)
{
relay_weechat_msg_send (ptr_client, msg, 0);
relay_weechat_msg_send (ptr_client, msg);
relay_weechat_msg_free (msg);
}
}
@@ -828,7 +828,7 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(test)
relay_weechat_msg_add_pointer (msg, &msg);
relay_weechat_msg_add_type (msg, RELAY_WEECHAT_MSG_OBJ_TIME);
relay_weechat_msg_add_time (msg, 1321993456);
relay_weechat_msg_send (client, msg, 1);
relay_weechat_msg_send (client, msg);
relay_weechat_msg_free (msg);
}