Added and implemented file_id parameter to file tranfers.

file_id is a 32byte identifier that can be used by users to identify
file tranfers across core/client restarts in order to resume broken
file tranfers.

In avatar tranfers it corresponds to the hash of the avatar.

Added tox_file_get_file_id() function to api to obtain the file_id
of an ongoing file transfer.

If not set, core will generate a random one.
This commit is contained in:
irungentoo 2015-03-17 13:44:48 -04:00
parent 2757b254fe
commit 24c70c9e84
No known key found for this signature in database
GPG Key ID: 10349DC9BED89E98
7 changed files with 177 additions and 41 deletions

View File

@ -100,6 +100,7 @@ void handle_custom_packet(Tox *m, uint32_t friend_num, const uint8_t *data, size
return;
}
uint8_t file_cmp_id[TOX_FILE_ID_LENGTH];
uint8_t filenum;
uint32_t file_accepted;
uint64_t file_size;
@ -119,6 +120,22 @@ void tox_file_receive(Tox *tox, uint32_t friend_number, uint32_t file_number, ui
return;
}
uint8_t file_id[TOX_FILE_ID_LENGTH];
if (!tox_file_get_file_id(tox, friend_number, file_number, file_id, 0)) {
ck_abort_msg("tox_file_get_file_id error");
}
if (memcmp(file_id, file_cmp_id, TOX_FILE_ID_LENGTH) != 0) {
ck_abort_msg("bad file_id");
}
uint8_t empty[TOX_FILE_ID_LENGTH] = {0};
if (memcmp(empty, file_cmp_id, TOX_FILE_ID_LENGTH) == 0) {
ck_abort_msg("empty file_id");
}
file_size = filesize;
TOX_ERR_FILE_CONTROL error;
@ -520,10 +537,18 @@ START_TEST(test_few_clients)
tox_callback_file_recv_control(tox3, file_print_control, &to_compare);
tox_callback_file_receive(tox3, tox_file_receive, &to_compare);
uint64_t totalf_size = 100 * 1024 * 1024;
uint32_t fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, (uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"),
0);
uint32_t fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, 0, (uint8_t *)"Gentoo.exe",
sizeof("Gentoo.exe"), 0);
ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail");
TOX_ERR_FILE_GET gfierr;
ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error");
ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error");
ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed");
ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error");
while (1) {
tox_iterate(tox1);

View File

@ -158,8 +158,8 @@ uint32_t add_filesender(Tox *m, uint16_t friendnum, char *filename)
fseek(tempfile, 0, SEEK_END);
uint64_t filesize = ftell(tempfile);
fseek(tempfile, 0, SEEK_SET);
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, filesize, (uint8_t *)filename, strlen(filename) + 1,
0);
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, filesize, 0, (uint8_t *)filename,
strlen(filename), 0);
if (filenum == -1)
return -1;

View File

@ -88,8 +88,8 @@ uint32_t add_filesender(Tox *m, uint16_t friendnum, char *filename)
fseek(tempfile, 0, SEEK_END);
uint64_t filesize = ftell(tempfile);
fseek(tempfile, 0, SEEK_SET);
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, filesize, (uint8_t *)filename, strlen(filename) + 1,
0);
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, filesize, 0, (uint8_t *)filename,
strlen(filename), 0);
if (filenum == -1)
return -1;

View File

