1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-27 05:16:38 +02:00
Files
weechat/src/plugins/xfer/xfer.c
T
2012-01-08 18:59:50 +01:00

1523 lines
49 KiB
C

/*
* Copyright (C) 2003-2012 Sebastien 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 <http://www.gnu.org/licenses/>.
*/
/*
* xfer.c: file transfer and direct chat plugin for WeeChat
*/
#include <stdlib.h>
#include <unistd.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 <netdb.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("Xfer (file transfer and direct chat) plugin for "
"WeeChat");
WEECHAT_PLUGIN_AUTHOR("Sebastien Helleu <flashcode@flashtux.org>");
WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION);
WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE);
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")
};
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 ? */
/*
* xfer_valid: check if a xfer pointer exists
* return 1 if xfer exists
* 0 if xfer is not found
*/
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;
}
/*
* xfer_signal_upgrade_cb: callback for "upgrade" signal
*/
int
xfer_signal_upgrade_cb (void *data, const char *signal, const char *type_data,
void *signal_data)
{
/* make C compiler happy */
(void) data;
(void) signal;
(void) type_data;
(void) signal_data;
xfer_signal_upgrade_received = 1;
return WEECHAT_RC_OK;
}
/*
* xfer_create_directories: create directories for xfer plugin
*/
void
xfer_create_directories ()
{
const char *weechat_dir;
char *dir1, *dir2;
/* create download directory */
weechat_dir = weechat_info_get ("weechat_dir", "");
if (weechat_dir)
{
dir1 = weechat_string_expand_home (weechat_config_string (xfer_config_file_download_path));
dir2 = weechat_string_replace (dir1, "%h", weechat_dir);
if (dir2)
(void) weechat_mkdir (dir2, 0700);
if (dir1)
free (dir1);
if (dir2)
free (dir2);
}
}
/*
* xfer_search_type: search xfer type with a string
* return -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;
}
/*
* xfer_search_protocol: search xfer protocol with a string
* return -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;
}
/*
* xfer_search: search a xfer
*/
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;
}
/*
* xfer_search_by_number: search a xfer by number (first xfer is 0)
*/
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;
}
/*
* xfer_search_by_buffer: search a xfer by buffer (for chat only)
*/
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;
}
/*
* xfer_close: close 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))
{
xfer_send_signal (xfer, "xfer_ended");
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_IS_FILE(xfer->type))
{
weechat_printf (NULL,
_("%s%s: file %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->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: chat closed with %s "
"(%d.%d.%d.%d)"),
XFER_PLUGIN_NAME,
xfer->remote_nick,
xfer->address >> 24,
(xfer->address >> 16) & 0xff,
(xfer->address >> 8) & 0xff,
xfer->address & 0xff);
}
}
/* remove empty file if received file failed and nothing was transfered */
if (((xfer->status == XFER_STATUS_FAILED)
|| (xfer->status == XFER_STATUS_ABORTED))
&& XFER_IS_FILE(xfer->type)
&& XFER_IS_RECV(xfer->type)
&& xfer->local_filename
&& xfer->pos == 0)
{
/* erase file only if really empty on disk */
if (stat (xfer->local_filename, &st) != -1)
{
if ((unsigned long long) st.st_size == 0)
unlink (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;
}
}
/*
* xfer_port_in_use: return 1 if a port is in used
* (by an active or connecting xfer)
*/
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;
}
/*
* xfer_send_signal: send a signal for a xfer
*/
void
xfer_send_signal (struct t_xfer *xfer, const char *signal)
{
struct t_infolist *infolist;
struct t_infolist_item *item;
char str_long[128];
infolist = weechat_infolist_new ();
if (infolist)
{
item = weechat_infolist_new_item (infolist);
if (item)
{
weechat_infolist_new_var_string (item, "plugin_name",
xfer->plugin_name);
weechat_infolist_new_var_string (item, "plugin_id",
xfer->plugin_id);
weechat_infolist_new_var_string (item, "type",
xfer_type_string[xfer->type]);
weechat_infolist_new_var_string (item, "protocol",
xfer_protocol_string[xfer->protocol]);
weechat_infolist_new_var_string (item, "remote_nick",
xfer->remote_nick);
weechat_infolist_new_var_string (item, "local_nick",
xfer->local_nick);
weechat_infolist_new_var_string (item, "charset_modifier",
xfer->charset_modifier);
weechat_infolist_new_var_string (item, "filename",
xfer->filename);
snprintf (str_long, sizeof (str_long), "%llu", xfer->size);
weechat_infolist_new_var_string (item, "size",
str_long);
snprintf (str_long, sizeof (str_long), "%llu", xfer->start_resume);
weechat_infolist_new_var_string (item, "start_resume",
str_long);
snprintf (str_long, sizeof (str_long), "%lu", xfer->address);
weechat_infolist_new_var_string (item, "address",
str_long);
weechat_infolist_new_var_integer (item, "port",
xfer->port);
weechat_hook_signal_send (signal, WEECHAT_HOOK_SIGNAL_POINTER,
infolist);
}
weechat_infolist_free (infolist);
}
}
/*
* xfer_alloc: allocate a new xfer
*/
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);
/* default values */
new_xfer->filename = NULL;
new_xfer->size = 0;
new_xfer->address = 0;
new_xfer->port = 0;
new_xfer->remote_nick = NULL;
new_xfer->local_nick = NULL;
new_xfer->charset_modifier = NULL;
new_xfer->type = 0;
new_xfer->protocol = 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->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->unterminated_message = NULL;
new_xfer->file = -1;
new_xfer->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->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;
}
/*
* xfer_new: add a xfer to list
*/
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, unsigned long address,
int port, int sock, const char *local_filename)
{
struct t_xfer *new_xfer;
const char *ptr_color;
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);
ptr_color = weechat_info_get ("irc_nick_color", remote_nick);
new_xfer->remote_nick_color = (ptr_color) ? strdup (ptr_color) : NULL;
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->address = address;
new_xfer->port = port;
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);
/* write info message on core buffer */
switch (type)
{
case XFER_TYPE_FILE_RECV:
weechat_printf (NULL,
_("%s: incoming file from %s "
"(%s.%s), ip: %d.%d.%d.%d, name: %s, %llu bytes "
"(protocol: %s)"),
XFER_PLUGIN_NAME,
remote_nick,
plugin_name,
plugin_id,
address >> 24,
(address >> 16) & 0xff,
(address >> 8) & 0xff,
address & 0xff,
filename,
size,
xfer_protocol_string[protocol]);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
break;
case XFER_TYPE_FILE_SEND:
weechat_printf (NULL,
_("%s: sending file to %s (%s.%s): %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), ip: %d.%d.%d.%d"),
XFER_PLUGIN_NAME,
remote_nick,
plugin_name,
plugin_id,
address >> 24,
(address >> 16) & 0xff,
(address >> 8) & 0xff,
address & 0xff);
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;
}
}
if ( ( (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;
}
/*
* xfer_free: free xfer struct and remove 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->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->remote_nick_color)
free (xfer->remote_nick_color);
if (xfer->unterminated_message)
free (xfer->unterminated_message);
if (xfer->local_filename)
free (xfer->local_filename);
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;
}
/*
* xfer_add_cb: callback for "xfer_add" signal
*/
int
xfer_add_cb (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;
int type, protocol, args, port_start, port_end, sock, port;
const char *weechat_dir;
char *dir1, *dir2, *filename2, *short_filename, *pos;
struct stat st;
struct hostent *host;
struct sockaddr_in addr;
socklen_t length;
struct in_addr tmpaddr;
unsigned long local_addr;
unsigned long long file_size;
struct t_xfer *ptr_xfer;
/* make C compiler happy */
(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;
sock = -1;
port = 0;
plugin_name = weechat_infolist_string (infolist, "plugin_name");
plugin_id = weechat_infolist_string (infolist, "plugin_id");
str_type = weechat_infolist_string (infolist, "type");
str_protocol = weechat_infolist_string (infolist, "protocol");
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;
}
}
filename2 = NULL;
file_size = 0;
port = 0;
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
{
dir1 = weechat_string_expand_home (weechat_config_string (xfer_config_file_upload_path));
if (!dir1)
{
weechat_printf (NULL,
_("%s%s: not enough memory"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
goto error;
}
weechat_dir = weechat_info_get ("weechat_dir", "");
dir2 = weechat_string_replace (dir1, "%h", weechat_dir);
if (!dir2)
{
weechat_printf (NULL,
_("%s%s: not enough memory"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
free (dir1);
goto error;
}
filename2 = malloc (strlen (dir2) + strlen (filename) + 4);
if (!filename2)
{
weechat_printf (NULL,
_("%s%s: not enough memory"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
free (dir1);
free (dir2);
goto error;
}
strcpy (filename2, dir2);
if (filename2[strlen (filename2) - 1] != DIR_SEPARATOR_CHAR)
strcat (filename2, DIR_SEPARATOR);
strcat (filename2, filename);
if (dir1)
free (dir1);
if (dir2)
free (dir2);
}
#endif
/* check if file exists */
if (stat (filename2, &st) == -1)
{
weechat_printf (NULL,
_("%s%s: cannot access file \"%s\""),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
filename2);
if (filename2)
free (filename2);
goto error;
}
file_size = st.st_size;
}
if (XFER_IS_RECV(type))
{
sscanf (weechat_infolist_string (infolist, "address"), "%lu", &local_addr);
port = weechat_infolist_integer (infolist, "port");
}
else
{
/* get local IP address */
sscanf (weechat_infolist_string (infolist, "address"), "%lu", &local_addr);
memset (&addr, 0, sizeof (struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl (local_addr);
/* look up the IP address from network_own_ip, if set */
if (weechat_config_string(xfer_config_network_own_ip)
&& weechat_config_string(xfer_config_network_own_ip)[0])
{
host = gethostbyname (weechat_config_string (xfer_config_network_own_ip));
if (host)
{
memcpy (&tmpaddr, host->h_addr_list[0], sizeof(struct in_addr));
local_addr = ntohl (tmpaddr.s_addr);
sock = weechat_infolist_integer (infolist, "socket");
if (sock > 0)
{
memset (&addr, 0, sizeof (struct sockaddr_in));
length = sizeof (addr);
getsockname (sock, (struct sockaddr *) &addr, &length);
addr.sin_family = AF_INET;
}
}
else
{
weechat_printf (NULL,
_("%s%s: could not find address for \"%s\", "
"falling back to local IP"),
weechat_prefix ("error"), XFER_PLUGIN_NAME,
weechat_config_string (xfer_config_network_own_ip));
}
}
/* open socket for xfer */
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
weechat_printf (NULL,
_("%s%s: cannot create socket for xfer"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
if (filename2)
free (filename2);
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 */
addr.sin_port = htons (port);
if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) == 0)
break;
}
port++;
}
if (port > port_end)
port = -1;
}
}
if (port == 0)
{
/* find port automatically */
addr.sin_port = 0;
if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) == 0)
{
length = sizeof (addr);
getsockname (sock, (struct sockaddr *) &addr, &length);
port = ntohs (addr.sin_port);
}
else
port = -1;
}
if (port == -1)
{
/* Could not find any port to bind */
weechat_printf (NULL,
_("%s%s: cannot find available port for xfer"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
close (sock);
if (filename2)
free (filename2);
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, local_addr,
port, sock, filename2);
else
ptr_xfer = xfer_new (plugin_name, plugin_id, type, protocol,
remote_nick, local_nick, charset_modifier, NULL,
0, proxy, local_addr, port, sock, NULL);
if (!ptr_xfer)
{
weechat_printf (NULL,
_("%s%s: error creating xfer"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
close (sock);
if (short_filename)
free (short_filename);
if (filename2)
free (filename2);
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 (short_filename)
free (short_filename);
if (filename2)
free (filename2);
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_OK;
error:
weechat_infolist_reset_item_cursor (infolist);
return WEECHAT_RC_ERROR;
}
/*
* xfer_start_resume_cb: callback called when resume is accepted by sender
*/
int
xfer_start_resume_cb (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) 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;
}
/*
* xfer_accept_resume_cb: callback called when sender receives resume request
* from recever
*/
int
xfer_accept_resume_cb (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) 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;
}
/*
* xfer_add_to_infolist: add a xfer in an infolist
* return 1 if ok, 0 if 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;
snprintf (value, sizeof (value), "%lu", xfer->address);
if (!weechat_infolist_new_var_string (ptr_item, "address", value))
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_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_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;
return 1;
}
/*
* xfer_print_log: print xfer infos in log (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 (" address . . . . . . : %lu", ptr_xfer->address);
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. . . . . : %ld", ptr_xfer->start_time);
weechat_log_printf (" start_transfer. . . : %ld", 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 (" 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 (" 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 . . : %ld", ptr_xfer->last_check_time);
weechat_log_printf (" last_check_pos. . . : %llu", ptr_xfer->last_check_pos);
weechat_log_printf (" last_activity . . . : %ld", 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 (" prev_xfer . . . . . : 0x%lx", ptr_xfer->prev_xfer);
weechat_log_printf (" next_xfer . . . . . : 0x%lx", ptr_xfer->next_xfer);
}
}
/*
* xfer_debug_dump_cb: callback for "debug_dump" signal
*/
int
xfer_debug_dump_cb (void *data, const char *signal, const char *type_data,
void *signal_data)
{
/* make C compiler happy */
(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;
}
/*
* weechat_plugin_init: initialize xfer plugin
*/
int
weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[])
{
int i, upgrading;
weechat_plugin = plugin;
if (!xfer_config_init ())
return WEECHAT_RC_ERROR;
if (xfer_config_read () < 0)
return WEECHAT_RC_ERROR;
xfer_create_directories ();
xfer_command_init ();
/* hook some signals */
weechat_hook_signal ("upgrade", &xfer_signal_upgrade_cb, NULL);
weechat_hook_signal ("xfer_add", &xfer_add_cb, NULL);
weechat_hook_signal ("xfer_start_resume", &xfer_start_resume_cb, NULL);
weechat_hook_signal ("xfer_accept_resume", &xfer_accept_resume_cb, NULL);
weechat_hook_signal ("debug_dump", &xfer_debug_dump_cb, NULL);
/* hook completions */
xfer_completion_init ();
xfer_info_init ();
/* look at arguments */
upgrading = 0;
for (i = 0; i < argc; i++)
{
if (weechat_strcasecmp (argv[i], "--upgrade") == 0)
upgrading = 1;
}
if (upgrading)
xfer_upgrade_load ();
return WEECHAT_RC_OK;
}
/*
* weechat_plugin_end: end xfer plugin
*/
int
weechat_plugin_end (struct t_weechat_plugin *plugin)
{
struct t_xfer *ptr_xfer;
/* make C compiler happy */
(void) plugin;
xfer_config_write ();
if (xfer_signal_upgrade_received)
xfer_upgrade_save ();
else
{
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);
}
}
}
return WEECHAT_RC_OK;
}