Implement file transfer feature

This will add separate "tfer" buffer with all necessary controls for
managing files and /send command to start transfer, which you can use
either from profile buffer or from private messages buffer; in case of
incoming files the buffer will be created as well if doesn't exist yet.
Every callbacks were defined according to the specification and this
implementation is fully corresponding to the documented behavior including
streams.
This commit is contained in:
nogaems 2018-05-22 14:47:53 +03:00
parent b1765af433
commit ecf3467e76
No known key found for this signature in database
GPG key ID: 91316FB98FDBB08B
14 changed files with 1511 additions and 3 deletions

View file

@ -19,11 +19,15 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <wordexp.h>
#include <tox/tox.h>
#include <weechat/weechat-plugin.h>
#include "twc-bootstrap.h"
#include "twc-tfer.h"
#include "twc-chat.h"
#include "twc-config.h"
#include "twc-friend-request.h"
@ -127,6 +131,22 @@ enum TWC_FRIEND_MATCH
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
* Tox ID.
@ -1175,6 +1195,125 @@ twc_cmd_tox(const void *pointer, void *data, struct t_gui_buffer *buffer,
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.
*/
@ -1298,4 +1437,12 @@ twc_commands_init()
" || unload %(tox_loaded_profiles)|%*"
" || reload %(tox_loaded_profiles)|%*",
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);
}