Merge branch 'file_transfer' of https://github.com/nogaems/tox-weechat into nogaems-file_transfer

This commit is contained in:
Håvard Pettersson 2018-12-22 21:14:53 +01:00
commit 93538e1fef
14 changed files with 1511 additions and 3 deletions

View File

@ -4,3 +4,4 @@ Main author:
Contributors: Contributors:
Gordon Quad <gordon@nowhere> Gordon Quad <gordon@nowhere>
Michael Raitza <spacefrogg-devel@meterriblecrew.net> Michael Raitza <spacefrogg-devel@meterriblecrew.net>
nogaems <nomad@ag.ru>

View File

@ -34,6 +34,7 @@ add_library(tox MODULE
src/twc-message-queue.c src/twc-message-queue.c
src/twc-profile.c src/twc-profile.c
src/twc-tox-callbacks.c src/twc-tox-callbacks.c
src/twc-tfer.c
src/twc-utils.c) src/twc-utils.c)
set_target_properties(tox PROPERTIES set_target_properties(tox PROPERTIES

View File

@ -13,6 +13,7 @@ Standard][3].
- Proxy support - Proxy support
- Multiple profiles - Multiple profiles
- Encrypted save files - Encrypted save files
- File transfer
## Installation ## Installation
Tox-WeeChat is tested with [WeeChat][2] 2.1 and [TokTok c-toxcore][5] 0.2.1. Tox-WeeChat is tested with [WeeChat][2] 2.1 and [TokTok c-toxcore][5] 0.2.1.

View File

@ -19,11 +19,15 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <wordexp.h>
#include <tox/tox.h> #include <tox/tox.h>
#include <weechat/weechat-plugin.h> #include <weechat/weechat-plugin.h>
#include "twc-bootstrap.h" #include "twc-bootstrap.h"
#include "twc-tfer.h"
#include "twc-chat.h" #include "twc-chat.h"
#include "twc-config.h" #include "twc-config.h"
#include "twc-friend-request.h" #include "twc-friend-request.h"
@ -127,6 +131,22 @@ enum TWC_FRIEND_MATCH
return WEECHAT_RC_OK; \ return WEECHAT_RC_OK; \
} }
/**
* Make sure a file exists.
*/
#define TWC_CHECK_FILE_EXISTS(filename) \
if(access(filename, F_OK) == -1 ) \
{ \
weechat_printf(NULL, "%sFile \"%s\" does not exist", \
weechat_prefix("error"), filename); \
return WEECHAT_RC_ERROR; \
}
#define TWC_RETURN_WITH_FILE_ERROR(filename, type) \
weechat_printf(NULL, "%s\"%s\" must be a regular file or pipe, " \
"not a %s", weechat_prefix("error"), filename, type); \
return WEECHAT_RC_ERROR;
/** /**
* Get number of friend matching string. Tries to match number, name and * Get number of friend matching string. Tries to match number, name and
* Tox ID. * Tox ID.
@ -1175,6 +1195,125 @@ twc_cmd_tox(const void *pointer, void *data, struct t_gui_buffer *buffer,
return WEECHAT_RC_ERROR; return WEECHAT_RC_ERROR;
} }
/**
* Command /send callback.
*/
int
twc_cmd_send(const void *pointer, void *data, struct t_gui_buffer *buffer,
int argc, char **argv, char **argv_eol)
{
if (argc == 1)
return WEECHAT_RC_ERROR;
struct t_twc_profile *profile = twc_profile_search_buffer(buffer);
TWC_CHECK_PROFILE(profile);
TWC_CHECK_PROFILE_LOADED(profile);
char recipient[TOX_MAX_NAME_LENGTH + 1] = {0};
char filename[FILENAME_MAX + 1] = {0};
size_t filename_arg_num;
/* /send <file> */
if (argc == 2)
{
if (profile->buffer == buffer || profile->tfer->buffer == buffer)
{
weechat_printf(profile->buffer, "%s%s", weechat_prefix("error"), "you must specify a friend");
return WEECHAT_RC_ERROR;
}
snprintf(recipient, TOX_MAX_NAME_LENGTH, "%s", weechat_buffer_get_string(buffer, "name"));
struct t_twc_chat *chat = twc_chat_search_buffer(buffer);
if (chat->group_number != -1)
{
weechat_printf(profile->buffer, "%s%s", weechat_prefix("error"), "the file transmission is "
"allowed only between friends");
return WEECHAT_RC_ERROR;
}
char *name = twc_get_name_nt(profile->tox, chat->friend_number);
sprintf(recipient, "%s", name);
filename_arg_num = 1;
free(name);
}
/* /send <number>|<name>|<Tox ID> <file> */
if (argc >= 3)
{
/* do a shell split in case a friend has spaces in his name
* and join the name */
int shell_argc;
char **shell_argv = weechat_string_split_shell(argv_eol[1], &shell_argc);
for (int i = 0; i < shell_argc -1; i++)
{
strcat(recipient, shell_argv[i]);
if (i < shell_argc - 2)
strcat(recipient, " ");
}
filename_arg_num = shell_argc;
weechat_string_free_split(shell_argv);
}
wordexp_t expanded;
wordexp(argv[filename_arg_num], &expanded, 0);
snprintf(filename, FILENAME_MAX, "%s", expanded.we_wordv[0]);
wordfree(&expanded);
TWC_CHECK_FILE_EXISTS(filename);
uint32_t friend_number = twc_match_friend(profile, recipient);
TWC_CHECK_FRIEND_NUMBER(profile, (signed) friend_number, recipient);
struct stat st;
stat(filename, &st);
switch (st.st_mode & S_IFMT)
{
case S_IFBLK:
TWC_RETURN_WITH_FILE_ERROR(filename, "block device");
case S_IFCHR:
TWC_RETURN_WITH_FILE_ERROR(filename, "character device");
case S_IFDIR:
TWC_RETURN_WITH_FILE_ERROR(filename, "directory");
case S_IFSOCK:
TWC_RETURN_WITH_FILE_ERROR(filename, "socket");
case S_IFREG:
break;
case S_IFLNK:
break;
default:
weechat_printf(NULL, "%sunknown file type", weechat_prefix("error"));
return WEECHAT_RC_ERROR;
}
char *stripped_name = twc_tfer_file_name_strip(filename, FILENAME_MAX + 1 - strlen(filename));
TOX_ERR_FILE_SEND error;
uint32_t file_number = tox_file_send(profile->tox, friend_number, TOX_FILE_KIND_DATA,
S_ISFIFO(st.st_mode) ? UINT64_MAX : (size_t)st.st_size,
NULL, (uint8_t *)stripped_name, strlen(filename), &error);
free(stripped_name);
if (error != TOX_ERR_FILE_SEND_OK)
{
weechat_printf(profile->buffer, "%ssending \"%s\" has been failed: %s",
weechat_prefix("error"), filename, twc_tox_err_file_send(error));
return WEECHAT_RC_ERROR;
}
if (!(profile->tfer->buffer))
{
twc_tfer_load(profile);
}
struct t_twc_tfer_file *file = twc_tfer_file_new(profile, recipient, filename,
friend_number, file_number,
st.st_size, TWC_TFER_FILE_TYPE_UPLOADING);
if (!file)
{
weechat_printf(profile->buffer, "%scannot open the file \"%s\"",
weechat_prefix("error"), filename);
return WEECHAT_RC_ERROR;
}
twc_tfer_file_add(profile->tfer, file);
twc_tfer_buffer_update(profile->tfer);
twc_tfer_update_status(profile->tfer, "waiting for action");
return WEECHAT_RC_OK;
}
/** /**
* Register Tox-WeeChat commands. * Register Tox-WeeChat commands.
*/ */
@ -1298,4 +1437,12 @@ twc_commands_init()
" || unload %(tox_loaded_profiles)|%*" " || unload %(tox_loaded_profiles)|%*"
" || reload %(tox_loaded_profiles)|%*", " || reload %(tox_loaded_profiles)|%*",
twc_cmd_tox, NULL, NULL); twc_cmd_tox, NULL, NULL);
weechat_hook_command("send", "send a file to a friend",
"<file>"
" || <number>|<name>|<Tox ID> <file>",
"file: path to the file\n"
"number, name, Tox ID: the friend you are sending the file to\n",
"%(filename)"
" || %(tox_friend_name)|%(tox_friend_tox_id) %(filename)",
twc_cmd_send, NULL, NULL);
} }

