diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/xfer/xfer-buffer.c | 20 | ||||
-rw-r--r-- | src/plugins/xfer/xfer-config.c | 8 | ||||
-rw-r--r-- | src/plugins/xfer/xfer-config.h | 1 | ||||
-rw-r--r-- | src/plugins/xfer/xfer-dcc.c | 152 | ||||
-rw-r--r-- | src/plugins/xfer/xfer-network.c | 26 | ||||
-rw-r--r-- | src/plugins/xfer/xfer.c | 118 | ||||
-rw-r--r-- | src/plugins/xfer/xfer.h | 22 |
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; |