diff options
author | Sebastien Helleu <flashcode@flashtux.org> | 2008-05-04 20:24:20 +0200 |
---|---|---|
committer | Sebastien Helleu <flashcode@flashtux.org> | 2008-05-04 20:24:20 +0200 |
commit | e7a16efa0cf5123f87ef6d7072fbbb9ccdd42bac (patch) | |
tree | ee16107b4a6d1dc30b3db6fcb60f2c979d77bd4b /src | |
parent | ff526c31684cc825ae0542fb0d35e76720f6d6ea (diff) | |
download | weechat-e7a16efa0cf5123f87ef6d7072fbbb9ccdd42bac.zip |
Added new plugin "xfer" (used by irc plugin for DCC file and chat) (warning: initial commit, not working yet)
Diffstat (limited to 'src')
43 files changed, 3592 insertions, 2317 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 22ab230bc..7ff067270 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -14,11 +14,22 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -SET(LIB_CORE_SRC weechat.c weechat.h wee-backtrace.c wee-backtrace.h -wee-command.c wee-command.h wee-config.c wee-config.h wee-config-file.c -wee-config-file.h wee-debug.c wee-debug.h wee-hook.c wee-hook.h wee-input.c -wee-input.h wee-list.c wee-list.h wee-log.c wee-log.h wee-string.c wee-string.h -wee-upgrade.c wee-upgrade.h wee-utf8.c wee-utf8.h wee-util.c wee-util.h) +SET(LIB_CORE_SRC +weechat.c weechat.h +wee-backtrace.c wee-backtrace.h +wee-command.c wee-command.h +wee-config.c wee-config.h +wee-config-file.c wee-config-file.h +wee-debug.c wee-debug.h +wee-hook.c wee-hook.h +wee-input.c wee-input.h +wee-list.c wee-list.h +wee-log.c wee-log.h +wee-network.c wee-network.h +wee-string.c wee-string.h +wee-upgrade.c wee-upgrade.h +wee-utf8.c wee-utf8.h +wee-util.c wee-util.h) # Check for flock support INCLUDE(CheckSymbolExists) diff --git a/src/core/Makefile.am b/src/core/Makefile.am index aff3c408c..32d24c438 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -38,6 +38,8 @@ lib_weechat_core_a_SOURCES = weechat.c \ wee-list.h \ wee-log.c \ wee-log.h \ + wee-network.c \ + wee-network.h \ wee-upgrade.c \ wee-upgrade.h \ wee-string.c \ diff --git a/src/core/wee-command.c b/src/core/wee-command.c index 31a995047..4ce2393fe 100644 --- a/src/core/wee-command.c +++ b/src/core/wee-command.c @@ -93,12 +93,12 @@ command_bar (void *data, struct t_gui_buffer *buffer, GUI_COLOR(GUI_COLOR_CHAT_BUFFER), ptr_bar->name, GUI_COLOR(GUI_COLOR_CHAT), - gui_bar_type_str[CONFIG_INTEGER(ptr_bar->type)], + gui_bar_type_string[CONFIG_INTEGER(ptr_bar->type)], (CONFIG_STRING(ptr_bar->conditions) && CONFIG_STRING(ptr_bar->conditions)[0]) ? CONFIG_STRING(ptr_bar->conditions) : "-", - gui_bar_position_str[CONFIG_INTEGER(ptr_bar->position)], - gui_bar_filling_str[CONFIG_INTEGER(ptr_bar->filling)], + gui_bar_position_string[CONFIG_INTEGER(ptr_bar->position)], + gui_bar_filling_string[CONFIG_INTEGER(ptr_bar->filling)], ((CONFIG_INTEGER(ptr_bar->position) == GUI_BAR_POSITION_BOTTOM) || (CONFIG_INTEGER(ptr_bar->position) == GUI_BAR_POSITION_TOP)) ? _("height") : _("width"), diff --git a/src/core/wee-config-file.c b/src/core/wee-config-file.c index 7dad5d34e..d639d2512 100644 --- a/src/core/wee-config-file.c +++ b/src/core/wee-config-file.c @@ -42,7 +42,7 @@ struct t_config_file *config_files = NULL; struct t_config_file *last_config_file = NULL; -char *config_option_type_str[CONFIG_NUM_OPTION_TYPES] = +char *config_option_type_string[CONFIG_NUM_OPTION_TYPES] = { "boolean", "integer", "string", "color" }; char *config_boolean_true[] = { "on", "yes", "y", "true", "t", "1", NULL }; char *config_boolean_false[] = { "off", "no", "n", "false", "f", "0", NULL }; @@ -367,7 +367,7 @@ config_file_new_option (struct t_config_file *config_file, var_type = -1; for (i = 0; i < CONFIG_NUM_OPTION_TYPES; i++) { - if (string_strcasecmp (type, config_option_type_str[i]) == 0) + if (string_strcasecmp (type, config_option_type_string[i]) == 0) { var_type = i; break; diff --git a/src/core/wee-config.c b/src/core/wee-config.c index d6733fb54..48191e78a 100644 --- a/src/core/wee-config.c +++ b/src/core/wee-config.c @@ -164,14 +164,6 @@ struct t_config_option *config_color_nicklist_prefix4; struct t_config_option *config_color_nicklist_prefix5; struct t_config_option *config_color_nicklist_more; struct t_config_option *config_color_nicklist_separator; -struct t_config_option *config_color_info; -struct t_config_option *config_color_info_bg; -struct t_config_option *config_color_info_waiting; -struct t_config_option *config_color_info_connecting; -struct t_config_option *config_color_info_active; -struct t_config_option *config_color_info_done; -struct t_config_option *config_color_info_failed; -struct t_config_option *config_color_info_aborted; /* config, history section */ @@ -1440,56 +1432,7 @@ config_weechat_init () N_("text color for nicklist separator"), NULL, GUI_COLOR_NICKLIST_SEPARATOR, 0, "blue", NULL, NULL, &config_change_color, NULL, NULL, NULL); - /* status info */ - config_color_info = config_file_new_option ( - weechat_config_file, ptr_section, - "info", "color", - N_("text color for status info"), - NULL, GUI_COLOR_INFO, 0, "default", - NULL, NULL, &config_change_color, NULL, NULL, NULL); - config_color_info_bg = config_file_new_option ( - weechat_config_file, ptr_section, - "info_bg", "color", - N_("background color for status info"), - NULL, -1, 0, "default", - NULL, NULL, &config_change_color, NULL, NULL, NULL); - config_color_info_waiting = config_file_new_option ( - weechat_config_file, ptr_section, - "info_waiting", "color", - N_("text color for \"waiting\" status info"), - NULL, GUI_COLOR_INFO_WAITING, 0, "lightcyan", - NULL, NULL, &config_change_color, NULL, NULL, NULL); - config_color_info_connecting = config_file_new_option ( - weechat_config_file, ptr_section, - "info_connecting", "color", - N_("text color for \"connecting\" status info"), - NULL, GUI_COLOR_INFO_CONNECTING, 0, "yellow", - NULL, NULL, &config_change_color, NULL, NULL, NULL); - config_color_info_active = config_file_new_option ( - weechat_config_file, ptr_section, - "info_active", "color", - N_("text color for \"active\" status info"), - NULL, GUI_COLOR_INFO_ACTIVE, 0, "lightblue", - NULL, NULL, &config_change_color, NULL, NULL, NULL); - config_color_info_done = config_file_new_option ( - weechat_config_file, ptr_section, - "info_done", "color", - N_("text color for \"done\" status info"), - NULL, GUI_COLOR_INFO_DONE, 0, "lightgreen", - NULL, NULL, &config_change_color, NULL, NULL, NULL); - config_color_info_failed = config_file_new_option ( - weechat_config_file, ptr_section, - "info_failed", "color", - N_("text color for \"failed\" status info"), - NULL, GUI_COLOR_INFO_FAILED, 0, "lightred", - NULL, NULL, &config_change_color, NULL, NULL, NULL); - config_color_info_aborted = config_file_new_option ( - weechat_config_file, ptr_section, - "info_aborted", "color", - N_("text color for \"aborted\" status info"), - NULL, GUI_COLOR_INFO_ABORTED, 0, "lightred", - NULL, NULL, &config_change_color, NULL, NULL, NULL); - + /* history */ ptr_section = config_file_new_section (weechat_config_file, "history", 0, 0, diff --git a/src/core/wee-config.h b/src/core/wee-config.h index 47b81ffb5..66126dda0 100644 --- a/src/core/wee-config.h +++ b/src/core/wee-config.h @@ -152,14 +152,6 @@ extern struct t_config_option *config_color_nicklist_prefix4; extern struct t_config_option *config_color_nicklist_prefix5; extern struct t_config_option *config_color_nicklist_more; extern struct t_config_option *config_color_nicklist_separator; -extern struct t_config_option *config_color_info; -extern struct t_config_option *config_color_info_bg; -extern struct t_config_option *config_color_info_waiting; -extern struct t_config_option *config_color_info_connecting; -extern struct t_config_option *config_color_info_active; -extern struct t_config_option *config_color_info_done; -extern struct t_config_option *config_color_info_failed; -extern struct t_config_option *config_color_info_aborted; extern struct t_config_option *config_history_max_lines; extern struct t_config_option *config_history_max_commands; diff --git a/src/core/wee-network.c b/src/core/wee-network.c new file mode 100644 index 000000000..1c9e5dc63 --- /dev/null +++ b/src/core/wee-network.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* wee-network.c: network functions for WeeChat */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> + +#include "weechat.h" +#include "wee-network.h" +#include "wee-config.h" +#include "wee-string.h" + + +/* + * network_convbase64_8x3_to_6x4 : convert 3 bytes of 8 bits in 4 bytes of 6 bits + */ + +void +network_convbase64_8x3_to_6x4 (char *from, char *to) +{ + unsigned char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/"; + + to[0] = base64_table [ (from[0] & 0xfc) >> 2 ]; + to[1] = base64_table [ ((from[0] & 0x03) << 4) + ((from[1] & 0xf0) >> 4) ]; + to[2] = base64_table [ ((from[1] & 0x0f) << 2) + ((from[2] & 0xc0) >> 6) ]; + to[3] = base64_table [ from[2] & 0x3f ]; +} + +/* + * network_base64encode: encode a string in base64 + */ + +void +network_base64encode (char *from, char *to) +{ + char *f, *t; + int from_len; + + from_len = strlen (from); + + f = from; + t = to; + + while (from_len >= 3) + { + network_convbase64_8x3_to_6x4 (f, t); + f += 3 * sizeof (*f); + t += 4 * sizeof (*t); + from_len -= 3; + } + + if (from_len > 0) + { + char rest[3] = { 0, 0, 0 }; + switch (from_len) + { + case 1 : + rest[0] = f[0]; + network_convbase64_8x3_to_6x4 (rest, t); + t[2] = t[3] = '='; + break; + case 2 : + rest[0] = f[0]; + rest[1] = f[1]; + network_convbase64_8x3_to_6x4 (rest, t); + t[3] = '='; + break; + } + t[4] = 0; + } +} + +/* + * network_pass_httpproxy: establish connection/authentification to an + * http proxy + * return : + * - 0 if connexion throw proxy was successful + * - 1 if connexion fails + */ + +int +network_pass_httpproxy (int sock, char *address, int port) +{ + + char buffer[256], authbuf[128], authbuf_base64[196]; + int n, m; + + if (CONFIG_STRING(config_proxy_username) + && CONFIG_STRING(config_proxy_username)[0]) + { + /* authentification */ + snprintf (authbuf, sizeof (authbuf), "%s:%s", + CONFIG_STRING(config_proxy_username), + (CONFIG_STRING(config_proxy_password)) ? + CONFIG_STRING(config_proxy_password) : ""); + network_base64encode (authbuf, authbuf_base64); + n = snprintf (buffer, sizeof (buffer), + "CONNECT %s:%d HTTP/1.0\r\nProxy-Authorization: Basic %s\r\n\r\n", + address, port, authbuf_base64); + } + else + { + /* no authentification */ + n = snprintf (buffer, sizeof (buffer), + "CONNECT %s:%d HTTP/1.0\r\n\r\n", address, port); + } + + m = send (sock, buffer, n, 0); + if (n != m) + return 1; + + n = recv (sock, buffer, sizeof (buffer), 0); + + /* success result must be like: "HTTP/1.0 200 OK" */ + if (n < 12) + return 1; + + if (memcmp (buffer, "HTTP/", 5) || memcmp (buffer + 9, "200", 3)) + return 1; + + return 0; +} + +/* + * network_resolve: resolve hostname on its IP address + * (works with ipv4 and ipv6) + * return : + * - 0 if resolution was successful + * - 1 if resolution fails + */ + +int +network_resolve (char *hostname, char *ip, int *version) +{ + char ipbuffer[NI_MAXHOST]; + struct addrinfo *res; + + if (version != NULL) + *version = 0; + + res = NULL; + + if (getaddrinfo (hostname, NULL, NULL, &res) != 0) + return 1; + + if (!res) + return 1; + + if (getnameinfo (res->ai_addr, res->ai_addrlen, ipbuffer, sizeof(ipbuffer), + NULL, 0, NI_NUMERICHOST) != 0) + { + freeaddrinfo (res); + return 1; + } + + if ((res->ai_family == AF_INET) && (version != NULL)) + *version = 4; + if ((res->ai_family == AF_INET6) && (version != NULL)) + *version = 6; + + strcpy (ip, ipbuffer); + + freeaddrinfo (res); + + return 0; +} + +/* + * network_pass_socks4proxy: establish connection/authentification thru a + * socks4 proxy + * return : + * - 0 if connexion thru proxy was successful + * - 1 if connexion fails + */ + +int +network_pass_socks4proxy (int sock, char *address, int port) +{ + /* + * socks4 protocol is explained here: + * http://archive.socks.permeo.com/protocol/socks4.protocol + * + */ + + struct s_socks4 + { + char version; /* 1 byte */ /* socks version : 4 or 5 */ + char method; /* 1 byte */ /* socks method : connect (1) or bind (2) */ + unsigned short port; /* 2 bytes */ /* destination port */ + unsigned long address; /* 4 bytes */ /* destination address */ + char user[64]; /* username (64 characters seems to be enought) */ + } socks4; + unsigned char buffer[24]; + char ip_addr[NI_MAXHOST]; + + socks4.version = 4; + socks4.method = 1; + socks4.port = htons (port); + network_resolve (address, ip_addr, NULL); + socks4.address = inet_addr (ip_addr); + strncpy (socks4.user, CONFIG_STRING(config_proxy_username), + sizeof (socks4.user) - 1); + + send (sock, (char *) &socks4, 8 + strlen (socks4.user) + 1, 0); + recv (sock, buffer, sizeof (buffer), 0); + + if (buffer[0] == 0 && buffer[1] == 90) + return 0; + + return 1; +} + +/* + * network_pass_socks5proxy: establish connection/authentification thru a + * socks5 proxy + * return : + * - 0 if connexion thru proxy was successful + * - 1 if connexion fails + */ + +int +network_pass_socks5proxy (int sock, char *address, int port) +{ + /* + * socks5 protocol is explained in RFC 1928 + * socks5 authentication with username/pass is explained in RFC 1929 + */ + + struct s_sock5 + { + char version; /* 1 byte */ /* socks version : 4 or 5 */ + char nmethods; /* 1 byte */ /* size in byte(s) of field 'method', here 1 byte */ + char method; /* 1-255 bytes */ /* socks method : noauth (0), auth(user/pass) (2), ... */ + } socks5; + unsigned char buffer[288]; + int username_len, password_len, addr_len, addr_buffer_len; + unsigned char *addr_buffer; + + socks5.version = 5; + socks5.nmethods = 1; + + if (CONFIG_STRING(config_proxy_username) + && CONFIG_STRING(config_proxy_username)[0]) + socks5.method = 2; /* with authentication */ + else + socks5.method = 0; /* without authentication */ + + send (sock, (char *) &socks5, sizeof(socks5), 0); + /* server socks5 must respond with 2 bytes */ + if (recv (sock, buffer, 2, 0) != 2) + return 1; + + if (CONFIG_STRING(config_proxy_username) + && CONFIG_STRING(config_proxy_username)[0]) + { + /* with authentication */ + /* -> socks server must respond with : + * - socks version (buffer[0]) = 5 => socks5 + * - socks method (buffer[1]) = 2 => authentication + */ + + if (buffer[0] != 5 || buffer[1] != 2) + return 1; + + /* authentication as in RFC 1929 */ + username_len = strlen (CONFIG_STRING(config_proxy_username)); + password_len = strlen (CONFIG_STRING(config_proxy_password)); + + /* make username/password buffer */ + buffer[0] = 1; + buffer[1] = (unsigned char) username_len; + memcpy(buffer + 2, CONFIG_STRING(config_proxy_username), username_len); + buffer[2 + username_len] = (unsigned char) password_len; + memcpy (buffer + 3 + username_len, + CONFIG_STRING(config_proxy_password), password_len); + + send (sock, buffer, 3 + username_len + password_len, 0); + + /* server socks5 must respond with 2 bytes */ + if (recv (sock, buffer, 2, 0) != 2) + return 1; + + /* buffer[1] = auth state, must be 0 for success */ + if (buffer[1] != 0) + return 1; + } + else + { + /* without authentication */ + /* -> socks server must respond with : + * - socks version (buffer[0]) = 5 => socks5 + * - socks method (buffer[1]) = 0 => no authentication + */ + if (!(buffer[0] == 5 && buffer[1] == 0)) + return 1; + } + + /* authentication successful then giving address/port to connect */ + addr_len = strlen(address); + addr_buffer_len = 4 + 1 + addr_len + 2; + addr_buffer = malloc (addr_buffer_len * sizeof(*addr_buffer)); + if (!addr_buffer) + return 1; + addr_buffer[0] = 5; /* version 5 */ + addr_buffer[1] = 1; /* command: 1 for connect */ + addr_buffer[2] = 0; /* reserved */ + addr_buffer[3] = 3; /* address type : ipv4 (1), domainname (3), ipv6 (4) */ + addr_buffer[4] = (unsigned char) addr_len; + memcpy (addr_buffer + 5, address, addr_len); /* server address */ + *((unsigned short *) (addr_buffer + 5 + addr_len)) = htons (port); /* server port */ + + send (sock, addr_buffer, addr_buffer_len, 0); + free (addr_buffer); + + /* dialog with proxy server */ + if (recv (sock, buffer, 4, 0) != 4) + return 1; + + if (!(buffer[0] == 5 && buffer[1] == 0)) + return 1; + + /* buffer[3] = address type */ + switch (buffer[3]) + { + case 1: + /* ipv4 + * server socks return server bound address and port + * address of 4 bytes and port of 2 bytes (= 6 bytes) + */ + if (recv (sock, buffer, 6, 0) != 6) + return 1; + break; + case 3: + /* domainname + * server socks return server bound address and port + */ + /* reading address length */ + if (recv (sock, buffer, 1, 0) != 1) + return 1; + addr_len = buffer[0]; + /* reading address + port = addr_len + 2 */ + if (recv (sock, buffer, addr_len + 2, 0) != (addr_len + 2)) + return 1; + break; + case 4: + /* ipv6 + * server socks return server bound address and port + * address of 16 bytes and port of 2 bytes (= 18 bytes) + */ + if (recv (sock, buffer, 18, 0) != 18) + return 1; + break; + default: + return 1; + } + + return 0; +} + +/* + * network_pass_proxy: establish connection/authentification to a proxy + * return : + * - 0 if connexion throw proxy was successful + * - 1 if connexion fails + */ + +int +network_pass_proxy (int sock, char *address, int port) +{ + int rc; + + rc = 1; + if (CONFIG_BOOLEAN(config_proxy_type)) + { + if (string_strcasecmp (CONFIG_STRING(config_proxy_type), "http") == 0) + rc = network_pass_httpproxy (sock, address, port); + if (string_strcasecmp (CONFIG_STRING(config_proxy_type), "socks4") == 0) + rc = network_pass_socks4proxy (sock, address, port); + if (string_strcasecmp (CONFIG_STRING(config_proxy_type), "socks5") == 0) + rc = network_pass_socks5proxy (sock, address, port); + } + return rc; +} + +/* + * network_connect_to: connect to a remote host + * return 1 if ok, 0 if failed + */ + +int +network_connect_to (int sock, unsigned long address, int port) +{ + struct sockaddr_in addr; + struct hostent *hostent; + char *ip4; + int ret; + + if (CONFIG_BOOLEAN(config_proxy_type)) + { + memset (&addr, 0, sizeof (addr)); + addr.sin_addr.s_addr = htonl (address); + ip4 = inet_ntoa(addr.sin_addr); + + memset (&addr, 0, sizeof (addr)); + addr.sin_port = htons (CONFIG_INTEGER(config_proxy_port)); + addr.sin_family = AF_INET; + hostent = gethostbyname (CONFIG_STRING(config_proxy_address)); + if (!hostent) + return 0; + memcpy(&(addr.sin_addr),*(hostent->h_addr_list), sizeof(struct in_addr)); + ret = connect (sock, (struct sockaddr *) &addr, sizeof (addr)); + if ((ret == -1) && (errno != EINPROGRESS)) + return 0; + if (network_pass_proxy (sock, ip4, port) == -1) + return 0; + } + else + { + memset (&addr, 0, sizeof (addr)); + addr.sin_port = htons (port); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl (address); + ret = connect (sock, (struct sockaddr *) &addr, sizeof (addr)); + if ((ret == -1) && (errno != EINPROGRESS)) + return 0; + } + return 1; +} diff --git a/src/core/wee-network.h b/src/core/wee-network.h new file mode 100644 index 000000000..cf4c61f73 --- /dev/null +++ b/src/core/wee-network.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_NETWORK_H +#define __WEECHAT_NETWORK_H 1 + +extern int network_pass_proxy (int sock, char *address, int port); +extern int network_connect_to (int sock, unsigned long address, int port); + +#endif /* wee-network.h */ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 02570b11f..4a1462d77 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -18,12 +18,23 @@ IF(NOT DISABLE_NCURSES) SUBDIRS( curses ) ENDIF(NOT DISABLE_NCURSES) -SET(LIB_GUI_COMMON_SRC gui-bar.c gui-bar.h gui-bar-item.c gui-bar-item.h -gui-buffer.c gui-buffer.h gui-chat.c gui-chat.h gui-color.c gui-color.h -gui-filter.c gui-filter.h gui-completion.c gui-completion.h gui-history.c -gui-history.h gui-hotlist.c gui-hotlist.h gui-infobar.c gui-infobar.h -gui-input.c gui-input.h gui-keyboard.c gui-keyboard.h gui-main.h gui-nicklist.c -gui-nicklist.h gui-status.c gui-status.h gui-window.c gui-window.h) +SET(LIB_GUI_COMMON_SRC +gui-bar.c gui-bar.h +gui-bar-item.c gui-bar-item.h +gui-buffer.c gui-buffer.h +gui-chat.c gui-chat.h +gui-color.c gui-color.h +gui-filter.c gui-filter.h +gui-completion.c gui-completion.h +gui-history.c gui-history.h +gui-hotlist.c gui-hotlist.h +gui-infobar.c gui-infobar.h +gui-input.c gui-input.h +gui-keyboard.c gui-keyboard.h +gui-main.h +gui-nicklist.c gui-nicklist.h +gui-status.c gui-status.h +gui-window.c gui-window.h) INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}) ADD_LIBRARY(weechat_gui_common STATIC ${LIB_GUI_COMMON_SRC}) diff --git a/src/gui/curses/gui-curses-color.c b/src/gui/curses/gui-curses-color.c index c1b767b5d..137efff23 100644 --- a/src/gui/curses/gui-curses-color.c +++ b/src/gui/curses/gui-curses-color.c @@ -416,14 +416,6 @@ gui_color_init_weechat () gui_color[GUI_COLOR_NICKLIST_PREFIX5] = gui_color_build (GUI_COLOR_NICKLIST_PREFIX5, CONFIG_COLOR(config_color_nicklist_prefix5), CONFIG_COLOR(config_color_nicklist_bg)); gui_color[GUI_COLOR_NICKLIST_MORE] = gui_color_build (GUI_COLOR_NICKLIST_MORE, CONFIG_COLOR(config_color_nicklist_more), CONFIG_COLOR(config_color_nicklist_bg)); gui_color[GUI_COLOR_NICKLIST_SEPARATOR] = gui_color_build (GUI_COLOR_NICKLIST_SEPARATOR, CONFIG_COLOR(config_color_nicklist_separator), CONFIG_COLOR(config_color_nicklist_bg)); - - gui_color[GUI_COLOR_INFO] = gui_color_build (GUI_COLOR_INFO, CONFIG_COLOR(config_color_info), CONFIG_COLOR(config_color_info_bg)); - gui_color[GUI_COLOR_INFO_WAITING] = gui_color_build (GUI_COLOR_INFO_WAITING, CONFIG_COLOR(config_color_info_waiting), CONFIG_COLOR(config_color_info_bg)); - gui_color[GUI_COLOR_INFO_CONNECTING] = gui_color_build (GUI_COLOR_INFO_CONNECTING, CONFIG_COLOR(config_color_info_connecting), CONFIG_COLOR(config_color_info_bg)); - gui_color[GUI_COLOR_INFO_ACTIVE] = gui_color_build (GUI_COLOR_INFO_ACTIVE, CONFIG_COLOR(config_color_info_active), CONFIG_COLOR(config_color_info_bg)); - gui_color[GUI_COLOR_INFO_DONE] = gui_color_build (GUI_COLOR_INFO_DONE, CONFIG_COLOR(config_color_info_done), CONFIG_COLOR(config_color_info_bg)); - gui_color[GUI_COLOR_INFO_FAILED] = gui_color_build (GUI_COLOR_INFO_FAILED, CONFIG_COLOR(config_color_info_failed), CONFIG_COLOR(config_color_info_bg)); - gui_color[GUI_COLOR_INFO_ABORTED] = gui_color_build (GUI_COLOR_INFO_ABORTED, CONFIG_COLOR(config_color_info_aborted), CONFIG_COLOR(config_color_info_bg)); } /* diff --git a/src/gui/gui-bar.c b/src/gui/gui-bar.c index abd5d99d8..8ac5ac4ba 100644 --- a/src/gui/gui-bar.c +++ b/src/gui/gui-bar.c @@ -38,14 +38,14 @@ #include "gui-window.h" -char *gui_bar_option_str[GUI_BAR_NUM_OPTIONS] = +char *gui_bar_option_string[GUI_BAR_NUM_OPTIONS] = { "priority", "type", "conditions", "position", "filling", "size", "size_max", "color_fg", "color_bg", "separator", "items" }; -char *gui_bar_type_str[GUI_BAR_NUM_TYPES] = +char *gui_bar_type_string[GUI_BAR_NUM_TYPES] = { "root", "window" }; -char *gui_bar_position_str[GUI_BAR_NUM_POSITIONS] = +char *gui_bar_position_string[GUI_BAR_NUM_POSITIONS] = { "bottom", "top", "left", "right" }; -char *gui_bar_filling_str[GUI_BAR_NUM_FILLING] = +char *gui_bar_filling_string[GUI_BAR_NUM_FILLING] = { "horizontal", "vertical" }; struct t_gui_bar *gui_bars = NULL; /* first bar */ @@ -71,7 +71,7 @@ gui_bar_search_option (char *option_name) for (i = 0; i < GUI_BAR_NUM_OPTIONS; i++) { - if (string_strcasecmp (gui_bar_option_str[i], option_name) == 0) + if (string_strcasecmp (gui_bar_option_string[i], option_name) == 0) return i; } @@ -91,7 +91,7 @@ gui_bar_search_type (char *type) for (i = 0; i < GUI_BAR_NUM_TYPES; i++) { - if (string_strcasecmp (type, gui_bar_type_str[i]) == 0) + if (string_strcasecmp (type, gui_bar_type_string[i]) == 0) return i; } @@ -111,7 +111,7 @@ gui_bar_search_position (char *position) for (i = 0; i < GUI_BAR_NUM_POSITIONS; i++) { - if (string_strcasecmp (position, gui_bar_position_str[i]) == 0) + if (string_strcasecmp (position, gui_bar_position_string[i]) == 0) return i; } @@ -868,12 +868,13 @@ gui_bar_create_option (char *bar_name, int index_option, char *value) ptr_option = NULL; - length = strlen (bar_name) + 1 + strlen (gui_bar_option_str[index_option]) + 1; + length = strlen (bar_name) + 1 + + strlen (gui_bar_option_string[index_option]) + 1; option_name = malloc (length); if (option_name) { snprintf (option_name, length, "%s.%s", - bar_name, gui_bar_option_str[index_option]); + bar_name, gui_bar_option_string[index_option]); switch (index_option) { @@ -1538,16 +1539,16 @@ gui_bar_print_log () log_printf (" priority . . . . . . . : %d", CONFIG_INTEGER(ptr_bar->priority)); log_printf (" type . . . . . . . . . : %d (%s)", CONFIG_INTEGER(ptr_bar->type), - gui_bar_type_str[CONFIG_INTEGER(ptr_bar->type)]); + gui_bar_type_string[CONFIG_INTEGER(ptr_bar->type)]); log_printf (" conditions . . . . . . : '%s'", CONFIG_STRING(ptr_bar->conditions)); log_printf (" conditions_count . . . : %d", ptr_bar->conditions_count); log_printf (" conditions_array . . . : 0x%x", ptr_bar->conditions_array); log_printf (" position . . . . . . . : %d (%s)", CONFIG_INTEGER(ptr_bar->position), - gui_bar_position_str[CONFIG_INTEGER(ptr_bar->position)]); + gui_bar_position_string[CONFIG_INTEGER(ptr_bar->position)]); log_printf (" filling. . . . . . . . : %d (%s)", CONFIG_INTEGER(ptr_bar->filling), - gui_bar_filling_str[CONFIG_INTEGER(ptr_bar->filling)]); + gui_bar_filling_string[CONFIG_INTEGER(ptr_bar->filling)]); log_printf (" size . . . . . . . . . : %d", CONFIG_INTEGER(ptr_bar->size)); log_printf (" size_max . . . . . . . : %d", CONFIG_INTEGER(ptr_bar->size_max)); log_printf (" color_fg . . . . . . . : %d", diff --git a/src/gui/gui-bar.h b/src/gui/gui-bar.h index f0cfb6a2a..3f888dd76 100644 --- a/src/gui/gui-bar.h +++ b/src/gui/gui-bar.h @@ -96,9 +96,9 @@ struct t_gui_bar /* variables */ -extern char *gui_bar_type_str[]; -extern char *gui_bar_position_str[]; -extern char *gui_bar_filling_str[]; +extern char *gui_bar_type_string[]; +extern char *gui_bar_position_string[]; +extern char *gui_bar_filling_string[]; extern struct t_gui_bar *gui_bars; extern struct t_gui_bar *last_gui_bar; extern struct t_gui_bar *gui_temp_bars; diff --git a/src/gui/gui-color.h b/src/gui/gui-color.h index 448dac134..1b455ba43 100644 --- a/src/gui/gui-color.h +++ b/src/gui/gui-color.h @@ -94,14 +94,6 @@ enum t_gui_color_enum GUI_COLOR_NICKLIST_MORE, GUI_COLOR_NICKLIST_SEPARATOR, - GUI_COLOR_INFO, - GUI_COLOR_INFO_WAITING, - GUI_COLOR_INFO_CONNECTING, - GUI_COLOR_INFO_ACTIVE, - GUI_COLOR_INFO_DONE, - GUI_COLOR_INFO_FAILED, - GUI_COLOR_INFO_ABORTED, - /* number of colors */ GUI_COLOR_NUM_COLORS, }; diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index dd04d0006..fb5f1964a 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -71,3 +71,7 @@ ENDIF(NOT DISABLE_SCRIPTS AND NOT DISABLE_PERL AND NOT DISABLE_PYTHON AND NOT DI IF(NOT DISABLE_TRIGGER) ADD_SUBDIRECTORY( trigger ) ENDIF(NOT DISABLE_TRIGGER) + +IF(NOT DISABLE_XFER) + ADD_SUBDIRECTORY( xfer ) +ENDIF(NOT DISABLE_XFER) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index ce6fdd4b9..7cd2c55e7 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -80,6 +80,10 @@ if PLUGIN_TRIGGER trigger_dir = trigger endif +if PLUGIN_XFER +xfer_dir = xfer +endif + SUBDIRS = . $(alias_dir) $(aspell_dir) $(charset_dir) $(debug_dir) \ $(demo_dir) $(fifo_dir) $(irc_dir) $(logger_dir) $(script_dir) \ - $(trigger_dir) + $(trigger_dir) $(xfer_dir) diff --git a/src/plugins/irc/CMakeLists.txt b/src/plugins/irc/CMakeLists.txt index 1a7d66d51..c4da9b459 100644 --- a/src/plugins/irc/CMakeLists.txt +++ b/src/plugins/irc/CMakeLists.txt @@ -14,17 +14,26 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -ADD_LIBRARY(irc MODULE irc.c irc.h irc-buffer.c irc-buffer.h irc-channel.c -irc-channel.h irc-color.c irc-color.h irc-command.c irc-command.h -irc-completion.c irc-completion.h irc-config.c irc-config.h irc-dcc.h -irc-debug.c irc-debug.h irc-display.c irc-display.h irc-input.c irc-input.h -irc-mode.c irc-mode.h irc-nick.c irc-nick.h irc-protocol.c irc-protocol.h +ADD_LIBRARY(irc MODULE +irc.c irc.h +irc-buffer.c irc-buffer.h +irc-channel.c irc-channel.h +irc-color.c irc-color.h +irc-command.c irc-command.h +irc-completion.c irc-completion.h +irc-config.c irc-config.h +irc-dcc.c irc-dcc.h +irc-debug.c irc-debug.h +irc-display.c irc-display.h +irc-input.c irc-input.h +irc-mode.c irc-mode.h +irc-nick.c irc-nick.h +irc-protocol.c irc-protocol.h irc-server.c irc-server.h) SET_TARGET_PROPERTIES(irc PROPERTIES PREFIX "") CHECK_INCLUDE_FILES("regex.h" HAVE_REGEX_H) CHECK_FUNCTION_EXISTS(regexec HAVE_REGEXEC) -CHECK_FUNCTION_EXISTS(uname HAVE_UNAME) TARGET_LINK_LIBRARIES(irc) diff --git a/src/plugins/irc/Makefile.am b/src/plugins/irc/Makefile.am index 8edaf725f..e4e25fe30 100644 --- a/src/plugins/irc/Makefile.am +++ b/src/plugins/irc/Makefile.am @@ -34,6 +34,7 @@ irc_la_SOURCES = irc.c \ irc-completion.h \ irc-config.c \ irc-config.h \ + irc-dcc.c \ irc-dcc.h \ irc-debug.c \ irc-debug.h \ @@ -50,7 +51,5 @@ irc_la_SOURCES = irc.c \ irc-server.c \ irc-server.h -# irc-dcc.c - irc_la_LDFLAGS = -module -irc_la_LIBADD = $(GNUTLS_LFLAGS) +irc_la_LIBADD = $(IRC_LFLAGS) $(GNUTLS_LFLAGS) diff --git a/src/plugins/irc/irc-command.c b/src/plugins/irc/irc-command.c index fbde8a9ce..6cfa50d64 100644 --- a/src/plugins/irc/irc-command.c +++ b/src/plugins/irc/irc-command.c @@ -25,6 +25,9 @@ #include <ctype.h> #include <sys/time.h> #include <time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> #include "../weechat-plugin.h" #include "irc.h" @@ -855,16 +858,29 @@ int irc_command_dcc (void *data, struct t_gui_buffer *buffer, int argc, char **argv, char **argv_eol) { + struct sockaddr_in addr; + socklen_t length; + unsigned long address; + struct t_plugin_infolist *infolist; + struct t_plugin_infolist_item *item; + char plugin_id[128], str_address[128]; + IRC_GET_SERVER_CHANNEL(buffer); if (!ptr_server || !ptr_server->is_connected) return WEECHAT_RC_ERROR; - /* make compiler happy */ + /* make C compiler happy */ (void) data; - (void) argv_eol; // to remove! if (argc > 1) { + /* use the local interface, from the server socket */ + memset (&addr, 0, sizeof (struct sockaddr_in)); + length = sizeof (addr); + getsockname (ptr_server->sock, (struct sockaddr *) &addr, &length); + addr.sin_family = AF_INET; + address = ntohl (addr.sin_addr.s_addr); + /* DCC SEND file */ if (weechat_strcasecmp (argv[1], "send") == 0) { @@ -872,8 +888,28 @@ irc_command_dcc (void *data, struct t_gui_buffer *buffer, int argc, { IRC_COMMAND_TOO_FEW_ARGUMENTS(ptr_server->buffer, "dcc send"); } - //irc_dcc_send_request (ptr_server, IRC_DCC_FILE_SEND, - // argv[2], argv_eol[3]); + infolist = weechat_infolist_new (); + if (infolist) + { + item = weechat_infolist_new_item (infolist); + if (item) + { + snprintf (plugin_id, sizeof (plugin_id), + "irc_%x", (unsigned int)ptr_server); + weechat_infolist_new_var_string (item, "plugin_id", plugin_id); + weechat_infolist_new_var_string (item, "type", "file_send"); + weechat_infolist_new_var_string (item, "protocol", "dcc"); + weechat_infolist_new_var_string (item, "nick", argv[2]); + weechat_infolist_new_var_string (item, "filename", argv_eol[3]); + snprintf (str_address, sizeof (str_address), + "%lu", address); + weechat_infolist_new_var_string (item, "address", str_address); + weechat_hook_signal_send ("xfer_add", + WEECHAT_HOOK_SIGNAL_POINTER, + infolist); + } + weechat_infolist_free (infolist); + } } /* DCC CHAT */ else if (weechat_strcasecmp (argv[1], "chat") == 0) @@ -882,8 +918,26 @@ irc_command_dcc (void *data, struct t_gui_buffer *buffer, int argc, { IRC_COMMAND_TOO_FEW_ARGUMENTS(ptr_server->buffer, "dcc chat"); } - //irc_dcc_send_request (ptr_server, IRC_DCC_CHAT_SEND, - // argv[2], NULL); + infolist = weechat_infolist_new (); + if (infolist) + { + item = weechat_infolist_new_item (infolist); + if (item) + { + snprintf (plugin_id, sizeof (plugin_id), + "irc_%x", (unsigned int)ptr_server); + weechat_infolist_new_var_string (item, "plugin_id", plugin_id); + weechat_infolist_new_var_string (item, "type", "chat_send"); + weechat_infolist_new_var_string (item, "nick", argv[2]); + snprintf (str_address, sizeof (str_address), + "%lu", address); + weechat_infolist_new_var_string (item, "address", str_address); + weechat_hook_signal_send ("xfer_add", + WEECHAT_HOOK_SIGNAL_POINTER, + infolist); + } + weechat_infolist_free (infolist); + } } /* close DCC CHAT */ else if (weechat_strcasecmp (argv[1], "close") == 0) diff --git a/src/plugins/irc/irc-config.c b/src/plugins/irc/irc-config.c index b0fbcac51..d55719c8e 100644 --- a/src/plugins/irc/irc-config.c +++ b/src/plugins/irc/irc-config.c @@ -33,7 +33,7 @@ #include "irc-server.h" -char *irc_config_server_option_str[IRC_CONFIG_NUM_SERVER_OPTIONS] = +char *irc_config_server_option_string[IRC_CONFIG_NUM_SERVER_OPTIONS] = { "autoconnect", "autoreconnect", "autoreconnect_delay", "addresses", "ipv6", "ssl", "password", "nicks", "username", "realname", "hostname", "command", "command_delay", "autojoin", "autorejoin", "notify_levels" @@ -47,7 +47,7 @@ struct t_config_file *irc_config_file = NULL; struct t_config_section *irc_config_section_server_default = NULL; struct t_config_section *irc_config_section_server = NULL; -/* config, look section */ +/* IRC config, look section */ struct t_config_option *irc_config_look_one_server_buffer; struct t_config_option *irc_config_look_open_near_server; @@ -59,7 +59,7 @@ struct t_config_option *irc_config_look_show_away_once; struct t_config_option *irc_config_look_notice_as_pv; struct t_config_option *irc_config_look_highlight; -/* config, network section */ +/* IRC config, network section */ struct t_config_option *irc_config_network_default_msg_part; struct t_config_option *irc_config_network_default_msg_quit; @@ -73,29 +73,14 @@ struct t_config_option *irc_config_network_colors_receive; struct t_config_option *irc_config_network_colors_send; struct t_config_option *irc_config_network_send_unknown_commands; -/* config, dcc section */ - -struct t_config_option *irc_config_dcc_auto_accept_files; -struct t_config_option *irc_config_dcc_auto_accept_chats; -struct t_config_option *irc_config_dcc_timeout; -struct t_config_option *irc_config_dcc_blocksize; -struct t_config_option *irc_config_dcc_fast_send; -struct t_config_option *irc_config_dcc_port_range; -struct t_config_option *irc_config_dcc_own_ip; -struct t_config_option *irc_config_dcc_download_path; -struct t_config_option *irc_config_dcc_upload_path; -struct t_config_option *irc_config_dcc_convert_spaces; -struct t_config_option *irc_config_dcc_auto_rename; -struct t_config_option *irc_config_dcc_auto_resume; - -/* config, log section */ +/* IRC config, log section */ struct t_config_option *irc_config_log_auto_log_server; struct t_config_option *irc_config_log_auto_log_channel; struct t_config_option *irc_config_log_auto_log_private; struct t_config_option *irc_config_log_hide_nickserv_pwd; -/* config, server section */ +/* IRC config, server section */ struct t_config_option *irc_config_server_default[IRC_CONFIG_NUM_SERVER_OPTIONS]; @@ -117,7 +102,7 @@ irc_config_search_server_option (char *option_name) for (i = 0; i < IRC_CONFIG_NUM_SERVER_OPTIONS; i++) { - if (weechat_strcasecmp (irc_config_server_option_str[i], + if (weechat_strcasecmp (irc_config_server_option_string[i], option_name) == 0) return i; } @@ -757,9 +742,9 @@ irc_config_server_create_option (void *data, struct t_config_file *config_file, option_name, value, &irc_config_server_change_cb, - irc_config_server_option_str[index_option], + irc_config_server_option_string[index_option], &irc_config_server_delete_cb, - irc_config_server_option_str[index_option]); + irc_config_server_option_string[index_option]); if (ptr_option) { @@ -857,10 +842,10 @@ irc_config_server_create_default_options (struct t_config_section *section) irc_config_file, section, i, - irc_config_server_option_str[i], + irc_config_server_option_string[i], default_value, &irc_config_server_default_change_cb, - irc_config_server_option_str[i], + irc_config_server_option_string[i], NULL, NULL); } @@ -1013,83 +998,6 @@ irc_config_init () N_("send unknown commands to IRC server"), NULL, 0, 0, "off", NULL, NULL, NULL, NULL, NULL, NULL); - ptr_section = weechat_config_new_section (irc_config_file, "dcc", - 0, 0, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL); - if (!ptr_section) - { - weechat_config_free (irc_config_file); - return 0; - } - - irc_config_dcc_auto_accept_files = weechat_config_new_option ( - irc_config_file, ptr_section, - "auto_accept_files", "boolean", - N_("automatically accept incoming dcc files (use carefully!)"), - NULL, 0, 0, "off", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_auto_accept_chats = weechat_config_new_option ( - irc_config_file, ptr_section, - "auto_accept_chats", "boolean", - N_("automatically accept dcc chats (use carefully!)"), - NULL, 0, 0, "off", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_timeout = weechat_config_new_option ( - irc_config_file, ptr_section, - "timeout", "integer", - N_("timeout for dcc request (in seconds)"), - NULL, 5, INT_MAX, "300", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_blocksize = weechat_config_new_option ( - irc_config_file, ptr_section, - "blocksize", "integer", - N_("block size for dcc packets in bytes"), - NULL, IRC_DCC_MIN_BLOCKSIZE, IRC_DCC_MAX_BLOCKSIZE, "65536", - NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_fast_send = weechat_config_new_option ( - irc_config_file, ptr_section, - "fast_send", "boolean", - N_("does not wait for ACK when sending file"), - NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_port_range = weechat_config_new_option ( - irc_config_file, ptr_section, - "port_range", "string", - N_("restricts outgoing dcc to use only ports in the given range " - "(useful for NAT) (syntax: a single port, ie. 5000 or a port " - "range, ie. 5000-5015, empty value means any port)"), - NULL, 0, 0, "", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_own_ip = weechat_config_new_option ( - irc_config_file, ptr_section, - "own_ip", "string", - N_("IP or DNS address used for outgoing dcc " - "(if empty, local interface IP is used)"), - NULL, 0, 0, "", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_download_path = weechat_config_new_option ( - irc_config_file, ptr_section, - "download_path", "string", - N_("path for writing incoming files with dcc"), - NULL, 0, 0, "%h/dcc", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_upload_path = weechat_config_new_option ( - irc_config_file, ptr_section, - "upload_path", "string", - N_("path for reading files when sending thru dcc (when no path is " - "specified)"), - NULL, 0, 0, "~", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_convert_spaces = weechat_config_new_option ( - irc_config_file, ptr_section, - "convert_spaces", "boolean", - N_("convert spaces to underscores when sending files"), - NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_auto_rename = weechat_config_new_option ( - irc_config_file, ptr_section, - "auto_rename", "boolean", - N_("rename incoming files if already exists (add '.1', '.2', ...)"), - NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_dcc_auto_resume = weechat_config_new_option ( - irc_config_file, ptr_section, - "auto_resume", "boolean", - N_("automatically resume dcc transfer if connection with remote host " - "is loosed"), - NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); - ptr_section = weechat_config_new_section (irc_config_file, "log", 0, 0, NULL, NULL, NULL, NULL, @@ -1162,11 +1070,7 @@ irc_config_init () int irc_config_read () { - int rc; - - rc = weechat_config_read (irc_config_file); - - return rc; + return weechat_config_read (irc_config_file); } /* diff --git a/src/plugins/irc/irc-config.h b/src/plugins/irc/irc-config.h index 7c3765513..dc161d94a 100644 --- a/src/plugins/irc/irc-config.h +++ b/src/plugins/irc/irc-config.h @@ -57,7 +57,7 @@ enum t_irc_config_server_option #define IRC_CONFIG_SERVER_DEFAULT_AUTOREJOIN 0 -extern char *irc_config_server_option_str[]; +extern char *irc_config_server_option_string[]; extern struct t_config_file *irc_config; extern struct t_config_option *irc_config_look_one_server_buffer; @@ -82,19 +82,6 @@ extern struct t_config_option *irc_config_network_colors_receive; extern struct t_config_option *irc_config_network_colors_send; extern struct t_config_option *irc_config_network_send_unknown_commands; -extern struct t_config_option *irc_config_dcc_auto_accept_files; -extern struct t_config_option *irc_config_dcc_auto_accept_chats; -extern struct t_config_option *irc_config_dcc_timeout; -extern struct t_config_option *irc_config_dcc_blocksize; -extern struct t_config_option *irc_config_dcc_fast_send; -extern struct t_config_option *irc_config_dcc_port_range; -extern struct t_config_option *irc_config_dcc_own_ip; -extern struct t_config_option *irc_config_dcc_download_path; -extern struct t_config_option *irc_config_dcc_upload_path; -extern struct t_config_option *irc_config_dcc_convert_spaces; -extern struct t_config_option *irc_config_dcc_auto_rename; -extern struct t_config_option *irc_config_dcc_auto_resume; - extern struct t_config_option *irc_config_log_auto_log_server; extern struct t_config_option *irc_config_log_auto_log_channel; extern struct t_config_option *irc_config_log_auto_log_private; diff --git a/src/plugins/irc/irc-dcc.c b/src/plugins/irc/irc-dcc.c index 4ca82b0bb..5a4efe982 100644 --- a/src/plugins/irc/irc-dcc.c +++ b/src/plugins/irc/irc-dcc.c @@ -44,504 +44,11 @@ struct t_irc_dcc *irc_dcc_list = NULL; /* DCC files & chat list */ struct t_irc_dcc *irc_last_dcc = NULL; /* last DCC in list */ -char *irc_dcc_status_string[] = /* strings for DCC status */ -{ N_("Waiting"), N_("Connecting"), N_("Active"), N_("Done"), N_("Failed"), - N_("Aborted") }; - - -/* - * irc_dcc_redraw: redraw DCC buffer (and add to hotlist) - */ - -void -irc_dcc_redraw (char *hotlist) -{ - struct t_gui_buffer *ptr_buffer; - - ptr_buffer = weechat_buffer_search ("irc", "<dcc>"); - if (ptr_buffer && hotlist) - weechat_buffer_set (ptr_buffer, "hotlist", hotlist); -} - -/* - * irc_dcc_search: search a DCC - */ - -struct t_irc_dcc * -irc_dcc_search (struct t_irc_server *server, int type, int status, int port) -{ - struct t_irc_dcc *ptr_dcc; - - for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) - { - if ((ptr_dcc->server == server) - && (ptr_dcc->type == type) - && (ptr_dcc->status = status) - && (ptr_dcc->port == port)) - return ptr_dcc; - } - - /* DCC not found */ - return NULL; -} - -/* - * irc_dcc_port_in_use: return 1 if a port is in used - * (by an active or connecting DCC) - */ - -int -irc_dcc_port_in_use (int port) -{ - struct t_irc_dcc *ptr_dcc; - - /* skip any currently used ports */ - for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) - { - if ((ptr_dcc->port == port) && (!IRC_DCC_ENDED(ptr_dcc->status))) - return 1; - } - - /* port not in use */ - return 0; -} - -/* - * irc_dcc_file_is_resumable: check if a file can be used for resuming a download - */ - -int -irc_dcc_file_is_resumable (struct t_irc_dcc *ptr_dcc, char *filename) -{ - struct stat st; - - if (!weechat_config_boolean (irc_config_dcc_auto_resume)) - return 0; - - if (access (filename, W_OK) == 0) - { - if (stat (filename, &st) != -1) - { - if ((unsigned long) st.st_size < ptr_dcc->size) - { - ptr_dcc->start_resume = (unsigned long) st.st_size; - ptr_dcc->pos = st.st_size; - ptr_dcc->last_check_pos = st.st_size; - return 1; - } - } - } - - /* not resumable */ - return 0; -} - -/* - * irc_dcc_find_filename: find local filename for a DCC - * if type if file/recv, add a suffix (like .1) if needed - * if download is resumable, set "start_resume" to good value - */ - -void -irc_dcc_find_filename (struct t_irc_dcc *ptr_dcc) -{ - char *weechat_home, *dir1, *dir2, *filename2, *dir_separator; - - if (!IRC_DCC_IS_FILE(ptr_dcc->type)) - return; - - dir1 = weechat_string_replace (weechat_config_string (irc_config_dcc_download_path), - "~", - getenv ("HOME")); - if (!dir1) - return; - - weechat_home = weechat_info_get ("weechat_dir"); - if (!weechat_home) - { - free (dir1); - return; - } - dir2 = weechat_string_replace (dir1, "%h", weechat_home); - if (!dir2) - { - free (dir1); - return; - } - - ptr_dcc->local_filename = malloc (strlen (dir2) + - strlen (ptr_dcc->nick) + - strlen (ptr_dcc->filename) + 4); - if (!ptr_dcc->local_filename) - return; - - strcpy (ptr_dcc->local_filename, dir2); - dir_separator = weechat_info_get("dir_separator"); - if (dir_separator - && (ptr_dcc->local_filename[strlen (ptr_dcc->local_filename) - 1] != dir_separator[0])) - strcat (ptr_dcc->local_filename, dir_separator); - strcat (ptr_dcc->local_filename, ptr_dcc->nick); - strcat (ptr_dcc->local_filename, "."); - strcat (ptr_dcc->local_filename, ptr_dcc->filename); - - if (dir1) - free (dir1); - if (dir2 ) - free (dir2); - - /* file already exists? */ - if (access (ptr_dcc->local_filename, F_OK) == 0) - { - if (irc_dcc_file_is_resumable (ptr_dcc, ptr_dcc->local_filename)) - return; - - /* if auto rename is not set, then abort DCC */ - if (!irc_cfg_dcc_auto_rename) - { - irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MSG); - return; - } - - filename2 = malloc (strlen (ptr_dcc->local_filename) + 16); - if (!filename2) - { - irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - return; - } - ptr_dcc->filename_suffix = 0; - do - { - ptr_dcc->filename_suffix++; - sprintf (filename2, "%s.%d", - ptr_dcc->local_filename, - ptr_dcc->filename_suffix); - if (access (filename2, F_OK) == 0) - { - if (irc_dcc_file_is_resumable (ptr_dcc, filename2)) - break; - } - else - break; - } - while (1); - - free (ptr_dcc->local_filename); - ptr_dcc->local_filename = strdup (filename2); - free (filename2); - } -} - -/* - * irc_dcc_calculate_speed: calculate DCC speed (for files only) - */ - -void -irc_dcc_calculate_speed (struct t_irc_dcc *ptr_dcc, int ended) -{ - time_t local_time, elapsed; - unsigned long bytes_per_sec_total; - - local_time = time (NULL); - if (ended || local_time > ptr_dcc->last_check_time) - { - if (ended) - { - /* calculate bytes per second (global) */ - elapsed = local_time - ptr_dcc->start_transfer; - if (elapsed == 0) - elapsed = 1; - ptr_dcc->bytes_per_sec = (ptr_dcc->pos - ptr_dcc->start_resume) / elapsed; - ptr_dcc->eta = 0; - } - else - { - /* calculate ETA */ - elapsed = local_time - ptr_dcc->start_transfer; - if (elapsed == 0) - elapsed = 1; - bytes_per_sec_total = (ptr_dcc->pos - ptr_dcc->start_resume) / elapsed; - if (bytes_per_sec_total == 0) - bytes_per_sec_total = 1; - ptr_dcc->eta = (ptr_dcc->size - ptr_dcc->pos) / bytes_per_sec_total; - - /* calculate bytes per second (since last check time) */ - elapsed = local_time - ptr_dcc->last_check_time; - if (elapsed == 0) - elapsed = 1; - ptr_dcc->bytes_per_sec = (ptr_dcc->pos - ptr_dcc->last_check_pos) / elapsed; - } - ptr_dcc->last_check_time = local_time; - ptr_dcc->last_check_pos = ptr_dcc->pos; - } -} - -/* - * irc_dcc_connect_to_sender: connect to sender - */ - -int -irc_dcc_connect_to_sender (struct t_irc_dcc *ptr_dcc) -{ - struct sockaddr_in addr; - struct hostent *hostent; - char *ip4; - int ret; - - if (cfg_proxy_use) - { - memset (&addr, 0, sizeof (addr)); - addr.sin_addr.s_addr = htonl (ptr_dcc->addr); - ip4 = inet_ntoa(addr.sin_addr); - - memset (&addr, 0, sizeof (addr)); - addr.sin_port = htons (cfg_proxy_port); - addr.sin_family = AF_INET; - if ((hostent = gethostbyname (cfg_proxy_address)) == NULL) - return 0; - memcpy(&(addr.sin_addr),*(hostent->h_addr_list), sizeof(struct in_addr)); - ret = connect (ptr_dcc->sock, (struct sockaddr *) &addr, sizeof (addr)); - if ((ret == -1) && (errno != EINPROGRESS)) - return 0; - if (irc_server_pass_proxy (ptr_dcc->sock, ip4, ptr_dcc->port, - ptr_dcc->server->username) == -1) - return 0; - } - else - { - memset (&addr, 0, sizeof (addr)); - addr.sin_port = htons (ptr_dcc->port); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl (ptr_dcc->addr); - ret = connect (ptr_dcc->sock, (struct sockaddr *) &addr, sizeof (addr)); - if ((ret == -1) && (errno != EINPROGRESS)) - return 0; - } - return 1; -} - -/* - * irc_dcc_connect: connect to another host - */ - -int -irc_dcc_connect (struct t_irc_dcc *ptr_dcc) -{ - if (ptr_dcc->type == IRC_DCC_CHAT_SEND) - ptr_dcc->status = IRC_DCC_WAITING; - else - ptr_dcc->status = IRC_DCC_CONNECTING; - - if (ptr_dcc->sock < 0) - { - ptr_dcc->sock = socket (AF_INET, SOCK_STREAM, 0); - if (ptr_dcc->sock < 0) - return 0; - } - - /* for chat or file sending, listen to socket for a connection */ - if (IRC_DCC_IS_SEND(ptr_dcc->type)) - { - if (fcntl (ptr_dcc->sock, F_SETFL, O_NONBLOCK) == -1) - return 0; - if (listen (ptr_dcc->sock, 1) == -1) - return 0; - if (fcntl (ptr_dcc->sock, F_SETFL, 0) == -1) - return 0; - } - - /* for chat receiving, connect to listening host */ - if (ptr_dcc->type == IRC_DCC_CHAT_RECV) - { - if (fcntl (ptr_dcc->sock, F_SETFL, O_NONBLOCK) == -1) - return 0; - irc_dcc_connect_to_sender (ptr_dcc); - } - - /* for file receiving, connection is made in child process (blocking) socket */ - - return 1; -} - -/* - * irc_dcc_free: free DCC struct and remove it from list - */ - -void -irc_dcc_free (struct t_irc_dcc *dcc) -{ - struct t_irc_dcc *new_dcc_list; - - if (!dcc) - return; - - /* DCC CHAT with channel => remove channel - (to prevent channel from becoming standard pv) */ - if (dcc->channel) - { - /* check if channel is used for another active DCC CHAT */ - if (!dcc->channel->dcc_chat - || (IRC_DCC_ENDED(dcc->channel->dcc_chat->status))) - { - gui_buffer_free (dcc->channel->buffer, 1); - if (dcc->channel) - irc_channel_free (dcc->server, dcc->channel); - } - } - - /* remove DCC from list */ - if (irc_last_dcc == dcc) - irc_last_dcc = dcc->prev_dcc; - if (dcc->prev_dcc) - { - (dcc->prev_dcc)->next_dcc = dcc->next_dcc; - new_dcc_list = irc_dcc_list; - } - else - new_dcc_list = dcc->next_dcc; - if (dcc->next_dcc) - (dcc->next_dcc)->prev_dcc = dcc->prev_dcc; - - /* free data */ - if (dcc->nick) - free (dcc->nick); - if (dcc->unterminated_message) - free (dcc->unterminated_message); - if (dcc->filename) - free (dcc->filename); - - free (dcc); - irc_dcc_list = new_dcc_list; -} - -/* - * irc_dcc_file_child_kill: kill child process and close pipe - */ - -void -irc_dcc_file_child_kill (struct t_irc_dcc *dcc) -{ - /* kill process */ - if (dcc->child_pid > 0) - { - kill (dcc->child_pid, SIGKILL); - waitpid (dcc->child_pid, NULL, 0); - dcc->child_pid = 0; - } - - /* close pipe used with child */ - if (dcc->child_read != -1) - { - close (dcc->child_read); - dcc->child_read = -1; - } - if (dcc->child_write != -1) - { - close (dcc->child_write); - dcc->child_write = -1; - } -} - -/* - * irc_dcc_close: close a DCC connection - */ - -void -irc_dcc_close (struct t_irc_dcc *dcc, int status) -{ - t_gui_buffer *ptr_buffer; - struct stat st; - - dcc->status = status; - - if ((status == IRC_DCC_DONE) || (status == IRC_DCC_ABORTED) - || (status == IRC_DCC_FAILED)) - { - if (IRC_DCC_IS_FILE(dcc->type)) - { - gui_chat_printf_info (dcc->server->buffer, - _("DCC: file %s%s%s"), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - dcc->filename, - GUI_COLOR(GUI_COLOR_CHAT)); - if (dcc->local_filename) - gui_chat_printf (dcc->server->buffer, - _(" (local filename: %s%s%s)"), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - dcc->local_filename, - GUI_COLOR(GUI_COLOR_CHAT)); - if (dcc->type == IRC_DCC_FILE_SEND) - gui_chat_printf (dcc->server->buffer, - _(" sent to ")); - else - gui_chat_printf (dcc->server->buffer, - _(" received from ")); - gui_chat_printf (dcc->server->buffer, "%s%s%s: %s\n", - GUI_COLOR(GUI_COLOR_CHAT_NICK), - dcc->nick, - GUI_COLOR(GUI_COLOR_CHAT), - (status == IRC_DCC_DONE) ? _("OK") : _("FAILED")); - irc_dcc_file_child_kill (dcc); - } - } - if (status == IRC_DCC_ABORTED) - { - if (IRC_DCC_IS_CHAT(dcc->type)) - { - if (dcc->channel) - ptr_buffer = dcc->channel->buffer; - else - ptr_buffer = dcc->server->buffer; - gui_chat_printf_info (ptr_buffer, - _("DCC chat closed with %s%s " - "%s(%s%d.%d.%d.%d%s)\n"), - GUI_COLOR(GUI_COLOR_CHAT_NICK), - dcc->nick, - GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS), - GUI_COLOR(GUI_COLOR_CHAT_HOST), - dcc->addr >> 24, - (dcc->addr >> 16) & 0xff, - (dcc->addr >> 8) & 0xff, - dcc->addr & 0xff, - GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS)); - } - } - - /* remove empty file if received file failed and nothing was transfered */ - if (((status == IRC_DCC_FAILED) || (status == IRC_DCC_ABORTED)) - && IRC_DCC_IS_FILE(dcc->type) - && IRC_DCC_IS_RECV(dcc->type) - && dcc->local_filename - && dcc->pos == 0) - { - /* erase file only if really empty on disk */ - if (stat (dcc->local_filename, &st) != -1) - { - if ((unsigned long) st.st_size == 0) - unlink (dcc->local_filename); - } - } - - if (IRC_DCC_IS_FILE(dcc->type)) - irc_dcc_calculate_speed (dcc, 1); - - if (dcc->sock >= 0) - { - close (dcc->sock); - dcc->sock = -1; - } - if (dcc->file >= 0) - { - close (dcc->file); - dcc->file = -1; - } -} /* * irc_dcc_channel_for_chat: create channel for DCC chat */ - +/* void irc_dcc_channel_for_chat (struct t_irc_dcc *dcc) { @@ -572,11 +79,11 @@ irc_dcc_channel_for_chat (struct t_irc_dcc *dcc) GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS), GUI_COLOR(GUI_COLOR_CHAT)); } - +*/ /* * irc_dcc_chat_remove_channel: remove a buffer for DCC chat */ - + /* void irc_dcc_chat_remove_channel (struct t_irc_channel *channel) { @@ -591,63 +98,11 @@ irc_dcc_chat_remove_channel (struct t_irc_channel *channel) ptr_dcc->channel = NULL; } } - -/* - * irc_dcc_recv_connect_init: connect to sender and init file or chat - */ - -void -irc_dcc_recv_connect_init (struct t_irc_dcc *dcc) -{ - if (!irc_dcc_connect (dcc)) - { - irc_dcc_close (dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } - else - { - /* DCC file => launch child process */ - if (IRC_DCC_IS_FILE(dcc->type)) - { - dcc->status = IRC_DCC_CONNECTING; - irc_dcc_file_recv_fork (dcc); - } - else - { - /* DCC CHAT => associate DCC with channel */ - dcc->status = IRC_DCC_ACTIVE; - irc_dcc_channel_for_chat (dcc); - } - } - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); -} - -/* - * irc_dcc_accept: accepts a DCC file or chat request - */ - -void -irc_dcc_accept (struct t_irc_dcc *dcc) -{ - if (IRC_DCC_IS_FILE(dcc->type) && (dcc->start_resume > 0)) - { - dcc->status = IRC_DCC_CONNECTING; - irc_server_sendf (dcc->server, - (strchr (dcc->filename, ' ')) ? - "PRIVMSG %s :\01DCC RESUME \"%s\" %d %u\01\n" : - "PRIVMSG %s :\01DCC RESUME %s %d %u\01", - dcc->nick, dcc->filename, - dcc->port, dcc->start_resume); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } - else - irc_dcc_recv_connect_init (dcc); -} - +*/ /* * irc_dcc_accept_resume: accepts a resume and inform the receiver */ - +/* void irc_dcc_accept_resume (struct t_irc_server *server, char *filename, int port, unsigned long pos_start) @@ -683,12 +138,12 @@ irc_dcc_accept_resume (struct t_irc_server *server, char *filename, int port, "position: %u): DCC not found or ended\n"), WEECHAT_ERROR, filename, port, pos_start); } - +*/ /* * irc_dcc_start_resume: called when "DCC ACCEPT" is received * (resume accepted by sender) */ - +/* void irc_dcc_start_resume (struct t_irc_server *server, char *filename, int port, unsigned long pos_start) @@ -711,1016 +166,11 @@ irc_dcc_start_resume (struct t_irc_server *server, char *filename, int port, "position: %u): DCC not found or ended\n"), WEECHAT_ERROR, filename, port, pos_start); } - -/* - * irc_dcc_alloc: allocate a new DCC file - */ - -struct t_irc_dcc * -irc_dcc_alloc () -{ - struct t_irc_dcc *new_dcc; - - /* create new DCC struct */ - if ((new_dcc = malloc (sizeof (*new_dcc))) == NULL) - return NULL; - - /* default values */ - new_dcc->server = NULL; - new_dcc->channel = NULL; - new_dcc->type = 0; - new_dcc->status = 0; - new_dcc->start_time = 0; - new_dcc->start_transfer = 0; - new_dcc->addr = 0; - new_dcc->port = 0; - new_dcc->nick = NULL; - new_dcc->sock = -1; - new_dcc->child_pid = 0; - new_dcc->child_read = -1; - new_dcc->child_write = -1; - new_dcc->unterminated_message = NULL; - new_dcc->fast_send = irc_cfg_dcc_fast_send; - new_dcc->file = -1; - new_dcc->filename = NULL; - new_dcc->local_filename = NULL; - new_dcc->filename_suffix = -1; - new_dcc->blocksize = irc_cfg_dcc_blocksize; - new_dcc->size = 0; - new_dcc->pos = 0; - new_dcc->ack = 0; - new_dcc->start_resume = 0; - new_dcc->last_check_time = 0; - new_dcc->last_check_pos = 0; - new_dcc->last_activity = 0; - new_dcc->bytes_per_sec = 0; - new_dcc->eta = 0; - - new_dcc->prev_dcc = NULL; - new_dcc->next_dcc = irc_dcc_list; - if (irc_dcc_list) - irc_dcc_list->prev_dcc = new_dcc; - else - irc_last_dcc = new_dcc; - irc_dcc_list = new_dcc; - - return new_dcc; -} - -/* - * irc_dcc_add: add a DCC file to queue - */ - -struct t_irc_dcc * -irc_dcc_add (struct t_irc_server *server, int type, unsigned long addr, int port, char *nick, - int sock, char *filename, char *local_filename, unsigned long size) -{ - struct t_irc_dcc *new_dcc; - - new_dcc = irc_dcc_alloc (); - if (!new_dcc) - { - gui_chat_printf_error (server->buffer, - _("%s not enough memory for new DCC\n"), - WEECHAT_ERROR); - return NULL; - } - - /* initialize new DCC */ - new_dcc->server = server; - new_dcc->channel = NULL; - new_dcc->type = type; - new_dcc->status = IRC_DCC_WAITING; - new_dcc->start_time = time (NULL); - new_dcc->start_transfer = time (NULL); - new_dcc->addr = addr; - new_dcc->port = port; - new_dcc->nick = strdup (nick); - new_dcc->sock = sock; - new_dcc->unterminated_message = NULL; - new_dcc->file = -1; - if (IRC_DCC_IS_CHAT(type)) - new_dcc->filename = strdup (_("DCC chat")); - else - new_dcc->filename = (filename) ? strdup (filename) : NULL; - new_dcc->local_filename = NULL; - new_dcc->filename_suffix = -1; - new_dcc->size = size; - new_dcc->pos = 0; - new_dcc->ack = 0; - new_dcc->start_resume = 0; - new_dcc->last_check_time = time (NULL); - new_dcc->last_check_pos = 0; - new_dcc->last_activity = time (NULL); - new_dcc->bytes_per_sec = 0; - new_dcc->eta = 0; - if (local_filename) - new_dcc->local_filename = strdup (local_filename); - else - irc_dcc_find_filename (new_dcc); - - gui_current_window->dcc_first = NULL; - gui_current_window->dcc_selected = NULL; - - /* write info message on server buffer */ - if (type == IRC_DCC_FILE_RECV) - { - gui_chat_printf_info (server->buffer, - _("Incoming DCC file from %s%s%s " - "(%s%d.%d.%d.%d%s)%s: %s%s%s, " - "%s%lu%s bytes\n"), - GUI_COLOR(GUI_COLOR_CHAT_NICK), - nick, - GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS), - GUI_COLOR(GUI_COLOR_CHAT_HOST), - addr >> 24, - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff, - GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS), - GUI_COLOR(GUI_COLOR_CHAT), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - filename, - GUI_COLOR(GUI_COLOR_CHAT), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - size, - GUI_COLOR(GUI_COLOR_CHAT)); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } - if (type == IRC_DCC_FILE_SEND) - { - gui_chat_printf_info (server->buffer, - _("Sending DCC file to %s%s%s: %s%s%s " - "(local filename: %s%s%s), %s%lu%s bytes\n"), - GUI_COLOR(GUI_COLOR_CHAT_NICK), - nick, - GUI_COLOR(GUI_COLOR_CHAT), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - filename, - GUI_COLOR(GUI_COLOR_CHAT), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - local_filename, - GUI_COLOR(GUI_COLOR_CHAT), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - size, - GUI_COLOR(GUI_COLOR_CHAT)); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } - if (type == IRC_DCC_CHAT_RECV) - { - gui_chat_printf_info (server->buffer, - _("Incoming DCC chat request from %s%s%s " - "(%s%d.%d.%d.%d%s)\n"), - GUI_COLOR(GUI_COLOR_CHAT_NICK), - nick, - GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS), - GUI_COLOR(GUI_COLOR_CHAT_HOST), - addr >> 24, - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff, - GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS)); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } - if (type == IRC_DCC_CHAT_SEND) - { - gui_chat_printf_info (server->buffer, - _("Sending DCC chat request to %s%s\n"), - GUI_COLOR(GUI_COLOR_CHAT_NICK), - nick); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } - - if (IRC_DCC_IS_FILE(type) && (!new_dcc->local_filename)) - { - irc_dcc_close (new_dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - return NULL; - } - - if (IRC_DCC_IS_FILE(type) && (new_dcc->start_resume > 0)) - { - gui_chat_printf_info (new_dcc->server->buffer, - _("DCC: file %s%s%s (local filename: %s%s%s) " - "will be resumed at position %u\n"), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - new_dcc->filename, - GUI_COLOR(GUI_COLOR_CHAT), - GUI_COLOR(GUI_COLOR_CHAT_CHANNEL), - new_dcc->local_filename, - GUI_COLOR(GUI_COLOR_CHAT), - new_dcc->start_resume); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } - - /* connect if needed and redraw DCC buffer */ - if (IRC_DCC_IS_SEND(type)) - { - if (!irc_dcc_connect (new_dcc)) - { - irc_dcc_close (new_dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - return NULL; - } - } - - if ( ( (type == IRC_DCC_CHAT_RECV) && (irc_cfg_dcc_auto_accept_chats) ) - || ( (type == IRC_DCC_FILE_RECV) && (irc_cfg_dcc_auto_accept_files) ) ) - irc_dcc_accept (new_dcc); - else - irc_dcc_redraw (WEECHAT_HOTLIST_PRIVATE); - gui_status_draw (gui_current_window->buffer, 0); - - return new_dcc; -} - -/* - * irc_dcc_send_request: send DCC request (file or chat) - */ - -void -irc_dcc_send_request (struct t_irc_server *server, int type, char *nick, - char *filename) -{ - char *dir1, *dir2, *filename2, *short_filename, *pos; - int spaces, args, port_start, port_end; - struct stat st; - int sock, port; - struct hostent *host; - struct in_addr tmpaddr; - struct sockaddr_in addr; - socklen_t length; - unsigned long local_addr; - struct t_irc_dcc *ptr_dcc; - - filename2 = NULL; - short_filename = NULL; - spaces = 0; - - if (type == IRC_DCC_FILE_SEND) - { - /* add home if filename not beginning with '/' or '~' (not for Win32) */ -#ifdef _WIN32 - filename2 = strdup (filename); -#else - if (filename[0] == '/') - filename2 = strdup (filename); - else if (filename[0] == '~') - filename2 = weechat_strreplace (filename, "~", getenv ("HOME")); - else - { - dir1 = weechat_strreplace (irc_cfg_dcc_upload_path, "~", - getenv ("HOME")); - if (!dir1) - return; - dir2 = weechat_strreplace (dir1, "%h", weechat_home); - if (!dir2) - { - free (dir1); - return; - } - filename2 = malloc (strlen (dir2) + strlen (filename) + 4); - if (!filename2) - { - gui_chat_printf_error (server->buffer, - _("%s not enough memory for DCC SEND\n"), - WEECHAT_ERROR); - return; - } - strcpy (filename2, dir2); - if (filename2[strlen (filename2) - 1] != DIR_SEPARATOR_CHAR) - strcat (filename2, DIR_SEPARATOR); - strcat (filename2, filename); - if (dir1) - free (dir1); - if (dir2) - free (dir2); - } -#endif - - /* check if file exists */ - if (stat (filename2, &st) == -1) - { - gui_chat_printf_error (server->buffer, - _("%s cannot access file \"%s\"\n"), - WEECHAT_ERROR, filename2); - if (filename2) - free (filename2); - return; - } - } - - /* get local IP address */ - - /* look up the IP address from dcc_own_ip, if set */ - local_addr = 0; - if (irc_cfg_dcc_own_ip && irc_cfg_dcc_own_ip[0]) - { - host = gethostbyname (irc_cfg_dcc_own_ip); - if (host) - { - memcpy (&tmpaddr, host->h_addr_list[0], sizeof(struct in_addr)); - local_addr = ntohl (tmpaddr.s_addr); - } - else - gui_chat_printf (server->buffer, - _("%s could not find address for '%s'. Falling " - "back to local IP.\n"), - WEECHAT_WARNING, irc_cfg_dcc_own_ip); - } - - /* use the local interface, from the server socket */ - memset (&addr, 0, sizeof (struct sockaddr_in)); - length = sizeof (addr); - getsockname (server->sock, (struct sockaddr *) &addr, &length); - addr.sin_family = AF_INET; - - /* fallback to the local IP address on the interface, if required */ - if (local_addr == 0) - local_addr = ntohl (addr.sin_addr.s_addr); - - /* open socket for DCC */ - sock = socket (AF_INET, SOCK_STREAM, 0); - if (sock < 0) - { - gui_chat_printf_error (server->buffer, - _("%s cannot create socket for DCC\n"), - WEECHAT_ERROR); - if (filename2) - free (filename2); - return; - } - - /* look for port */ - - port = 0; - - if (irc_cfg_dcc_port_range && irc_cfg_dcc_port_range[0]) - { - /* find a free port in the specified range */ - args = sscanf (irc_cfg_dcc_port_range, "%d-%d", &port_start, &port_end); - if (args > 0) - { - port = port_start; - if (args == 1) - port_end = port_start; - - /* loop through the entire allowed port range */ - while (port <= port_end) - { - if (!irc_dcc_port_in_use (port)) - { - /* attempt to bind to the free port */ - addr.sin_port = htons (port); - if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) == 0) - break; - } - port++; - } - - if (port > port_end) - port = -1; - } - } - - if (port == 0) - { - /* find port automatically */ - addr.sin_port = 0; - if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) == 0) - { - length = sizeof (addr); - getsockname (sock, (struct sockaddr *) &addr, &length); - port = ntohs (addr.sin_port); - } - else - port = -1; - } - - if (port == -1) - { - /* Could not find any port to bind */ - gui_chat_printf_error (server->buffer, - _("%s cannot find available port for DCC\n"), - WEECHAT_ERROR); - close (sock); - if (filename2) - free (filename2); - return; - } - - if (type == IRC_DCC_FILE_SEND) - { - /* extract short filename (without path) */ - pos = strrchr (filename2, DIR_SEPARATOR_CHAR); - if (pos) - short_filename = strdup (pos + 1); - else - short_filename = strdup (filename2); - - /* convert spaces to underscore if asked and needed */ - pos = short_filename; - spaces = 0; - while (pos[0]) - { - if (pos[0] == ' ') - { - if (irc_cfg_dcc_convert_spaces) - pos[0] = '_'; - else - spaces = 1; - } - pos++; - } - } - - /* add DCC entry and listen to socket */ - if (type == IRC_DCC_CHAT_SEND) - ptr_dcc = irc_dcc_add (server, IRC_DCC_CHAT_SEND, local_addr, port, - nick, sock, NULL, NULL, 0); - else - ptr_dcc = irc_dcc_add (server, IRC_DCC_FILE_SEND, local_addr, port, - nick, sock, short_filename, filename2, - st.st_size); - if (!ptr_dcc) - { - gui_chat_printf_error (server->buffer, - _("%s cannot send DCC\n"), - WEECHAT_ERROR); - close (sock); - if (short_filename) - free (short_filename); - if (filename2) - free (filename2); - return; - } - - /* send DCC request to nick */ - if (type == IRC_DCC_CHAT_SEND) - irc_server_sendf (server, - "PRIVMSG %s :\01DCC CHAT chat %lu %d\01", - nick, local_addr, port); - else - irc_server_sendf (server, - (spaces) ? - "PRIVMSG %s :\01DCC SEND \"%s\" %lu %d %u\01\n" : - "PRIVMSG %s :\01DCC SEND %s %lu %d %u\01", - nick, short_filename, local_addr, port, - (unsigned long) st.st_size); - - if (short_filename) - free (short_filename); - if (filename2) - free (filename2); -} - -/* - * irc_dcc_chat_send: send data to remote host via DCC CHAT - */ - -int -irc_dcc_chat_send (struct t_irc_dcc *dcc, char *buffer, int size_buf) -{ - if (!dcc) - return -1; - - return send (dcc->sock, buffer, size_buf, 0); -} - -/* - * irc_dcc_chat_sendf: send formatted data to remote host via DCC CHAT - */ - -void -irc_dcc_chat_sendf (struct t_irc_dcc *dcc, char *format, ...) -{ - va_list args; - static char buffer[4096]; - int size_buf; - - if (!dcc || (dcc->sock < 0)) - return; - - va_start (args, format); - size_buf = vsnprintf (buffer, sizeof (buffer) - 1, format, args); - va_end (args); - - if ((size_buf == 0) || (strcmp (buffer, "\r\n") == 0)) - return; - - buffer[sizeof (buffer) - 1] = '\0'; - if ((size_buf < 0) || (size_buf > (int) (sizeof (buffer) - 1))) - size_buf = strlen (buffer); - - if (irc_dcc_chat_send (dcc, buffer, strlen (buffer)) <= 0) - { - gui_chat_printf_error (dcc->server->buffer, - _("%s error sending data to \"%s\" via DCC " - "CHAT\n"), - WEECHAT_ERROR, dcc->nick); - irc_dcc_close (dcc, IRC_DCC_FAILED); - } -} - -/* - * irc_dcc_chat_recv: receive data from DCC CHAT host - */ - -void -irc_dcc_chat_recv (struct t_irc_dcc *dcc) -{ - fd_set read_fd; - static struct timeval timeout; - static char buffer[4096 + 2]; - char *buf2, *pos, *ptr_buf, *next_ptr_buf; - int num_read; - - FD_ZERO (&read_fd); - FD_SET (dcc->sock, &read_fd); - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - /* something to read on socket? */ - if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) <= 0) - return; - - if (!FD_ISSET (dcc->sock, &read_fd)) - return; - - /* there's something to read on socket! */ - num_read = recv (dcc->sock, buffer, sizeof (buffer) - 2, 0); - if (num_read > 0) - { - buffer[num_read] = '\0'; - - buf2 = NULL; - ptr_buf = buffer; - if (dcc->unterminated_message) - { - buf2 = malloc (strlen (dcc->unterminated_message) + - strlen (buffer) + 1); - if (buf2) - { - strcpy (buf2, dcc->unterminated_message); - strcat (buf2, buffer); - } - ptr_buf = buf2; - free (dcc->unterminated_message); - dcc->unterminated_message = NULL; - } - - while (ptr_buf && ptr_buf[0]) - { - next_ptr_buf = NULL; - pos = strstr (ptr_buf, "\r\n"); - if (pos) - { - pos[0] = '\0'; - next_ptr_buf = pos + 2; - } - else - { - pos = strstr (ptr_buf, "\n"); - if (pos) - { - pos[0] = '\0'; - next_ptr_buf = pos + 1; - } - else - { - dcc->unterminated_message = strdup (ptr_buf); - ptr_buf = NULL; - next_ptr_buf = NULL; - } - } - - if (ptr_buf) - { - if (irc_protocol_is_highlight (ptr_buf, dcc->server->nick)) - { - irc_display_nick (dcc->channel->buffer, NULL, - dcc->nick, - GUI_MSG_TYPE_NICK | GUI_MSG_TYPE_HIGHLIGHT, - 1, - GUI_COLOR(GUI_COLOR_CHAT_HIGHLIGHT), 0); - if ((cfg_look_infobar_delay_highlight > 0) - && (dcc->channel->buffer != gui_current_window->buffer)) - { - gui_infobar_printf (cfg_look_infobar_delay_highlight, - GUI_COLOR_INFOBAR_HIGHLIGHT, - _("Private %s> %s"), - dcc->nick, ptr_buf); - } - } - else - irc_display_nick (dcc->channel->buffer, NULL, - dcc->nick, - GUI_MSG_TYPE_NICK, 1, - GUI_COLOR(GUI_COLOR_CHAT_NICK_OTHER), 0); - gui_chat_printf_type (dcc->channel->buffer, - GUI_MSG_TYPE_MSG, - NULL, -1, - "%s%s\n", - GUI_COLOR(GUI_COLOR_CHAT), - ptr_buf); - } - - ptr_buf = next_ptr_buf; - } - - if (buf2) - free (buf2); - } - else - { - irc_dcc_close (dcc, IRC_DCC_ABORTED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } -} - -/* - * irc_dcc_file_create_pipe: create pipe for communication with child process - * return 1 if ok, 0 if error - */ - -int -irc_dcc_file_create_pipe (struct t_irc_dcc *dcc) -{ - int child_pipe[2]; - - if (pipe (child_pipe) < 0) - { - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to create pipe\n"), - WEECHAT_ERROR); - irc_dcc_close (dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - return 0; - } - - dcc->child_read = child_pipe[0]; - dcc->child_write = child_pipe[1]; - return 1; -} - -/* - * irc_dcc_file_write_pipe: write data into pipe - */ - -void -irc_dcc_file_write_pipe (struct t_irc_dcc *dcc, int status, int error) -{ - char buffer[1 + 1 + 12 + 1]; /* status + error + pos + \0 */ - - snprintf (buffer, sizeof (buffer), "%c%c%012lu", - status + '0', error + '0', dcc->pos); - write (dcc->child_write, buffer, sizeof (buffer)); -} - -/* - * irc_dcc_file_send_child: child process for sending file - */ - -void -irc_dcc_file_send_child (struct t_irc_dcc *dcc) -{ - int num_read, num_sent; - static char buffer[IRC_DCC_MAX_BLOCKSIZE]; - uint32_t ack; - time_t last_sent, new_time; - - last_sent = time (NULL); - while (1) - { - /* read DCC ACK (sent by receiver) */ - if (dcc->pos > dcc->ack) - { - /* we should receive ACK for packets sent previously */ - while (1) - { - num_read = recv (dcc->sock, (char *) &ack, 4, MSG_PEEK); - if ((num_read < 1) && - ((num_read != -1) || (errno != EAGAIN))) - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_SEND_BLOCK); - return; - } - if (num_read == 4) - { - recv (dcc->sock, (char *) &ack, 4, 0); - dcc->ack = ntohl (ack); - - /* DCC send ok? */ - if ((dcc->pos >= dcc->size) - && (dcc->ack >= dcc->size)) - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_DONE, IRC_DCC_NO_ERROR); - return; - } - } - else - break; - } - } - - /* send a block to receiver */ - if ((dcc->pos < dcc->size) && - (dcc->fast_send || (dcc->pos <= dcc->ack))) - { - lseek (dcc->file, dcc->pos, SEEK_SET); - num_read = read (dcc->file, buffer, dcc->blocksize); - if (num_read < 1) - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_READ_LOCAL); - return; - } - num_sent = send (dcc->sock, buffer, num_read, 0); - if (num_sent < 0) - { - /* socket is temporarily not available (receiver can't receive - amount of data we sent ?!) */ - if (errno == EAGAIN) - usleep (1000); - else - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_SEND_BLOCK); - return; - } - } - if (num_sent > 0) - { - dcc->pos += (unsigned long) num_sent; - new_time = time (NULL); - if (last_sent != new_time) - { - last_sent = new_time; - irc_dcc_file_write_pipe (dcc, IRC_DCC_ACTIVE, IRC_DCC_NO_ERROR); - } - } - } - else - usleep (1000); - } -} - -/* - * irc_dcc_file_recv_child: child process for receiving file - */ - -void -irc_dcc_file_recv_child (struct t_irc_dcc *dcc) -{ - int num_read; - static char buffer[IRC_DCC_MAX_BLOCKSIZE]; - uint32_t pos; - time_t last_sent, new_time; - - /* first connect to sender (blocking) */ - if (!irc_dcc_connect_to_sender (dcc)) - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_CONNECT_SENDER); - return; - } - - /* connection is ok, change DCC status (inform parent process) */ - irc_dcc_file_write_pipe (dcc, IRC_DCC_ACTIVE, IRC_DCC_NO_ERROR); - - last_sent = time (NULL); - while (1) - { - num_read = recv (dcc->sock, buffer, sizeof (buffer), 0); - if (num_read == -1) - { - /* socket is temporarily not available (sender is not fast ?!) */ - if (errno == EAGAIN) - usleep (1000); - else - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_RECV_BLOCK); - return; - } - } - else - { - if (num_read == 0) - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_RECV_BLOCK); - return; - } - - if (write (dcc->file, buffer, num_read) == -1) - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_FAILED, IRC_DCC_ERROR_WRITE_LOCAL); - return; - } - - dcc->pos += (unsigned long) num_read; - pos = htonl (dcc->pos); - - /* we don't check return code, not a problem if an ACK send failed */ - send (dcc->sock, (char *) &pos, 4, 0); - - /* file received ok? */ - if (dcc->pos >= dcc->size) - { - irc_dcc_file_write_pipe (dcc, IRC_DCC_DONE, IRC_DCC_NO_ERROR); - return; - } - - new_time = time (NULL); - if (last_sent != new_time) - { - last_sent = new_time; - irc_dcc_file_write_pipe (dcc, IRC_DCC_ACTIVE, IRC_DCC_NO_ERROR); - } - } - } -} - -/* - * irc_dcc_file_child_read: read data from child via pipe - */ - -void -irc_dcc_file_child_read (struct t_irc_dcc *dcc) -{ - fd_set read_fd; - static struct timeval timeout; - char bufpipe[1 + 1 + 12 + 1]; - int num_read; - char *error; - - FD_ZERO (&read_fd); - FD_SET (dcc->child_read, &read_fd); - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - /* something to read on child pipe? */ - if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) <= 0) - return; - - if (!FD_ISSET (dcc->child_read, &read_fd)) - return; - - /* there's something to read in pipe! */ - num_read = read (dcc->child_read, bufpipe, sizeof (bufpipe)); - if (num_read > 0) - { - error = NULL; - dcc->pos = strtol (bufpipe + 2, &error, 10); - dcc->last_activity = time (NULL); - irc_dcc_calculate_speed (dcc, 0); - - /* read error code */ - switch (bufpipe[1] - '0') - { - /* errors for sender */ - case IRC_DCC_ERROR_READ_LOCAL: - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to read local " - "file\n"), - WEECHAT_ERROR); - break; - case IRC_DCC_ERROR_SEND_BLOCK: - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to send block to " - "receiver\n"), - WEECHAT_ERROR); - break; - case IRC_DCC_ERROR_READ_ACK: - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to read ACK from " - "receiver\n"), - WEECHAT_ERROR); - break; - /* errors for receiver */ - case IRC_DCC_ERROR_CONNECT_SENDER: - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to connect to " - "sender\n"), - WEECHAT_ERROR); - break; - case IRC_DCC_ERROR_RECV_BLOCK: - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to receive block " - "from sender\n"), - WEECHAT_ERROR); - break; - case IRC_DCC_ERROR_WRITE_LOCAL: - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to write local " - "file\n"), - WEECHAT_ERROR); - break; - } - - /* read new DCC status */ - switch (bufpipe[0] - '0') - { - case IRC_DCC_ACTIVE: - if (dcc->status == IRC_DCC_CONNECTING) - { - /* connection was successful by child, init transfert times */ - dcc->status = IRC_DCC_ACTIVE; - dcc->start_transfer = time (NULL); - dcc->last_check_time = time (NULL); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - } - else - irc_dcc_redraw (WEECHAT_HOTLIST_LOW); - break; - case IRC_DCC_DONE: - irc_dcc_close (dcc, IRC_DCC_DONE); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - break; - case IRC_DCC_FAILED: - irc_dcc_close (dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - break; - } - } -} - -/* - * irc_dcc_file_send_fork: fork process for sending file - */ - -void -irc_dcc_file_send_fork (struct t_irc_dcc *dcc) -{ - pid_t pid; - - if (!irc_dcc_file_create_pipe (dcc)) - return; - - dcc->file = open (dcc->local_filename, O_RDONLY | O_NONBLOCK, 0644); - - switch (pid = fork ()) - { - /* fork failed */ - case -1: - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to fork\n"), - WEECHAT_ERROR); - irc_dcc_close (dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - return; - /* child process */ - case 0: - setuid (getuid ()); - irc_dcc_file_send_child (dcc); - _exit (EXIT_SUCCESS); - } - - /* parent process */ - dcc->child_pid = pid; -} - -/* - * irc_dcc_file_recv_fork: fork process for receiving file - */ - -void -irc_dcc_file_recv_fork (struct t_irc_dcc *dcc) -{ - pid_t pid; - - if (!irc_dcc_file_create_pipe (dcc)) - return; - - if (dcc->start_resume > 0) - dcc->file = open (dcc->local_filename, - O_APPEND | O_WRONLY | O_NONBLOCK); - else - dcc->file = open (dcc->local_filename, - O_CREAT | O_TRUNC | O_WRONLY | O_NONBLOCK, - 0644); - - switch (pid = fork ()) - { - /* fork failed */ - case -1: - gui_chat_printf_error (dcc->server->buffer, - _("%s DCC: unable to fork\n"), - WEECHAT_ERROR); - irc_dcc_close (dcc, IRC_DCC_FAILED); - irc_dcc_redraw (WEECHAT_HOTLIST_MESSAGE); - return; - /* child process */ - case 0: - setuid (getuid ()); - irc_dcc_file_recv_child (dcc); - _exit (EXIT_SUCCESS); - } - - /* parent process */ - dcc->child_pid = pid; -} - +*/ /* * irc_dcc_handle: receive/send data for all active DCC */ - +/* void irc_dcc_handle () { @@ -1733,7 +183,7 @@ irc_dcc_handle () for (dcc = irc_dcc_list; dcc; dcc = dcc->next_dcc) { - /* check DCC timeout */ + // check DCC timeout if (IRC_DCC_IS_FILE(dcc->type) && !IRC_DCC_ENDED(dcc->status)) { if ((irc_cfg_dcc_timeout != 0) @@ -1757,7 +207,7 @@ irc_dcc_handle () timeout.tv_sec = 0; timeout.tv_usec = 0; - /* something to read on socket? */ + // something to read on socket? if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) > 0) { if (FD_ISSET (dcc->sock, &read_fd)) @@ -1815,7 +265,7 @@ irc_dcc_handle () timeout.tv_sec = 0; timeout.tv_usec = 0; - /* something to read on socket? */ + // something to read on socket? if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) > 0) { if (FD_ISSET (dcc->sock, &read_fd)) @@ -1855,71 +305,4 @@ irc_dcc_handle () } } } - -/* - * irc_dcc_end: close all opened sockets (called when WeeChat is exiting) - */ - -void -irc_dcc_end () -{ - struct t_irc_dcc *ptr_dcc; - - for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) - { - if (ptr_dcc->sock >= 0) - { - if (ptr_dcc->status == IRC_DCC_ACTIVE) - weechat_log_printf (_("Aborting active DCC: \"%s\" from %s"), - ptr_dcc->filename, ptr_dcc->nick); - irc_dcc_close (ptr_dcc, IRC_DCC_FAILED); - } - } -} - -/* - * irc_dcc_print_log: print DCC infos in log (usually for crash dump) - */ - -void -irc_dcc_print_log () -{ - struct t_irc_dcc *ptr_dcc; - - for (ptr_dcc = irc_dcc_list; ptr_dcc; ptr_dcc = ptr_dcc->next_dcc) - { - weechat_log_printf (""); - weechat_log_printf ("[DCC (addr:0x%x)]", ptr_dcc); - weechat_log_printf (" server. . . . . . . : 0x%x", ptr_dcc->server); - weechat_log_printf (" channel . . . . . . : 0x%x", ptr_dcc->channel); - weechat_log_printf (" type. . . . . . . . : %d", ptr_dcc->type); - weechat_log_printf (" status. . . . . . . : %d", ptr_dcc->status); - weechat_log_printf (" start_time. . . . . : %ld", ptr_dcc->start_time); - weechat_log_printf (" start_transfer. . . : %ld", ptr_dcc->start_transfer); - weechat_log_printf (" addr. . . . . . . . : %lu", ptr_dcc->addr); - weechat_log_printf (" port. . . . . . . . : %d", ptr_dcc->port); - weechat_log_printf (" nick. . . . . . . . : '%s'", ptr_dcc->nick); - weechat_log_printf (" sock. . . . . . . . : %d", ptr_dcc->sock); - weechat_log_printf (" child_pid . . . . . : %d", ptr_dcc->child_pid); - weechat_log_printf (" child_read. . . . . : %d", ptr_dcc->child_read); - weechat_log_printf (" child_write . . . . : %d", ptr_dcc->child_write); - weechat_log_printf (" unterminated_message: '%s'", ptr_dcc->unterminated_message); - weechat_log_printf (" fast_send . . . . . : %d", ptr_dcc->fast_send); - weechat_log_printf (" file. . . . . . . . : %d", ptr_dcc->file); - weechat_log_printf (" filename. . . . . . : '%s'", ptr_dcc->filename); - weechat_log_printf (" local_filename. . . : '%s'", ptr_dcc->local_filename); - weechat_log_printf (" filename_suffix . . : %d", ptr_dcc->filename_suffix); - weechat_log_printf (" blocksize . . . . . : %d", ptr_dcc->blocksize); - weechat_log_printf (" size. . . . . . . . : %lu", ptr_dcc->size); - weechat_log_printf (" pos . . . . . . . . : %lu", ptr_dcc->pos); - weechat_log_printf (" ack . . . . . . . . : %lu", ptr_dcc->ack); - weechat_log_printf (" start_resume. . . . : %lu", ptr_dcc->start_resume); - weechat_log_printf (" last_check_time . . : %ld", ptr_dcc->last_check_time); - weechat_log_printf (" last_check_pos. . . : %lu", ptr_dcc->last_check_pos); - weechat_log_printf (" last_activity . . . : %ld", ptr_dcc->last_activity); - weechat_log_printf (" bytes_per_sec . . . : %lu", ptr_dcc->bytes_per_sec); - weechat_log_printf (" eta . . . . . . . . : %lu", ptr_dcc->eta); - weechat_log_printf (" prev_dcc. . . . . . : 0x%x", ptr_dcc->prev_dcc); - weechat_log_printf (" next_dcc. . . . . . : 0x%x", ptr_dcc->next_dcc); - } -} +*/ diff --git a/src/plugins/irc/irc-debug.c b/src/plugins/irc/irc-debug.c index a652a4963..91fdf4ee4 100644 --- a/src/plugins/irc/irc-debug.c +++ b/src/plugins/irc/irc-debug.c @@ -63,20 +63,16 @@ irc_debug_printf (struct t_irc_server *server, int send, int modified, if (!irc_debug_buffer) { - /* search for irc debug buffer */ - irc_debug_buffer = weechat_buffer_search ("irc", "debug"); + irc_debug_buffer = weechat_buffer_new ("irc", "debug", + NULL, NULL, + &irc_debug_buffer_close_cb, NULL); + + /* failed to create buffer ? then exit */ if (!irc_debug_buffer) - { - irc_debug_buffer = weechat_buffer_new ("irc", "debug", - NULL, NULL, - &irc_debug_buffer_close_cb, NULL); - /* failed to create buffer ? then exit */ - if (!irc_debug_buffer) - return; - - weechat_buffer_set (irc_debug_buffer, - "title", _("IRC debug messages")); - } + return; + + weechat_buffer_set (irc_debug_buffer, + "title", _("IRC debug messages")); } buf = weechat_iconv_to_internal (NULL, message); @@ -144,8 +140,6 @@ irc_debug_signal_debug_dump_cb (void *data, char *signal, char *type_data, irc_server_print_log (); - //irc_dcc_print_log (); - weechat_log_printf (""); weechat_log_printf ("***** End of \"%s\" plugin dump *****", weechat_plugin->name); diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c index fe6834771..bef2fdcaf 100644 --- a/src/plugins/irc/irc-server.c +++ b/src/plugins/irc/irc-server.c @@ -36,10 +36,6 @@ #include <arpa/inet.h> #include <netdb.h> -#ifdef HAVE_GNUTLS -#include <gnutls/gnutls.h> -#endif - #include "../weechat-plugin.h" #include "irc.h" #include "irc-server.h" @@ -2057,386 +2053,6 @@ irc_server_child_read_cb (void *arg_server) } /* - * irc_server_convbase64_8x3_to_6x4 : convert 3 bytes of 8 bits in 4 bytes of 6 bits - */ - -void -irc_server_convbase64_8x3_to_6x4 (char *from, char *to) -{ - unsigned char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - to[0] = base64_table [ (from[0] & 0xfc) >> 2 ]; - to[1] = base64_table [ ((from[0] & 0x03) << 4) + ((from[1] & 0xf0) >> 4) ]; - to[2] = base64_table [ ((from[1] & 0x0f) << 2) + ((from[2] & 0xc0) >> 6) ]; - to[3] = base64_table [ from[2] & 0x3f ]; -} - -/* - * irc_server_base64encode: encode a string in base64 - */ - -void -irc_server_base64encode (char *from, char *to) -{ - char *f, *t; - int from_len; - - from_len = strlen(from); - - f = from; - t = to; - - while (from_len >= 3) - { - irc_server_convbase64_8x3_to_6x4 (f, t); - f += 3 * sizeof (*f); - t += 4 * sizeof (*t); - from_len -= 3; - } - - if (from_len > 0) - { - char rest[3] = { 0, 0, 0 }; - switch (from_len) - { - case 1 : - rest[0] = f[0]; - irc_server_convbase64_8x3_to_6x4 (rest, t); - t[2] = t[3] = '='; - break; - case 2 : - rest[0] = f[0]; - rest[1] = f[1]; - irc_server_convbase64_8x3_to_6x4 (rest, t); - t[3] = '='; - break; - } - t[4] = 0; - } -} - -/* - * irc_server_pass_httpproxy: establish connection/authentification to an - * http proxy - * return : - * - 0 if connexion throw proxy was successful - * - 1 if connexion fails - */ - -int -irc_server_pass_httpproxy (int sock, char *address, int port) -{ - - char buffer[256], authbuf[128], authbuf_base64[196]; - char *config_proxy_username, *config_proxy_password; - int n, m; - - config_proxy_username = weechat_config_string ( - weechat_config_get ("weechat.proxy.username")); - config_proxy_username = weechat_config_string ( - weechat_config_get ("weechat.proxy.password")); - - if (config_proxy_username && config_proxy_username[0]) - { - /* authentification */ - snprintf (authbuf, sizeof (authbuf), "%s:%s", - config_proxy_username, - (config_proxy_password) ? config_proxy_password : ""); - irc_server_base64encode (authbuf, authbuf_base64); - n = snprintf (buffer, sizeof (buffer), - "CONNECT %s:%d HTTP/1.0\r\nProxy-Authorization: Basic %s\r\n\r\n", - address, port, authbuf_base64); - } - else - { - /* no authentification */ - n = snprintf (buffer, sizeof (buffer), - "CONNECT %s:%d HTTP/1.0\r\n\r\n", address, port); - } - - m = send (sock, buffer, n, 0); - if (n != m) - return 1; - - n = recv (sock, buffer, sizeof (buffer), 0); - - /* success result must be like: "HTTP/1.0 200 OK" */ - if (n < 12) - return 1; - - if (memcmp (buffer, "HTTP/", 5) || memcmp (buffer + 9, "200", 3)) - return 1; - - return 0; -} - -/* - * irc_server_resolve: resolve hostname on its IP address - * (works with ipv4 and ipv6) - * return : - * - 0 if resolution was successful - * - 1 if resolution fails - */ - -int -irc_server_resolve (char *hostname, char *ip, int *version) -{ - char ipbuffer[NI_MAXHOST]; - struct addrinfo *res; - - if (version != NULL) - *version = 0; - - res = NULL; - - if (getaddrinfo (hostname, NULL, NULL, &res) != 0) - return 1; - - if (!res) - return 1; - - if (getnameinfo (res->ai_addr, res->ai_addrlen, ipbuffer, sizeof(ipbuffer), NULL, 0, NI_NUMERICHOST) != 0) - { - freeaddrinfo (res); - return 1; - } - - if ((res->ai_family == AF_INET) && (version != NULL)) - *version = 4; - if ((res->ai_family == AF_INET6) && (version != NULL)) - *version = 6; - - strcpy (ip, ipbuffer); - - freeaddrinfo (res); - - return 0; -} - -/* - * irc_server_pass_socks4proxy: establish connection/authentification thru a - * socks4 proxy - * return : - * - 0 if connexion thru proxy was successful - * - 1 if connexion fails - */ - -int -irc_server_pass_socks4proxy (int sock, char *address, int port, char *username) -{ - /* - * socks4 protocol is explained here: - * http://archive.socks.permeo.com/protocol/socks4.protocol - * - */ - - struct s_socks4 - { - char version; /* 1 byte */ /* socks version : 4 or 5 */ - char method; /* 1 byte */ /* socks method : connect (1) or bind (2) */ - unsigned short port; /* 2 bytes */ /* destination port */ - unsigned long address; /* 4 bytes */ /* destination address */ - char user[64]; /* username (64 characters seems to be enought) */ - } socks4; - unsigned char buffer[24]; - char ip_addr[NI_MAXHOST]; - - socks4.version = 4; - socks4.method = 1; - socks4.port = htons (port); - irc_server_resolve (address, ip_addr, NULL); - socks4.address = inet_addr (ip_addr); - strncpy (socks4.user, username, sizeof (socks4.user) - 1); - - send (sock, (char *) &socks4, 8 + strlen (socks4.user) + 1, 0); - recv (sock, buffer, sizeof (buffer), 0); - - if (buffer[0] == 0 && buffer[1] == 90) - return 0; - - return 1; -} - -/* - * irc_server_pass_socks5proxy: establish connection/authentification thru a - * socks5 proxy - * return : - * - 0 if connexion thru proxy was successful - * - 1 if connexion fails - */ - -int -irc_server_pass_socks5proxy (int sock, char *address, int port) -{ - /* - * socks5 protocol is explained in RFC 1928 - * socks5 authentication with username/pass is explained in RFC 1929 - */ - - struct s_sock5 - { - char version; /* 1 byte */ /* socks version : 4 or 5 */ - char nmethods; /* 1 byte */ /* size in byte(s) of field 'method', here 1 byte */ - char method; /* 1-255 bytes */ /* socks method : noauth (0), auth(user/pass) (2), ... */ - } socks5; - unsigned char buffer[288]; - int username_len, password_len, addr_len, addr_buffer_len; - unsigned char *addr_buffer; - char *config_proxy_username, *config_proxy_password; - - socks5.version = 5; - socks5.nmethods = 1; - - config_proxy_username = weechat_config_string ( - weechat_config_get ("weechat.proxy.username")); - config_proxy_username = weechat_config_string ( - weechat_config_get ("weechat.proxy.password")); - - if (config_proxy_username && config_proxy_username[0]) - socks5.method = 2; /* with authentication */ - else - socks5.method = 0; /* without authentication */ - - send (sock, (char *) &socks5, sizeof(socks5), 0); - /* server socks5 must respond with 2 bytes */ - if (recv (sock, buffer, 2, 0) != 2) - return 1; - - if (config_proxy_username && config_proxy_username[0]) - { - /* with authentication */ - /* -> socks server must respond with : - * - socks version (buffer[0]) = 5 => socks5 - * - socks method (buffer[1]) = 2 => authentication - */ - - if (buffer[0] != 5 || buffer[1] != 2) - return 1; - - /* authentication as in RFC 1929 */ - username_len = strlen (config_proxy_username); - password_len = strlen (config_proxy_password); - - /* make username/password buffer */ - buffer[0] = 1; - buffer[1] = (unsigned char) username_len; - memcpy(buffer + 2, config_proxy_username, username_len); - buffer[2 + username_len] = (unsigned char) password_len; - memcpy (buffer + 3 + username_len, config_proxy_password, password_len); - - send (sock, buffer, 3 + username_len + password_len, 0); - - /* server socks5 must respond with 2 bytes */ - if (recv (sock, buffer, 2, 0) != 2) - return 1; - - /* buffer[1] = auth state, must be 0 for success */ - if (buffer[1] != 0) - return 1; - } - else - { - /* without authentication */ - /* -> socks server must respond with : - * - socks version (buffer[0]) = 5 => socks5 - * - socks method (buffer[1]) = 0 => no authentication - */ - if (!(buffer[0] == 5 && buffer[1] == 0)) - return 1; - } - - /* authentication successful then giving address/port to connect */ - addr_len = strlen(address); - addr_buffer_len = 4 + 1 + addr_len + 2; - addr_buffer = malloc (addr_buffer_len * sizeof(*addr_buffer)); - if (!addr_buffer) - return 1; - addr_buffer[0] = 5; /* version 5 */ - addr_buffer[1] = 1; /* command: 1 for connect */ - addr_buffer[2] = 0; /* reserved */ - addr_buffer[3] = 3; /* address type : ipv4 (1), domainname (3), ipv6 (4) */ - addr_buffer[4] = (unsigned char) addr_len; - memcpy (addr_buffer + 5, address, addr_len); /* server address */ - *((unsigned short *) (addr_buffer + 5 + addr_len)) = htons (port); /* server port */ - - send (sock, addr_buffer, addr_buffer_len, 0); - free (addr_buffer); - - /* dialog with proxy server */ - if (recv (sock, buffer, 4, 0) != 4) - return 1; - - if (!(buffer[0] == 5 && buffer[1] == 0)) - return 1; - - /* buffer[3] = address type */ - switch (buffer[3]) - { - case 1: - /* ipv4 - * server socks return server bound address and port - * address of 4 bytes and port of 2 bytes (= 6 bytes) - */ - if (recv (sock, buffer, 6, 0) != 6) - return 1; - break; - case 3: - /* domainname - * server socks return server bound address and port - */ - /* reading address length */ - if (recv (sock, buffer, 1, 0) != 1) - return 1; - addr_len = buffer[0]; - /* reading address + port = addr_len + 2 */ - if (recv (sock, buffer, addr_len + 2, 0) != (addr_len + 2)) - return 1; - break; - case 4: - /* ipv6 - * server socks return server bound address and port - * address of 16 bytes and port of 2 bytes (= 18 bytes) - */ - if (recv (sock, buffer, 18, 0) != 18) - return 1; - break; - default: - return 1; - } - - return 0; -} - -/* - * irc_server_pass_proxy: establish connection/authentification to a proxy - * return : - * - 0 if connexion throw proxy was successful - * - 1 if connexion fails - */ - -int -irc_server_pass_proxy (int sock, char *address, int port, char *username) -{ - int rc; - char *config_proxy_type; - - config_proxy_type = weechat_config_string ( - weechat_config_get ("weechat.proxy.type")); - - rc = 1; - if (config_proxy_type) - { - if (weechat_strcasecmp (config_proxy_type, "http") == 0) - rc = irc_server_pass_httpproxy (sock, address, port); - if (weechat_strcasecmp (config_proxy_type, "socks4") == 0) - rc = irc_server_pass_socks4proxy (sock, address, port, username); - if (weechat_strcasecmp (config_proxy_type, "socks5") == 0) - rc = irc_server_pass_socks5proxy (sock, address, port); - } - return rc; -} - -/* * irc_server_child: child process trying to connect to server */ @@ -2497,10 +2113,9 @@ irc_server_child (struct t_irc_server *server) return 0; } - if (irc_server_pass_proxy (server->sock, - server->addresses_array[server->current_address], - server->ports_array[server->current_address], - server->username)) + if (weechat_network_pass_proxy (server->sock, + server->addresses_array[server->current_address], + server->ports_array[server->current_address])) { write (server->child_write, "4", 1); freeaddrinfo (res); @@ -3175,6 +2790,76 @@ irc_server_set_default_notify_level (struct t_irc_server *server, int notify) } /* + * irc_server_xfer_send_ready_cb: callback called when user send (file or chat) + * to someone and that xfer plugin successfully + * initialized xfer and is ready for sending + * in that case, irc plugin send message to + * remote nick and wait for "accept" reply + */ + +int +irc_server_xfer_send_ready_cb (void *data, char *signal, char *type_data, + void *signal_data) +{ + struct t_plugin_infolist *infolist; + struct t_irc_server *server, *ptr_server; + char *plugin_id, *type; + + /* make C compiler happy */ + (void) data; + (void) signal; + (void) type_data; + + infolist = (struct t_plugin_infolist *)signal_data; + + if (weechat_infolist_next (infolist)) + { + plugin_id = weechat_infolist_string (infolist, "plugin_id"); + if (plugin_id) + { + if (strncmp (plugin_id, "irc_", 4) == 0) + { + sscanf (plugin_id + 4, "%x", (unsigned int *)&server); + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server == server) + break; + } + if (ptr_server) + { + type = weechat_infolist_string (infolist, "type"); + if (type) + { + if (strcmp (type, "file_send") == 0) + { + irc_server_sendf (server, + "PRIVMSG %s :\01DCC SEND \"%s\" " + "%s %d %s\01\n", + weechat_infolist_string (infolist, "nick"), + weechat_infolist_string (infolist, "filename"), + weechat_infolist_string (infolist, "address"), + weechat_infolist_integer (infolist, "port"), + weechat_infolist_string (infolist, "size")); + } + else if (strcmp (type, "chat_send") == 0) + { + irc_server_sendf (server, + "PRIVMSG %s :\01DCC CHAT chat %s %d\01", + weechat_infolist_string (infolist, "nick"), + weechat_infolist_string (infolist, "address"), + weechat_infolist_integer (infolist, "port")); + } + } + } + } + } + } + + return WEECHAT_RC_OK; +} + +/* * irc_server_print_log: print server infos in log (usually for crash dump) */ diff --git a/src/plugins/irc/irc-server.h b/src/plugins/irc/irc-server.h index cf18813ab..1593f3425 100644 --- a/src/plugins/irc/irc-server.h +++ b/src/plugins/irc/irc-server.h @@ -186,6 +186,8 @@ extern void irc_server_disconnect (struct t_irc_server *server, int reconnect); extern void irc_server_disconnect_all (); extern void irc_server_free (struct t_irc_server *server); extern void irc_server_free_data (struct t_irc_server *server); +extern int irc_server_xfer_send_ready_cb (void *data, char *signal, + char *type_data, void *signal_data); extern void irc_server_print_log (); #endif /* irc-server.h */ diff --git a/src/plugins/irc/irc.c b/src/plugins/irc/irc.c index a41d5a503..70dae0e2d 100644 --- a/src/plugins/irc/irc.c +++ b/src/plugins/irc/irc.c @@ -22,10 +22,6 @@ #include <stdlib.h> #include <string.h> -#ifdef HAVE_GNUTLS -#include <gnutls/gnutls.h> -#endif - #include "../weechat-plugin.h" #include "irc.h" #include "irc-command.h" @@ -56,31 +52,6 @@ gnutls_certificate_credentials gnutls_xcred; /* gnutls client credentials */ /* - * irc_create_directories: create directories for IRC plugin - */ - -void -irc_create_directories () -{ - char *weechat_dir, *dir1, *dir2; - - /* create DCC download directory */ - weechat_dir = weechat_info_get ("weechat_dir"); - if (weechat_dir) - { - dir1 = weechat_string_replace (weechat_config_string (irc_config_dcc_download_path), - "~", getenv ("HOME")); - dir2 = weechat_string_replace (dir1, "%h", weechat_dir); - if (dir2) - (void) weechat_mkdir (dir2, 0700); - if (dir1) - free (dir1); - if (dir2) - free (dir2); - } -} - -/* * irc_signal_quit_cb: callback for "quit" signal */ @@ -128,14 +99,13 @@ weechat_plugin_init (struct t_weechat_plugin *plugin) if (irc_config_read () < 0) return WEECHAT_RC_ERROR; - - irc_create_directories (); - + irc_command_init (); - + /* hook some signals */ irc_debug_init (); weechat_hook_signal ("quit", &irc_signal_quit_cb, NULL); + weechat_hook_signal ("xfer_send_ready", &irc_server_xfer_send_ready_cb, NULL); /* hook completions */ irc_completion_init (); diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c index 913d06532..90979594e 100644 --- a/src/plugins/plugin.c +++ b/src/plugins/plugin.c @@ -38,6 +38,7 @@ #include "../core/wee-hook.h" #include "../core/wee-list.h" #include "../core/wee-log.h" +#include "../core/wee-network.h" #include "../core/wee-string.h" #include "../core/wee-utf8.h" #include "../core/wee-util.h" @@ -396,8 +397,17 @@ plugin_load (char *filename) new_plugin->command = &plugin_api_command; - new_plugin->info_get = &plugin_api_info_get; + new_plugin->network_pass_proxy = &network_pass_proxy; + new_plugin->network_connect_to = &network_connect_to; + new_plugin->info_get = &plugin_api_info_get; + + new_plugin->infolist_new = &plugin_infolist_new; + new_plugin->infolist_new_item = &plugin_infolist_new_item; + new_plugin->infolist_new_var_integer = &plugin_infolist_new_var_integer; + new_plugin->infolist_new_var_string = &plugin_infolist_new_var_string; + new_plugin->infolist_new_var_pointer = &plugin_infolist_new_var_pointer; + new_plugin->infolist_new_var_time = &plugin_infolist_new_var_time; new_plugin->infolist_get = &plugin_api_infolist_get; new_plugin->infolist_next = &plugin_api_infolist_next; new_plugin->infolist_prev = &plugin_api_infolist_prev; diff --git a/src/plugins/weechat-plugin.h b/src/plugins/weechat-plugin.h index b9baf6a50..557fc6608 100644 --- a/src/plugins/weechat-plugin.h +++ b/src/plugins/weechat-plugin.h @@ -388,11 +388,29 @@ struct t_weechat_plugin /* command */ void (*command) (struct t_weechat_plugin *plugin, struct t_gui_buffer *buffer, char *command); + + /* network */ + int (*network_pass_proxy) (int sock, char *address, int port); + int (*network_connect_to) (int sock, unsigned long address, int port); /* infos */ char *(*info_get) (struct t_weechat_plugin *plugin, char *info); /* infolists */ + struct t_plugin_infolist *(*infolist_new) (); + struct t_plugin_infolist_item *(*infolist_new_item) (struct t_plugin_infolist *list); + struct t_plugin_infolist_var *(*infolist_new_var_integer) (struct t_plugin_infolist_item *item, + char *name, + int value); + struct t_plugin_infolist_var *(*infolist_new_var_string) (struct t_plugin_infolist_item *item, + char *name, + char *value); + struct t_plugin_infolist_var *(*infolist_new_var_pointer) (struct t_plugin_infolist_item *item, + char *name, + void *pointer); + struct t_plugin_infolist_var *(*infolist_new_var_time) (struct t_plugin_infolist_item *item, + char *name, + time_t time); struct t_plugin_infolist *(*infolist_get) (char *name, void *pointer, char *arguments); int (*infolist_next) (struct t_plugin_infolist *infolist); @@ -792,11 +810,29 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin); #define weechat_command(__buffer, __command) \ weechat_plugin->command(weechat_plugin, __buffer, __command) +/* network */ +#define weechat_network_pass_proxy(__sock, __address, __port) \ + weechat_plugin->network_pass_proxy(__sock, __address, __port) +#define weechat_network_connect_to(__sock, __address, __port) \ + weechat_plugin->network_connect_to(__sock, __address, __port) + /* infos */ #define weechat_info_get(__name) \ weechat_plugin->info_get(weechat_plugin, __name) /* infolists */ +#define weechat_infolist_new() \ + weechat_plugin->infolist_new() +#define weechat_infolist_new_item(__list) \ + weechat_plugin->infolist_new_item(__list) +#define weechat_infolist_new_var_integer(__item, __name, __value) \ + weechat_plugin->infolist_new_var_integer(__item, __name, __value) +#define weechat_infolist_new_var_string(__item, __name, __value) \ + weechat_plugin->infolist_new_var_string(__item, __name, __value) +#define weechat_infolist_new_var_pointer(__item, __name, __pointer) \ + weechat_plugin->infolist_new_var_pointer(__item, __name, __pointer) +#define weechat_infolist_new_var_time(__item, __name, __time) \ + weechat_plugin->infolist_new_var_time(__item, __name, __time) #define weechat_infolist_get(__name, __pointer, __arguments) \ weechat_plugin->infolist_get(__name, __pointer, __arguments) #define weechat_infolist_next(__list) \ diff --git a/src/plugins/xfer/CMakeLists.txt b/src/plugins/xfer/CMakeLists.txt new file mode 100644 index 000000000..f9849e367 --- /dev/null +++ b/src/plugins/xfer/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2003-2008 FlashCode <flashcode@flashtux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +ADD_LIBRARY(xfer MODULE +xfer.c xfer.h +xfer-buffer.c xfer-buffer.h +xfer-chat.c xfer-chat.h +xfer-config.c xfer-config.h +xfer-dcc.c xfer-dcc.h +xfer-file.c xfer-file.h +xfer-network.c xfer-network.h) +SET_TARGET_PROPERTIES(xfer PROPERTIES PREFIX "") + +TARGET_LINK_LIBRARIES(xfer) + +INSTALL(TARGETS xfer LIBRARY DESTINATION lib/${PROJECT_NAME}/plugins) diff --git a/src/plugins/xfer/Makefile.am b/src/plugins/xfer/Makefile.am new file mode 100644 index 000000000..ad7538bb4 --- /dev/null +++ b/src/plugins/xfer/Makefile.am @@ -0,0 +1,39 @@ +# Copyright (c) 2003-2008 FlashCode <flashcode@flashtux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +INCLUDES = -DLOCALEDIR=\"$(datadir)/locale\" + +libdir = ${weechat_libdir}/plugins + +lib_LTLIBRARIES = xfer.la + +xfer_la_SOURCES = xfer.c \ + xfer.h \ + xfer-buffer.c \ + xfer-buffer.h \ + xfer-chat.c \ + xfer-chat.h \ + xfer-config.c \ + xfer-config.h \ + xfer-dcc.c \ + xfer-dcc.h \ + xfer-file.c \ + xfer-file.h \ + xfer-network.c \ + xfer-network.h + +xfer_la_LDFLAGS = -module +xfer_la_LIBADD = $(XFER_LFLAGS) diff --git a/src/plugins/xfer/xfer-buffer.c b/src/plugins/xfer/xfer-buffer.c new file mode 100644 index 000000000..52823985f --- /dev/null +++ b/src/plugins/xfer/xfer-buffer.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* xfer-buffer.c: display xfer list on xfer buffer */ + + +#include <stdlib.h> +#include <stdio.h> + +#include "../weechat-plugin.h" +#include "xfer.h" +#include "xfer-buffer.h" +#include "xfer-config.h" + + +struct t_gui_buffer *xfer_buffer = NULL; +int xfer_buffer_selected_line = 0; + + +/* + * xfer_buffer_close_cb: callback called when xfer buffer is closed + */ + +int +xfer_buffer_close_cb (void *data, struct t_gui_buffer *buffer) +{ + /* make C compiler happy */ + (void) data; + (void) buffer; + + xfer_buffer = NULL; + + return WEECHAT_RC_OK; +} + +/* + * xfer_buffer_open: open xfer buffer (to display list of xfer) + */ + +void +xfer_buffer_open () +{ + if (!xfer_buffer) + { + xfer_buffer = weechat_buffer_new ("xfer", "xfer", + NULL, NULL, + &xfer_buffer_close_cb, NULL); + + /* failed to create buffer ? then exit */ + if (!xfer_buffer) + return; + + weechat_buffer_set (xfer_buffer, "type", "free"); + weechat_buffer_set (xfer_buffer, "title", _("Xfer list")); + } +} + +/* + * xfer_buffer_refresh: update a xfer in buffer and update hotlist for xfer buffer + */ + +void +xfer_buffer_refresh (char *hotlist) +{ + struct t_xfer *ptr_xfer; + char str_color[256]; + int line; + + if (xfer_buffer) + { + line = 0; + for (ptr_xfer = xfer_list; ptr_xfer; ptr_xfer = ptr_xfer->next_xfer) + { + if (XFER_IS_FILE(ptr_xfer->type)) + { + snprintf (str_color, sizeof (str_color), + "%s,%s", + weechat_config_string (xfer_config_color_text), + weechat_config_string (xfer_config_color_text_bg)); + weechat_printf_y (xfer_buffer, line * 2, + "%s%s%-20s \"%s\"", + weechat_color(str_color), + (line == xfer_buffer_selected_line) ? + "*** " : " ", + ptr_xfer->nick, ptr_xfer->filename); + weechat_printf_y (xfer_buffer, (line * 2) + 1, + "%s%s%s %s%-15s ", + weechat_color(str_color), + (line == xfer_buffer_selected_line) ? + "*** " : " ", + (XFER_IS_SEND(ptr_xfer->type)) ? + "<<--" : "-->>", + weechat_color( + weechat_config_string ( + xfer_config_color_status[ptr_xfer->status])), + _(xfer_status_string[ptr_xfer->status])); + } + line++; + } + weechat_buffer_set (xfer_buffer, "hotlist", hotlist); + } +} diff --git a/src/plugins/xfer/xfer-buffer.h b/src/plugins/xfer/xfer-buffer.h new file mode 100644 index 000000000..aea4291e1 --- /dev/null +++ b/src/plugins/xfer/xfer-buffer.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_XFER_DISPLAY_H +#define __WEECHAT_XFER_DISPLAY_H 1 + +extern struct t_gui_buffer *xfer_buffer; + +extern void xfer_buffer_open (); +extern void xfer_buffer_refresh (char *hotlist); + +#endif /* xfer.h */ diff --git a/src/plugins/xfer/xfer-chat.c b/src/plugins/xfer/xfer-chat.c new file mode 100644 index 000000000..4999817e5 --- /dev/null +++ b/src/plugins/xfer/xfer-chat.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* xfer-chat.c: chat with direct connection to remote host */ + + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "../weechat-plugin.h" +#include "xfer.h" +#include "xfer-chat.h" +#include "xfer-buffer.h" + + +/* + * xfer_chat_send: send data to remote host via xfer chat + */ + +int +xfer_chat_send (struct t_xfer *xfer, char *buffer, int size_buf) +{ + if (!xfer) + return -1; + + return send (xfer->sock, buffer, size_buf, 0); +} + +/* + * xfer_chat_sendf: send formatted data to remote host via DCC CHAT + */ + +void +xfer_chat_sendf (struct t_xfer *xfer, char *format, ...) +{ + va_list args; + static char buffer[4096]; + int size_buf; + + if (!xfer || (xfer->sock < 0)) + return; + + va_start (args, format); + size_buf = vsnprintf (buffer, sizeof (buffer) - 1, format, args); + va_end (args); + + if (size_buf == 0) + return; + + buffer[sizeof (buffer) - 1] = '\0'; + if ((size_buf < 0) || (size_buf > (int) (sizeof (buffer) - 1))) + size_buf = strlen (buffer); + + if (xfer_chat_send (xfer, buffer, strlen (buffer)) <= 0) + { + weechat_printf (NULL, + _("%s%s: error sending data to \"%s\" via xfer chat"), + weechat_prefix ("error"), "xfer", xfer->nick); + xfer_close (xfer, XFER_STATUS_FAILED); + } +} + +/* + * xfer_chat_recv: receive data from xfer chat remote host + */ + +void +xfer_chat_recv (struct t_xfer *xfer) +{ + fd_set read_fd; + static struct timeval timeout; + static char buffer[4096 + 2]; + char *buf2, *pos, *ptr_buf, *next_ptr_buf; + int num_read; + + FD_ZERO (&read_fd); + FD_SET (xfer->sock, &read_fd); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + /* something to read on socket? */ + if (select (FD_SETSIZE, &read_fd, NULL, NULL, &timeout) <= 0) + return; + + if (!FD_ISSET (xfer->sock, &read_fd)) + return; + + /* there's something to read on socket! */ + num_read = recv (xfer->sock, buffer, sizeof (buffer) - 2, 0); + if (num_read > 0) + { + buffer[num_read] = '\0'; + + buf2 = NULL; + ptr_buf = buffer; + if (xfer->unterminated_message) + { + buf2 = malloc (strlen (xfer->unterminated_message) + + strlen (buffer) + 1); + if (buf2) + { + strcpy (buf2, xfer->unterminated_message); + strcat (buf2, buffer); + } + ptr_buf = buf2; + free (xfer->unterminated_message); + xfer->unterminated_message = NULL; + } + + while (ptr_buf && ptr_buf[0]) + { + next_ptr_buf = NULL; + pos = strstr (ptr_buf, "\n"); + if (pos) + { + pos[0] = '\0'; + next_ptr_buf = pos + 1; + } + else + { + xfer->unterminated_message = strdup (ptr_buf); + ptr_buf = NULL; + next_ptr_buf = NULL; + } + + if (ptr_buf) + { + weechat_printf (xfer->buffer, "%s\t%s", xfer->nick, ptr_buf); + } + + ptr_buf = next_ptr_buf; + } + + if (buf2) + free (buf2); + } + else + { + xfer_close (xfer, XFER_STATUS_ABORTED); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + } +} diff --git a/src/plugins/xfer/xfer-chat.h b/src/plugins/xfer/xfer-chat.h new file mode 100644 index 000000000..b3470aca5 --- /dev/null +++ b/src/plugins/xfer/xfer-chat.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_XFER_CHAT_H +#define __WEECHAT_XFER_CHAT_H 1 + + +#endif /* xfer-chat.h */ diff --git a/src/plugins/xfer/xfer-config.c b/src/plugins/xfer/xfer-config.c new file mode 100644 index 000000000..c532c7bc5 --- /dev/null +++ b/src/plugins/xfer/xfer-config.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* xfer-config.c: xfer configuration options */ + + +#include <stdlib.h> +#include <limits.h> + +#include "../weechat-plugin.h" +#include "xfer.h" +#include "xfer-config.h" + + +struct t_config_file *xfer_config_file = NULL; + +/* xfer config, look section */ + +struct t_config_option *xfer_config_look_auto_open_buffer; + +/* xfer config, color section */ + +struct t_config_option *xfer_config_color_text; +struct t_config_option *xfer_config_color_text_bg; +struct t_config_option *xfer_config_color_selected_bg; +struct t_config_option *xfer_config_color_status[XFER_NUM_STATUS]; + +/* xfer config, network section */ + +struct t_config_option *xfer_config_network_timeout; +struct t_config_option *xfer_config_network_blocksize; +struct t_config_option *xfer_config_network_fast_send; +struct t_config_option *xfer_config_network_port_range; +struct t_config_option *xfer_config_network_own_ip; + +/* xfer config, file section */ + +struct t_config_option *xfer_config_file_download_path; +struct t_config_option *xfer_config_file_upload_path; +struct t_config_option *xfer_config_file_convert_spaces; +struct t_config_option *xfer_config_file_auto_rename; +struct t_config_option *xfer_config_file_auto_resume; +struct t_config_option *xfer_config_file_auto_accept_files; +struct t_config_option *xfer_config_file_auto_accept_chats; + + + +/* + * xfer_config_reload: reload xfer configuration file + */ + +int +xfer_config_reload (void *data, struct t_config_file *config_file) +{ + /* make C compiler happy */ + (void) data; + + return weechat_config_reload (config_file); +} + +/* + * xfer_config_init: init xfer configuration file + * return: 1 if ok, 0 if error + */ + +int +xfer_config_init () +{ + struct t_config_section *ptr_section; + + xfer_config_file = weechat_config_new (XFER_CONFIG_NAME, + &xfer_config_reload, NULL); + if (!xfer_config_file) + return 0; + + ptr_section = weechat_config_new_section (xfer_config_file, "look", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (xfer_config_file); + return 0; + } + + xfer_config_look_auto_open_buffer = weechat_config_new_option ( + xfer_config_file, ptr_section, + "auto_open_buffer", "boolean", + N_("auto open xfer buffer and switch to it when a new xfer is added " + "to list"), + NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); + + ptr_section = weechat_config_new_section (xfer_config_file, "color", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (xfer_config_file); + return 0; + } + + xfer_config_color_text = weechat_config_new_option ( + xfer_config_file, ptr_section, + "text", "color", + N_("text color"), + NULL, 0, 0, "default", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_color_text_bg = weechat_config_new_option ( + xfer_config_file, ptr_section, + "text_bg", "color", + N_("background color"), + NULL, 0, 0, "default", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_color_selected_bg = weechat_config_new_option ( + xfer_config_file, ptr_section, + "selected_bg", "color", + N_("background color for selected line"), + NULL, 0, 0, "magenta", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_color_status[XFER_STATUS_WAITING] = weechat_config_new_option ( + xfer_config_file, ptr_section, + "status_waiting", "color", + N_("text color for \"waiting\" status"), + NULL, 0, 0, "lightcyan", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_color_status[XFER_STATUS_CONNECTING] = weechat_config_new_option ( + xfer_config_file, ptr_section, + "status_connecting", "color", + N_("text color for \"connecting\" status"), + NULL, 0, 0, "yellow", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_color_status[XFER_STATUS_ACTIVE] = weechat_config_new_option ( + xfer_config_file, ptr_section, + "status_active", "color", + N_("text color for \"active\" status"), + NULL, 0, 0, "lightblue", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_color_status[XFER_STATUS_DONE] = weechat_config_new_option ( + xfer_config_file, ptr_section, + "status_done", "color", + N_("text color for \"done\" status"), + NULL, 0, 0, "lightgreen", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_color_status[XFER_STATUS_FAILED] = weechat_config_new_option ( + xfer_config_file, ptr_section, + "status_failed", "color", + N_("text color for \"failed\" status"), + NULL, 0, 0, "lightred", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_color_status[XFER_STATUS_ABORTED] = weechat_config_new_option ( + xfer_config_file, ptr_section, + "status_aborted", "color", + N_("text color for \"aborted\" status"), + NULL, 0, 0, "lightred", + NULL, NULL, NULL, NULL, NULL, NULL); + + ptr_section = weechat_config_new_section (xfer_config_file, "network", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (xfer_config_file); + return 0; + } + + xfer_config_network_timeout = weechat_config_new_option ( + xfer_config_file, ptr_section, + "timeout", "integer", + N_("timeout for xfer request (in seconds)"), + NULL, 5, INT_MAX, "300", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_network_blocksize = weechat_config_new_option ( + xfer_config_file, ptr_section, + "blocksize", "integer", + N_("block size for sending packets, in bytes"), + NULL, XFER_BLOCKSIZE_MIN, XFER_BLOCKSIZE_MAX, "65536", + NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_network_fast_send = weechat_config_new_option ( + xfer_config_file, ptr_section, + "fast_send", "boolean", + N_("does not wait for ACK when sending file"), + NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_network_port_range = weechat_config_new_option ( + xfer_config_file, ptr_section, + "port_range", "string", + N_("restricts outgoing files/chats to use only ports in the given " + "range (useful for NAT) (syntax: a single port, ie. 5000 or a port " + "range, ie. 5000-5015, empty value means any port)"), + NULL, 0, 0, "", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_network_own_ip = weechat_config_new_option ( + xfer_config_file, ptr_section, + "own_ip", "string", + N_("IP or DNS address used for sending files/chats " + "(if empty, local interface IP is used)"), + NULL, 0, 0, "", NULL, NULL, NULL, NULL, NULL, NULL); + + ptr_section = weechat_config_new_section (xfer_config_file, "file", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (xfer_config_file); + return 0; + } + + xfer_config_file_download_path = weechat_config_new_option ( + xfer_config_file, ptr_section, + "download_path", "string", + N_("path for writing incoming files"), + NULL, 0, 0, "%h/xfer", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_file_upload_path = weechat_config_new_option ( + xfer_config_file, ptr_section, + "upload_path", "string", + N_("path for reading files when sending (when no path is " + "specified by user)"), + NULL, 0, 0, "~", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_file_convert_spaces = weechat_config_new_option ( + xfer_config_file, ptr_section, + "convert_spaces", "boolean", + N_("convert spaces to underscores when sending files"), + NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_file_auto_rename = weechat_config_new_option ( + xfer_config_file, ptr_section, + "auto_rename", "boolean", + N_("rename incoming files if already exists (add '.1', '.2', ...)"), + NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_file_auto_resume = weechat_config_new_option ( + xfer_config_file, ptr_section, + "auto_resume", "boolean", + N_("automatically resume file transfer if connection with remote host " + "is lost"), + NULL, 0, 0, "on", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_file_auto_accept_files = weechat_config_new_option ( + xfer_config_file, ptr_section, + "auto_accept_files", "boolean", + N_("automatically accept incoming files (use carefully!)"), + NULL, 0, 0, "off", NULL, NULL, NULL, NULL, NULL, NULL); + xfer_config_file_auto_accept_chats = weechat_config_new_option ( + xfer_config_file, ptr_section, + "auto_accept_chats", "boolean", + N_("automatically accept chat requests (use carefully!)"), + NULL, 0, 0, "off", NULL, NULL, NULL, NULL, NULL, NULL); + + return 1; +} + +/* + * xfer_config_read: read xfer configuration file + * return: 0 = successful + * -1 = configuration file file not found + * -2 = error in configuration file + */ + +int +xfer_config_read () +{ + return weechat_config_read (xfer_config_file); +} + +/* + * xfer_config_write: write xfer configuration file + * return: 0 if ok + * < 0 if error + */ + +int +xfer_config_write () +{ + return weechat_config_write (xfer_config_file); +} diff --git a/src/plugins/xfer/xfer-config.h b/src/plugins/xfer/xfer-config.h new file mode 100644 index 000000000..a7753d024 --- /dev/null +++ b/src/plugins/xfer/xfer-config.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_XFER_CONFIG_H +#define __WEECHAT_XFER_CONFIG_H 1 + +#define XFER_CONFIG_NAME "xfer" + +extern struct t_config_file *xfer_config; + +extern struct t_config_option *xfer_config_look_auto_open_buffer; + +extern struct t_config_option *xfer_config_color_text; +extern struct t_config_option *xfer_config_color_text_bg; +extern struct t_config_option *xfer_config_color_selected_bg; +extern struct t_config_option *xfer_config_color_status[]; + +extern struct t_config_option *xfer_config_network_timeout; +extern struct t_config_option *xfer_config_network_blocksize; +extern struct t_config_option *xfer_config_network_fast_send; +extern struct t_config_option *xfer_config_network_port_range; +extern struct t_config_option *xfer_config_network_own_ip; + +extern struct t_config_option *xfer_config_file_download_path; +extern struct t_config_option *xfer_config_file_upload_path; +extern struct t_config_option *xfer_config_file_convert_spaces; +extern struct t_config_option *xfer_config_file_auto_rename; +extern struct t_config_option *xfer_config_file_auto_resume; +extern struct t_config_option *xfer_config_file_auto_accept_files; +extern struct t_config_option *xfer_config_file_auto_accept_chats; + +extern int xfer_config_init (); +extern int xfer_config_read (); +extern int xfer_config_write (); + +#endif /* xfer-config.h */ diff --git a/src/plugins/xfer/xfer-dcc.c b/src/plugins/xfer/xfer-dcc.c new file mode 100644 index 000000000..5737ce515 --- /dev/null +++ b/src/plugins/xfer/xfer-dcc.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* xfer-dcc.c: file transfert via DCC protocol */ + + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <time.h> +#include <netdb.h> +#include <errno.h> + +#include "../weechat-plugin.h" +#include "xfer.h" +#include "xfer-dcc.h" +#include "xfer-network.h" + + +/* + * xfer_dcc_send_file_child: child process for sending file with DCC protocol + */ + +void +xfer_dcc_send_file_child (struct t_xfer *xfer) +{ + int num_read, num_sent; + static char buffer[XFER_BLOCKSIZE_MAX]; + uint32_t ack; + time_t last_sent, new_time; + + last_sent = time (NULL); + while (1) + { + /* read DCC ACK (sent by receiver) */ + if (xfer->pos > xfer->ack) + { + /* we should receive ACK for packets sent previously */ + while (1) + { + num_read = recv (xfer->sock, (char *) &ack, 4, MSG_PEEK); + if ((num_read < 1) && + ((num_read != -1) || (errno != EAGAIN))) + { + xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, + XFER_ERROR_SEND_BLOCK); + return; + } + if (num_read == 4) + { + recv (xfer->sock, (char *) &ack, 4, 0); + xfer->ack = ntohl (ack); + + /* DCC send ok? */ + if ((xfer->pos >= xfer->size) + && (xfer->ack >= xfer->size)) + { + xfer_network_write_pipe (xfer, XFER_STATUS_DONE, + XFER_NO_ERROR); + return; + } + } + else + break; + } + } + + /* send a block to receiver */ + if ((xfer->pos < xfer->size) && + (xfer->fast_send || (xfer->pos <= xfer->ack))) + { + lseek (xfer->file, xfer->pos, SEEK_SET); + num_read = read (xfer->file, buffer, xfer->blocksize); + if (num_read < 1) + { + xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, + XFER_ERROR_READ_LOCAL); + return; + } + num_sent = send (xfer->sock, buffer, num_read, 0); + if (num_sent < 0) + { + /* socket is temporarily not available (receiver can't receive + amount of data we sent ?!) */ + if (errno == EAGAIN) + usleep (1000); + else + { + xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, + XFER_ERROR_SEND_BLOCK); + return; + } + } + if (num_sent > 0) + { + xfer->pos += (unsigned long) num_sent; + new_time = time (NULL); + if (last_sent != new_time) + { + last_sent = new_time; + xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, + XFER_NO_ERROR); + } + } + } + else + usleep (1000); + } +} + +/* + * xfer_dcc_recv_file_child: child process for receiving file + */ + +void +xfer_dcc_recv_file_child (struct t_xfer *xfer) +{ + int num_read; + static char buffer[XFER_BLOCKSIZE_MAX]; + uint32_t pos; + time_t last_sent, new_time; + + /* first connect to sender (blocking) */ + if (!weechat_network_connect_to (xfer->sock, xfer->address, xfer->port)) + { + xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, + XFER_ERROR_CONNECT_SENDER); + return; + } + + /* connection is ok, change DCC status (inform parent process) */ + xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, + XFER_NO_ERROR); + + last_sent = time (NULL); + while (1) + { + num_read = recv (xfer->sock, buffer, sizeof (buffer), 0); + if (num_read == -1) + { + /* socket is temporarily not available (sender is not fast ?!) */ + if (errno == EAGAIN) + usleep (1000); + else + { + xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, + XFER_ERROR_RECV_BLOCK); + return; + } + } + else + { + if (num_read == 0) + { + xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, + XFER_ERROR_RECV_BLOCK); + return; + } + + if (write (xfer->file, buffer, num_read) == -1) + { + xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, + XFER_ERROR_WRITE_LOCAL); + return; + } + + xfer->pos += (unsigned long) num_read; + pos = htonl (xfer->pos); + + /* we don't check return code, not a problem if an ACK send failed */ + send (xfer->sock, (char *) &pos, 4, 0); + + /* file received ok? */ + if (xfer->pos >= xfer->size) + { + xfer_network_write_pipe (xfer, XFER_STATUS_DONE, + XFER_NO_ERROR); + return; + } + + new_time = time (NULL); + if (last_sent != new_time) + { + last_sent = new_time; + xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, + XFER_NO_ERROR); + } + } + } +} diff --git a/src/plugins/xfer/xfer-dcc.h b/src/plugins/xfer/xfer-dcc.h new file mode 100644 index 000000000..7c0349f0c --- /dev/null +++ b/src/plugins/xfer/xfer-dcc.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_XFER_DCC_H +#define __WEECHAT_XFER_DCC_H 1 + +extern void xfer_dcc_send_file_child (struct t_xfer *xfer); +extern void xfer_dcc_recv_file_child (struct t_xfer *xfer); + +#endif /* xfer-dcc.h */ diff --git a/src/plugins/xfer/xfer-file.c b/src/plugins/xfer/xfer-file.c new file mode 100644 index 000000000..cc067f1b4 --- /dev/null +++ b/src/plugins/xfer/xfer-file.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* xfer-file.c: file functions for xfer plugin */ + + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <sys/wait.h> + +#include "../weechat-plugin.h" +#include "xfer.h" +#include "xfer-file.h" +#include "xfer-buffer.h" +#include "xfer-config.h" + + +/* + * xfer_file_resume: resume a download + * return 1 if ok, 0 if not resumable + */ + +int +xfer_file_resume (struct t_xfer *xfer, char *filename) +{ + struct stat st; + + if (!weechat_config_boolean (xfer_config_file_auto_resume)) + return 0; + + if (access (filename, W_OK) == 0) + { + if (stat (filename, &st) != -1) + { + if ((unsigned long) st.st_size < xfer->size) + { + xfer->start_resume = (unsigned long) st.st_size; + xfer->pos = st.st_size; + xfer->last_check_pos = st.st_size; + return 1; + } + } + } + + /* not resumable */ + return 0; +} + +/* + * xfer_file_find_filename: find local filename for a xfer + * if type if file/recv, add a suffix (like .1) if needed + * if download is resumable, set "start_resume" to good value + */ + +void +xfer_file_find_filename (struct t_xfer *xfer) +{ + char *weechat_home, *dir1, *dir2, *filename2, *dir_separator; + + if (!XFER_IS_FILE(xfer->type)) + return; + + dir1 = weechat_string_replace (weechat_config_string (xfer_config_file_download_path), + "~", + getenv ("HOME")); + if (!dir1) + return; + + weechat_home = weechat_info_get ("weechat_dir"); + if (!weechat_home) + { + free (dir1); + return; + } + dir2 = weechat_string_replace (dir1, "%h", weechat_home); + if (!dir2) + { + free (dir1); + return; + } + + xfer->local_filename = malloc (strlen (dir2) + + strlen (xfer->nick) + + strlen (xfer->filename) + 4); + if (!xfer->local_filename) + return; + + strcpy (xfer->local_filename, dir2); + dir_separator = weechat_info_get("dir_separator"); + if (dir_separator + && (xfer->local_filename[strlen (xfer->local_filename) - 1] != dir_separator[0])) + strcat (xfer->local_filename, dir_separator); + strcat (xfer->local_filename, xfer->nick); + strcat (xfer->local_filename, "."); + strcat (xfer->local_filename, xfer->filename); + + if (dir1) + free (dir1); + if (dir2 ) + free (dir2); + + /* file already exists? */ + if (access (xfer->local_filename, F_OK) == 0) + { + if (xfer_file_resume (xfer, xfer->local_filename)) + return; + + /* if auto rename is not set, then abort xfer */ + if (!xfer_config_file_auto_rename) + { + xfer_close (xfer, XFER_STATUS_FAILED); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + return; + } + + filename2 = malloc (strlen (xfer->local_filename) + 16); + if (!filename2) + { + xfer_close (xfer, XFER_STATUS_FAILED); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + return; + } + xfer->filename_suffix = 0; + do + { + xfer->filename_suffix++; + sprintf (filename2, "%s.%d", + xfer->local_filename, + xfer->filename_suffix); + if (access (filename2, F_OK) == 0) + { + if (xfer_file_resume (xfer, filename2)) + break; + } + else + break; + } + while (1); + + free (xfer->local_filename); + xfer->local_filename = strdup (filename2); + free (filename2); + } +} + +/* + * xfer_file_calculate_speed: calculate xfer speed (for files only) + */ + +void +xfer_file_calculate_speed (struct t_xfer *xfer, int ended) +{ + time_t local_time, elapsed; + unsigned long bytes_per_sec_total; + + local_time = time (NULL); + if (ended || local_time > xfer->last_check_time) + { + if (ended) + { + /* calculate bytes per second (global) */ + elapsed = local_time - xfer->start_transfer; + if (elapsed == 0) + elapsed = 1; + xfer->bytes_per_sec = (xfer->pos - xfer->start_resume) / elapsed; + xfer->eta = 0; + } + else + { + /* calculate ETA */ + elapsed = local_time - xfer->start_transfer; + if (elapsed == 0) + elapsed = 1; + bytes_per_sec_total = (xfer->pos - xfer->start_resume) / elapsed; + if (bytes_per_sec_total == 0) + bytes_per_sec_total = 1; + xfer->eta = (xfer->size - xfer->pos) / bytes_per_sec_total; + + /* calculate bytes per second (since last check time) */ + elapsed = local_time - xfer->last_check_time; + if (elapsed == 0) + elapsed = 1; + xfer->bytes_per_sec = (xfer->pos - xfer->last_check_pos) / elapsed; + } + xfer->last_check_time = local_time; + xfer->last_check_pos = xfer->pos; + } +} diff --git a/src/plugins/xfer/xfer-file.h b/src/plugins/xfer/xfer-file.h new file mode 100644 index 000000000..3158507a5 --- /dev/null +++ b/src/plugins/xfer/xfer-file.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_XFER_FILE_H +#define __WEECHAT_XFER_FILE_H 1 + +extern int xfer_file_resume (struct t_xfer *xfer, char *filename); +extern void xfer_file_find_filename (struct t_xfer *xfer); +extern void xfer_file_calculate_speed (struct t_xfer *xfer, int ended); + +#endif /* xfer-file.h */ diff --git a/src/plugins/xfer/xfer-network.c b/src/plugins/xfer/xfer-network.c new file mode 100644 index 000000000..2c9247996 --- /dev/null +++ b/src/plugins/xfer/xfer-network.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* xfer-network.c: network functions for xfer plugin */ + + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <signal.h> +#include <time.h> + +#include "../weechat-plugin.h" +#include "xfer.h" +#include "xfer-network.h" +#include "xfer-buffer.h" +#include "xfer-dcc.h" +#include "xfer-file.h" + + +/* + * xfer_network_connect: connect to another host + */ + +int +xfer_network_connect (struct t_xfer *xfer) +{ + if (xfer->type == XFER_TYPE_CHAT_SEND) + xfer->status = XFER_STATUS_WAITING; + else + xfer->status = XFER_STATUS_CONNECTING; + + if (xfer->sock < 0) + { + xfer->sock = socket (AF_INET, SOCK_STREAM, 0); + if (xfer->sock < 0) + return 0; + } + + /* for chat or file sending, listen to socket for a connection */ + if (XFER_IS_SEND(xfer->type)) + { + if (fcntl (xfer->sock, F_SETFL, O_NONBLOCK) == -1) + return 0; + if (listen (xfer->sock, 1) == -1) + return 0; + if (fcntl (xfer->sock, F_SETFL, 0) == -1) + return 0; + } + + /* for chat receiving, connect to listening host */ + if (xfer->type == XFER_TYPE_CHAT_RECV) + { + if (fcntl (xfer->sock, F_SETFL, O_NONBLOCK) == -1) + return 0; + weechat_network_connect_to (xfer->sock, xfer->address, xfer->port); + } + + /* for file receiving, connection is made in child process (blocking) */ + + return 1; +} + +/* + * xfer_network_create_pipe: create pipe for communication with child process + * return 1 if ok, 0 if error + */ + +int +xfer_network_create_pipe (struct t_xfer *xfer) +{ + int child_pipe[2]; + + if (pipe (child_pipe) < 0) + { + weechat_printf (NULL, + _("%s%s: unable to create pipe"), + weechat_prefix ("error"), "xfer"); + xfer_close (xfer, XFER_STATUS_FAILED); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + return 0; + } + + xfer->child_read = child_pipe[0]; + xfer->child_write = child_pipe[1]; + + return 1; +} + +/* + * xfer_network_write_pipe: write data into pipe + */ + +void +xfer_network_write_pipe (struct t_xfer *xfer, int status, int error) +{ + char buffer[1 + 1 + 12 + 1]; /* status + error + pos + \0 */ + + snprintf (buffer, sizeof (buffer), "%c%c%012lu", + status + '0', error + '0', xfer->pos); + write (xfer->child_write, buffer, sizeof (buffer)); +} + +/* + * xfer_network_child_read_cb: read data from child via pipe + */ + +int +xfer_network_child_read_cb (void *arg_xfer) +{ + struct t_xfer *xfer; + char bufpipe[1 + 1 + 12 + 1]; + int num_read; + char *error; + + xfer = (struct t_xfer *)arg_xfer; + + num_read = read (xfer->child_read, bufpipe, sizeof (bufpipe)); + if (num_read > 0) + { + error = NULL; + xfer->pos = strtol (bufpipe + 2, &error, 10); + xfer->last_activity = time (NULL); + xfer_file_calculate_speed (xfer, 0); + + /* read error code */ + switch (bufpipe[1] - '0') + { + /* errors for sender */ + case XFER_ERROR_READ_LOCAL: + weechat_printf (NULL, + _("%s%s: unable to read local file"), + weechat_prefix ("error"), "xfer"); + break; + case XFER_ERROR_SEND_BLOCK: + weechat_printf (NULL, + _("%s%s: unable to send block to receiver"), + weechat_prefix ("error"), "xfer"); + break; + case XFER_ERROR_READ_ACK: + weechat_printf (NULL, + _("%s%s: unable to read ACK from receiver"), + weechat_prefix ("error"), "xfer"); + break; + /* errors for receiver */ + case XFER_ERROR_CONNECT_SENDER: + weechat_printf (NULL, + _("%s%s: unable to connect to sender"), + weechat_prefix ("error"), "xfer"); + break; + case XFER_ERROR_RECV_BLOCK: + weechat_printf (NULL, + _("%s%s: unable to receive block from sender"), + weechat_prefix ("error"), "xfer"); + break; + case XFER_ERROR_WRITE_LOCAL: + weechat_printf (NULL, + _("%s%s: unable to write local file"), + weechat_prefix ("error"), "xfer"); + break; + } + + /* read new DCC status */ + switch (bufpipe[0] - '0') + { + case XFER_STATUS_ACTIVE: + if (xfer->status == XFER_STATUS_CONNECTING) + { + /* connection was successful by child, init transfert times */ + xfer->status = XFER_STATUS_ACTIVE; + xfer->start_transfer = time (NULL); + xfer->last_check_time = time (NULL); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + } + else + xfer_buffer_refresh (WEECHAT_HOTLIST_LOW); + break; + case XFER_STATUS_DONE: + xfer_close (xfer, XFER_STATUS_DONE); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + break; + case XFER_STATUS_FAILED: + xfer_close (xfer, XFER_STATUS_FAILED); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + break; + } + } + + return WEECHAT_RC_OK; +} + +/* + * xfer_network_send_file_fork: fork process for sending file + */ + +void +xfer_network_send_file_fork (struct t_xfer *xfer) +{ + pid_t pid; + + if (!xfer_network_create_pipe (xfer)) + return; + + xfer->file = open (xfer->local_filename, O_RDONLY | O_NONBLOCK, 0644); + + switch (pid = fork ()) + { + /* fork failed */ + case -1: + weechat_printf (NULL, + _("%s%s: unable to fork"), + weechat_prefix ("error"), "xfer"); + xfer_close (xfer, XFER_STATUS_FAILED); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + return; + /* child process */ + case 0: + setuid (getuid ()); + switch (xfer->protocol) + { + case XFER_NO_PROTOCOL: + _exit (EXIT_SUCCESS); + break; + case XFER_PROTOCOL_DCC: + xfer_dcc_send_file_child (xfer); + break; + case XFER_NUM_PROTOCOLS: + break; + } + _exit (EXIT_SUCCESS); + } + + /* parent process */ + xfer->child_pid = pid; + xfer->hook_fd = weechat_hook_fd (xfer->child_read, + 1, 0, 0, + xfer_network_child_read_cb, + xfer); +} + +/* + * xfer_network_recv_file_fork: fork process for receiving file + */ + +void +xfer_network_recv_file_fork (struct t_xfer *xfer) +{ + pid_t pid; + + if (!xfer_network_create_pipe (xfer)) + return; + + if (xfer->start_resume > 0) + xfer->file = open (xfer->local_filename, + O_APPEND | O_WRONLY | O_NONBLOCK); + else + xfer->file = open (xfer->local_filename, + O_CREAT | O_TRUNC | O_WRONLY | O_NONBLOCK, + 0644); + + switch (pid = fork ()) + { + /* fork failed */ + case -1: + weechat_printf (NULL, + _("%s%s: unable to fork"), + weechat_prefix ("error"), "xfer"); + xfer_close (xfer, XFER_STATUS_FAILED); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + return; + /* child process */ + case 0: + setuid (getuid ()); + switch (xfer->protocol) + { + case XFER_NO_PROTOCOL: + _exit (EXIT_SUCCESS); + break; + case XFER_PROTOCOL_DCC: + xfer_dcc_recv_file_child (xfer); + break; + case XFER_NUM_PROTOCOLS: + break; + } + _exit (EXIT_SUCCESS); + } + + /* parent process */ + xfer->child_pid = pid; + xfer->hook_fd = weechat_hook_fd (xfer->child_read, + 1, 0, 0, + xfer_network_child_read_cb, + xfer); +} + +/* + * xfer_network_connect_init: connect to sender and init file or chat + */ + +void +xfer_network_connect_init (struct t_xfer *xfer) +{ + if (!xfer_network_connect (xfer)) + { + xfer_close (xfer, XFER_STATUS_FAILED); + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + } + else + { + /* for a file: launch child process */ + if (XFER_IS_FILE(xfer->type)) + { + xfer->status = XFER_STATUS_CONNECTING; + xfer_network_recv_file_fork (xfer); + } + else + { + /* for a chat => associate with buffer */ + xfer->status = XFER_STATUS_ACTIVE; + // TODO: create buffer for xfer chat + } + } + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); +} + +/* + * xfer_network_child_kill: kill child process and close pipe + */ + +void +xfer_network_child_kill (struct t_xfer *xfer) +{ + /* kill process */ + if (xfer->child_pid > 0) + { + kill (xfer->child_pid, SIGKILL); + waitpid (xfer->child_pid, NULL, 0); + xfer->child_pid = 0; + } + + /* close pipe used with child */ + if (xfer->child_read != -1) + { + close (xfer->child_read); + xfer->child_read = -1; + } + if (xfer->child_write != -1) + { + close (xfer->child_write); + xfer->child_write = -1; + } +} diff --git a/src/plugins/xfer/xfer-network.h b/src/plugins/xfer/xfer-network.h new file mode 100644 index 000000000..a542ed542 --- /dev/null +++ b/src/plugins/xfer/xfer-network.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_XFER_NETWORK_H +#define __WEECHAT_XFER_NETWORK_H 1 + +extern int xfer_network_connect (struct t_xfer *xfer); +extern int xfer_network_create_pipe (struct t_xfer *xfer); +extern void xfer_network_write_pipe (struct t_xfer *xfer, int status, + int error); +extern void xfer_network_connect_init (struct t_xfer *xfer); +extern void xfer_network_child_kill (struct t_xfer *xfer); + +#endif /* xfer-network.h */ diff --git a/src/plugins/xfer/xfer.c b/src/plugins/xfer/xfer.c new file mode 100644 index 000000000..5df3f2370 --- /dev/null +++ b/src/plugins/xfer/xfer.c @@ -0,0 +1,1044 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* xfer.c: file transfert 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 <netinet/in.h> +#include <netdb.h> + +#include "../weechat-plugin.h" +#include "xfer.h" +#include "xfer-buffer.h" +#include "xfer-config.h" +#include "xfer-file.h" +#include "xfer-network.h" + + +WEECHAT_PLUGIN_NAME("xfer"); +WEECHAT_PLUGIN_DESCRIPTION("Xfer (file transfert and direct chat) plugin for " + "WeeChat"); +WEECHAT_PLUGIN_AUTHOR("FlashCode <flashcode@flashtux.org>"); +WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION); +WEECHAT_PLUGIN_WEECHAT_VERSION(WEECHAT_VERSION); +WEECHAT_PLUGIN_LICENSE("GPL3"); + +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_debug = 0; + + +/* + * xfer_create_directories: create directories for xfer plugin + */ + +void +xfer_create_directories () +{ + char *weechat_dir, *dir1, *dir2; + + /* create download directory */ + weechat_dir = weechat_info_get ("weechat_dir"); + if (weechat_dir) + { + dir1 = weechat_string_replace (weechat_config_string (xfer_config_file_download_path), + "~", getenv ("HOME")); + 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 (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 (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 (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 ((ptr_xfer->type == type) + && (ptr_xfer->status = status) + && (ptr_xfer->port == port)) + return ptr_xfer; + } + + /* xfer not found */ + return NULL; +} + +/* + * 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, char *signal) +{ + struct t_plugin_infolist *infolist; + struct t_plugin_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_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, "nick", + xfer->nick); + weechat_infolist_new_var_string (item, "filename", + xfer->filename); + snprintf (str_long, sizeof (str_long), "%lu", xfer->size); + weechat_infolist_new_var_string (item, "size", + 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->nick = NULL; + + new_xfer->type = 0; + new_xfer->protocol = 0; + new_xfer->status = 0; + new_xfer->buffer = 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->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; + + return new_xfer; +} + +/* + * xfer_accept: accept a xfer file or chat request + */ + +void +xfer_accept (struct t_xfer *xfer) +{ + if (XFER_IS_FILE(xfer->type) && (xfer->start_resume > 0)) + { + xfer->status = XFER_STATUS_CONNECTING; + xfer_send_signal (xfer, "xfer_accepted"); + /* + irc_server_sendf (dcc->server, + (strchr (dcc->filename, ' ')) ? + "PRIVMSG %s :\01DCC RESUME \"%s\" %d %u\01\n" : + "PRIVMSG %s :\01DCC RESUME %s %d %u\01", + dcc->nick, dcc->filename, + dcc->port, dcc->start_resume); + */ + xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); + } + else + xfer_network_connect_init (xfer); +} + +/* + * xfer_new: add a xfer to list + */ + +struct t_xfer * +xfer_new (char *plugin_id, enum t_xfer_type type, + enum t_xfer_protocol protocol, char *nick, char *filename, + unsigned long size, unsigned long address, int port, int sock, + char *local_filename) +{ + struct t_xfer *new_xfer; + + new_xfer = xfer_alloc (); + if (!new_xfer) + { + weechat_printf (NULL, + _("%s%s: not enough memory for new xfer"), + weechat_prefix ("error"), "xfer"); + return NULL; + } + + if (!xfer_buffer + && weechat_config_boolean (xfer_config_look_auto_open_buffer)) + { + xfer_buffer_open (); + } + + /* initialize new xfer */ + new_xfer->plugin_id = strdup (plugin_id); + new_xfer->type = type; + new_xfer->protocol = protocol; + new_xfer->nick = strdup (nick); + if (XFER_IS_FILE(type)) + new_xfer->filename = (filename) ? strdup (filename) : NULL; + new_xfer->size = size; + 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 server buffer */ + switch (type) + { + case XFER_TYPE_FILE_RECV: + weechat_printf (NULL, + _("%s: incoming file from %s " + "(%d.%d.%d.%d): %s, %lu bytes (protocol: %s)"), + "xfer", + nick, + 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 " + "(local filename: %s), %lu bytes (protocol: %s)"), + "xfer", + nick, + 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 " + "(%d.%d.%d.%d)"), + "xfer", + nick, + 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"), + "xfer", + nick); + 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 %u"), + "xfer", + 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_CHAT_RECV) + && (weechat_config_boolean (xfer_config_file_auto_accept_chats)) ) + || ( (type == XFER_TYPE_FILE_RECV) + && (weechat_config_boolean (xfer_config_file_auto_accept_files)) ) ) + xfer_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; + + /* close chat buffer */ + if (xfer->buffer) + { + /* TODO: close chat buffer */ + } + + /* 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->nick) + free (xfer->nick); + if (xfer->filename) + free (xfer->filename); + if (xfer->unterminated_message) + free (xfer->unterminated_message); + if (xfer->local_filename) + free (xfer->local_filename); + + free (xfer); + + xfer_list = new_xfer_list; +} + +/* + * 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)) + { + if (XFER_IS_FILE(xfer->type)) + { + weechat_printf (NULL, + _("%s: file %s %s %s: %s"), + "xfer", + xfer->filename, + (xfer->type == XFER_TYPE_FILE_SEND) ? + _("sent to") : _("received from"), + xfer->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", + xfer->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) 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_add_cb: callback for "xfer_add" signal + */ + +int +xfer_add_cb (void *data, char *signal, char *type_data, void *signal_data) +{ + struct t_plugin_infolist *infolist; + char *plugin_id, *str_type, *nick, *filename, *str_protocol; + int type, protocol; + char *weechat_dir, *dir1, *dir2, *filename2, *short_filename, *pos; + int spaces, args, port_start, port_end; + struct stat st; + int sock, port; + struct hostent *host; + struct sockaddr_in addr; + socklen_t length; + struct in_addr tmpaddr; + unsigned long local_addr, file_size; + struct t_xfer *ptr_xfer; + + /* make C compiler happy */ + (void) data; + (void) signal; + (void) type_data; + + filename2 = NULL; + short_filename = NULL; + spaces = 0; + + if (!signal_data) + { + weechat_printf (NULL, + _("%s%s: missing arguments"), + weechat_prefix ("error"), "xfer"); + return WEECHAT_RC_ERROR; + } + + infolist = (struct t_plugin_infolist *)signal_data; + + if (!weechat_infolist_next (infolist)) + { + weechat_printf (NULL, + _("%s%s: missing arguments"), + weechat_prefix ("error"), "xfer"); + return WEECHAT_RC_ERROR; + } + + plugin_id = weechat_infolist_string (infolist, "plugin_id"); + str_type = weechat_infolist_string (infolist, "type"); + nick = weechat_infolist_string (infolist, "nick"); + filename = weechat_infolist_string (infolist, "filename"); + str_protocol = weechat_infolist_string (infolist, "protocol"); + protocol = XFER_NO_PROTOCOL; + + if (!plugin_id || !str_type || !nick) + { + weechat_printf (NULL, + _("%s%s: missing arguments"), + weechat_prefix ("error"), "xfer"); + return WEECHAT_RC_ERROR; + } + + type = xfer_search_type (str_type); + if (type < 0) + { + weechat_printf (NULL, + _("%s%s: unknown xfer type \"%s\""), + weechat_prefix ("error"), "xfer", str_type); + return WEECHAT_RC_ERROR; + } + + if (XFER_IS_FILE(type) && !filename) + { + weechat_printf (NULL, + _("%s%s: filename missing for type \"%s\""), + weechat_prefix ("error"), "xfer", str_type); + return WEECHAT_RC_ERROR; + } + + if (XFER_IS_FILE(type) && !str_protocol) + { + weechat_printf (NULL, + _("%s%s: protocol missing for type \"%s\""), + weechat_prefix ("error"), "xfer", str_type); + return WEECHAT_RC_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", str_protocol); + return WEECHAT_RC_ERROR; + } + } + + filename2 = NULL; + file_size = 0; + port = 0; + + if (type == XFER_TYPE_FILE_RECV) + { + filename2 = weechat_infolist_string (infolist, "filename"); + sscanf (weechat_infolist_string (infolist, "size"), "%lu", &file_size); + port = weechat_infolist_integer (infolist, "port"); + } + + 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_replace (filename, "~", getenv ("HOME")); + else + { + dir1 = weechat_string_replace (weechat_config_string (xfer_config_file_upload_path), + "~", + getenv ("HOME")); + if (!dir1) + { + weechat_printf (NULL, + _("%s%s: not enough memory"), + weechat_prefix ("error"), "xfer"); + return WEECHAT_RC_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"); + free (dir1); + return WEECHAT_RC_ERROR; + } + filename2 = malloc (strlen (dir2) + strlen (filename) + 4); + if (!filename2) + { + weechat_printf (NULL, + _("%s%s: not enough memory"), + weechat_prefix ("error"), "xfer"); + free (dir1); + free (dir2); + return WEECHAT_RC_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", filename2); + if (filename2) + free (filename2); + return WEECHAT_RC_ERROR; + } + file_size = st.st_size; + } + + if (XFER_IS_RECV(type)) + { + sscanf (weechat_infolist_string (infolist, "address"), "%lu", &local_addr); + sscanf (weechat_infolist_string (infolist, "size"), "%lu", &file_size); + } + else + { + /* get local IP address */ + sscanf (weechat_infolist_string (infolist, "address"), "%lu", &local_addr); + + /* look up the IP address from dcc_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); + } + else + { + weechat_printf (NULL, + _("%s%s: could not find address for \"%s\", " + "falling back to local IP"), + weechat_prefix ("error"), "xfer", + 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"); + if (filename2) + free (filename2); + return WEECHAT_RC_ERROR; + } + + /* look for port */ + + memset (&addr, 0, sizeof (struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl (local_addr); + + 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"); + close (sock); + if (filename2) + free (filename2); + return WEECHAT_RC_ERROR; + } + } + + if (type == XFER_TYPE_FILE_SEND) + { + /* extract short filename (without path) */ + pos = strrchr (filename2, DIR_SEPARATOR_CHAR); + if (pos) + short_filename = strdup (pos + 1); + else + short_filename = strdup (filename2); + + /* convert spaces to underscore if asked and needed */ + pos = short_filename; + spaces = 0; + while (pos[0]) + { + if (pos[0] == ' ') + { + if (weechat_config_boolean (xfer_config_file_convert_spaces)) + pos[0] = '_'; + else + spaces = 1; + } + pos++; + } + } + + /* add xfer entry and listen to socket if type is file or chat "send" */ + if (XFER_IS_FILE(type)) + ptr_xfer = xfer_new (plugin_id, type, protocol, nick, short_filename, + file_size, local_addr, port, sock, filename2); + else + ptr_xfer = xfer_new (plugin_id, type, protocol, nick, NULL, 0, + local_addr, port, sock, NULL); + + if (!ptr_xfer) + { + weechat_printf (NULL, + _("%s%s: error creating xfer"), + weechat_prefix ("error"), "xfer"); + close (sock); + if (short_filename) + free (short_filename); + if (filename2) + free (filename2); + + return WEECHAT_RC_ERROR; + } + + /* send signal if type is file or chat "send" */ + xfer_send_signal (ptr_xfer, "xfer_send_ready"); + + if (short_filename) + free (short_filename); + if (filename2) + free (filename2); + + return WEECHAT_RC_OK; +} + +/* + * xfer_print_log: print DCC 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%x)]", ptr_xfer); + 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 (" nick. . . . . . . . : '%s'", ptr_xfer->nick); + weechat_log_printf (" filename. . . . . . : '%s'", ptr_xfer->filename); + weechat_log_printf (" size. . . . . . . . : %lu", ptr_xfer->size); + 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%x", ptr_xfer->buffer); + 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 (" 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 . . . . . . . . : %lu", ptr_xfer->pos); + weechat_log_printf (" ack . . . . . . . . : %lu", ptr_xfer->ack); + weechat_log_printf (" start_resume. . . . : %lu", ptr_xfer->start_resume); + weechat_log_printf (" last_check_time . . : %ld", ptr_xfer->last_check_time); + weechat_log_printf (" last_check_pos. . . : %lu", ptr_xfer->last_check_pos); + weechat_log_printf (" last_activity . . . : %ld", ptr_xfer->last_activity); + weechat_log_printf (" bytes_per_sec . . . : %lu", ptr_xfer->bytes_per_sec); + weechat_log_printf (" eta . . . . . . . . : %lu", ptr_xfer->eta); + weechat_log_printf (" prev_xfer . . . . . : 0x%x", ptr_xfer->prev_xfer); + weechat_log_printf (" next_xfer . . . . . : 0x%x", ptr_xfer->next_xfer); + } +} + +/* + * xfer_debug_dump_cb: callback for "debug_dump" signal + */ + +int +xfer_debug_dump_cb (void *data, char *signal, char *type_data, + void *signal_data) +{ + /* make C compiler happy */ + (void) data; + (void) signal; + (void) type_data; + (void) signal_data; + + 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) +{ + weechat_plugin = plugin; + + if (!xfer_config_init ()) + return WEECHAT_RC_ERROR; + + if (xfer_config_read () < 0) + return WEECHAT_RC_ERROR; + + xfer_create_directories (); + + weechat_hook_signal ("xfer_add", &xfer_add_cb, NULL); + weechat_hook_signal ("debug_dump", &xfer_debug_dump_cb, NULL); + + 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; + + 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", + ptr_xfer->filename, ptr_xfer->nick); + weechat_log_printf (_("%s: aborting active xfer: \"%s\" from %s"), + "xfer", + ptr_xfer->filename, ptr_xfer->nick); + } + xfer_close (ptr_xfer, XFER_STATUS_FAILED); + } + } + + xfer_config_write (); + + return WEECHAT_RC_OK; +} diff --git a/src/plugins/xfer/xfer.h b/src/plugins/xfer/xfer.h new file mode 100644 index 000000000..ac580d5a4 --- /dev/null +++ b/src/plugins/xfer/xfer.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2003-2008 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_XFER_H +#define __WEECHAT_XFER_H 1 + +#define weechat_plugin weechat_xfer_plugin + +/* xfer types */ + +enum t_xfer_type +{ + XFER_TYPE_FILE_RECV = 0, + XFER_TYPE_FILE_SEND, + XFER_TYPE_CHAT_RECV, + XFER_TYPE_CHAT_SEND, + /* number of xfer types */ + XFER_NUM_TYPES, +}; + +/* xfer protocol (for file transfert) */ + +enum t_xfer_protocol +{ + XFER_NO_PROTOCOL = 0, + XFER_PROTOCOL_DCC, + /* number of xfer protocols */ + XFER_NUM_PROTOCOLS, +}; + +/* xfer status */ + +enum t_xfer_status +{ + XFER_STATUS_WAITING = 0, /* waiting for host answer */ + XFER_STATUS_CONNECTING, /* connecting to host */ + XFER_STATUS_ACTIVE, /* sending/receiving data */ + XFER_STATUS_DONE, /* transfer done */ + XFER_STATUS_FAILED, /* transfer failed */ + XFER_STATUS_ABORTED, /* transfer aborded by user */ + /* number of xfer status */ + XFER_NUM_STATUS, +}; + +/* xfer errors */ + +enum t_xfer_error +{ + XFER_NO_ERROR = 0, /* no error to report, all ok! */ + XFER_ERROR_READ_LOCAL, /* unable to read local file */ + XFER_ERROR_SEND_BLOCK, /* unable to send block to receiver */ + XFER_ERROR_READ_ACK, /* unable to read ACK from receiver */ + XFER_ERROR_CONNECT_SENDER, /* unable to connect to sender */ + XFER_ERROR_RECV_BLOCK, /* unable to recv block from sender */ + XFER_ERROR_WRITE_LOCAL, /* unable to write to local file */ + /* number of errors */ + XFER_NUM_ERRORS, +}; + +/* xfer blocksize */ + +#define XFER_BLOCKSIZE_MIN 1024 /* min blocksize when sending file */ +#define XFER_BLOCKSIZE_MAX 102400 /* max blocksize when sending file */ + +/* separator in filenames */ + +#ifdef _WIN32 + #define DIR_SEPARATOR "\\" + #define DIR_SEPARATOR_CHAR '\\' +#else + #define DIR_SEPARATOR "/" + #define DIR_SEPARATOR_CHAR '/' +#endif + +/* macros for type/status */ + +#define XFER_IS_FILE(type) ((type == XFER_TYPE_FILE_RECV) || \ + (type == XFER_TYPE_FILE_SEND)) +#define XFER_IS_CHAT(type) ((type == XFER_TYPE_CHAT_RECV) || \ + (type == XFER_TYPE_CHAT_SEND)) +#define XFER_IS_RECV(type) ((type == XFER_TYPE_FILE_RECV) || \ + (type == XFER_TYPE_CHAT_RECV)) +#define XFER_IS_SEND(type) ((type == XFER_TYPE_FILE_SEND) || \ + (type == XFER_TYPE_CHAT_SEND)) + +#define XFER_HAS_ENDED(status) ((status == XFER_STATUS_DONE) || \ + (status == XFER_STATUS_FAILED) || \ + (status == XFER_STATUS_ABORTED)) + +struct t_xfer +{ + /* data received by xfer to initiate a transfer */ + char *plugin_id; /* plugin identifier */ + enum t_xfer_type type; /* xfer type (send/recv file) */ + enum t_xfer_protocol protocol; /* xfer protocol (for file transfer) */ + char *nick; /* remote nick */ + char *filename; /* filename */ + unsigned long size; /* file size */ + unsigned long address; /* local or remote IP address */ + int port; /* remote port */ + + /* internal data */ + enum t_xfer_status status; /* xfer status (waiting, sending,..) */ + struct t_gui_buffer *buffer; /* buffer (for chat only) */ + int fast_send; /* fast send file: does not wait ACK */ + int blocksize; /* block size for sending file */ + time_t start_time; /* time when xfer started */ + time_t start_transfer; /* time when xfer transfer started */ + int sock; /* socket for connection */ + pid_t child_pid; /* pid of child process (send/recv) */ + int child_read; /* to read into child pipe */ + int child_write; /* to write into child pipe */ + struct t_hook *hook_fd; /* hook for socket or child pipe */ + char *unterminated_message; /* beginning of a message */ + int file; /* local file (read or write) */ + char *local_filename; /* local filename (with path) */ + int filename_suffix; /* suffix (like .1) if renaming file */ + unsigned long pos; /* number of bytes received/sent */ + unsigned long ack; /* number of bytes received OK */ + unsigned long start_resume; /* start of resume (in bytes) */ + time_t last_check_time; /* last time we checked bytes snt/rcv*/ + unsigned long last_check_pos; /* bytes sent/recv at last check */ + time_t last_activity; /* time of last byte received/sent */ + unsigned long bytes_per_sec; /* bytes per second */ + unsigned long eta; /* estimated time of arrival */ + struct t_xfer *prev_xfer; /* link to previous xfer */ + struct t_xfer *next_xfer; /* link to next xfer */ +}; + +extern struct t_weechat_plugin *weechat_xfer_plugin; +extern char *xfer_type_string[]; +extern char *xfer_protocol_string[]; +extern char *xfer_status_string[]; +extern struct t_xfer *xfer_list, *last_xfer; +extern int xfer_debug; + +extern void xfer_close (struct t_xfer *xfer, enum t_xfer_status status); +extern struct t_xfer *xfer_alloc (); + +#endif /* xfer.h */ |