summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/xfer/xfer-buffer.c20
-rw-r--r--src/plugins/xfer/xfer-config.c8
-rw-r--r--src/plugins/xfer/xfer-config.h1
-rw-r--r--src/plugins/xfer/xfer-dcc.c152
-rw-r--r--src/plugins/xfer/xfer-network.c26
-rw-r--r--src/plugins/xfer/xfer.c118
-rw-r--r--src/plugins/xfer/xfer.h22
7 files changed, 333 insertions, 14 deletions
diff --git a/src/plugins/xfer/xfer-buffer.c b/src/plugins/xfer/xfer-buffer.c
index 0addbfee3..7a88c2e82 100644
--- a/src/plugins/xfer/xfer-buffer.c
+++ b/src/plugins/xfer/xfer-buffer.c
@@ -44,7 +44,7 @@ xfer_buffer_refresh (const char *hotlist)
{
struct t_xfer *ptr_xfer, *xfer_selected;
char str_color[256], suffix[32], status[64], date[128], eta[128];
- char str_ip[32];
+ char str_ip[32], str_hash[128];
char *progress_bar, *str_pos, *str_total, *str_bytes_per_sec;
int i, length, line, progress_bar_size, num_bars;
unsigned long long pos, pct_complete;
@@ -103,9 +103,22 @@ xfer_buffer_refresh (const char *hotlist)
ptr_xfer->remote_address & 0xff);
}
+ str_hash[0] = '\0';
+ if (ptr_xfer->hash_target
+ && ptr_xfer->hash_handle
+ && (ptr_xfer->hash_status != XFER_HASH_STATUS_UNKNOWN)
+ && ((ptr_xfer->status == XFER_STATUS_ACTIVE)
+ || (ptr_xfer->status == XFER_STATUS_DONE)
+ || (ptr_xfer->status == XFER_STATUS_HASHING)))
+ {
+ snprintf (str_hash, sizeof (str_hash),
+ " (%s)",
+ _(xfer_hash_status_string[ptr_xfer->hash_status]));
+ }
+
/* display first line with remote nick, filename and plugin name/id */
weechat_printf_y (xfer_buffer, (line * 2) + 2,
- "%s%s%-24s %s%s%s%s (%s.%s)%s",
+ "%s%s%-24s %s%s%s%s (%s.%s)%s%s",
weechat_color(str_color),
(line == xfer_buffer_selected_line) ?
"*** " : " ",
@@ -117,7 +130,8 @@ xfer_buffer_refresh (const char *hotlist)
suffix,
ptr_xfer->plugin_name,
ptr_xfer->plugin_id,
- str_ip);
+ str_ip,
+ str_hash);
snprintf (status, sizeof (status),
"%s", _(xfer_status_string[ptr_xfer->status]));
diff --git a/src/plugins/xfer/xfer-config.c b/src/plugins/xfer/xfer-config.c
index 2a8c2993f..f81197d21 100644
--- a/src/plugins/xfer/xfer-config.c
+++ b/src/plugins/xfer/xfer-config.c
@@ -57,6 +57,7 @@ struct t_config_option *xfer_config_network_timeout;
struct t_config_option *xfer_config_file_auto_accept_chats;
struct t_config_option *xfer_config_file_auto_accept_files;
struct t_config_option *xfer_config_file_auto_accept_nicks;
+struct t_config_option *xfer_config_file_auto_check_crc32;
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_convert_spaces;
@@ -65,7 +66,6 @@ struct t_config_option *xfer_config_file_upload_path;
struct t_config_option *xfer_config_file_use_nick_in_filename;
-
/*
* Callback for changes on an option that requires a refresh of xfer list.
*/
@@ -287,6 +287,12 @@ xfer_config_init ()
"specific server) or \"nick\" (for all servers); example: "
"\"freenode.FlashCode,andrew\""),
NULL, 0, 0, "", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+ xfer_config_file_auto_check_crc32 = weechat_config_new_option (
+ xfer_config_file, ptr_section,
+ "auto_check_crc32", "boolean",
+ N_("automatically check CRC32 file checksum if it is found in the "
+ "filename (8 hexadecimal chars)"),
+ NULL, 0, 0, "off", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
xfer_config_file_auto_rename = weechat_config_new_option (
xfer_config_file, ptr_section,
"auto_rename", "boolean",
diff --git a/src/plugins/xfer/xfer-config.h b/src/plugins/xfer/xfer-config.h
index d515d74e0..65f38538d 100644
--- a/src/plugins/xfer/xfer-config.h
+++ b/src/plugins/xfer/xfer-config.h
@@ -47,6 +47,7 @@ struct t_config_option *xfer_config_file_auto_accept_files;
struct t_config_option *xfer_config_file_auto_accept_nicks;
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_check_crc32;
extern struct t_config_option *xfer_config_file_convert_spaces;
extern struct t_config_option *xfer_config_file_download_path;
extern struct t_config_option *xfer_config_file_upload_path;
diff --git a/src/plugins/xfer/xfer-dcc.c b/src/plugins/xfer/xfer-dcc.c
index b186e1627..8060ac8b8 100644
--- a/src/plugins/xfer/xfer-dcc.c
+++ b/src/plugins/xfer/xfer-dcc.c
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <unistd.h>
+#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -31,6 +32,7 @@
#include <time.h>
#include <netdb.h>
#include <errno.h>
+#include <gcrypt.h>
#include "../weechat-plugin.h"
#include "xfer.h"
@@ -219,6 +221,79 @@ xfer_dcc_recv_file_send_ack (struct t_xfer *xfer)
}
/*
+ * Reads a resumed xfer from disk for hashing.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+xfer_dcc_resume_hash (struct t_xfer *xfer)
+{
+ char *buf;
+ unsigned long long total_read;
+ ssize_t length_buf, to_read, num_read;
+ int ret, fd;
+
+ total_read = 0;
+ ret = 1;
+ fd = 0;
+
+ length_buf = 1024 * 1024;
+ buf = malloc (length_buf);
+ if (!buf)
+ return 0;
+
+ while (fd <= 0)
+ {
+ fd = open (xfer->local_filename, O_RDONLY);
+ if (fd < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ fd = 0;
+ ret = 0;
+ break;
+ }
+ }
+
+ if (fd)
+ {
+ while (total_read < xfer->start_resume)
+ {
+ to_read = xfer->start_resume - total_read;
+ if (to_read > length_buf)
+ num_read = read (fd, buf, length_buf);
+ else
+ num_read = read (fd, buf, to_read);
+ if (num_read > 0)
+ {
+ gcry_md_write (*xfer->hash_handle, buf, num_read);
+ total_read += num_read;
+ }
+ else if (num_read < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ ret = 0;
+ break;
+ }
+ }
+
+ while (close (fd) < 0)
+ {
+ if (errno != EINTR)
+ break;
+ }
+ }
+
+ free (buf);
+
+ return ret;
+}
+
+/*
* Child process for receiving file with DCC protocol.
*/
@@ -230,6 +305,26 @@ xfer_dcc_recv_file_child (struct t_xfer *xfer)
time_t last_sent, new_time;
unsigned long long pos_last_ack;
fd_set read_fds, write_fds, except_fds;
+ ssize_t written, total_written;
+ unsigned char *bin_hash;
+ char hash[9];
+
+ /* if resuming, hash the portion of the file we have */
+ if ((xfer->start_resume > 0) && xfer->hash_handle)
+ {
+ xfer_network_write_pipe (xfer, XFER_STATUS_HASHING,
+ XFER_NO_ERROR);
+ if (!xfer_dcc_resume_hash (xfer))
+ {
+ gcry_md_close (*xfer->hash_handle);
+ free (xfer->hash_handle);
+ xfer->hash_handle = NULL;
+ xfer_network_write_pipe (xfer, XFER_STATUS_HASHING,
+ XFER_ERROR_HASH_RESUME_ERROR);
+ }
+ xfer_network_write_pipe (xfer, XFER_STATUS_CONNECTING,
+ XFER_NO_ERROR);
+ }
/* first connect to sender (blocking) */
if (!weechat_network_connect_to (xfer->proxy, xfer->sock,
@@ -289,6 +384,8 @@ xfer_dcc_recv_file_child (struct t_xfer *xfer)
}
else
{
+ total_written = 0;
+
if (num_read == 0)
{
xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
@@ -296,11 +393,30 @@ xfer_dcc_recv_file_child (struct t_xfer *xfer)
return;
}
- if (write (xfer->file, buffer, num_read) == -1)
+ /* bytes received, write to disk */
+ while (total_written < num_read)
{
- xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
- XFER_ERROR_WRITE_LOCAL);
- return;
+ written = write (xfer->file,
+ buffer + total_written,
+ num_read - total_written);
+ if (written < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
+ XFER_ERROR_WRITE_LOCAL);
+ return;
+ }
+ else
+ {
+ if (xfer->hash_handle)
+ {
+ gcry_md_write (*xfer->hash_handle,
+ buffer + total_written,
+ written);
+ }
+ total_written += written;
+ }
}
xfer->pos += (unsigned long long) num_read;
@@ -308,6 +424,34 @@ xfer_dcc_recv_file_child (struct t_xfer *xfer)
/* file received OK? */
if (xfer->pos >= xfer->size)
{
+ /* check hash and report result to pipe */
+ if (xfer->hash_handle)
+ {
+ gcry_md_final (*xfer->hash_handle);
+ bin_hash = gcry_md_read (*xfer->hash_handle, 0);
+ if (bin_hash)
+ {
+ snprintf (hash, 9, "%.2X%.2X%.2X%.2X",
+ bin_hash[0], bin_hash[1], bin_hash[2],
+ bin_hash[3]);
+ if (weechat_strcasecmp (hash,
+ xfer->hash_target) == 0)
+ {
+ xfer_network_write_pipe (xfer,
+ XFER_STATUS_HASHED,
+ XFER_NO_ERROR);
+ }
+ else
+ {
+ xfer_network_write_pipe (xfer,
+ XFER_STATUS_HASHED,
+ XFER_ERROR_HASH_MISMATCH);
+ }
+ }
+ }
+
+ fsync (xfer->file);
+
/*
* extra delay before sending ACK, otherwise the send of ACK
* may fail
diff --git a/src/plugins/xfer/xfer-network.c b/src/plugins/xfer/xfer-network.c
index 5e5acc8d8..57d163f92 100644
--- a/src/plugins/xfer/xfer-network.c
+++ b/src/plugins/xfer/xfer-network.c
@@ -151,11 +151,28 @@ xfer_network_child_read_cb (void *arg_xfer, int fd)
_("%s%s: unable to send ACK to sender"),
weechat_prefix ("error"), XFER_PLUGIN_NAME);
break;
+ case XFER_ERROR_HASH_MISMATCH:
+ weechat_printf (NULL,
+ _("%s%s: wrong CRC32 for file %s"),
+ weechat_prefix ("error"), XFER_PLUGIN_NAME,
+ xfer->filename);
+ xfer->hash_status = XFER_HASH_STATUS_MISMATCH;
+ break;
+ case XFER_ERROR_HASH_RESUME_ERROR:
+ weechat_printf (NULL,
+ _("%s%s: CRC32 error while resuming"),
+ weechat_prefix ("error"), XFER_PLUGIN_NAME);
+ xfer->hash_status = XFER_HASH_STATUS_RESUME_ERROR;
+ break;
}
/* read new DCC status */
switch (bufpipe[0] - '0')
{
+ case XFER_STATUS_CONNECTING:
+ xfer->status = XFER_STATUS_CONNECTING;
+ xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
+ break;
case XFER_STATUS_ACTIVE:
if (xfer->status == XFER_STATUS_CONNECTING)
{
@@ -176,6 +193,15 @@ xfer_network_child_read_cb (void *arg_xfer, int fd)
xfer_close (xfer, XFER_STATUS_FAILED);
xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
break;
+ case XFER_STATUS_HASHING:
+ xfer->status = XFER_STATUS_HASHING;
+ xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
+ break;
+ case XFER_STATUS_HASHED:
+ if (bufpipe[1] - '0' == XFER_NO_ERROR)
+ xfer->hash_status = XFER_HASH_STATUS_MATCH;
+ xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
+ break;
}
}
diff --git a/src/plugins/xfer/xfer.c b/src/plugins/xfer/xfer.c
index 2db2cdafd..20733edd4 100644
--- a/src/plugins/xfer/xfer.c
+++ b/src/plugins/xfer/xfer.c
@@ -30,6 +30,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
+#include <gcrypt.h>
#include "../weechat-plugin.h"
#include "xfer.h"
@@ -51,19 +52,25 @@ WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE);
struct t_weechat_plugin *weechat_xfer_plugin = NULL;
-char *xfer_type_string[] = /* strings for types */
+char *xfer_type_string[] = /* strings for types */
{ "file_recv", "file_send", "chat_recv",
"chat_send"
};
-char *xfer_protocol_string[] = /* strings for protocols */
+char *xfer_protocol_string[] = /* strings for protocols */
{ "none", "dcc"
};
-char *xfer_status_string[] = /* strings for status */
+char *xfer_status_string[] = /* strings for status */
{ N_("waiting"), N_("connecting"),
N_("active"), N_("done"), N_("failed"),
- N_("aborted")
+ N_("aborted"), N_("hashing")
+};
+
+char *xfer_hash_status_string[] = /* strings for hash status */
+{ "?",
+ N_("CRC in progress"), N_("CRC OK"),
+ N_("wrong CRC"), N_("CRC error")
};
struct t_xfer *xfer_list = NULL; /* list of files/chats */
@@ -72,7 +79,6 @@ int xfer_count = 0; /* number of xfer */
int xfer_signal_upgrade_received = 0; /* signal "upgrade" received ? */
-
void xfer_disconnect_all ();
@@ -568,6 +574,59 @@ xfer_nick_auto_accepted (const char *server, const char *nick)
}
/*
+ * Searches CRC32 in a filename.
+ *
+ * If more than one CRC32 are found, the last one is returned
+ * (with the higher index in filename).
+ *
+ * The chars before/after CRC32 must be either beginning/end of string or
+ * non-hexadecimal chars.
+ *
+ * Examples:
+ *
+ * test_filename => -1 (not found: no CRC32)
+ * test_1234abcd => 5 ("1234abcd")
+ * 1234abcd_test => 0 ("1234abcd")
+ * 1234abcd_12345678 => 9 ("12345678")
+ * 123456789abcdef => -1 (not found: missing delimiter around CRC32)
+ *
+ * Returns pointer to last CRC32 in string, NULL if no CRC32 was found.
+ */
+
+const char *
+xfer_filename_crc32 (const char *filename)
+{
+ int length;
+ const char *ptr_string, *ptr_crc32;
+
+ length = 0;
+ ptr_crc32 = NULL;
+
+ ptr_string = filename;
+ while (ptr_string && ptr_string[0])
+ {
+ if (((ptr_string[0] >= '0') && (ptr_string[0] <= '9'))
+ || ((ptr_string[0] >= 'A') && (ptr_string[0] <= 'F'))
+ || ((ptr_string[0] >= 'a') && (ptr_string[0] <= 'f')))
+ {
+ length++;
+ }
+ else
+ {
+ if (length == 8)
+ ptr_crc32 = ptr_string - 8;
+ length = 0;
+ }
+
+ ptr_string = weechat_utf8_next_char (ptr_string);
+ }
+ if (length == 8)
+ ptr_crc32 = ptr_string - 8;
+
+ return ptr_crc32;
+}
+
+/*
* Adds a xfer to list.
*
* Returns pointer to new xfer, NULL if error.
@@ -582,7 +641,7 @@ xfer_new (const char *plugin_name, const char *plugin_id,
int port, int sock, const char *local_filename)
{
struct t_xfer *new_xfer;
- const char *ptr_color;
+ const char *ptr_color, *ptr_crc32;
new_xfer = xfer_alloc ();
if (!new_xfer)
@@ -634,6 +693,36 @@ xfer_new (const char *plugin_name, const char *plugin_id,
else
xfer_file_find_filename (new_xfer);
+ new_xfer->hash_handle = NULL;
+ new_xfer->hash_target = NULL;
+ new_xfer->hash_status = XFER_HASH_STATUS_UNKNOWN;
+
+ if ((type == XFER_TYPE_FILE_RECV)
+ && weechat_config_boolean (xfer_config_file_auto_check_crc32))
+ {
+ ptr_crc32 = xfer_filename_crc32 (new_xfer->filename);
+ if (ptr_crc32)
+ {
+ new_xfer->hash_handle = malloc (sizeof (gcry_md_hd_t));
+ if (new_xfer->hash_handle)
+ {
+ if (gcry_md_open (new_xfer->hash_handle, GCRY_MD_CRC32, 0) == 0)
+ {
+ new_xfer->hash_target = weechat_strndup (ptr_crc32, 8);
+ new_xfer->hash_status = XFER_HASH_STATUS_IN_PROGRESS;
+ }
+ else
+ {
+ free (new_xfer->hash_handle);
+ new_xfer->hash_handle = NULL;
+ weechat_printf (NULL,
+ _("%s%s: hashing error"),
+ weechat_prefix ("error"), XFER_PLUGIN_NAME);
+ }
+ }
+ }
+ }
+
/* write info message on core buffer */
switch (type)
{
@@ -788,6 +877,13 @@ xfer_free (struct t_xfer *xfer)
free (xfer->unterminated_message);
if (xfer->local_filename)
free (xfer->local_filename);
+ if (xfer->hash_handle)
+ {
+ gcry_md_close (*xfer->hash_handle);
+ free (xfer->hash_handle);
+ }
+ if (xfer->hash_target)
+ free (xfer->hash_target);
free (xfer);
@@ -1450,6 +1546,12 @@ xfer_add_to_infolist (struct t_infolist *infolist, struct t_xfer *xfer)
snprintf (value, sizeof (value), "%llu", xfer->eta);
if (!weechat_infolist_new_var_string (ptr_item, "eta", value))
return 0;
+ if (!weechat_infolist_new_var_string (ptr_item, "hash_target", xfer->hash_target))
+ return 0;
+ if (!weechat_infolist_new_var_integer (ptr_item, "hash_status", xfer->hash_status))
+ return 0;
+ if (!weechat_infolist_new_var_string (ptr_item, "hash_status_string", xfer_hash_status_string[xfer->hash_status]))
+ return 0;
return 1;
}
@@ -1512,6 +1614,10 @@ xfer_print_log ()
weechat_log_printf (" last_activity . . . : %ld", ptr_xfer->last_activity);
weechat_log_printf (" bytes_per_sec . . . : %llu", ptr_xfer->bytes_per_sec);
weechat_log_printf (" eta . . . . . . . . : %llu", ptr_xfer->eta);
+ weechat_log_printf (" hash_target . . . . : '%s'", ptr_xfer->hash_target);
+ weechat_log_printf (" hash_status . . . . : %d (%s)",
+ ptr_xfer->hash_status,
+ xfer_hash_status_string[ptr_xfer->hash_status]);
weechat_log_printf (" prev_xfer . . . . . : 0x%lx", ptr_xfer->prev_xfer);
weechat_log_printf (" next_xfer . . . . . : 0x%lx", ptr_xfer->next_xfer);
}
diff --git a/src/plugins/xfer/xfer.h b/src/plugins/xfer/xfer.h
index 962d4a00e..2afbd50b3 100644
--- a/src/plugins/xfer/xfer.h
+++ b/src/plugins/xfer/xfer.h
@@ -21,6 +21,7 @@
#define __WEECHAT_XFER_H 1
#include <unistd.h>
+#include <gcrypt.h>
#define weechat_plugin weechat_xfer_plugin
#define XFER_PLUGIN_NAME "xfer"
@@ -57,6 +58,8 @@ enum t_xfer_status
XFER_STATUS_DONE, /* transfer done */
XFER_STATUS_FAILED, /* transfer failed */
XFER_STATUS_ABORTED, /* transfer aborted by user */
+ XFER_STATUS_HASHING, /* partial local file is being hashed*/
+ XFER_STATUS_HASHED, /* received file has been hashed */
/* number of xfer status */
XFER_NUM_STATUS,
};
@@ -75,10 +78,25 @@ enum t_xfer_error
XFER_ERROR_RECV_BLOCK, /* unable to recv block from sender */
XFER_ERROR_WRITE_LOCAL, /* unable to write to local file */
XFER_ERROR_SEND_ACK, /* unable to send ACK to sender */
+ XFER_ERROR_HASH_MISMATCH, /* CRC32 does not match */
+ XFER_ERROR_HASH_RESUME_ERROR, /* other error with CRC32 hash */
/* number of errors */
XFER_NUM_ERRORS,
};
+/* hash status */
+
+enum t_xfer_hash_status
+{
+ XFER_HASH_STATUS_UNKNOWN = 0, /* hashing not being performed */
+ XFER_HASH_STATUS_IN_PROGRESS, /* hash in progress */
+ XFER_HASH_STATUS_MATCH, /* hash finished and matches target */
+ XFER_HASH_STATUS_MISMATCH, /* hash finished with mismatch */
+ XFER_HASH_STATUS_RESUME_ERROR, /* hash failed while resuming */
+ /* number of hash status */
+ XFER_NUM_HASH_STATUS,
+};
+
/* xfer block size */
#define XFER_BLOCKSIZE_MIN 1024 /* min block size */
@@ -153,6 +171,9 @@ struct t_xfer
time_t last_activity; /* time of last byte received/sent */
unsigned long long bytes_per_sec; /* bytes per second */
unsigned long long eta; /* estimated time of arrival */
+ gcry_md_hd_t *hash_handle; /* handle for CRC32 hash */
+ char *hash_target; /* the CRC32 hash to check against */
+ enum t_xfer_hash_status hash_status; /* hash status */
struct t_xfer *prev_xfer; /* link to previous xfer */
struct t_xfer *next_xfer; /* link to next xfer */
};
@@ -161,6 +182,7 @@ extern struct t_weechat_plugin *weechat_xfer_plugin;
extern char *xfer_type_string[];
extern char *xfer_protocol_string[];
extern char *xfer_status_string[];
+extern char *xfer_hash_status_string[];
extern struct t_xfer *xfer_list, *last_xfer;
extern int xfer_count;