weechat/src/plugins/xfer/xfer.c

1911 lines
59 KiB
C

/*
* xfer.c - file transfer and direct chat plugin for WeeChat
*
* Copyright (C) 2003-2020 Sébastien Helleu <flashcode@flashtux.org>
*
* 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 <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <resolv.h>
#include <gcrypt.h>
#include <arpa/inet.h>
#include "../weechat-plugin.h"
#include "xfer.h"
#include "xfer-buffer.h"
#include "xfer-command.h"
#include "xfer-completion.h"
#include "xfer-config.h"
#include "xfer-file.h"
#include "xfer-info.h"
#include "xfer-network.h"
#include "xfer-upgrade.h"
WEECHAT_PLUGIN_NAME(XFER_PLUGIN_NAME);
WEECHAT_PLUGIN_DESCRIPTION(N_("DCC file transfer and direct chat"));
WEECHAT_PLUGIN_AUTHOR("Sébastien Helleu <flashcode@flashtux.org>");
WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION);
WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE);
WEECHAT_PLUGIN_PRIORITY(7000);
struct t_weechat_plugin *weechat_xfer_plugin = NULL;
char *xfer_type_string[] = /* strings for types */
{ "file_recv", "file_send", "chat_recv",
"chat_send"
};
char *xfer_protocol_string[] = /* strings for protocols */
{ "none", "dcc"
};
char *xfer_status_string[] = /* strings for status */
{ N_("waiting"), N_("connecting"),
N_("active"), N_("done"), N_("failed"),
N_("aborted"), N_("hashing")
};
char *xfer_hash_status_string[] = /* strings for hash status */
{ "?",
N_("CRC in progress"), N_("CRC OK"),
N_("wrong CRC"), N_("CRC error")
};
struct t_xfer *xfer_list = NULL; /* list of files/chats */
struct t_xfer *last_xfer = NULL; /* last file/chat in list */
int xfer_count = 0; /* number of xfer */
int xfer_signal_upgrade_received = 0; /* signal "upgrade" received ? */
void xfer_disconnect_all ();
/*
* Checks if a xfer pointer is valid.
*
* Returns:
* 1: xfer exists
* 0: xfer does not exist
*/
int
xfer_valid (struct t_xfer *xfer)
{
struct t_xfer *ptr_xfer;
if (!xfer)
return 0;
for (ptr_xfer = xfer_list; ptr_xfer;
ptr_xfer = ptr_xfer->next_xfer)
{
if (ptr_xfer == xfer)
return 1;
}
/* xfer not found */
return 0;
}
/*
* Callback for signal "upgrade".
*/
int
xfer_signal_upgrade_cb (const void *pointer, void *data,
const char *signal, const char *type_data,
void *signal_data)
{
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
xfer_signal_upgrade_received = 1;
if (signal_data && (strcmp (signal_data, "quit") == 0))
xfer_disconnect_all ();
return WEECHAT_RC_OK;
}
/*
* Creates directories for xfer plugin.
*/
void
xfer_create_directories ()
{
char *path;
/* create download directory */
path = weechat_string_eval_path_home (
weechat_config_string (xfer_config_file_download_path),
NULL, NULL, NULL);
if (path)
{
(void) weechat_mkdir_parents (path, 0700);
free (path);
}
/* create upload directory */
path = weechat_string_eval_path_home (
weechat_config_string (xfer_config_file_upload_path),
NULL, NULL, NULL);
if (path)
{
(void) weechat_mkdir_parents (path, 0700);
free (path);
}
}
/*
* Searches for xfer type.
*
* Returns index of type in enum t_xfer_type, -1 if not found.
*/
int
xfer_search_type (const char *type)
{
int i;
for (i = 0; i < XFER_NUM_TYPES; i++)
{
if (weechat_strcasecmp (xfer_type_string[i], type) == 0)
return i;
}
/* xfer type not found */
return -1;
}
/*
* Searches for xfer protocol.
*
* Returns index of protocol in enum t_xfer_protocol, -1 if not found.
*/
int
xfer_search_protocol (const char *protocol)
{
int i;
for (i = 0; i < XFER_NUM_PROTOCOLS; i++)
{
if (weechat_strcasecmp (xfer_protocol_string[i], protocol) == 0)
return i;
}
/* xfer protocol not found */
return -1;
}
/*
* Searches for a xfer.
*
* Returns pointer to xfer found, NULL if not found.
*/
struct t_xfer *
xfer_search (const char *plugin_name, const char *plugin_id, enum t_xfer_type type,
enum t_xfer_status status, int port)
{
struct t_xfer *ptr_xfer;
for (ptr_xfer = xfer_list; ptr_xfer; ptr_xfer = ptr_xfer->next_xfer)
{
if ((weechat_strcasecmp (ptr_xfer->plugin_name, plugin_name) == 0)
&& (weechat_strcasecmp (ptr_xfer->plugin_id, plugin_id) == 0)
&& (ptr_xfer->type == type)
&& (ptr_xfer->status = status)
&& (ptr_xfer->port == port))
return ptr_xfer;
}
/* xfer not found */
return NULL;
}
/*
* Searches for a xfer by number (first xfer is 0).
*
* Returns pointer to xfer found, NULL if not found.
*/
struct t_xfer *
xfer_search_by_number (int number)
{
struct t_xfer *ptr_xfer;
int i;
i = 0;
for (ptr_xfer = xfer_list; ptr_xfer; ptr_xfer = ptr_xfer->next_xfer)
{
if (i == number)
return ptr_xfer;
i++;
}
/* xfer not found */
return NULL;
}
/*
* Searches for a xfer by buffer (for chat only).
*
* Returns pointer to xfer found, NULL if not found.
*/
struct t_xfer *
xfer_search_by_buffer (struct t_gui_buffer *buffer)
{
struct t_xfer *ptr_xfer;
if (!buffer)
return NULL;
for (ptr_xfer = xfer_list; ptr_xfer; ptr_xfer = ptr_xfer->next_xfer)
{
if (ptr_xfer->buffer == buffer)
return ptr_xfer;
}
/* xfer not found */
return NULL;
}
/*
* Closes a xfer.
*/
void
xfer_close (struct t_xfer *xfer, enum t_xfer_status status)
{
struct stat st;
xfer->status = status;
if (XFER_HAS_ENDED(xfer->status))
{
if (xfer->hook_fd)
{
weechat_unhook (xfer->hook_fd);
xfer->hook_fd = NULL;
}
if (xfer->hook_timer)
{
weechat_unhook (xfer->hook_timer);
xfer->hook_timer = NULL;
}
if (xfer->hook_connect)
{
weechat_unhook (xfer->hook_connect);
xfer->hook_connect = NULL;
}
if (XFER_IS_FILE(xfer->type))
{
weechat_printf (NULL,
_("%s%s: file %s %s %s (%s): %s"),
(xfer->status == XFER_STATUS_DONE) ? "" : weechat_prefix ("error"),
XFER_PLUGIN_NAME,
xfer->filename,
(xfer->type == XFER_TYPE_FILE_SEND) ? _("sent to") : _("received from"),
xfer->remote_nick,
xfer->remote_address_str,
(xfer->status == XFER_STATUS_DONE) ? _("OK") : _("FAILED"));
xfer_network_child_kill (xfer);
}
}
if (xfer->status == XFER_STATUS_ABORTED)
{
if (XFER_IS_CHAT(xfer->type))
{
weechat_printf (xfer->buffer,
_("%s%s: chat closed with %s "
"(%s)"),
weechat_prefix ("network"),
XFER_PLUGIN_NAME,
xfer->remote_nick,
xfer->remote_address_str);
}
}
/* remove empty file if received file failed and nothing was transferred */
if (((xfer->status == XFER_STATUS_FAILED)
|| (xfer->status == XFER_STATUS_ABORTED))
&& XFER_IS_FILE(xfer->type)
&& XFER_IS_RECV(xfer->type)
&& xfer->temp_local_filename
&& xfer->pos == 0)
{
/* erase file only if really empty on disk */
if (stat (xfer->temp_local_filename, &st) != -1)
{
if ((unsigned long long) st.st_size == 0)
unlink (xfer->temp_local_filename);
}
}
/* rename received file if it has a suffix */
if ((xfer->status == XFER_STATUS_DONE)
&& XFER_IS_FILE(xfer->type)
&& XFER_IS_RECV(xfer->type)
&& (strcmp (xfer->local_filename, xfer->temp_local_filename) != 0))
{
rename (xfer->temp_local_filename, xfer->local_filename);
}
if (XFER_IS_FILE(xfer->type))
xfer_file_calculate_speed (xfer, 1);
if (xfer->sock >= 0)
{
close (xfer->sock);
xfer->sock = -1;
}
if (xfer->file >= 0)
{
close (xfer->file);
xfer->file = -1;
}
if (XFER_HAS_ENDED(xfer->status))
xfer_send_signal (xfer, "xfer_ended");
}
/*
* Disconnects all active xfer (with a socket).
*/
void
xfer_disconnect_all ()
{
struct t_xfer *ptr_xfer;
for (ptr_xfer = xfer_list; ptr_xfer; ptr_xfer = ptr_xfer->next_xfer)
{
if (ptr_xfer->sock >= 0)
{
if (ptr_xfer->status == XFER_STATUS_ACTIVE)
{
weechat_printf (NULL,
_("%s%s: aborting active xfer: \"%s\" from %s"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
ptr_xfer->filename, ptr_xfer->remote_nick);
weechat_log_printf (_("%s%s: aborting active xfer: \"%s\" from %s"),
"", XFER_PLUGIN_NAME,
ptr_xfer->filename,
ptr_xfer->remote_nick);
}
xfer_close (ptr_xfer, XFER_STATUS_FAILED);
}
}
}
/*
* Checks if a port is in used.
*
* Returns:
* 1: port is in used (by an active or connecting xfer)
* 0: port is not in used
*/
int
xfer_port_in_use (int port)
{
struct t_xfer *ptr_xfer;
/* skip any currently used ports */
for (ptr_xfer = xfer_list; ptr_xfer; ptr_xfer = ptr_xfer->next_xfer)
{
if ((ptr_xfer->port == port) && (!XFER_HAS_ENDED(ptr_xfer->status)))
return 1;
}
/* port not in use */
return 0;
}
/*
* Sends a signal for a xfer.
*/
void
xfer_send_signal (struct t_xfer *xfer, const char *signal)
{
struct t_infolist *infolist;
infolist = weechat_infolist_new ();
if (infolist)
{
if (xfer_add_to_infolist (infolist, xfer))
{
(void) weechat_hook_signal_send (signal,
WEECHAT_HOOK_SIGNAL_POINTER,
infolist);
}
weechat_infolist_free (infolist);
}
}
/*
* Allocates a new xfer.
*
* Returns pointer to new xfer, NULL if error.
*/
struct t_xfer *
xfer_alloc ()
{
struct t_xfer *new_xfer;
time_t time_now;
/* create new xfer struct */
if ((new_xfer = malloc (sizeof (*new_xfer))) == NULL)
return NULL;
time_now = time (NULL);
new_xfer->plugin_id = NULL;
new_xfer->plugin_name = NULL;
new_xfer->type = 0;
new_xfer->protocol = 0;
new_xfer->remote_nick = NULL;
new_xfer->local_nick = NULL;
new_xfer->charset_modifier = NULL;
new_xfer->filename = NULL;
new_xfer->size = 0;
new_xfer->proxy = NULL;
new_xfer->local_address = NULL;
new_xfer->local_address_length = 0;
new_xfer->local_address_str = NULL;
new_xfer->remote_address = NULL;
new_xfer->remote_address_length = 0;
new_xfer->remote_address_str = NULL;
new_xfer->port = 0;
new_xfer->status = 0;
new_xfer->buffer = NULL;
new_xfer->remote_nick_color = NULL;
new_xfer->fast_send = weechat_config_boolean (xfer_config_network_fast_send);
new_xfer->send_ack = weechat_config_boolean (xfer_config_network_send_ack);
new_xfer->blocksize = weechat_config_integer (xfer_config_network_blocksize);
new_xfer->start_time = time_now;
new_xfer->start_transfer = time_now;
new_xfer->sock = -1;
new_xfer->child_pid = 0;
new_xfer->child_read = -1;
new_xfer->child_write = -1;
new_xfer->hook_fd = NULL;
new_xfer->hook_timer = NULL;
new_xfer->hook_connect = NULL;
new_xfer->unterminated_message = NULL;
new_xfer->file = -1;
new_xfer->local_filename = NULL;
new_xfer->temp_local_filename = NULL;
new_xfer->filename_suffix = -1;
new_xfer->pos = 0;
new_xfer->ack = 0;
new_xfer->start_resume = 0;
new_xfer->last_check_time = time_now;
new_xfer->last_check_pos = time_now;
new_xfer->last_activity = 0;
new_xfer->bytes_per_sec = 0;
new_xfer->eta = 0;
new_xfer->hash_handle = NULL;
new_xfer->hash_target = NULL;
new_xfer->hash_status = XFER_HASH_STATUS_UNKNOWN;
new_xfer->prev_xfer = NULL;
new_xfer->next_xfer = xfer_list;
if (xfer_list)
xfer_list->prev_xfer = new_xfer;
else
last_xfer = new_xfer;
xfer_list = new_xfer;
xfer_count++;
return new_xfer;
}
/*
* Checks if the given server/nick is auto-accepted.
*
* Returns:
* 1: nick auto-accepted
* 0: nick not auto-accepted
*/
int
xfer_nick_auto_accepted (const char *server, const char *nick)
{
int rc, num_nicks, i;
char **nicks, *pos;
rc = 0;
nicks = weechat_string_split (
weechat_config_string (xfer_config_file_auto_accept_nicks),
",",
NULL,
WEECHAT_STRING_SPLIT_STRIP_LEFT
| WEECHAT_STRING_SPLIT_STRIP_RIGHT
| WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
0,
&num_nicks);
if (nicks)
{
for (i = 0; i < num_nicks; i++)
{
pos = strrchr (nicks[i], '.');
if (pos)
{
if ((weechat_strncasecmp (server, nicks[i], pos - nicks[i]) == 0)
&& (weechat_strcasecmp (nick, pos + 1) == 0))
{
rc = 1;
break;
}
}
else
{
if (weechat_strcasecmp (nick, nicks[i]) == 0)
{
rc = 1;
break;
}
}
}
weechat_string_free_split (nicks);
}
return rc;
}
/*
* Searches CRC32 in a filename.
*
* If more than one CRC32 are found, the last one is returned
* (with the higher index in filename).
*
* The chars before/after CRC32 must be either beginning/end of string or
* non-hexadecimal chars.
*
* Examples:
*
* test_filename => -1 (not found: no CRC32)
* test_1234abcd => 5 ("1234abcd")
* 1234abcd_test => 0 ("1234abcd")
* 1234abcd_12345678 => 9 ("12345678")
* 123456789abcdef => -1 (not found: missing delimiter around CRC32)
*
* Returns pointer to last CRC32 in string, NULL if no CRC32 was found.
*/
const char *
xfer_filename_crc32 (const char *filename)
{
int length;
const char *ptr_string, *ptr_crc32;
length = 0;
ptr_crc32 = NULL;
ptr_string = filename;
while (ptr_string && ptr_string[0])
{
if (((ptr_string[0] >= '0') && (ptr_string[0] <= '9'))
|| ((ptr_string[0] >= 'A') && (ptr_string[0] <= 'F'))
|| ((ptr_string[0] >= 'a') && (ptr_string[0] <= 'f')))
{
length++;
}
else
{
if (length == 8)
ptr_crc32 = ptr_string - 8;
length = 0;
}
ptr_string = weechat_utf8_next_char (ptr_string);
}
if (length == 8)
ptr_crc32 = ptr_string - 8;
return ptr_crc32;
}
/*
* Adds a xfer to list.
*
* Returns pointer to new xfer, NULL if error.
*/
struct t_xfer *
xfer_new (const char *plugin_name, const char *plugin_id,
enum t_xfer_type type, enum t_xfer_protocol protocol,
const char *remote_nick, const char *local_nick,
const char *charset_modifier, const char *filename,
unsigned long long size, const char *proxy, struct sockaddr *address,
socklen_t address_length, int port, int sock,
const char *local_filename)
{
struct t_xfer *new_xfer;
const char *ptr_crc32;
char str_address[NI_MAXHOST], *color;
int rc;
new_xfer = xfer_alloc ();
if (!new_xfer)
{
weechat_printf (NULL,
_("%s%s: not enough memory for new xfer"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
return NULL;
}
if (!xfer_buffer
&& weechat_config_boolean (xfer_config_look_auto_open_buffer))
{
xfer_buffer_open ();
}
/* initialize new xfer */
new_xfer->plugin_name = strdup (plugin_name);
new_xfer->plugin_id = strdup (plugin_id);
new_xfer->type = type;
new_xfer->protocol = protocol;
new_xfer->remote_nick = strdup (remote_nick);
color = weechat_info_get ("irc_nick_color_name", remote_nick);
new_xfer->remote_nick_color = (color) ? strdup (color) : NULL;
if (color)
free (color);
new_xfer->local_nick = (local_nick) ? strdup (local_nick) : NULL;
new_xfer->charset_modifier = (charset_modifier) ? strdup (charset_modifier) : NULL;
if (XFER_IS_FILE(type))
new_xfer->filename = (filename) ? strdup (filename) : NULL;
else
new_xfer->filename = strdup (_("xfer chat"));
new_xfer->size = size;
new_xfer->proxy = (proxy) ? strdup (proxy) : NULL;
new_xfer->port = port;
rc = getnameinfo ((struct sockaddr *)address, address_length, str_address,
sizeof (str_address), NULL, 0, NI_NUMERICHOST);
if (rc != 0)
{
weechat_printf (NULL,
_("%s%s: unable to interpret address: error %d %s"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
rc, gai_strerror (rc));
snprintf (str_address, sizeof (str_address), "?");
}
if (XFER_IS_RECV(type))
{
new_xfer->local_address_str = strdup ("");
xfer_set_remote_address (new_xfer, address, address_length, str_address);
}
else
{
xfer_set_local_address (new_xfer, address, address_length, str_address);
new_xfer->remote_address_str = strdup ("");
}
new_xfer->status = XFER_STATUS_WAITING;
new_xfer->sock = sock;
if (local_filename)
new_xfer->local_filename = strdup (local_filename);
else
xfer_file_find_filename (new_xfer);
new_xfer->hash_handle = NULL;
new_xfer->hash_target = NULL;
new_xfer->hash_status = XFER_HASH_STATUS_UNKNOWN;
if ((type == XFER_TYPE_FILE_RECV)
&& weechat_config_boolean (xfer_config_file_auto_check_crc32))
{
ptr_crc32 = xfer_filename_crc32 (new_xfer->filename);
if (ptr_crc32)
{
new_xfer->hash_handle = malloc (sizeof (gcry_md_hd_t));
if (new_xfer->hash_handle)
{
if (gcry_md_open (new_xfer->hash_handle, GCRY_MD_CRC32, 0) == 0)
{
new_xfer->hash_target = weechat_strndup (ptr_crc32, 8);
new_xfer->hash_status = XFER_HASH_STATUS_IN_PROGRESS;
}
else
{
free (new_xfer->hash_handle);
new_xfer->hash_handle = NULL;
weechat_printf (NULL,
_("%s%s: hashing error"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
}
}
}
}
/* write info message on core buffer */
switch (type)
{
case XFER_TYPE_FILE_RECV:
weechat_printf (NULL,
_("%s: incoming file from %s "
"(%s, %s.%s), name: %s, %llu bytes "
"(protocol: %s)"),
XFER_PLUGIN_NAME,
remote_nick,
str_address,
plugin_name,
plugin_id,
filename,
size,
xfer_protocol_string[protocol]);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
break;
case XFER_TYPE_FILE_SEND:
weechat_printf (NULL,
_("%s: offering file to %s (%s.%s), name: %s "
"(local filename: %s), %llu bytes (protocol: %s)"),
XFER_PLUGIN_NAME,
remote_nick,
plugin_name,
plugin_id,
filename,
local_filename,
size,
xfer_protocol_string[protocol]);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
break;
case XFER_TYPE_CHAT_RECV:
weechat_printf (NULL,
_("%s: incoming chat request from %s "
"(%s, %s.%s)"),
XFER_PLUGIN_NAME,
remote_nick,
str_address,
plugin_name,
plugin_id);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
break;
case XFER_TYPE_CHAT_SEND:
weechat_printf (NULL,
_("%s: sending chat request to %s (%s.%s)"),
XFER_PLUGIN_NAME,
remote_nick,
plugin_name,
plugin_id);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
break;
case XFER_NUM_TYPES:
break;
}
if (XFER_IS_FILE(type) && (!new_xfer->local_filename))
{
xfer_close (new_xfer, XFER_STATUS_FAILED);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
return NULL;
}
if (XFER_IS_FILE(type) && (new_xfer->start_resume > 0))
{
weechat_printf (NULL,
_("%s: file %s (local filename: %s) "
"will be resumed at position %llu"),
XFER_PLUGIN_NAME,
new_xfer->filename,
new_xfer->local_filename,
new_xfer->start_resume);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
}
/* connect if needed and display again xfer buffer */
if (XFER_IS_SEND(type))
{
if (!xfer_network_connect (new_xfer))
{
xfer_close (new_xfer, XFER_STATUS_FAILED);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
return NULL;
}
}
/*
* auto-accept file/chat if nick is auto-accepted, or if file/chat is
* auto-accepted
*/
if ((XFER_IS_RECV(type)
&& xfer_nick_auto_accepted (new_xfer->plugin_id, new_xfer->remote_nick))
|| ((type == XFER_TYPE_FILE_RECV)
&& weechat_config_boolean (xfer_config_file_auto_accept_files))
|| ((type == XFER_TYPE_CHAT_RECV)
&& weechat_config_boolean (xfer_config_file_auto_accept_chats)))
{
xfer_network_accept (new_xfer);
}
else
{
xfer_buffer_refresh (WEECHAT_HOTLIST_PRIVATE);
}
return new_xfer;
}
/*
* Sets the remote address field.
*/
void
xfer_set_remote_address (struct t_xfer *xfer, const struct sockaddr *address,
socklen_t length, const char *address_str)
{
if (xfer->remote_address)
free (xfer->remote_address);
xfer->remote_address = malloc (length);
xfer->remote_address_length = length;
if (xfer->remote_address)
memcpy (xfer->remote_address, address, length);
if (xfer->remote_address_str)
free (xfer->remote_address_str);
xfer->remote_address_str = strdup ((address_str) ? address_str : "");
}
/*
* Sets the local address field.
*/
void
xfer_set_local_address (struct t_xfer *xfer, const struct sockaddr *address,
socklen_t length, const char *address_str)
{
if (xfer->local_address)
free (xfer->local_address);
xfer->local_address = malloc (length);
xfer->local_address_length = length;
if (xfer->local_address)
memcpy (xfer->local_address, address, length);
if (xfer->local_address_str)
free (xfer->local_address_str);
xfer->local_address_str = strdup ((address_str) ? address_str : "");
}
/*
* Frees xfer struct and removes it from list.
*/
void
xfer_free (struct t_xfer *xfer)
{
struct t_xfer *new_xfer_list;
if (!xfer)
return;
/* remove xfer from list */
if (last_xfer == xfer)
last_xfer = xfer->prev_xfer;
if (xfer->prev_xfer)
{
(xfer->prev_xfer)->next_xfer = xfer->next_xfer;
new_xfer_list = xfer_list;
}
else
new_xfer_list = xfer->next_xfer;
if (xfer->next_xfer)
(xfer->next_xfer)->prev_xfer = xfer->prev_xfer;
/* free data */
if (xfer->plugin_id)
free (xfer->plugin_id);
if (xfer->plugin_name)
free (xfer->plugin_name);
if (xfer->remote_nick)
free (xfer->remote_nick);
if (xfer->local_nick)
free (xfer->local_nick);
if (xfer->charset_modifier)
free (xfer->charset_modifier);
if (xfer->filename)
free (xfer->filename);
if (xfer->proxy)
free (xfer->proxy);
if (xfer->local_address)
free (xfer->local_address);
if (xfer->local_address_str)
free (xfer->local_address_str);
if (xfer->remote_address)
free (xfer->remote_address);
if (xfer->remote_address_str)
free (xfer->remote_address_str);
if (xfer->buffer)
free (xfer->buffer);
if (xfer->remote_nick_color)
free (xfer->remote_nick_color);
if (xfer->hook_fd)
weechat_unhook (xfer->hook_fd);
if (xfer->hook_timer)
weechat_unhook (xfer->hook_timer);
if (xfer->hook_connect)
weechat_unhook (xfer->hook_connect);
if (xfer->unterminated_message)
free (xfer->unterminated_message);
if (xfer->local_filename)
free (xfer->local_filename);
if (xfer->temp_local_filename)
free (xfer->temp_local_filename);
if (xfer->hash_handle)
{
gcry_md_close (*xfer->hash_handle);
free (xfer->hash_handle);
}
if (xfer->hash_target)
free (xfer->hash_target);
free (xfer);
xfer_list = new_xfer_list;
xfer_count--;
if (xfer_buffer_selected_line >= xfer_count)
xfer_buffer_selected_line = (xfer_count == 0) ? 0 : xfer_count - 1;
}
/*
* Frees all xfers.
*/
void
xfer_free_all ()
{
while (xfer_list)
{
xfer_free (xfer_list);
}
}
/*
* Resolves address.
*
* Returns:
* 1: OK
* 0: error
*/
int
xfer_resolve_addr (const char *str_address, const char *str_port,
struct sockaddr *addr, socklen_t *addr_len, int ai_flags)
{
struct addrinfo *ainfo, hints;
int rc;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_flags = ai_flags;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
res_init ();
rc = getaddrinfo (str_address, str_port, &hints, &ainfo);
if ((rc == 0) && ainfo && ainfo->ai_addr)
{
if (ainfo->ai_addrlen > *addr_len)
{
weechat_printf (NULL,
_("%s%s: address \"%s\" resolved to a larger "
"sockaddr than expected"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
str_address);
freeaddrinfo (ainfo);
return 0;
}
memcpy (addr, ainfo->ai_addr, ainfo->ai_addrlen);
*addr_len = ainfo->ai_addrlen;
freeaddrinfo (ainfo);
return 1;
}
weechat_printf (NULL,
_("%s%s: invalid address \"%s\": error %d %s"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
str_address, rc, gai_strerror (rc));
if ((rc == 0) && ainfo)
freeaddrinfo (ainfo);
return 0;
}
/*
* Callback for signal "xfer_add".
*/
int
xfer_add_cb (const void *pointer, void *data,
const char *signal, const char *type_data,
void *signal_data)
{
struct t_infolist *infolist;
const char *plugin_name, *plugin_id, *str_type, *str_protocol;
const char *remote_nick, *local_nick, *charset_modifier, *filename, *proxy;
const char *str_address, *str_port;
int type, protocol, args, port_start, port_end, sock, port;
char *path, *filename2, *short_filename, *pos, str_port_temp[16];
struct stat st;
struct sockaddr_storage addr, own_ip_addr, bind_addr;
struct sockaddr *out_addr = (struct sockaddr*)&addr;
socklen_t length, bind_addr_len;
unsigned long long file_size;
struct t_xfer *ptr_xfer;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
if (!signal_data)
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME, "xfer_add");
return WEECHAT_RC_ERROR;
}
infolist = (struct t_infolist *)signal_data;
if (!weechat_infolist_next (infolist))
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME, "xfer_add");
return WEECHAT_RC_ERROR;
}
filename2 = NULL;
short_filename = NULL;
file_size = 0;
sock = -1;
plugin_name = weechat_infolist_string (infolist, "plugin_name");
plugin_id = weechat_infolist_string (infolist, "plugin_id");
str_type = weechat_infolist_string (infolist, "type_string");
str_protocol = weechat_infolist_string (infolist, "protocol_string");
remote_nick = weechat_infolist_string (infolist, "remote_nick");
local_nick = weechat_infolist_string (infolist, "local_nick");
charset_modifier = weechat_infolist_string (infolist, "charset_modifier");
filename = weechat_infolist_string (infolist, "filename");
proxy = weechat_infolist_string (infolist, "proxy");
protocol = XFER_NO_PROTOCOL;
if (!plugin_name || !plugin_id || !str_type || !remote_nick || !local_nick)
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME, "xfer_add");
goto error;
}
type = xfer_search_type (str_type);
if (type < 0)
{
weechat_printf (NULL,
_("%s%s: unknown xfer type \"%s\""),
weechat_prefix ("error"), XFER_PLUGIN_NAME, str_type);
goto error;
}
if (XFER_IS_FILE(type) && (!filename || !str_protocol))
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME, "xfer_add");
goto error;
}
if (XFER_IS_FILE(type))
{
protocol = xfer_search_protocol (str_protocol);
if (protocol < 0)
{
weechat_printf (NULL,
_("%s%s: unknown xfer protocol \"%s\""),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
str_protocol);
goto error;
}
}
if (type == XFER_TYPE_FILE_RECV)
{
filename2 = strdup (filename);
sscanf (weechat_infolist_string (infolist, "size"), "%llu", &file_size);
}
if (type == XFER_TYPE_FILE_SEND)
{
/* add home if filename not beginning with '/' or '~' (not for Win32) */
#ifdef _WIN32
filename2 = strdup (filename);
#else
if (filename[0] == '/')
filename2 = strdup (filename);
else if (filename[0] == '~')
filename2 = weechat_string_expand_home (filename);
else
{
path = weechat_string_eval_path_home (
weechat_config_string (xfer_config_file_upload_path),
NULL, NULL, NULL);
if (!path)
{
weechat_printf (NULL,
_("%s%s: not enough memory (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_add, path");
goto error;
}
filename2 = malloc (strlen (path) + strlen (filename) + 4);
if (!filename2)
{
weechat_printf (NULL,
_("%s%s: not enough memory (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_add, filename2");
free (path);
goto error;
}
strcpy (filename2, path);
if (filename2[strlen (filename2) - 1] != DIR_SEPARATOR_CHAR)
strcat (filename2, DIR_SEPARATOR);
strcat (filename2, filename);
free (path);
}
#endif /* _WIN32 */
if (!filename2)
{
weechat_printf (NULL,
_("%s%s: not enough memory (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_add, filename2");
goto error;
}
/* check if file exists */
if (stat (filename2, &st) == -1)
{
weechat_printf (NULL,
_("%s%s: cannot access file \"%s\""),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
filename2);
goto error;
}
file_size = st.st_size;
}
port = weechat_infolist_integer (infolist, "port");
/* resolve address */
if (XFER_IS_RECV(type))
{
str_address = weechat_infolist_string (infolist, "remote_address");
snprintf (str_port_temp, sizeof (str_port_temp), "%d", port);
str_port = str_port_temp;
length = sizeof (addr);
if (!xfer_resolve_addr (str_address, str_port,
(struct sockaddr*)&addr, &length,
AI_NUMERICSERV | AI_NUMERICHOST))
{
goto error;
}
}
else
{
memset (&bind_addr, 0, sizeof (bind_addr));
/* determine bind_addr family from either own_ip or default */
if (weechat_config_string (xfer_config_network_own_ip)
&& weechat_config_string (xfer_config_network_own_ip)[0])
{
/* resolve own_ip to a numeric address */
str_address = weechat_config_string (xfer_config_network_own_ip);
length = sizeof (own_ip_addr);
if (!xfer_resolve_addr (str_address, NULL,
(struct sockaddr*)&own_ip_addr, &length,
AI_NUMERICSERV))
{
goto error;
}
/* set the advertised address to own_ip */
out_addr = (struct sockaddr*)&own_ip_addr;
/* bind_addr's family should be the advertised family */
bind_addr.ss_family = own_ip_addr.ss_family;
}
else
{
/* no own_ip, so bind_addr's family comes from irc connection */
str_address = weechat_infolist_string (infolist, "local_address");
length = sizeof (addr);
if (!xfer_resolve_addr (str_address, NULL,
(struct sockaddr*)&addr, &length,
AI_NUMERICSERV | AI_NUMERICHOST))
{
goto error;
}
bind_addr.ss_family = addr.ss_family;
}
/* determine bind wildcard address */
if (bind_addr.ss_family == AF_INET)
{
((struct sockaddr_in*)&bind_addr)->sin_addr.s_addr = INADDR_ANY;
bind_addr_len = sizeof (struct sockaddr_in);
}
else
{
memcpy (&((struct sockaddr_in6*)&bind_addr)->sin6_addr,
&in6addr_any, sizeof (in6addr_any));
bind_addr_len = sizeof (struct sockaddr_in6);
}
/* open socket for xfer */
sock = socket (bind_addr.ss_family, SOCK_STREAM, 0);
if (sock < 0)
{
weechat_printf (NULL,
_("%s%s: cannot create socket for xfer: error %d %s"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
errno, strerror (errno));
goto error;
}
/* look for port */
port = 0;
if (weechat_config_string (xfer_config_network_port_range)
&& weechat_config_string (xfer_config_network_port_range)[0])
{
/* find a free port in the specified range */
args = sscanf (weechat_config_string (xfer_config_network_port_range),
"%d-%d", &port_start, &port_end);
if (args > 0)
{
port = port_start;
if (args == 1)
port_end = port_start;
/* loop through the entire allowed port range */
while (port <= port_end)
{
if (!xfer_port_in_use (port))
{
/* attempt to bind to the free port */
if (bind_addr.ss_family == AF_INET)
((struct sockaddr_in *)&bind_addr)->sin_port = htons (port);
else
((struct sockaddr_in6 *)&bind_addr)->sin6_port = htons (port);
if (bind (sock, (struct sockaddr *)&bind_addr, bind_addr_len) == 0)
break;
}
port++;
}
if (port > port_end)
port = -1;
}
}
if (port == 0)
{
/* find port automatically */
if (bind (sock, (struct sockaddr *)&bind_addr, bind_addr_len) == 0)
{
getsockname (sock, (struct sockaddr *)&bind_addr, &bind_addr_len);
if (bind_addr.ss_family == AF_INET)
port = ntohs (((struct sockaddr_in *)&bind_addr)->sin_port);
else
port = ntohs (((struct sockaddr_in6 *)&bind_addr)->sin6_port);
}
else
port = -1;
}
if (port == -1)
{
/* Could not find any port to bind */
weechat_printf (NULL,
_("%s%s: cannot find available port for xfer"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
close (sock);
goto error;
}
}
if (XFER_IS_FILE(type))
{
/* extract short filename (without path) */
pos = strrchr (filename2, DIR_SEPARATOR_CHAR);
if (pos)
short_filename = strdup (pos + 1);
else
short_filename = strdup (filename2);
/* convert spaces to underscore if asked and needed */
pos = short_filename;
while (pos[0])
{
if (pos[0] == ' ')
{
if (weechat_config_boolean (xfer_config_file_convert_spaces))
pos[0] = '_';
}
pos++;
}
}
if (type == XFER_TYPE_FILE_RECV)
{
if (filename2)
{
free (filename2);
filename2 = NULL;
}
}
/* add xfer entry and listen to socket if type is file or chat "send" */
if (XFER_IS_FILE(type))
{
ptr_xfer = xfer_new (plugin_name, plugin_id, type, protocol,
remote_nick, local_nick, charset_modifier,
short_filename, file_size, proxy,
out_addr, length, port, sock, filename2);
}
else
{
ptr_xfer = xfer_new (plugin_name, plugin_id, type, protocol,
remote_nick, local_nick, charset_modifier, NULL,
0, proxy, out_addr, length, port, sock, NULL);
}
if (!ptr_xfer)
{
weechat_printf (NULL,
_("%s%s: error creating xfer"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
close (sock);
goto error;
}
/* send signal if type is file or chat "send" */
if (XFER_IS_SEND(ptr_xfer->type) && !XFER_HAS_ENDED(ptr_xfer->status))
xfer_send_signal (ptr_xfer, "xfer_send_ready");
if (filename2)
free (filename2);
if (short_filename)
free (short_filename);
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_OK;
error:
if (filename2)
free (filename2);
if (short_filename)
free (short_filename);
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_ERROR;
}
/*
* Callback called when resume is accepted by sender.
*/
int
xfer_start_resume_cb (const void *pointer, void *data,
const char *signal, const char *type_data,
void *signal_data)
{
struct t_infolist *infolist;
struct t_xfer *ptr_xfer;
const char *plugin_name, *plugin_id, *filename, *str_start_resume;
int port;
unsigned long long start_resume;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
if (!signal_data)
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_start_resume");
return WEECHAT_RC_ERROR;
}
infolist = (struct t_infolist *)signal_data;
if (!weechat_infolist_next (infolist))
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_start_resume");
return WEECHAT_RC_ERROR;
}
plugin_name = weechat_infolist_string (infolist, "plugin_name");
plugin_id = weechat_infolist_string (infolist, "plugin_id");
filename = weechat_infolist_string (infolist, "filename");
port = weechat_infolist_integer (infolist, "port");
str_start_resume = weechat_infolist_string (infolist, "start_resume");
if (!plugin_name || !plugin_id || !filename || !str_start_resume)
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_start_resume");
goto error;
}
sscanf (str_start_resume, "%llu", &start_resume);
ptr_xfer = xfer_search (plugin_name, plugin_id, XFER_TYPE_FILE_RECV,
XFER_STATUS_CONNECTING, port);
if (ptr_xfer)
{
ptr_xfer->pos = start_resume;
ptr_xfer->ack = start_resume;
ptr_xfer->start_resume = start_resume;
ptr_xfer->last_check_pos = start_resume;
xfer_network_connect_init (ptr_xfer);
}
else
{
weechat_printf (NULL,
_("%s%s: unable to resume file \"%s\" (port: %d, "
"start position: %llu): xfer not found or not ready "
"for transfer"),
weechat_prefix ("error"), XFER_PLUGIN_NAME, filename,
port, start_resume);
}
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_OK;
error:
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_ERROR;
}
/*
* Callback called when sender receives resume request from receiver.
*/
int
xfer_accept_resume_cb (const void *pointer, void *data,
const char *signal, const char *type_data,
void *signal_data)
{
struct t_infolist *infolist;
struct t_xfer *ptr_xfer;
const char *plugin_name, *plugin_id, *filename, *str_start_resume;
int port;
unsigned long long start_resume;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
if (!signal_data)
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_accept_resume");
return WEECHAT_RC_ERROR;
}
infolist = (struct t_infolist *)signal_data;
if (!weechat_infolist_next (infolist))
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_accept_resume");
return WEECHAT_RC_ERROR;
}
plugin_name = weechat_infolist_string (infolist, "plugin_name");
plugin_id = weechat_infolist_string (infolist, "plugin_id");
filename = weechat_infolist_string (infolist, "filename");
port = weechat_infolist_integer (infolist, "port");
str_start_resume = weechat_infolist_string (infolist, "start_resume");
if (!plugin_name || !plugin_id || !filename || !str_start_resume)
{
weechat_printf (NULL,
_("%s%s: missing arguments (%s)"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
"xfer_accept_resume");
goto error;
}
sscanf (str_start_resume, "%llu", &start_resume);
ptr_xfer = xfer_search (plugin_name, plugin_id, XFER_TYPE_FILE_SEND,
XFER_STATUS_CONNECTING, port);
if (ptr_xfer)
{
ptr_xfer->pos = start_resume;
ptr_xfer->ack = start_resume;
ptr_xfer->start_resume = start_resume;
ptr_xfer->last_check_pos = start_resume;
xfer_send_signal (ptr_xfer, "xfer_send_accept_resume");
weechat_printf (NULL,
_("%s: file %s resumed at position %llu"),
XFER_PLUGIN_NAME,
ptr_xfer->filename,
ptr_xfer->start_resume);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
}
else
{
weechat_printf (NULL,
_("%s%s: unable to accept resume file \"%s\" (port: %d, "
"start position: %llu): xfer not found or not ready "
"for transfer"),
weechat_prefix ("error"), XFER_PLUGIN_NAME, filename,
port, start_resume);
}
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_OK;
error:
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_ERROR;
}
/*
* Adds a xfer in an infolist.
*
* Returns:
* 1: OK
* 0: error
*/
int
xfer_add_to_infolist (struct t_infolist *infolist, struct t_xfer *xfer)
{
struct t_infolist_item *ptr_item;
char value[128];
if (!infolist || !xfer)
return 0;
ptr_item = weechat_infolist_new_item (infolist);
if (!ptr_item)
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "plugin_name", xfer->plugin_name))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "plugin_id", xfer->plugin_id))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "type", xfer->type))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "type_string", xfer_type_string[xfer->type]))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "protocol", xfer->protocol))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "protocol_string", xfer_protocol_string[xfer->protocol]))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "remote_nick", xfer->remote_nick))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "local_nick", xfer->local_nick))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "charset_modifier", xfer->charset_modifier))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "filename", xfer->filename))
return 0;
snprintf (value, sizeof (value), "%llu", xfer->size);
if (!weechat_infolist_new_var_string (ptr_item, "size", value))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "proxy", xfer->proxy))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "local_address", xfer->local_address_str))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "remote_address", xfer->remote_address_str))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "port", xfer->port))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "status", xfer->status))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "status_string", xfer_status_string[xfer->status]))
return 0;
if (!weechat_infolist_new_var_pointer (ptr_item, "buffer", xfer->buffer))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "remote_nick_color", xfer->remote_nick_color))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "fast_send", xfer->fast_send))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "blocksize", xfer->blocksize))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "start_time", xfer->start_time))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "start_transfer", xfer->start_transfer))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "sock", xfer->sock))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "child_pid", xfer->child_pid))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "child_read", xfer->child_read))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "child_write", xfer->child_write))
return 0;
if (!weechat_infolist_new_var_pointer (ptr_item, "hook_fd", xfer->hook_fd))
return 0;
if (!weechat_infolist_new_var_pointer (ptr_item, "hook_timer", xfer->hook_timer))
return 0;
if (!weechat_infolist_new_var_pointer (ptr_item, "hook_connect", xfer->hook_connect))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "unterminated_message", xfer->unterminated_message))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "file", xfer->file))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "local_filename", xfer->local_filename))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "temp_local_filename", xfer->temp_local_filename))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "filename_suffix", xfer->filename_suffix))
return 0;
snprintf (value, sizeof (value), "%llu", xfer->pos);
if (!weechat_infolist_new_var_string (ptr_item, "pos", value))
return 0;
snprintf (value, sizeof (value), "%llu", xfer->ack);
if (!weechat_infolist_new_var_string (ptr_item, "ack", value))
return 0;
snprintf (value, sizeof (value), "%llu", xfer->start_resume);
if (!weechat_infolist_new_var_string (ptr_item, "start_resume", value))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "last_check_time", xfer->last_check_time))
return 0;
snprintf (value, sizeof (value), "%llu", xfer->last_check_pos);
if (!weechat_infolist_new_var_string (ptr_item, "last_check_pos", value))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "last_activity", xfer->last_activity))
return 0;
snprintf (value, sizeof (value), "%llu", xfer->bytes_per_sec);
if (!weechat_infolist_new_var_string (ptr_item, "bytes_per_sec", value))
return 0;
snprintf (value, sizeof (value), "%llu", xfer->eta);
if (!weechat_infolist_new_var_string (ptr_item, "eta", value))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "hash_target", xfer->hash_target))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "hash_status", xfer->hash_status))
return 0;
if (!weechat_infolist_new_var_string (ptr_item, "hash_status_string", xfer_hash_status_string[xfer->hash_status]))
return 0;
return 1;
}
/*
* Prints xfer infos in WeeChat log file (usually for crash dump).
*/
void
xfer_print_log ()
{
struct t_xfer *ptr_xfer;
for (ptr_xfer = xfer_list; ptr_xfer; ptr_xfer = ptr_xfer->next_xfer)
{
weechat_log_printf ("");
weechat_log_printf ("[xfer (addr:0x%lx)]", ptr_xfer);
weechat_log_printf (" plugin_name . . . . . . : '%s'", ptr_xfer->plugin_name);
weechat_log_printf (" plugin_id . . . . . . . : '%s'", ptr_xfer->plugin_id);
weechat_log_printf (" type. . . . . . . . . . : %d (%s)",
ptr_xfer->type,
xfer_type_string[ptr_xfer->type]);
weechat_log_printf (" protocol. . . . . . . . : %d (%s)",
ptr_xfer->protocol,
xfer_protocol_string[ptr_xfer->protocol]);
weechat_log_printf (" remote_nick . . . . . . : '%s'", ptr_xfer->remote_nick);
weechat_log_printf (" local_nick. . . . . . . : '%s'", ptr_xfer->local_nick);
weechat_log_printf (" charset_modifier. . . . : '%s'", ptr_xfer->charset_modifier);
weechat_log_printf (" filename. . . . . . . . : '%s'", ptr_xfer->filename);
weechat_log_printf (" size. . . . . . . . . . : %llu", ptr_xfer->size);
weechat_log_printf (" proxy . . . . . . . . . : '%s'", ptr_xfer->proxy);
weechat_log_printf (" local_address . . . . . : 0x%lx", ptr_xfer->local_address);
weechat_log_printf (" local_address_length. . : %d", ptr_xfer->local_address_length);
weechat_log_printf (" local_address_str . . . : '%s'" , ptr_xfer->local_address_str);
weechat_log_printf (" remote_address. . . . . : 0x%lx", ptr_xfer->remote_address);
weechat_log_printf (" remote_address_length . : %d", ptr_xfer->remote_address_length);
weechat_log_printf (" remote_address_str. . . : '%s'", ptr_xfer->remote_address_str);
weechat_log_printf (" port. . . . . . . . . . : %d", ptr_xfer->port);
weechat_log_printf (" status. . . . . . . . . : %d (%s)",
ptr_xfer->status,
xfer_status_string[ptr_xfer->status]);
weechat_log_printf (" buffer. . . . . . . . . : 0x%lx", ptr_xfer->buffer);
weechat_log_printf (" remote_nick_color . . . : '%s'", ptr_xfer->remote_nick_color);
weechat_log_printf (" fast_send . . . . . . . : %d", ptr_xfer->fast_send);
weechat_log_printf (" blocksize . . . . . . . : %d", ptr_xfer->blocksize);
weechat_log_printf (" start_time. . . . . . . : %lld", (long long)ptr_xfer->start_time);
weechat_log_printf (" start_transfer. . . . . : %lld", (long long)ptr_xfer->start_transfer);
weechat_log_printf (" sock. . . . . . . . . . : %d", ptr_xfer->sock);
weechat_log_printf (" child_pid . . . . . . . : %d", ptr_xfer->child_pid);
weechat_log_printf (" child_read. . . . . . . : %d", ptr_xfer->child_read);
weechat_log_printf (" child_write . . . . . . : %d", ptr_xfer->child_write);
weechat_log_printf (" hook_fd . . . . . . . . : 0x%lx", ptr_xfer->hook_fd);
weechat_log_printf (" hook_timer. . . . . . . : 0x%lx", ptr_xfer->hook_timer);
weechat_log_printf (" hook_connect. . . . . . : 0x%lx", ptr_xfer->hook_connect);
weechat_log_printf (" unterminated_message. . : '%s'", ptr_xfer->unterminated_message);
weechat_log_printf (" file. . . . . . . . . . : %d", ptr_xfer->file);
weechat_log_printf (" local_filename. . . . . : '%s'", ptr_xfer->local_filename);
weechat_log_printf (" temp_local_filename . . : '%s'", ptr_xfer->temp_local_filename);
weechat_log_printf (" filename_suffix . . . . : %d", ptr_xfer->filename_suffix);
weechat_log_printf (" pos . . . . . . . . . . : %llu", ptr_xfer->pos);
weechat_log_printf (" ack . . . . . . . . . . : %llu", ptr_xfer->ack);
weechat_log_printf (" start_resume. . . . . . : %llu", ptr_xfer->start_resume);
weechat_log_printf (" last_check_time . . . . : %lld", (long long)ptr_xfer->last_check_time);
weechat_log_printf (" last_check_pos. . . . . : %llu", ptr_xfer->last_check_pos);
weechat_log_printf (" last_activity . . . . . : %lld", (long long)ptr_xfer->last_activity);
weechat_log_printf (" bytes_per_sec . . . . . : %llu", ptr_xfer->bytes_per_sec);
weechat_log_printf (" eta . . . . . . . . . . : %llu", ptr_xfer->eta);
weechat_log_printf (" hash_target . . . . . . : '%s'", ptr_xfer->hash_target);
weechat_log_printf (" hash_status . . . . . . : %d (%s)",
ptr_xfer->hash_status,
xfer_hash_status_string[ptr_xfer->hash_status]);
weechat_log_printf (" prev_xfer . . . . . . . : 0x%lx", ptr_xfer->prev_xfer);
weechat_log_printf (" next_xfer . . . . . . . : 0x%lx", ptr_xfer->next_xfer);
}
}
/*
* Callback for signal "debug_dump".
*/
int
xfer_debug_dump_cb (const void *pointer, void *data,
const char *signal, const char *type_data,
void *signal_data)
{
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
if (!signal_data
|| (weechat_strcasecmp ((char *)signal_data, XFER_PLUGIN_NAME) == 0))
{
weechat_log_printf ("");
weechat_log_printf ("***** \"%s\" plugin dump *****",
weechat_plugin->name);
xfer_print_log ();
weechat_log_printf ("");
weechat_log_printf ("***** End of \"%s\" plugin dump *****",
weechat_plugin->name);
}
return WEECHAT_RC_OK;
}
/*
* Initializes xfer plugin.
*/
int
weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[])
{
/* make C compiler happy */
(void) argc;
(void) argv;
weechat_plugin = plugin;
if (!xfer_config_init ())
return WEECHAT_RC_ERROR;
xfer_config_read ();
xfer_create_directories ();
xfer_command_init ();
/* hook some signals */
weechat_hook_signal ("upgrade",
&xfer_signal_upgrade_cb, NULL, NULL);
weechat_hook_signal ("xfer_add",
&xfer_add_cb, NULL, NULL);
weechat_hook_signal ("xfer_start_resume",
&xfer_start_resume_cb, NULL, NULL);
weechat_hook_signal ("xfer_accept_resume",
&xfer_accept_resume_cb, NULL, NULL);
weechat_hook_signal ("debug_dump",
&xfer_debug_dump_cb, NULL, NULL);
/* hook completions */
xfer_completion_init ();
xfer_info_init ();
if (weechat_xfer_plugin->upgrading)
xfer_upgrade_load ();
return WEECHAT_RC_OK;
}
/*
* Ends xfer plugin.
*/
int
weechat_plugin_end (struct t_weechat_plugin *plugin)
{
/* make C compiler happy */
(void) plugin;
xfer_config_write ();
if (xfer_signal_upgrade_received)
xfer_upgrade_save ();
else
xfer_disconnect_all ();
xfer_free_all ();
return WEECHAT_RC_OK;
}