View File

@ -26,6 +26,7 @@
#include "twc-list.h" #include "twc-list.h"
#include "twc-profile.h" #include "twc-profile.h"
#include "twc-tfer.h"
#include "twc.h" #include "twc.h"
#include "twc-config.h" #include "twc-config.h"
@ -41,7 +42,7 @@ struct t_config_option *twc_config_short_id_size;
char *twc_profile_option_names[TWC_PROFILE_NUM_OPTIONS] = { char *twc_profile_option_names[TWC_PROFILE_NUM_OPTIONS] = {
"save_file", "autoload", "autojoin", "autojoin_delay", "save_file", "autoload", "autojoin", "autojoin_delay",
"max_friend_requests", "proxy_address", "proxy_port", "proxy_type", "max_friend_requests", "proxy_address", "proxy_port", "proxy_type",
"udp", "ipv6", "passphrase", "logging", "udp", "ipv6", "passphrase", "logging", "downloading_path",
}; };
/** /**
@ -186,7 +187,10 @@ twc_config_profile_change_callback(const void *pointer, void *data,
twc_profile_set_logging(item->profile, logging_enabled); twc_profile_set_logging(item->profile, logging_enabled);
} }
} }
break;
case TWC_PROFILE_OPTION_DOWNLOADING_PATH:
twc_tfer_update_downloading_path(profile);
break;
default: default:
break; break;
} }
@ -285,6 +289,12 @@ twc_config_init_option(struct t_twc_profile *profile,
description = "use UDP when communicating with the Tox network"; description = "use UDP when communicating with the Tox network";
default_value = "on"; default_value = "on";
break; break;
case TWC_PROFILE_OPTION_DOWNLOADING_PATH:
type = "string";
description = "path to downloaded files (\"%h\" will be replaced by "
"WeeChat home folder and \"%p\" by profile name";
default_value = "%h/tfer/%p/";
break;
default: default:
return NULL; return NULL;
} }

View File

@ -21,6 +21,7 @@
#define TOX_WEECHAT_LIST_H #define TOX_WEECHAT_LIST_H
#include <stdlib.h> #include <stdlib.h>
#include "twc-tfer.h"
struct t_twc_list struct t_twc_list
{ {
@ -42,6 +43,7 @@ struct t_twc_list_item
struct t_twc_group_chat_invite *group_chat_invite; struct t_twc_group_chat_invite *group_chat_invite;
struct t_twc_chat *chat; struct t_twc_chat *chat;
struct t_twc_queued_message *queued_message; struct t_twc_queued_message *queued_message;
struct t_twc_tfer_file *file;
}; };
struct t_twc_list_item *next_item; struct t_twc_list_item *next_item;

View File

@ -169,9 +169,11 @@ twc_profile_new(const char *name)
profile->group_chat_invites = twc_list_new(); profile->group_chat_invites = twc_list_new();
profile->message_queues = weechat_hashtable_new( profile->message_queues = weechat_hashtable_new(
32, WEECHAT_HASHTABLE_INTEGER, WEECHAT_HASHTABLE_POINTER, NULL, NULL); 32, WEECHAT_HASHTABLE_INTEGER, WEECHAT_HASHTABLE_POINTER, NULL, NULL);
profile->tfer = twc_tfer_new();
/* set up config */ /* set up config */
twc_config_init_profile(profile); twc_config_init_profile(profile);
twc_tfer_update_downloading_path(profile);
return profile; return profile;
} }
@ -456,6 +458,10 @@ twc_profile_load(struct t_twc_profile *profile)
tox_callback_conference_peer_name(profile->tox, tox_callback_conference_peer_name(profile->tox,
twc_group_peer_name_callback); twc_group_peer_name_callback);
tox_callback_conference_title(profile->tox, twc_group_title_callback); tox_callback_conference_title(profile->tox, twc_group_title_callback);
tox_callback_file_recv_control(profile->tox, twc_file_recv_control_callback);
tox_callback_file_chunk_request(profile->tox, twc_file_chunk_request_callback);
tox_callback_file_recv(profile->tox, twc_file_recv_callback);
tox_callback_file_recv_chunk(profile->tox, twc_file_recv_chunk_callback);
return TWC_RC_OK; return TWC_RC_OK;
} }
@ -567,6 +573,8 @@ twc_profile_search_buffer(struct t_gui_buffer *buffer)
{ {
if (profile_item->profile->buffer == buffer) if (profile_item->profile->buffer == buffer)
return profile_item->profile; return profile_item->profile;
if (profile_item->profile->tfer->buffer == buffer)
return profile_item->profile;
size_t chat_index; size_t chat_index;
struct t_twc_list_item *chat_item; struct t_twc_list_item *chat_item;
@ -580,6 +588,23 @@ twc_profile_search_buffer(struct t_gui_buffer *buffer)
return NULL; return NULL;
} }
/**
* Return the profile associated with a tox instance, if any.
*/
struct t_twc_profile *
twc_profile_search_tox(struct Tox *tox)
{
size_t profile_index;
struct t_twc_list_item *profile_item;
twc_list_foreach(twc_profiles, profile_index, profile_item)
{
if (profile_item->profile->tox == tox)
return profile_item->profile;
}
return NULL;
}
/** /**
* Enable or disable the WeeChat logger for all buffers for a profile. * Enable or disable the WeeChat logger for all buffers for a profile.
* *
@ -657,11 +682,18 @@ twc_profile_free(struct t_twc_profile *profile)
weechat_buffer_set_pointer(profile->buffer, "close_callback", NULL); weechat_buffer_set_pointer(profile->buffer, "close_callback", NULL);
weechat_buffer_close(profile->buffer); weechat_buffer_close(profile->buffer);
} }
/* close tfer's buffer */
if (profile->tfer->buffer)
{
weechat_buffer_set_pointer(profile->tfer->buffer, "close_callback", NULL);
weechat_buffer_close(profile->tfer->buffer);
}
/* free things */ /* free things */
twc_chat_free_list(profile->chats); twc_chat_free_list(profile->chats);
twc_friend_request_free_list(profile->friend_requests); twc_friend_request_free_list(profile->friend_requests);
twc_group_chat_invite_free_list(profile->group_chat_invites); twc_group_chat_invite_free_list(profile->group_chat_invites);
twc_tfer_free(profile->tfer);
twc_message_queue_free_profile(profile); twc_message_queue_free_profile(profile);
free(profile->name); free(profile->name);
free(profile); free(profile);