@ -1023,13 +1023,58 @@ void callback_file_reqchunk(Messenger *m, void (*function)(Messenger *m, uint32_
#define MAX_FILENAME_LENGTH 255
/* Copy the file transfer file id to file_id
*
* return 0 on success.
* return -1 if friend not valid.
* return -2 if filenumber not valid
*/
int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint8_t *file_id)
{
if (friend_not_valid(m, friendnumber))
return -1;
if (m->friendlist[friendnumber].status != FRIEND_ONLINE)
return -2;
uint32_t temp_filenum;
uint8_t send_receive, file_number;
if (filenumber >= (1 << 16)) {
send_receive = 1;
temp_filenum = (filenumber >> 16) - 1;
} else {
send_receive = 0;
temp_filenum = filenumber;
}
if (temp_filenum >= MAX_CONCURRENT_FILE_PIPES)
return -2;
file_number = temp_filenum;
struct File_Transfers *ft;
if (send_receive) {
ft = &m->friendlist[friendnumber].file_receiving[file_number];
} else {
ft = &m->friendlist[friendnumber].file_sending[file_number];
}
if (ft->status == FILESTATUS_NONE)
return -2;
memcpy(file_id, ft->id, FILE_ID_LENGTH);
return 0;
}
/* Send a file send request.
* Maximum filename length is 255 bytes.
* return 1 on success
* return 0 on failure
*/
static int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t filenumber, uint32_t file_type,
uint64_t filesize, const uint8_t *filename, uint16_t filename_length)
uint64_t filesize, const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length)
{
if (friend_not_valid(m, friendnumber))
return 0;
@ -1037,13 +1082,18 @@ static int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t fi
if (filename_length > MAX_FILENAME_LENGTH)
return 0;
uint8_t packet[1 + sizeof(file_type) + sizeof(filesize) + filename_length];
uint8_t packet[1 + sizeof(file_type) + sizeof(filesize) + FILE_ID_LENGTH + filename_length];
packet[0] = filenumber;
file_type = htonl(file_type);
memcpy(packet + 1, &file_type, sizeof(file_type));
host_to_net((uint8_t *)&filesize, sizeof(filesize));
memcpy(packet + 1 + sizeof(file_type), &filesize, sizeof(filesize));
memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize), filename, filename_length);
memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize), file_id, FILE_ID_LENGTH);
if (filename_length) {
memcpy(packet + 1 + sizeof(file_type) + sizeof(filesize) + FILE_ID_LENGTH, filename, filename_length);
}
return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_SENDREQUEST, packet, sizeof(packet), 0);
}
@ -1057,7 +1107,7 @@ static int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t fi
*
*/
long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_type, uint64_t filesize,
const uint8_t *filename, uint16_t filename_length)
const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length)
{
if (friend_not_valid(m, friendnumber))
return -1;
@ -1078,7 +1128,7 @@ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_
if (i == MAX_CONCURRENT_FILE_PIPES)
return -3;
if (file_sendrequest(m, friendnumber, i, file_type, filesize, filename, filename_length) == 0)
if (file_sendrequest(m, friendnumber, i, file_type, filesize, file_id, filename, filename_length) == 0)
return -4;
struct File_Transfers *ft = &m->friendlist[friendnumber].file_sending[i];
@ -1088,6 +1138,7 @@ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_
ft->requested = 0;
ft->slots_allocated = 0;
ft->paused = FILE_PAUSE_NOT;
memcpy(ft->id, file_id, FILE_ID_LENGTH);
++m->friendlist[friendnumber].num_sending_files;
@ -1932,9 +1983,9 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
}
case PACKET_ID_FILE_SENDREQUEST: {
const unsigned int head_length = 1 + sizeof(uint32_t) + sizeof(uint64_t);
const unsigned int head_length = 1 + sizeof(uint32_t) + sizeof(uint64_t) + FILE_ID_LENGTH;
if (data_length < head_length + 1)
if (data_length < head_length)
break;
uint8_t filenumber = data[0];
@ -1944,16 +1995,15 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
memcpy(&file_type, data + 1, sizeof(file_type));
file_type = ntohl(file_type);
/* Check if the name is the right size if file is avatar. */
if (file_type == FILEKIND_AVATAR && filename_length != crypto_hash_sha256_BYTES)
break;
memcpy(&filesize, data + 1 + sizeof(uint32_t), sizeof(filesize));
net_to_host((uint8_t *) &filesize, sizeof(filesize));
m->friendlist[i].file_receiving[filenumber].status = FILESTATUS_NOT_ACCEPTED;
m->friendlist[i].file_receiving[filenumber].size = filesize;
m->friendlist[i].file_receiving[filenumber].transferred = 0;
m->friendlist[i].file_receiving[filenumber].paused = FILE_PAUSE_NOT;
struct File_Transfers *ft = &m->friendlist[i].file_receiving[filenumber];
ft->status = FILESTATUS_NOT_ACCEPTED;
ft->size = filesize;
ft->transferred = 0;
ft->paused = FILE_PAUSE_NOT;
memcpy(ft->id, data + 1 + sizeof(uint32_t) + sizeof(uint64_t), FILE_ID_LENGTH);
/* Force NULL terminate file name. */
uint8_t filename_terminated[filename_length + 1];
@ -1965,7 +2015,7 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
real_filenumber <<= 16;
if (m->file_sendrequest)
(*m->file_sendrequest)(m, i, real_filenumber, file_type, filesize, filename_terminated, filename_length,
(*m->file_sendrequest)(m, i, real_filenumber, file_type, filesize, filename, filename_length,
m->file_sendrequest_userdata);
break;

View File

@ -125,6 +125,8 @@ typedef enum {
}
USERSTATUS;
#define FILE_ID_LENGTH 32
struct File_Transfers {
uint64_t size;
uint64_t transferred;
@ -133,6 +135,7 @@ struct File_Transfers {
uint32_t last_packet_number; /* number of the last packet sent. */
uint64_t requested; /* total data requested by the request chunk callback */
unsigned int slots_allocated; /* number of slots allocated to this transfer. */
uint8_t id[FILE_ID_LENGTH];
};
enum {
FILESTATUS_NONE,
@ -610,6 +613,15 @@ void callback_file_data(Messenger *m, void (*function)(Messenger *m, uint32_t, u
void callback_file_reqchunk(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint64_t, size_t, void *),
void *userdata);
/* Copy the file transfer file id to file_id
*
* return 0 on success.
* return -1 if friend not valid.
* return -2 if filenumber not valid
*/
int file_get_id(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint8_t *file_id);
/* Send a file send request.
* Maximum filename length is 255 bytes.
* return file number on success
@ -620,7 +632,7 @@ void callback_file_reqchunk(Messenger *m, void (*function)(Messenger *m, uint32_
*
*/
long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_type, uint64_t filesize,
const uint8_t *filename, uint16_t filename_length);
const uint8_t *file_id, const uint8_t *filename, uint16_t filename_length);
/* Send a file control request.
*

View File

@ -889,21 +889,42 @@ void tox_callback_file_recv_control(Tox *tox, tox_file_recv_control_cb *function
callback_file_control(m, function, user_data);
}
uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t file_size, const uint8_t *filename,
size_t filename_length, TOX_ERR_FILE_SEND *error)
bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_number, uint8_t *file_id,
TOX_ERR_FILE_GET *error)
{
const Messenger *m = tox;
int ret = file_get_id(m, friend_number, file_number, file_id);
if (ret == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_OK);
return 1;
} else if (ret == -1) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_FRIEND_NOT_FOUND);
} else {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_GET_NOT_FOUND);
}
return 0;
}
uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t file_size, const uint8_t *file_id,
const uint8_t *filename, size_t filename_length, TOX_ERR_FILE_SEND *error)
{
if (!filename) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_NULL);
return UINT32_MAX;
}
if (!filename_length) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_NAME_EMPTY);
return UINT32_MAX;
uint8_t f_id[FILE_ID_LENGTH];
if (!file_id) {
/* Tox keys are 32 bytes like FILE_ID_LENGTH. */
new_symmetric_key(f_id);
file_id = f_id;
}
Messenger *m = tox;
long int file_num = new_filesender(m, friend_number, kind, file_size, filename, filename_length);
long int file_num = new_filesender(m, friend_number, kind, file_size, file_id, filename, filename_length);
if (file_num >= 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_OK);

View File

@ -255,6 +255,11 @@ bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
*/
#define TOX_HASH_LENGTH /*crypto_hash_sha256_BYTES*/ 32
/**
* The number of bytes in a file id.
*/
#define TOX_FILE_ID_LENGTH 32
/*******************************************************************************
*
* :: Global enumerations
@ -1412,9 +1417,9 @@ enum TOX_FILE_KIND {
*
* Clients who receive avatar send requests can reject it (by sending
* TOX_FILE_CONTROL_CANCEL before any other controls), or accept it (by
* sending TOX_FILE_CONTROL_RESUME). The filename of length TOX_HASH_LENGTH bytes
* will contain the hash. A client can compare this hash with a
* saved hash and send TOX_FILE_CONTROL_CANCEL to terminate the avatar
* sending TOX_FILE_CONTROL_RESUME). The file_id of length TOX_HASH_LENGTH bytes
* (same length as TOX_FILE_ID_LENGTH) will contain the hash. A client can compare
* this hash with a saved hash and send TOX_FILE_CONTROL_CANCEL to terminate the avatar
* transfer if it matches.
*/
TOX_FILE_KIND_AVATAR
@ -1534,6 +1539,32 @@ typedef void tox_file_recv_control_cb(Tox *tox, uint32_t friend_number, uint32_t
void tox_callback_file_recv_control(Tox *tox, tox_file_recv_control_cb *function, void *user_data);
typedef enum TOX_ERR_FILE_GET {
TOX_ERR_FILE_GET_OK,
/**
* The friend_number passed did not designate a valid friend.
*/
TOX_ERR_FILE_GET_FRIEND_NOT_FOUND,
/**
* No file transfer with the given file number was found for the given friend.
*/
TOX_ERR_FILE_GET_NOT_FOUND
} TOX_ERR_FILE_GET;
/**
* Copy the file id associated to the file transfer to a byte array.
*
* @param friend_number The friend number of the friend the file is being
* transferred to.
* @param file_number The friend-specific identifier for the file transfer.
* @param file_id A memory region of at least TOX_FILE_ID_LENGTH bytes. If
* this parameter is NULL, this function has no effect.
*
* @return true on success.
*/
bool tox_file_get_file_id(const Tox *tox, uint32_t friend_number, uint32_t file_number, uint8_t *file_id,
TOX_ERR_FILE_GET *error);
/*******************************************************************************
*
* :: File transmission: sending
@ -1557,8 +1588,7 @@ typedef enum TOX_ERR_FILE_SEND {
*/
TOX_ERR_FILE_SEND_NAME_EMPTY,
/**
* Filename length exceeded 255 bytes or if kind was equal to TOX_FILE_KIND_AVATAR
* the length was not TOX_HASH_LENGTH.
* Filename length exceeded 255 bytes.
*/
TOX_ERR_FILE_SEND_NAME_INVALID_LENGTH,
/**
@ -1581,11 +1611,6 @@ typedef enum TOX_ERR_FILE_SEND {
* File transmission occurs in chunks, which are requested through the
* `file_request_chunk` event.
*
* File numbers are stable across tox_save/tox_load cycles, so that file
* transfers can be resumed when a client restarts. The client needs to
* associate (friend Public Key, file number) with the local path of the file and
* persist this information to support resuming of transfers across restarts.
*
* If the file contents change during a transfer, the behaviour is unspecified
* in general. What will actually happen depends on the mode in which the file
* was modified and how the client determines the file size.
@ -1616,6 +1641,9 @@ typedef enum TOX_ERR_FILE_SEND {
* @param kind The meaning of the file to be sent.
* @param file_size Size in bytes of the file the client wants to send, 0 if
* unknown or streaming.
* @param file_id A file identifier of length TOX_FILE_ID_LENGTH that can be used to
* uniquely identify file transfers across core restarts. If NULL, a random one will
* be generated by core. It can then be obtained by using tox_file_get_file_id().
* @param filename Name of the file. Does not need to be the actual name. This
* name will be sent along with the file send request.
* @param filename_length Size in bytes of the filename.
@ -1624,8 +1652,8 @@ typedef enum TOX_ERR_FILE_SEND {
* number is per friend. File numbers are reused after a transfer terminates.
* on failure, this function returns UINT32_MAX.
*/
uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t file_size, const uint8_t *filename,
size_t filename_length, TOX_ERR_FILE_SEND *error);
uint32_t tox_file_send(Tox *tox, uint32_t friend_number, uint32_t kind, uint64_t file_size, const uint8_t *file_id,
const uint8_t *filename, size_t filename_length, TOX_ERR_FILE_SEND *error);
typedef enum TOX_ERR_FILE_SEND_CHUNK {