View File

@ -25,6 +25,8 @@
#include <tox/tox.h> #include <tox/tox.h>
#include <weechat/weechat-plugin.h> #include <weechat/weechat-plugin.h>
#include "twc-tfer.h"
enum t_twc_profile_option enum t_twc_profile_option
{ {
TWC_PROFILE_OPTION_SAVEFILE = 0, TWC_PROFILE_OPTION_SAVEFILE = 0,
@ -39,6 +41,7 @@ enum t_twc_profile_option
TWC_PROFILE_OPTION_IPV6, TWC_PROFILE_OPTION_IPV6,
TWC_PROFILE_OPTION_PASSPHRASE, TWC_PROFILE_OPTION_PASSPHRASE,
TWC_PROFILE_OPTION_LOGGING, TWC_PROFILE_OPTION_LOGGING,
TWC_PROFILE_OPTION_DOWNLOADING_PATH,
TWC_PROFILE_NUM_OPTIONS, TWC_PROFILE_NUM_OPTIONS,
}; };
@ -59,6 +62,8 @@ struct t_twc_profile
struct t_twc_list *friend_requests; struct t_twc_list *friend_requests;
struct t_twc_list *group_chat_invites; struct t_twc_list *group_chat_invites;
struct t_hashtable *message_queues; struct t_hashtable *message_queues;
struct t_twc_tfer *tfer;
}; };
extern struct t_twc_list *twc_profiles; extern struct t_twc_list *twc_profiles;
@ -128,6 +133,9 @@ twc_profile_search_name(const char *name);
struct t_twc_profile * struct t_twc_profile *
twc_profile_search_buffer(struct t_gui_buffer *buffer); twc_profile_search_buffer(struct t_gui_buffer *buffer);
struct t_twc_profile *
twc_profile_search_tox(struct Tox *tox);
enum t_twc_rc enum t_twc_rc
twc_profile_set_logging(struct t_twc_profile *profile, bool logging); twc_profile_set_logging(struct t_twc_profile *profile, bool logging);

843
src/twc-tfer.c Normal file
View File

@ -0,0 +1,843 @@
/*
* This file is part of Tox-WeeChat.
*
* Tox-WeeChat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox-WeeChat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox-WeeChat. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <tox/tox.h>
#include <weechat/weechat-plugin.h>
#include "twc-tfer.h"
#include "twc-profile.h"
#include "twc-list.h"
#include "twc-utils.h"
#include "twc.h"
#define PROGRESS_BAR_LEN (50)
#define TWC_TFER_UPDATE_STATUS_AND_RETURN(fmt, ...) {\
sprintf(status, fmt, ##__VA_ARGS__); \
twc_tfer_update_status(profile->tfer, status); \
weechat_string_free_split(argv); \
free(status); \
return WEECHAT_RC_OK; \
}
#define TWC_TFER_MESSAGE(present, past) {\
int result = twc_tfer_file_ ## present(profile, n); \
switch (result) \
{ \
case 1: \
TWC_TFER_UPDATE_STATUS_AND_RETURN("request number %ld has been " #past, n); \
case 0: \
TWC_TFER_UPDATE_STATUS_AND_RETURN("request number %ld cannot be " #past " because " \
"of tox internal issues", n); \
case -1: \
TWC_TFER_UPDATE_STATUS_AND_RETURN("request number %ld cannot be " #past, n); \
} }\
/**
* Create a new "tfer" object that handles a list of transmitting files and
* a buffer for managing them.
*/
struct t_twc_tfer *
twc_tfer_new()
{
struct t_twc_tfer *tfer = malloc(sizeof(struct t_twc_tfer));
tfer->files = twc_list_new();
tfer->buffer = NULL;
return tfer;
}
/**
* Load "tfer" buffer and make it ready for usage.
*/
enum t_twc_rc
twc_tfer_load(struct t_twc_profile *profile)
{
/* create "tfer" buffer */
struct t_gui_buffer *buffer;
char *name = malloc(sizeof(profile->name) + 5);
sprintf(name, "tfer/%s", profile->name);
profile->tfer->buffer = buffer = weechat_buffer_new(name,
twc_tfer_buffer_input_callback,
(void *)profile, NULL,
twc_tfer_buffer_close_callback,
(void *)profile, NULL);
free(name);
if (!buffer)
return TWC_RC_ERROR;
/* set all parameters of the buffer*/
weechat_buffer_set(buffer, "type", "free");
weechat_buffer_set(buffer, "notify", "1");
weechat_buffer_set(buffer, "day_change", "0");
weechat_buffer_set(buffer, "clear", "0");
weechat_buffer_set(buffer, "nicklist", "0");
weechat_buffer_set(buffer, "title", "File transmission");
twc_tfer_print_legend(profile->tfer);
return TWC_RC_OK;
}
/**
* Display status line and available commands.
*/
int
twc_tfer_print_legend(struct t_twc_tfer *tfer)
{
char *text[TWC_TFER_LEGEND_LINES] = {
"status: OK", /* This line is reserved for the status */
"r: refresh | a <n>: accept | d <n>: decline",
"p <n>: pause | c <n>: continue | b <n>: abort",
"files:"
};
int i;
for (i = 0; i < TWC_TFER_LEGEND_LINES; i++)
{
weechat_printf_y(tfer->buffer, i, "%s", text[i]);
}
return WEECHAT_RC_OK;
}
/**
* Expand %h and %p in path.
* Returned string must be freed.
*/
char *
twc_tfer_expanded_path(struct t_twc_profile *profile, const char *base_path)
{
const char *weechat_dir = weechat_info_get("weechat_dir", NULL);
char *home_expanded = weechat_string_replace(base_path, "%h", weechat_dir);
char *full_path =
weechat_string_replace(home_expanded, "%p", profile->name);
free(home_expanded);
return full_path;
}
/**
* Set profile-associated path for downloads.
* If it is impossible to create a directory with the path that
* has been set in tox.profile.<name>.downloading_path then default
* value will be used.
*/
void
twc_tfer_update_downloading_path(struct t_twc_profile *profile)
{
const char *base_path = TWC_PROFILE_OPTION_STRING(profile,
TWC_PROFILE_OPTION_DOWNLOADING_PATH);
char *full_path = twc_tfer_expanded_path(profile, base_path);
if (!weechat_mkdir_parents(full_path, 0755))
{
char *bad_path = full_path;
base_path = weechat_config_string(twc_config_profile_default[TWC_PROFILE_OPTION_DOWNLOADING_PATH]);
full_path = twc_tfer_expanded_path(profile, base_path);
weechat_printf(profile->buffer, "cannot create directory \"%s\","
"using default value: \"%s\"", bad_path, full_path);
free(bad_path);
weechat_mkdir_parents(full_path, 0755);
}
free(profile->tfer->downloading_path);
profile->tfer->downloading_path = full_path;
}
/**
* Check if there's an access to the file.
*/
bool
twc_tfer_file_check(const char *filename)
{
return access(filename, F_OK) == -1 ? false : true;
}
/**
* Add "*(<number>).*" to the filename.
* Returns a pointer to allocated string and must be freed after use.
*/
char *
twc_tfer_file_unique_name(const char* original)
{
char *name = malloc(sizeof(char) * (FILENAME_MAX + 1));
name[FILENAME_MAX] = '\0';
/* in case if someone sent way too long filename */
strncpy(name, original, FILENAME_MAX);
if (!twc_tfer_file_check(name))
return name;
/* a file with the given name is already exist */
int i;
char *extension;
char *dot;
if ((dot = strrchr(original, '.')))
{
name[dot - original] = '\0';
extension = dot;
}
else
extension = "";
char body[strlen(name)+1];
strcpy(body, name);
/* check if there is already a postfix number in the end of the file
* surrounded by parenthesis */
char *left, *right;
long number = 1; /* number postfix */
if ((left = strrchr(body, '(')) && (right = strrchr(body, ')')))
{
if (!(number = strtol(left + sizeof(char), NULL, 0)) &&
*(right - sizeof(char)) != '0')
{
number = 1;
}
else
{
*left = '\0';
}
}
/* trying names until success */
i = number;
do
{
snprintf(name, FILENAME_MAX, "%s(%i)%s", body, i, extension);
i++;
}
while (twc_tfer_file_check(name));
return name;
}
/**
* Delete everything before "/" (including "/") from the filename.
* Returns a pointer to allocated string and must be freed after use.
*/
char *
twc_tfer_file_name_strip(const char *original, size_t size)
{
char *name = malloc(sizeof(char) * size);
char *slash, *offset = (char *)original;
if ((slash = strrchr(original, '/')))
offset = slash + sizeof(char);
if (strlen(offset))
{
sprintf(name, "%s", offset);
return name;
}
else
return NULL;
}
/**
* Create a new file.
*/
struct t_twc_tfer_file *
twc_tfer_file_new(struct t_twc_profile *profile,
const char *nickname, const char *filename,
uint32_t friend_number, uint32_t file_number,
uint64_t size, enum t_twc_tfer_file_type filetype)
{
struct t_twc_tfer_file *file = malloc(sizeof(struct t_twc_tfer_file));
file->status = TWC_TFER_FILE_STATUS_REQUEST;
file->type = filetype;
file->position = 0;
file->timestamp = 0;
file->cached_speed = 0;
file->after_last_cache = 0;
file->nickname = strdup(nickname);
file->friend_number = friend_number;
file->file_number = file_number;
file->size = size;
if (filetype == TWC_TFER_FILE_TYPE_DOWNLOADING)
{
char *full_path = malloc(sizeof(char) * (FILENAME_MAX + 1));
sprintf(full_path, "%s", profile->tfer->downloading_path);
char *final_name = twc_tfer_file_name_strip(filename,
FILENAME_MAX + 1 - strlen(full_path));
if (!final_name)
return NULL;
char *slash = strrchr(full_path, '/');
if (*(slash + sizeof(char)) != '\0')
strcat(full_path, "/");
strcat(full_path, final_name);
char *final_path = twc_tfer_file_unique_name(full_path);
file->filename = strdup(strrchr(final_path, '/') + sizeof(char));
file->full_path = final_path;
file->fp = fopen(final_path, "w");
free(final_name);
free(full_path);
}
else
{
file->filename = twc_tfer_file_name_strip(filename,
FILENAME_MAX + 1 - strlen(filename));
file->full_path = NULL;
file->fp = fopen(filename, "r");
}
if (!(file->fp))
return NULL;
return file;
}
/**
* Add file to the buffer
*/
void
twc_tfer_file_add(struct t_twc_tfer *tfer, struct t_twc_tfer_file *file)
{
twc_list_item_new_data_add(tfer->files, file);
}
/**
* Get file type: "<=" (downloading) or "=>" (uploading).
*/
const char *
twc_tfer_file_get_type_str(struct t_twc_tfer_file *file)
{
return file->type == TWC_TFER_FILE_TYPE_DOWNLOADING ? "<=" : "=>";
}
/**
* Get file status string (excluding TWC_TFER_FILE_STATUS_IN_PROGRESS).
*/
const char *
twc_tfer_file_get_status_str(struct t_twc_tfer_file *file)
{
char *statuses[] = {
"[request]",
"",
"[paused]",
"[done]",
"[declined]",
"[aborted]"
};
return statuses[file->status];
}
/**
* Get cut file size.
*/
float
twc_tfer_cut_size(size_t size)
{
float ret = size;
int i = 0;
while((ret>1024) && (i < TWC_MAX_SIZE_SUFFIX))
{
ret /= 1024.0;
i++;
}
return ret;
}
/**
* Get file size suffix.
*/
const char *
twc_tfer_size_suffix(uint64_t size)
{
char *suffixes[] = {"", "K", "M", "G", "T"};
uint64_t ret = size;
int i = 0;
while((ret > 1024) && (i < TWC_MAX_SIZE_SUFFIX))
{
ret /= 1024.0;
i++;
}
return suffixes[i];
}
/**
* Get cut speed value.
*/
float
twc_tfer_cut_speed(float speed)
{
float ret = speed;
int i = 0;
while((ret>1024) && (i < TWC_MAX_SPEED_SUFFIX))
{
ret /= 1024.0;
i++;
}
return ret;
}
/**
* Get speed suffix.
*/
const char *
twc_tfer_speed_suffix(float speed)
{
char *suffixes[] = {"bytes/s", "KB/s", "MB/s", "GB/s", "TB/s"};
uint64_t ret = speed;
int i = 0;
while((ret > 1024) && (i < TWC_MAX_SPEED_SUFFIX))
{
ret /= 1024.0;
i++;
}
return suffixes[i];
}
/**
* Get current time with nanoseconds since the Epoch.
*/
double
twc_tfer_get_time()
{
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
return (double)tp.tv_sec + (double)(tp.tv_nsec/1E9);
}
/**
* Get speed of transmission.
*/
float
twc_tfer_get_speed(struct t_twc_tfer_file *file)
{
if (file->timestamp == 0)
return 0;
double diff = twc_tfer_get_time() - file->timestamp;
if (diff < 1)
return file->cached_speed;
float result = (float)(file->after_last_cache) / (diff * diff);
file->cached_speed = result;
return result;
}
/**
* Update buffer strings for a certain file.
*/
void
twc_tfer_file_update(struct t_twc_tfer *tfer, struct t_twc_tfer_file *file)
{
size_t index = twc_tfer_file_get_index(tfer, file);
size_t line = index * 2 + TWC_TFER_LEGEND_LINES;
const char *type = twc_tfer_file_get_type_str(file);
size_t indent = 0;
size_t remainder = index;
do
{
remainder = remainder / 10;
indent++;
}
while (remainder > 0);
indent += 5; /* length of ") => " */
char placeholder[indent + 1];
memset(placeholder, ' ', indent);
placeholder[indent] = '\0';
const char *status = twc_tfer_file_get_status_str(file);
if (file->size == UINT64_MAX)
{
weechat_printf_y(tfer->buffer, line, "%i) %s %s: %s [STREAM]",
index, type, file->nickname, file->filename);
}
else
{
float display_size = twc_tfer_cut_size(file->size);
const char *size_suffix = twc_tfer_size_suffix(file->size);
weechat_printf_y(tfer->buffer, line, "%i) %s %s: %s %i (%.2f%s)",
index,
type,
file->nickname,
file->filename,
file->size,
display_size,
size_suffix);
}
if (file->status == TWC_TFER_FILE_STATUS_IN_PROGRESS)
{
float speed = twc_tfer_get_speed(file);
float display_speed = twc_tfer_cut_speed(speed);
const char *speed_suffix = twc_tfer_speed_suffix(speed);
if (file->size == UINT64_MAX)
{
weechat_printf_y(tfer->buffer, line + 1, "%s%.2f%s",
placeholder, display_speed, speed_suffix);
return;
}
double ratio = (double)(file->position)/(double)(file->size);
int percents = (int)(ratio * 100);
char progress_bar[PROGRESS_BAR_LEN+1];
memset(progress_bar, ' ', PROGRESS_BAR_LEN);
int i;
for (i = 0; i < PROGRESS_BAR_LEN * ratio; i++)
progress_bar[i] = '=';
if (i < PROGRESS_BAR_LEN)
progress_bar[i] = '>';
progress_bar[PROGRESS_BAR_LEN] = '\0';
float display_pos = twc_tfer_cut_size(file->position);
const char *pos_suffix = twc_tfer_size_suffix(file->position);
weechat_printf_y(tfer->buffer, line + 1, "%s%i%% [%s] %.2f%s %.2f%s",
placeholder,
percents,
progress_bar,
display_pos,
pos_suffix,
display_speed,
speed_suffix);
}
else
weechat_printf_y(tfer->buffer, line + 1, "%s%s",
placeholder,status);
}
/**
* Allocate and return "uint8_t data[length]" chunk of data starting from "position".
*/
uint8_t *
twc_tfer_file_get_chunk(struct t_twc_tfer_file *file, uint64_t position, size_t length)
{
fseek(file->fp, position, SEEK_SET);
uint8_t *data = malloc(sizeof(uint8_t) * length);
size_t read = fread(data, sizeof(uint8_t), length, file->fp);
while ((read < length) && !feof(file->fp))
{
read += fread(data + read * sizeof(uint8_t), sizeof(uint8_t), length - read, file->fp);
}
if (read != length)
return NULL;
return data;
}
/**
* Write a chunk to the file.
*/
bool
twc_tfer_file_write_chunk(struct t_twc_tfer_file *file, const uint8_t *data, uint64_t position, size_t length)
{
fseek(file->fp, position, SEEK_SET);
size_t wrote = fwrite(data, sizeof(uint8_t), length, file->fp);
while (wrote < length)
{
wrote += fwrite(data + wrote * sizeof(uint8_t), sizeof(uint8_t), length - wrote, file->fp);
}
if (wrote != length)
return false;
return true;
}
/**
* Return file by its "uint32_t file_number".
*/
struct t_twc_tfer_file *
twc_tfer_file_get_by_number(struct t_twc_tfer *tfer, uint32_t file_number)
{
size_t index;
struct t_twc_list_item *item;
twc_list_foreach(tfer->files, index, item)
{
if (item->file->file_number == file_number)
return item->file;
}
return NULL;
}
/**
* Return file index.
*/
size_t
twc_tfer_file_get_index(struct t_twc_tfer *tfer, struct t_twc_tfer_file *file)
{
size_t index;
struct t_twc_list_item *item;
twc_list_foreach(tfer->files, index, item)
{
if (item->file == file)
return index;
}
return SIZE_MAX;
}
/**
* Update status line of the "tfer" buffer.
*/
int
twc_tfer_update_status(struct t_twc_tfer *tfer, const char *status)
{
weechat_printf_y(tfer->buffer, 0, "status: %s", status);
weechat_buffer_set(tfer->buffer, "hotlist", WEECHAT_HOTLIST_HIGHLIGHT);
return WEECHAT_RC_OK;
}
/**
* Update "tfer" buffer
*/
void
twc_tfer_buffer_update(struct t_twc_tfer *tfer)
{
size_t index;
struct t_twc_list_item *item;
twc_list_foreach(tfer->files, index, item)
{
twc_tfer_file_update(tfer, item->file);
}
}
/**
* Refresh the entire buffer, i.e. delete files with status of:
* [denied], [aborted], [done].
*/
void
twc_tfer_buffer_refresh(struct t_twc_tfer *tfer)
{
size_t index;
struct t_twc_list_item *item;
twc_list_foreach(tfer->files, index, item)
{
enum t_twc_tfer_file_status status = item->file->status;
if (status == TWC_TFER_FILE_STATUS_DECLINED ||
status == TWC_TFER_FILE_STATUS_ABORTED ||
status == TWC_TFER_FILE_STATUS_DONE)
{
struct t_twc_tfer_file *file = twc_list_remove(item);
twc_tfer_file_free(file);
}
}
weechat_buffer_clear(tfer->buffer);
twc_tfer_print_legend(tfer);
twc_tfer_buffer_update(tfer);
}
/**
* Send TOX_FILE_CONTROL command to a client.
* "сheck" is a file status that a file should be in before sending a control command.
* "send" is a control comand you are going to send.
* "set" is a file status that will be set after successful sending a control command.
*/
int
twc_tfer_file_send_control(struct t_twc_profile *profile, size_t index,
enum t_twc_tfer_file_status check,
enum TOX_FILE_CONTROL send,
enum t_twc_tfer_file_status set)
{
struct t_twc_tfer_file *file;
struct t_twc_list_item *item = twc_list_get(profile->tfer->files, index);
file = item->file;
if (file->status != check)
return -1;
if (file->type == TWC_TFER_FILE_TYPE_UPLOADING &&
send == TOX_FILE_CONTROL_RESUME)
return -1;
enum TOX_ERR_FILE_CONTROL control_error;
tox_file_control(profile->tox, file->friend_number, file->file_number, send,
&control_error);
if (control_error)
{
weechat_printf(profile->buffer, "%scannot send control command for \"%s\" file: %s",
weechat_prefix("error"), file->filename, twc_tox_err_file_control(control_error));
return 0;
}
else
{
if (send == TOX_FILE_CONTROL_CANCEL)
{
fclose(file->fp);
if (file->type == TWC_TFER_FILE_TYPE_DOWNLOADING && file->size != UINT64_MAX)
remove(file->full_path);
}
file->status = set;
twc_tfer_file_update(profile->tfer, file);
return 1;
}
}
/**
* Accept a file with number <index> in the list.
* Returns 1 if successful, 0 when there's an issue with tox calls
* and -1 if the request is already accepted or declined.
*/
int
twc_tfer_file_accept(struct t_twc_profile *profile, size_t index)
{
return twc_tfer_file_send_control(profile, index,
TWC_TFER_FILE_STATUS_REQUEST,
TOX_FILE_CONTROL_RESUME,
TWC_TFER_FILE_STATUS_IN_PROGRESS);
}
/**
* Decline a file with number <index> in the list.
* Returns 1 if successful, 0 when there's an issue with tox calls
* and -1 if the request is already accepted or declined.
*/
int
twc_tfer_file_decline(struct t_twc_profile *profile, size_t index)
{
return twc_tfer_file_send_control(profile, index,
TWC_TFER_FILE_STATUS_REQUEST,
TOX_FILE_CONTROL_CANCEL,
TWC_TFER_FILE_STATUS_DECLINED);
}
/**
* Pause transmission of the file with number <index> in the list.
* Returns 1 if successful, 0 when there's an issue with tox calls
* and -1 if transmission is already paused.
*/
int
twc_tfer_file_pause(struct t_twc_profile *profile, size_t index)
{
return twc_tfer_file_send_control(profile, index,
TWC_TFER_FILE_STATUS_IN_PROGRESS,
TOX_FILE_CONTROL_PAUSE,
TWC_TFER_FILE_STATUS_PAUSED);
}
/**
* Continue transmission of the file with number <index> in the list.
* Returns 1 if successful, 0 when there's an issue with tox calls
* and -1 if transmission is already going on.
*/
int
twc_tfer_file_continue(struct t_twc_profile *profile, size_t index)
{
return twc_tfer_file_send_control(profile, index,
TWC_TFER_FILE_STATUS_PAUSED,
TOX_FILE_CONTROL_RESUME,
TWC_TFER_FILE_STATUS_IN_PROGRESS);
}
/**
* Abort transmission of the file with number <index> in the list.
* Returns 1 if successful, 0 when there's an issue with tox calls
* and -1 if transmission is already aborted.
*/
int
twc_tfer_file_abort(struct t_twc_profile *profile, size_t index)
{
return twc_tfer_file_send_control(profile, index,
TWC_TFER_FILE_STATUS_IN_PROGRESS,
TOX_FILE_CONTROL_CANCEL,
TWC_TFER_FILE_STATUS_ABORTED);
}
/**
* Called when input text is entered on buffer.
*/
int
twc_tfer_buffer_input_callback(const void *pointer, void *data,
struct t_gui_buffer *buffer,
const char *input_data)
{
struct t_twc_profile *profile;
profile = (struct t_twc_profile *)pointer;
int argc;
char **argv = weechat_string_split_shell(input_data, &argc);
char *status = malloc(sizeof(char) * weechat_window_get_integer(weechat_current_window(), "win_width") + 1);
/* refresh file list, i.e delete files that have been marked as "denied", "aborted" and "done" */
if (weechat_strcasecmp(argv[0], "r") == 0)
{
if (argc == 1)
{
twc_tfer_buffer_refresh(profile->tfer);
TWC_TFER_UPDATE_STATUS_AND_RETURN("refreshed");
}
else
{
TWC_TFER_UPDATE_STATUS_AND_RETURN("this command doesn't accept any arguments");
}
}
if (strstr("adpcbADPCB", argv[0]) && argc < 2)
TWC_TFER_UPDATE_STATUS_AND_RETURN("too few arguments");
if (argc == 2)
{
size_t n = (size_t)strtol(argv[1], NULL, 0);
if ((n == 0 && strcmp(argv[1], "0") != 0) || n > (profile->tfer->files->count - 1))
{
TWC_TFER_UPDATE_STATUS_AND_RETURN("<n> must be existing number of file");
}
/* accept */
if (weechat_strcasecmp(argv[0], "a") == 0)
{
TWC_TFER_MESSAGE(accept, accepted);
}
/* decline */
if (weechat_strcasecmp(argv[0], "d") == 0)
{
TWC_TFER_MESSAGE(decline, declined);
}
/* pause */
if (weechat_strcasecmp(argv[0], "p") == 0)
{
TWC_TFER_MESSAGE(pause, paused);
}
/* continue */
if (weechat_strcasecmp(argv[0], "c") == 0)
{
TWC_TFER_MESSAGE(continue, continued);
}
/* abort */
if (weechat_strcasecmp(argv[0], "b") == 0)
{
TWC_TFER_MESSAGE(abort, aborted);
}
}
if (argc > 2)
{
TWC_TFER_UPDATE_STATUS_AND_RETURN("too many arguments");
}
TWC_TFER_UPDATE_STATUS_AND_RETURN("unknown command: %s", argv[0]);
}
/**
* Called when buffer is closed.
*/
int
twc_tfer_buffer_close_callback(const void *pointer, void *data,
struct t_gui_buffer *buffer)
{
struct t_twc_profile *profile = (struct t_twc_profile *)pointer;
profile->tfer->buffer = NULL;
return WEECHAT_RC_OK;
}
void
twc_tfer_file_free(struct t_twc_tfer_file *file)
{
free(file->filename);
free(file->nickname);
if (file->full_path)
free(file->full_path);
free(file);
}
void
twc_tfer_free(struct t_twc_tfer *tfer)
{
struct t_twc_tfer_file *file;
while ((file = twc_list_pop(tfer->files)))
{
twc_tfer_file_free(file);
}
free(tfer->files);
free(tfer->downloading_path);
free(tfer);
}

159
src/twc-tfer.h Normal file
View File

@ -0,0 +1,159 @@
/*
* This file is part of Tox-WeeChat.
*
* Tox-WeeChat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox-WeeChat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox-WeeChat. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TOX_WEECHAT_TFER_H
#define TOX_WEECHAT_TFER_H
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <tox/tox.h>
#include <weechat/weechat-plugin.h>
#include "twc-list.h"
#include "twc-profile.h"
#define TWC_TFER_LEGEND_LINES (4)
#define TWC_TFER_FILE_STATUS_MAX_LENGTH (256)
#define TWC_MAX_CHUNK_LENGTH (1371)
#define TWC_MAX_SIZE_SUFFIX (5)
#define TWC_MAX_SPEED_SUFFIX (5)
enum t_twc_tfer_file_status
{
TWC_TFER_FILE_STATUS_REQUEST,
TWC_TFER_FILE_STATUS_IN_PROGRESS,
TWC_TFER_FILE_STATUS_PAUSED,
TWC_TFER_FILE_STATUS_DONE,
TWC_TFER_FILE_STATUS_DECLINED,
TWC_TFER_FILE_STATUS_ABORTED,
};
enum t_twc_tfer_file_type
{
TWC_TFER_FILE_TYPE_DOWNLOADING,
TWC_TFER_FILE_TYPE_UPLOADING,
};
struct t_twc_tfer_file
{
enum t_twc_tfer_file_status status;
enum t_twc_tfer_file_type type;
uint64_t position; /* already transmitted (in bytes) */
uint64_t size;
uint32_t friend_number;
uint32_t file_number;
FILE *fp;
char *filename;
char *full_path;
char *nickname;
double timestamp;
float cached_speed;
size_t after_last_cache;
};
struct t_twc_tfer
{
struct t_twc_list *files;
struct t_gui_buffer *buffer;
char *downloading_path;
};
int
twc_tfer_buffer_input_callback(const void *pointer, void *data,
struct t_gui_buffer *weechat_buffer,
const char *input_data);
int
twc_tfer_buffer_close_callback(const void *pointer, void *data,
struct t_gui_buffer *weechat_buffer);
struct t_twc_tfer *
twc_tfer_new();
enum t_twc_rc
twc_tfer_load(struct t_twc_profile *profile);
bool
twc_tfer_has_buffer(struct t_twc_profile *profile);
int
twc_tfer_print_legend(struct t_twc_tfer *tfer);
double
twc_tfer_get_time();
void
twc_tfer_update_downloading_path(struct t_twc_profile *profile);
char *
twc_tfer_file_name_strip(const char *original, size_t size);
struct t_twc_tfer_file *
twc_tfer_file_new(struct t_twc_profile *profile,
const char *nickname, const char *filename,
uint32_t friend_number, uint32_t file_number,
uint64_t size, enum t_twc_tfer_file_type filetype);
void
twc_tfer_file_add(struct t_twc_tfer *tfer, struct t_twc_tfer_file *file);
uint8_t *
twc_tfer_file_get_chunk(struct t_twc_tfer_file *file, uint64_t position, size_t length);
bool
twc_tfer_file_write_chunk(struct t_twc_tfer_file *file, const uint8_t *data, uint64_t position, size_t length);
struct t_twc_tfer_file *
twc_tfer_file_get_by_number(struct t_twc_tfer *tfer, uint32_t file_number);
size_t
twc_tfer_file_get_index(struct t_twc_tfer *tfer, struct t_twc_tfer_file *file);
void
twc_tfer_file_update(struct t_twc_tfer *tfer, struct t_twc_tfer_file *file);
int
twc_tfer_file_accept(struct t_twc_profile *profile, size_t index);
int
twc_tfer_file_decline(struct t_twc_profile *profile, size_t index);
int
twc_tfer_file_pause(struct t_twc_profile *profile, size_t index);
int
twc_tfer_file_continue(struct t_twc_profile *profile, size_t index);
int
twc_tfer_file_abort(struct t_twc_profile *profile, size_t index);
int
twc_tfer_update_status(struct t_twc_tfer *tfer, const char *status);
void
twc_tfer_buffer_update(struct t_twc_tfer *tfer);
void
twc_tfer_file_err_send_message(char *message, enum TOX_ERR_FILE_SEND error);
void
twc_tfer_file_free(struct t_twc_tfer_file *file);
void
twc_tfer_free(struct t_twc_tfer *tfer);
#endif /* TOX_WEECHAT_TFER_H */

View File

@ -33,10 +33,15 @@
#include "twc-message-queue.h" #include "twc-message-queue.h"
#include "twc-profile.h" #include "twc-profile.h"
#include "twc-utils.h" #include "twc-utils.h"
#include "twc-tfer.h"
#include "twc.h" #include "twc.h"
#include "twc-tox-callbacks.h" #include "twc-tox-callbacks.h"
#define TWC_TFER_FILE_UPDATE_STATUS(st) \
file->status = st; \
twc_tfer_file_update(profile->tfer, file);
int int
twc_do_timer_cb(const void *pointer, void *data, int remaining_calls) twc_do_timer_cb(const void *pointer, void *data, int remaining_calls)
{ {
@ -228,6 +233,22 @@ twc_name_change_callback(Tox *tox, uint32_t friend_number, const uint8_t *name,
weechat_printf(profile->buffer, "%s%s is now known as %s", weechat_printf(profile->buffer, "%s%s is now known as %s",
weechat_prefix("network"), old_name, new_name); weechat_prefix("network"), old_name, new_name);
if (profile->tfer->buffer)
{
size_t index;
struct t_twc_list_item *item;
struct t_twc_tfer_file *file;
twc_list_foreach(profile->tfer->files, index, item)
{
file = item->file;
if (file->friend_number == friend_number)
{
free(file->nickname);
file->nickname = strdup(new_name);
twc_tfer_file_update(profile->tfer, item->file);
}
}
}
} }
free(old_name); free(old_name);
@ -564,6 +585,180 @@ twc_group_title_callback(Tox *tox, uint32_t group_number, uint32_t peer_number,
free(topic); free(topic);
} }
void
twc_file_recv_control_callback(Tox *tox, uint32_t friend_number, uint32_t file_number,
TOX_FILE_CONTROL control, void *user_data)
{
struct t_twc_profile *profile = twc_profile_search_tox(tox);
struct t_twc_tfer_file *file = twc_tfer_file_get_by_number(profile->tfer, file_number);
if (!file)
{
weechat_printf(profile->tfer->buffer, "%sthere is no file with number %i in queue",
weechat_prefix("error"), file_number);
return;
}
switch (control)
{
case TOX_FILE_CONTROL_RESUME:
TWC_TFER_FILE_UPDATE_STATUS(TWC_TFER_FILE_STATUS_IN_PROGRESS);
break;
case TOX_FILE_CONTROL_PAUSE:
if (file->position !=0)
TWC_TFER_FILE_UPDATE_STATUS(TWC_TFER_FILE_STATUS_PAUSED);
break;
case TOX_FILE_CONTROL_CANCEL:
fclose(file->fp);
if (file->type == TWC_TFER_FILE_TYPE_DOWNLOADING && file->size != UINT64_MAX)
remove(file->full_path);
if (file->position != 0)
{
TWC_TFER_FILE_UPDATE_STATUS(TWC_TFER_FILE_STATUS_ABORTED);
}
else
{
TWC_TFER_FILE_UPDATE_STATUS(TWC_TFER_FILE_STATUS_DECLINED);
}
break;
}
}
void
twc_file_chunk_request_callback(Tox *tox, uint32_t friend_number, uint32_t file_number,
uint64_t position, size_t length, void *user_data)
{
struct t_twc_profile *profile = twc_profile_search_tox(tox);
struct t_twc_tfer_file *file = twc_tfer_file_get_by_number(profile->tfer, file_number);
/* the file is missing */
if (!file)
{
weechat_printf(profile->tfer->buffer, "%sthere is no file with number %i in queue",
weechat_prefix("error"), file_number);
return;
}
/* 0-length chunk requested that means the file transmission is completed */
if (length == 0)
{
TWC_TFER_FILE_UPDATE_STATUS(TWC_TFER_FILE_STATUS_DONE);
/* This friend_number will be re-used and re-assigned for another file,
* to prevent collisions in twc_tfer_file_get_by_number calls let's
* set it to a value that won't be used by toxcore.
*/
file->file_number = UINT32_MAX;
fclose(file->fp);
return;
}
uint8_t *data = twc_tfer_file_get_chunk(file, position, length);
if (!data)
{
weechat_printf(profile->buffer, "%serror while reading the file %s",
weechat_prefix("error"), file->filename);
return;
}
enum TOX_ERR_FILE_SEND_CHUNK error;
tox_file_send_chunk(profile->tox, friend_number, file_number, position, data, length, &error);
if (error)
weechat_printf(profile->buffer, "%s%s: chunk sending error: %s",
weechat_prefix("error"), file->filename, twc_tox_err_file_send_chunk(error));
else
{
file->position += length;
file->after_last_cache += length;
TWC_TFER_FILE_UPDATE_STATUS(TWC_TFER_FILE_STATUS_IN_PROGRESS);
if ((twc_tfer_get_time() - file->timestamp) > 1)
{
file->timestamp = twc_tfer_get_time();
file->after_last_cache = 0;
}
}
free(data);
}
void
twc_file_recv_callback(Tox *tox, uint32_t friend_number, uint32_t file_number,
uint32_t kind, uint64_t file_size, const uint8_t *filename,
size_t filename_length, void *user_data)
{
struct t_twc_profile *profile = twc_profile_search_tox(tox);
if (kind == TOX_FILE_KIND_AVATAR)
{
TOX_ERR_FILE_CONTROL error;
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, &error);
if (error)
{
weechat_printf(profile->buffer, "%scannot cancel avatar receiving",
weechat_prefix("error"));
}
return;
}
char *name = twc_get_name_nt(tox, friend_number);
char *fname = twc_null_terminate(filename, filename_length);
struct t_twc_tfer_file *file = twc_tfer_file_new(profile, name, fname,
friend_number, file_number,
file_size, TWC_TFER_FILE_TYPE_DOWNLOADING);
free(name);
free(fname);
if (!file)
{
weechat_printf(profile->buffer, "%scannot open the file \"%s\" with write permissions",
weechat_prefix("error"), filename);
return;
}
if (!(profile->tfer->buffer))
{
twc_tfer_load(profile);
}
twc_tfer_file_add(profile->tfer, file);
twc_tfer_file_update(profile->tfer, file);
twc_tfer_update_status(profile->tfer, "waiting for action");
}
void
twc_file_recv_chunk_callback(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
const uint8_t *data, size_t length, void *user_data)
{
struct t_twc_profile *profile = twc_profile_search_tox(tox);
struct t_twc_tfer_file *file = twc_tfer_file_get_by_number(profile->tfer, file_number);
/* the file is missing */
if (!file)
{
weechat_printf(profile->tfer->buffer, "%sthere is no file with number %i in queue",
weechat_prefix("error"), file_number);
return;
}
/* 0-length chunk transmitted that means the file transmission is completed */
if (length == 0)
{
TWC_TFER_FILE_UPDATE_STATUS(TWC_TFER_FILE_STATUS_DONE);
/* This friend_number will be re-used and re-assigned for another file,
* to prevent collisions in twc_tfer_file_get_by_number calls let's
* set it to a value that won't be used by toxcore.
*/
file->file_number = UINT32_MAX;
fclose(file->fp);
return;
}
bool result = twc_tfer_file_write_chunk(file, data, position, length);
if (!result)
{
weechat_printf(profile->buffer, "%serror while writing the file %s",
weechat_prefix("error"), file->filename);
return;
}
else
{
file->position += length;
file->after_last_cache += length;
twc_tfer_file_update(profile->tfer, file);
if ((twc_tfer_get_time() - file->timestamp) > 1)
{
file->timestamp = twc_tfer_get_time();
file->after_last_cache = 0;
}
}
}
#ifndef NDEBUG #ifndef NDEBUG
void void
twc_tox_log_callback(Tox *tox, TOX_LOG_LEVEL level, const char *file, twc_tox_log_callback(Tox *tox, TOX_LOG_LEVEL level, const char *file,

View File

@ -71,11 +71,26 @@ twc_group_peer_name_callback(Tox *tox, uint32_t group_number,
size_t nick_len, size_t nick_len,
void *data); void *data);
void void
twc_group_title_callback(Tox *tox, uint32_t group_number, uint32_t peer_number, twc_group_title_callback(Tox *tox, uint32_t group_number, uint32_t peer_number,
const uint8_t *title, size_t length, void *data); const uint8_t *title, size_t length, void *data);
void
twc_file_recv_control_callback(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_CONTROL control,
void *user_data);
void
twc_file_chunk_request_callback(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
size_t length, void *user_data);
void
twc_file_recv_callback(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t kind, uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *user_data);
void
twc_file_recv_chunk_callback(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position,
const uint8_t *data, size_t length, void *user_data);
#ifndef NDEBUG #ifndef NDEBUG
void void
twc_tox_log_callback(Tox *tox, TOX_LOG_LEVEL level, const char *file, twc_tox_log_callback(Tox *tox, TOX_LOG_LEVEL level, const char *file,

View File

@ -232,3 +232,82 @@ twc_set_buffer_logging(struct t_gui_buffer *buffer, bool logging)
return weechat_hook_signal_send(signal, WEECHAT_HOOK_SIGNAL_POINTER, return weechat_hook_signal_send(signal, WEECHAT_HOOK_SIGNAL_POINTER,
buffer); buffer);
} }
/**
* These following twc_tox_err_file_* functions convert enum TOX_ERR_FILE_*
* error codes to meaningful messages of type char *.
*/
char *
twc_tox_err_file_control(enum TOX_ERR_FILE_CONTROL error)
{
char *messages[] = {
"success",
"the friend number passed did not designate a valid friend",
"this client is currently not connected to the friend",
"no file transfer with the given file number was found for the given friend",
"a RESUME control was sent, but the file transfer is running normally",
"A RESUME control was sent, but the file transfer was paused by the other party",
"a PAUSE control was sent, but the file transfer was already paused",
"packet queue is full"
};
return messages[error];
}
char *
twc_tox_err_file_get(enum TOX_ERR_FILE_GET error)
{
char *messages[] = {
"success",
"one of the arguments to the function was NULL when it was not expected",
"the friend number passed did not designate a valid friend",
"no file transfer with the given number was found for the given friend"
};
return messages[error];
}
char *
twc_tox_err_file_seek(enum TOX_ERR_FILE_SEEK error)
{
char *messages[] = {
"success",
"the friend number passed did not designate a valid friend",
"the client is currently not connected to the friend",
"no file transfer with the given file number was found for the given friend",
"file was not in a state where it could be seeked",
"seek position was invalid",
"packet queue is full"
};
return messages[error];
}
char *
twc_tox_err_file_send(enum TOX_ERR_FILE_SEND error)
{
char *messages[] = {
"success",
"one of the arguments of the function was NULL when it was not expected",
"the friend number passed did not designate a valid friend",
"this client is currently not connected to the friend",
"filename lenth exceeded TOX_MAX_FILENAME_LENGTH bytes",
"too many ongoing transfers"
};
return messages[error];
}
char *
twc_tox_err_file_send_chunk(enum TOX_ERR_FILE_SEND_CHUNK error)
{
char *messages[] = {
"success",
"the length parameter was non-zero, but data was NULL",
"the friend number passed did not designate a valid friend",
"this client is currently not connected to the friend",
"no file transfer with the given file number was found for the given friend",
"not called from the request chunk callback",
"attempted to send more or less data than requested",
"packet queue is full",
"position parameter was wrong"
};
return messages[error];
}

View File

@ -58,4 +58,19 @@ twc_fit_utf8(const char *str, int max);
int int
twc_set_buffer_logging(struct t_gui_buffer *buffer, bool logging); twc_set_buffer_logging(struct t_gui_buffer *buffer, bool logging);
char *
twc_tox_err_file_control(enum TOX_ERR_FILE_CONTROL error);
char *
twc_tox_err_file_get(enum TOX_ERR_FILE_GET error);
char *
twc_tox_err_file_seek(enum TOX_ERR_FILE_SEEK error);
char *
twc_tox_err_file_send(enum TOX_ERR_FILE_SEND error);
char *
twc_tox_err_file_send_chunk(enum TOX_ERR_FILE_SEND_CHUNK error);
#endif /* TOX_WEECHAT_UTILS_H */ #endif /* TOX_WEECHAT_UTILS_H */