Implementation of new api file transfers.

Everything should work except resuming.
This commit is contained in:
irungentoo 2015-03-10 17:31:50 -04:00
parent f5eca31637
commit 0207fcdfb0
No known key found for this signature in database
GPG Key ID: 10349DC9BED89E98
5 changed files with 646 additions and 252 deletions

View File

@ -21,6 +21,7 @@
#define c_sleep(x) usleep(1000*x)
#endif
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
@ -83,7 +84,7 @@ void handle_custom_packet(Tox *m, uint32_t friend_num, const uint8_t *data, size
if (memcmp(f_data, data, len) == 0) {
++custom_packet;
} else {
printf("Custom packet fail. %u\n", number );
ck_abort_msg("Custom packet fail. %u", number);
}
return;
@ -92,45 +93,104 @@ void handle_custom_packet(Tox *m, uint32_t friend_num, const uint8_t *data, size
uint8_t filenum;
uint32_t file_accepted;
uint64_t file_size;
void file_request_accept(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, const uint8_t *filename,
uint16_t filename_length, void *userdata)
void tox_file_receive(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_KIND kind, uint64_t filesize,
const uint8_t *filename, size_t filename_length, void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
if (filename_length == sizeof("Gentoo.exe") && memcmp(filename, "Gentoo.exe", sizeof("Gentoo.exe")) == 0)
++file_accepted;
if (kind != TOX_FILE_KIND_DATA) {
ck_abort_msg("Bad kind");
return;
}
if (!(filename_length == sizeof("Gentoo.exe") && memcmp(filename, "Gentoo.exe", sizeof("Gentoo.exe")) == 0)) {
ck_abort_msg("Bad filename");
return;
}
file_size = filesize;
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_ACCEPT, NULL, 0);
TOX_ERR_FILE_CONTROL error;
if (tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, &error)) {
++file_accepted;
} else {
ck_abort_msg("tox_file_control failed. %i", error);
}
}
uint32_t file_sent;
uint32_t sendf_ok;
void file_print_control(Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type,
const uint8_t *data, uint16_t length, void *userdata)
void file_print_control(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_CONTROL control,
void *userdata)
{
if (*((uint32_t *)userdata) != 974536)
return;
if (receive_send == 0 && control_type == TOX_FILECONTROL_FINISHED)
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_FINISHED, NULL, 0);
if (receive_send == 1 && control_type == TOX_FILECONTROL_FINISHED)
file_sent = 1;
if (receive_send == 1 && control_type == TOX_FILECONTROL_ACCEPT)
/* First send file num is 0.*/
if (file_number == 0 && control == TOX_FILE_CONTROL_RESUME)
sendf_ok = 1;
}
uint8_t sending_num;
uint64_t sending_pos;
_Bool file_sending_done;
void tox_file_request_chunk(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, size_t length,
void *user_data)
{
if (*((uint32_t *)user_data) != 974536)
return;
if (!sendf_ok) {
ck_abort_msg("Didn't get resume control");
}
if (sending_pos != position) {
ck_abort_msg("Bad position");
return;
}
if (length == 0) {
file_sending_done = 1;
return;
}
TOX_ERR_FILE_SEND_CHUNK error;
uint8_t f_data[length];
memset(f_data, sending_num, length);
if (tox_file_send_chunk(tox, friend_number, file_number, f_data, length, &error)) {
++sending_num;
sending_pos += length;
} else {
ck_abort_msg("Could not send chunk %i", error);
}
if (error != TOX_ERR_FILE_SEND_CHUNK_OK) {
ck_abort_msg("Wrong error code");
}
}
uint64_t size_recv;
uint8_t num;
void write_file(Tox *m, int friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata)
_Bool file_recv;
void write_file(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *user_data)
{
if (*((uint32_t *)userdata) != 974536)
if (*((uint32_t *)user_data) != 974536)
return;
if (size_recv != position) {
ck_abort_msg("Bad position");
return;
}
if (length == 0) {
file_recv = 1;
return;
}
uint8_t f_data[length];
memset(f_data, num, length);
++num;
@ -138,7 +198,7 @@ void write_file(Tox *m, int friendnumber, uint8_t filenumber, const uint8_t *dat
if (memcmp(f_data, data, length) == 0) {
size_recv += length;
} else {
printf("FILE_CORRUPTED\n");
ck_abort_msg("FILE_CORRUPTED");
}
}
@ -371,41 +431,32 @@ START_TEST(test_few_clients)
c_sleep(50);
}
filenum = file_accepted = file_size = file_sent = sendf_ok = size_recv = 0;
file_accepted = file_size = file_recv = sendf_ok = size_recv = 0;
long long unsigned int f_time = time(NULL);
tox_callback_file_data(tox3, write_file, &to_compare);
tox_callback_file_receive_chunk(tox3, write_file, &to_compare);
tox_callback_file_control(tox2, file_print_control, &to_compare);
tox_callback_file_request_chunk(tox2, tox_file_request_chunk, &to_compare);
tox_callback_file_control(tox3, file_print_control, &to_compare);
tox_callback_file_send_request(tox3, file_request_accept, &to_compare);
tox_callback_file_receive(tox3, tox_file_receive, &to_compare);
uint64_t totalf_size = 100 * 1024 * 1024;
int fnum = tox_new_file_sender(tox2, 0, totalf_size, (uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"));
ck_assert_msg(fnum != -1, "tox_new_file_sender fail");
int fpiece_size = tox_file_data_size(tox2, 0);
uint8_t f_data[fpiece_size];
uint8_t num = 0;
memset(f_data, num, fpiece_size);
uint32_t fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, (uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"),
0);
ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail");
while (1) {
file_sent = 0;
tox_iteration(tox1);
tox_iteration(tox2);
tox_iteration(tox3);
if (sendf_ok)
while (tox_file_send_data(tox2, 0, fnum, f_data, fpiece_size < totalf_size ? fpiece_size : totalf_size) == 0) {
if (totalf_size <= fpiece_size) {
sendf_ok = 0;
tox_file_send_control(tox2, 0, 0, fnum, TOX_FILECONTROL_FINISHED, NULL, 0);
}
++num;
memset(f_data, num, fpiece_size);
totalf_size -= fpiece_size;
if (file_sending_done) {
if (sendf_ok && file_recv && totalf_size == file_size && size_recv == file_size && sending_pos == size_recv) {
break;
} else {
ck_abort_msg("Something went wrong in file transfer %u %u %u %u %u", sendf_ok, file_recv, totalf_size == file_size,
size_recv == file_size, sending_pos == size_recv);
}
if (file_sent && size_recv == file_size)
break;
}
uint32_t tox1_interval = tox_iteration_interval(tox1);
uint32_t tox2_interval = tox_iteration_interval(tox2);

View File

@ -349,6 +349,18 @@ static int add_receipt(Messenger *m, int32_t friendnumber, uint32_t packet_num,
new->next = NULL;
return 0;
}
/*
* return -1 on failure.
* return 0 if packet was received.
*/
static int friend_received_packet(const Messenger *m, int32_t friendnumber, uint32_t number)
{
if (friend_not_valid(m, friendnumber))
return -1;
return cryptpacket_received(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
m->friendlist[friendnumber].friendcon_id), number);
}
static int do_receipts(Messenger *m, int32_t friendnumber)
{
@ -360,8 +372,7 @@ static int do_receipts(Messenger *m, int32_t friendnumber)
while (receipts) {
struct Receipts *temp_r = receipts->next;
if (cryptpacket_received(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
m->friendlist[friendnumber].friendcon_id), receipts->packet_num) == -1)
if (friend_received_packet(m, friendnumber, receipts->packet_num) == -1)
break;
if (m->read_receipt)
@ -970,11 +981,11 @@ void callback_file_sendrequest(Messenger *m, void (*function)(Messenger *m, uin
/* Set the callback for file control requests.
*
* Function(Tox *tox, uint32_t friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata)
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, unsigned int control_type, void *userdata)
*
*/
void callback_file_control(Messenger *m, void (*function)(Messenger *m, uint32_t, uint8_t, uint8_t, uint8_t,
const uint8_t *, uint16_t, void *), void *userdata)
void callback_file_control(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, unsigned int, void *),
void *userdata)
{
m->file_filecontrol = function;
m->file_filecontrol_userdata = userdata;
@ -982,17 +993,28 @@ void callback_file_control(Messenger *m, void (*function)(Messenger *m, uint32_t
/* Set the callback for file data.
*
* Function(Tox *tox, uint32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata)
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, uint8_t *data, size_t length, void *userdata)
*
*/
void callback_file_data(Messenger *m, void (*function)(Messenger *m, uint32_t, uint8_t, const uint8_t *,
uint16_t length,
void *), void *userdata)
void callback_file_data(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint64_t, const uint8_t *,
size_t, void *), void *userdata)
{
m->file_filedata = function;
m->file_filedata_userdata = userdata;
}
/* Set the callback for file request chunk.
*
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void *userdata)
*
*/
void callback_file_reqchunk(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint64_t, size_t, void *),
void *userdata)
{
m->file_reqchunk = function;
m->file_reqchunk_userdata = userdata;
}
#define MAX_FILENAME_LENGTH 255
/* Send a file send request.
@ -1053,132 +1075,182 @@ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_
m->friendlist[friendnumber].file_sending[i].status = FILESTATUS_NOT_ACCEPTED;
m->friendlist[friendnumber].file_sending[i].size = filesize;
m->friendlist[friendnumber].file_sending[i].transferred = 0;
m->friendlist[friendnumber].file_sending[i].type = file_type;
m->friendlist[friendnumber].file_sending[i].paused = FILE_PAUSE_NOT;
++m->friendlist[friendnumber].num_sending_files;
return i;
}
/* Send a file control request.
* send_receive is 0 if we want the control packet to target a sending file, 1 if it targets a receiving file.
*
* return 0 on success
* return -1 on failure
*/
int file_control(const Messenger *m, int32_t friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id,
const uint8_t *data, uint16_t length)
int send_file_control_packet(const Messenger *m, int32_t friendnumber, uint8_t send_receive, uint8_t filenumber,
uint8_t control_type, uint8_t *data, uint16_t data_length)
{
if (length > MAX_CRYPTO_DATA_SIZE - 3)
if (1 + 3 + data_length > MAX_CRYPTO_DATA_SIZE)
return -1;
uint8_t packet[3 + data_length];
packet[0] = send_receive;
packet[1] = filenumber;
packet[2] = control_type;
if (data_length) {
memcpy(packet, packet + 3, data_length);
}
return write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_CONTROL, packet, sizeof(packet), 0);
}
/* Send a file control request.
*
* return 0 on success
* return -1 if friend not valid.
* return -2 if friend not online.
* return -3 if file number invalid.
* return -4 if file control is bad.
* return -5 if file already paused.
* return -6 if resume file failed because it was only paused by the other.
* return -7 if resume file failed because it wasn't paused.
* return -8 if packet failed to send.
*/
int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, unsigned int control)
{
if (friend_not_valid(m, friendnumber))
return -1;
if (send_receive == 1) {
if (m->friendlist[friendnumber].file_receiving[filenumber].status == FILESTATUS_NONE)
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 {
if (m->friendlist[friendnumber].file_sending[filenumber].status == FILESTATUS_NONE)
return -1;
send_receive = 0;
temp_filenum = filenumber;
}
if (send_receive > 1)
return -1;
if (temp_filenum >= MAX_CONCURRENT_FILE_PIPES)
return -3;
uint8_t packet[MAX_CRYPTO_DATA_SIZE];
packet[0] = send_receive;
packet[1] = filenumber;
packet[2] = message_id;
uint64_t transferred = 0;
file_number = temp_filenum;
if (message_id == FILECONTROL_RESUME_BROKEN) {
if (length != sizeof(uint64_t))
return -1;
struct File_Transfers *ft;
uint8_t remaining[sizeof(uint64_t)];
memcpy(remaining, data, sizeof(uint64_t));
host_to_net(remaining, sizeof(uint64_t));
memcpy(packet + 3, remaining, sizeof(uint64_t));
memcpy(&transferred, data, sizeof(uint64_t));
if (send_receive) {
ft = &m->friendlist[friendnumber].file_receiving[file_number];
} else {
memcpy(packet + 3, data, length);
ft = &m->friendlist[friendnumber].file_sending[file_number];
}
if (write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_CONTROL, packet, length + 3, 0)) {
if (send_receive == 1)
switch (message_id) {
case FILECONTROL_ACCEPT:
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_TRANSFERRING;
break;
if (ft->status == FILESTATUS_NONE)
return -3;
case FILECONTROL_PAUSE:
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_PAUSED_BY_US;
break;
if (control > FILECONTROL_KILL)
return -4;
case FILECONTROL_KILL:
case FILECONTROL_FINISHED:
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_NONE;
break;
if (control == FILECONTROL_PAUSE && (ft->paused & FILE_PAUSE_US))
return -5;
case FILECONTROL_RESUME_BROKEN:
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_PAUSED_BY_OTHER;
m->friendlist[friendnumber].file_receiving[filenumber].transferred = transferred;
break;
if (control == FILECONTROL_ACCEPT && ft->status == FILESTATUS_TRANSFERRING) {
if (!(ft->paused & FILE_PAUSE_US)) {
if (ft->paused & FILE_PAUSE_OTHER) {
return -6;
} else {
return -7;
}
else
switch (message_id) {
case FILECONTROL_ACCEPT:
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_TRANSFERRING;
break;
case FILECONTROL_PAUSE:
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_PAUSED_BY_US;
break;
case FILECONTROL_KILL:
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_NONE;
break;
case FILECONTROL_FINISHED:
break;
}
return 0;
} else {
return -1;
}
}
if (send_file_control_packet(m, friendnumber, send_receive, file_number, control, 0, 0)) {
if (control == FILECONTROL_KILL) {
ft->status = FILESTATUS_NONE;
if (send_receive == 0) {
--m->friendlist[friendnumber].num_sending_files;
}
} else if (control == FILECONTROL_PAUSE) {
ft->paused |= FILE_PAUSE_US;
} else if (control == FILECONTROL_ACCEPT) {
ft->status = FILESTATUS_TRANSFERRING;
if (ft->paused & FILE_PAUSE_US) {
ft->paused ^= FILE_PAUSE_US;
}
}
} else {
return -8;
}
return 0;
}
#define MIN_SLOTS_FREE (CRYPTO_MIN_QUEUE_LENGTH / 2)
/* Send file data.
*
* return 0 on success
* return -1 on failure
* return -1 if friend not valid.
* return -2 if friend not online.
* return -3 if filenumber invalid.
* return -4 if file transfer not transferring.
* return -5 if trying to send too much data.
* return -6 if packet queue full.
*/
int file_data(const Messenger *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length)
int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, const uint8_t *data, uint16_t length)
{
if (length > MAX_CRYPTO_DATA_SIZE - 1)
return -1;
if (friend_not_valid(m, friendnumber))
return -1;
if (m->friendlist[friendnumber].file_sending[filenumber].status != FILESTATUS_TRANSFERRING)
return -1;
if (m->friendlist[friendnumber].status != FRIEND_ONLINE)
return -2;
if (filenumber > MAX_CONCURRENT_FILE_PIPES)
return -3;
struct File_Transfers *ft = &m->friendlist[friendnumber].file_sending[filenumber];
if (ft->status != FILESTATUS_TRANSFERRING)
return -4;
if (ft->paused != FILE_PAUSE_NOT)
return -4;
if (length > MAX_CRYPTO_DATA_SIZE - 2)
return -5;
if (ft->size) {
if (ft->size - ft->transferred < length) {
return -5;
}
}
/* Prevent file sending from filling up the entire buffer preventing messages from being sent. TODO: remove */
if (crypto_num_free_sendqueue_slots(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
m->friendlist[friendnumber].friendcon_id)) < MIN_SLOTS_FREE)
return -1;
return -6;
uint8_t packet[MAX_CRYPTO_DATA_SIZE];
packet[0] = filenumber;
memcpy(packet + 1, data, length);
uint8_t packet[2 + length];
packet[0] = PACKET_ID_FILE_DATA;
packet[1] = filenumber;
memcpy(packet + 2, data, length);
int64_t ret = write_cryptpacket(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
m->friendlist[friendnumber].friendcon_id), packet, sizeof(packet), 1);
if (ret != -1) {
//TODO record packet ids to check if other received complete file.
ft->transferred += length;
if (length == 0 || ft->size == ft->transferred) {
ft->status = FILESTATUS_FINISHED;
ft->last_packet_number = ret;
}
if (write_cryptpacket_id(m, friendnumber, PACKET_ID_FILE_DATA, packet, length + 1, 1)) {
m->friendlist[friendnumber].file_sending[filenumber].transferred += length;
return 0;
}
return -1;
return -6;
}
@ -1209,105 +1281,142 @@ uint64_t file_dataremaining(const Messenger *m, int32_t friendnumber, uint8_t fi
}
}
static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber)
{
if (!m->friendlist[friendnumber].num_sending_files)
return;
int free_slots = crypto_num_free_sendqueue_slots(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c,
m->friendlist[friendnumber].friendcon_id));
if (free_slots <= MIN_SLOTS_FREE)
return;
free_slots -= MIN_SLOTS_FREE;
unsigned int i, num = m->friendlist[friendnumber].num_sending_files;
for (i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
struct File_Transfers *ft = &m->friendlist[friendnumber].file_sending[i];
if (ft->status != FILESTATUS_NONE) {
--num;
if (ft->status == FILESTATUS_FINISHED) {
/* Check if file was entirely sent. */
if (friend_received_packet(m, friendnumber, ft->last_packet_number) == 0) {
if (m->file_reqchunk)
(*m->file_reqchunk)(m, friendnumber, i, ft->transferred, 0, m->file_reqchunk_userdata);
ft->status = FILESTATUS_NONE;
--m->friendlist[friendnumber].num_sending_files;
}
}
}
while (ft->status == FILESTATUS_TRANSFERRING && (ft->paused == FILE_PAUSE_NOT)) {
if (free_slots == 0)
break;
uint16_t length = MAX_CRYPTO_DATA_SIZE - 2;
if (ft->size) {
if (ft->size == ft->transferred) {
break;
}
if (ft->size - ft->transferred < length) {
length = ft->size - ft->transferred;
}
}
if (m->file_reqchunk)
(*m->file_reqchunk)(m, friendnumber, i, ft->transferred, length, m->file_reqchunk_userdata);
--free_slots;
}
if (num == 0)
break;
}
}
/* Run this when the friend disconnects.
* Sets all current file transfers to broken.
*/
static void break_files(const Messenger *m, int32_t friendnumber)
{
uint32_t i;
/* TODO
for (i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
if (m->friendlist[friendnumber].file_sending[i].status != FILESTATUS_NONE)
m->friendlist[friendnumber].file_sending[i].status = FILESTATUS_BROKEN;
for (i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
if (m->friendlist[friendnumber].file_sending[i].status != FILESTATUS_NONE)
m->friendlist[friendnumber].file_sending[i].status = FILESTATUS_BROKEN;
if (m->friendlist[friendnumber].file_receiving[i].status != FILESTATUS_NONE)
m->friendlist[friendnumber].file_receiving[i].status = FILESTATUS_BROKEN;
}
if (m->friendlist[friendnumber].file_receiving[i].status != FILESTATUS_NONE)
m->friendlist[friendnumber].file_receiving[i].status = FILESTATUS_BROKEN;
}*/
}
static int handle_filecontrol(const Messenger *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t message_id, uint8_t *data,
uint16_t length)
/* return -1 on failure, 0 on success.
*/
static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, uint8_t *data, uint16_t length)
{
if (receive_send > 1)
return -1;
if (control_type > FILECONTROL_RESUME_BROKEN)
return -1;
uint32_t real_filenumber = filenumber;
struct File_Transfers *ft;
if (receive_send == 0) {
if (m->friendlist[friendnumber].file_receiving[filenumber].status == FILESTATUS_NONE) {
/* Tell the other to kill the file sending if we don't know this one. */
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_TEMPORARY;
file_control(m, friendnumber, !receive_send, filenumber, FILECONTROL_KILL, NULL, 0);
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_NONE;
return -1;
}
switch (message_id) {
case FILECONTROL_ACCEPT:
if (m->friendlist[friendnumber].file_receiving[filenumber].status != FILESTATUS_PAUSED_BY_US) {
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_TRANSFERRING;
return 0;
}
return -1;
case FILECONTROL_PAUSE:
if (m->friendlist[friendnumber].file_receiving[filenumber].status != FILESTATUS_PAUSED_BY_US) {
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_PAUSED_BY_OTHER;
return 0;
}
return -1;
case FILECONTROL_KILL:
m->friendlist[friendnumber].file_receiving[filenumber].status = FILESTATUS_NONE;
case FILECONTROL_FINISHED:
return 0;
}
real_filenumber += 1;
real_filenumber <<= 16;
ft = &m->friendlist[friendnumber].file_receiving[filenumber];
} else {
if (m->friendlist[friendnumber].file_sending[filenumber].status == FILESTATUS_NONE) {
/* Tell the other to kill the file sending if we don't know this one. */
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_TEMPORARY;
file_control(m, friendnumber, !receive_send, filenumber, FILECONTROL_KILL, NULL, 0);
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_NONE;
return -1;
}
switch (message_id) {
case FILECONTROL_ACCEPT:
if (m->friendlist[friendnumber].file_sending[filenumber].status != FILESTATUS_PAUSED_BY_US) {
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_TRANSFERRING;
return 0;
}
return -1;
case FILECONTROL_PAUSE:
if (m->friendlist[friendnumber].file_sending[filenumber].status != FILESTATUS_PAUSED_BY_US) {
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_PAUSED_BY_OTHER;
}
return 0;
case FILECONTROL_KILL:
case FILECONTROL_FINISHED:
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_NONE;
return 0;
case FILECONTROL_RESUME_BROKEN: {
if (m->friendlist[friendnumber].file_sending[filenumber].status == FILESTATUS_BROKEN && length == sizeof(uint64_t)) {
m->friendlist[friendnumber].file_sending[filenumber].status = FILESTATUS_PAUSED_BY_US;
net_to_host(data, sizeof(uint64_t));
return 0;
}
return -1;
}
}
ft = &m->friendlist[friendnumber].file_sending[filenumber];
}
return -1;
if (ft->status == FILESTATUS_NONE) {
/* File transfer doesn't exist, tell the other to kill it. */
send_file_control_packet(m, friendnumber, !receive_send, filenumber, FILECONTROL_KILL, 0, 0);
return -1;
}
if (control_type == FILECONTROL_ACCEPT) {
ft->status = FILESTATUS_TRANSFERRING;
if (ft->paused & FILE_PAUSE_OTHER) {
ft->paused ^= FILE_PAUSE_OTHER;
}
if (m->file_filecontrol)
(*m->file_filecontrol)(m, friendnumber, real_filenumber, control_type, m->file_filecontrol_userdata);
} else if (control_type == FILECONTROL_PAUSE) {
ft->paused |= FILE_PAUSE_OTHER;
if (m->file_filecontrol)
(*m->file_filecontrol)(m, friendnumber, real_filenumber, control_type, m->file_filecontrol_userdata);
} else if (control_type == FILECONTROL_KILL) {
if (m->file_filecontrol)
(*m->file_filecontrol)(m, friendnumber, real_filenumber, control_type, m->file_filecontrol_userdata);
ft->status = FILESTATUS_NONE;
if (receive_send) {
--m->friendlist[friendnumber].num_sending_files;
}
} else if (control_type == FILECONTROL_RESUME_BROKEN) {
//TODO
} else {
return -1;
}
return 0;
}
/**************************************/
@ -1766,6 +1875,7 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
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;
/* Force NULL terminate file name. */
uint8_t filename_terminated[data_length - head_length + 1];
@ -1794,26 +1904,51 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
if (handle_filecontrol(m, i, send_receive, filenumber, control_type, data + 3, data_length - 3) == -1)
break;
if (m->file_filecontrol)
(*m->file_filecontrol)(m, i, send_receive, filenumber, control_type, data + 3, data_length - 3,
m->file_filecontrol_userdata);
break;
}
case PACKET_ID_FILE_DATA: {
if (data_length < 2)
if (data_length <= 1)
break;
uint8_t filenumber = data[0];
struct File_Transfers *ft = &m->friendlist[i].file_receiving[filenumber];
if (m->friendlist[i].file_receiving[filenumber].status == FILESTATUS_NONE)
if (ft->status == FILESTATUS_NONE)
break;
m->friendlist[i].file_receiving[filenumber].transferred += (data_length - 1);
uint64_t position = ft->transferred;
uint32_t real_filenumber = filenumber;
real_filenumber += 1;
real_filenumber <<= 16;
uint16_t file_data_length = (data_length - 1);
uint8_t *file_data;
if (file_data_length == 0) {
file_data = NULL;
} else {
file_data = data + 1;
}
if (m->file_filedata)
(*m->file_filedata)(m, i, filenumber, data + 1, data_length - 1, m->file_filedata_userdata);
(*m->file_filedata)(m, i, real_filenumber, position, file_data, file_data_length, m->file_filedata_userdata);
ft->transferred += file_data_length;
if (ft->size && ft->transferred >= ft->size) {
file_data_length = 0;
file_data = NULL;
position = ft->transferred;
/* Full file received. */
if (m->file_filedata)
(*m->file_filedata)(m, i, real_filenumber, position, file_data, file_data_length, m->file_filedata_userdata);
}
/* Data is zero, filetransfer is over. */
if (file_data_length == 0) {
ft->status = FILESTATUS_NONE;
}
break;
}
@ -1907,6 +2042,7 @@ void do_friends(Messenger *m)
check_friend_tcp_udp(m, i);
do_receipts(m, i);
do_reqchunk_filecb(m, i);
}
}
}

View File

@ -128,18 +128,25 @@ USERSTATUS;
struct File_Transfers {
uint64_t size;
uint64_t transferred;
uint8_t status; /* 0 == no transfer, 1 = not accepted, 2 = paused by the other, 3 = transferring, 4 = broken, 5 = paused by us */
unsigned int type;
uint8_t status; /* 0 == no transfer, 1 = not accepted, 3 = transferring, 4 = broken, 5 = finished */
uint8_t paused; /* 0: not paused, 1 = paused by us, 2 = paused by other, 3 = paused by both. */
uint32_t last_packet_number; /* number of the last packet sent. */
};
enum {
FILESTATUS_NONE,
FILESTATUS_NOT_ACCEPTED,
FILESTATUS_PAUSED_BY_OTHER,
FILESTATUS_TRANSFERRING,
FILESTATUS_BROKEN,
FILESTATUS_PAUSED_BY_US,
FILESTATUS_TEMPORARY
//FILESTATUS_BROKEN,
FILESTATUS_FINISHED
};
enum {
FILE_PAUSE_NOT,
FILE_PAUSE_US,
FILE_PAUSE_OTHER,
FILE_PAUSE_BOTH
};
/* This cannot be bigger than 256 */
#define MAX_CONCURRENT_FILE_PIPES 256
@ -147,7 +154,6 @@ enum {
FILECONTROL_ACCEPT,
FILECONTROL_PAUSE,
FILECONTROL_KILL,
FILECONTROL_FINISHED,
FILECONTROL_RESUME_BROKEN
};
@ -179,6 +185,7 @@ typedef struct {
uint64_t share_relays_lastsent;
uint8_t last_connection_udp_tcp;
struct File_Transfers file_sending[MAX_CONCURRENT_FILE_PIPES];
unsigned int num_sending_files;
struct File_Transfers file_receiving[MAX_CONCURRENT_FILE_PIPES];
struct {
@ -249,10 +256,12 @@ struct Messenger {
void (*file_sendrequest)(struct Messenger *m, uint32_t, uint32_t, unsigned int, uint64_t, const uint8_t *, size_t,
void *);
void *file_sendrequest_userdata;
void (*file_filecontrol)(struct Messenger *m, uint32_t, uint8_t, uint8_t, uint8_t, const uint8_t *, uint16_t, void *);
void (*file_filecontrol)(struct Messenger *m, uint32_t, uint32_t, unsigned int, void *);
void *file_filecontrol_userdata;
void (*file_filedata)(struct Messenger *m, uint32_t, uint8_t, const uint8_t *, uint16_t length, void *);
void (*file_filedata)(struct Messenger *m, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t, void *);
void *file_filedata_userdata;
void (*file_reqchunk)(struct Messenger *m, uint32_t, uint32_t, uint64_t, size_t, void *);
void *file_reqchunk_userdata;
void (*msi_packet)(struct Messenger *m, uint32_t, const uint8_t *, uint16_t, void *);
void *msi_packet_userdata;
@ -562,20 +571,27 @@ void callback_file_sendrequest(Messenger *m, void (*function)(Messenger *m, uin
/* Set the callback for file control requests.
*
* Function(Tox *tox, uint32_t friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata)
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, unsigned int control_type, void *userdata)
*
*/
void callback_file_control(Messenger *m, void (*function)(Messenger *m, uint32_t, uint8_t, uint8_t, uint8_t,
const uint8_t *, uint16_t, void *), void *userdata);
void callback_file_control(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, unsigned int, void *),
void *userdata);
/* Set the callback for file data.
*
* Function(Tox *tox, uint32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata)
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, uint8_t *data, size_t length, void *userdata)
*
*/
void callback_file_data(Messenger *m, void (*function)(Messenger *m, uint32_t, uint8_t, const uint8_t *,
uint16_t length,
void *), void *userdata);
void callback_file_data(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint64_t, const uint8_t *,
size_t, void *), void *userdata);
/* Set the callback for file request chunk.
*
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void *userdata)
*
*/
void callback_file_reqchunk(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint64_t, size_t, void *),
void *userdata);
/* Send a file send request.
* Maximum filename length is 255 bytes.
@ -590,20 +606,30 @@ long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_
const uint8_t *filename, uint16_t filename_length);
/* Send a file control request.
* send_receive is 0 if we want the control packet to target a sending file, 1 if it targets a receiving file.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 if friend not valid.
* return -2 if friend not online.
* return -3 if file number invalid.
* return -4 if file control is bad.
* return -5 if file already paused.
* return -6 if resume file failed because it was only paused by the other.
* return -7 if resume file failed because it wasn't paused.
* return -8 if packet failed to send.
*/
int file_control(const Messenger *m, int32_t friendnumber, uint8_t send_receive, uint8_t filenumber, uint8_t message_id,
const uint8_t *data, uint16_t length);
int file_control(const Messenger *m, int32_t friendnumber, uint32_t filenumber, unsigned int control);
/* Send file data.
*
* return 1 on success
* return 0 on failure
* return 0 on success
* return -1 if friend not valid.
* return -2 if friend not online.
* return -3 if filenumber invalid.
* return -4 if file transfer not transferring.
* return -5 if trying to send too much data.
* return -6 if packet queue full.
*/
int file_data(const Messenger *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length);
int file_data(const Messenger *m, int32_t friendnumber, uint32_t filenumber, const uint8_t *data, uint16_t length);
/* Give the number of bytes left to be sent/received.
*

View File

@ -811,6 +811,173 @@ void tox_callback_friend_action(Tox *tox, tox_friend_action_cb *function, void *
m_callback_action(m, function, user_data);
}
bool tox_hash(uint8_t *hash, const uint8_t *data, size_t length)
{
if (!hash || !data) {
return 0;
}
crypto_hash_sha256(hash, data, length);
return 1;
}
bool tox_file_control(Tox *tox, uint32_t friend_number, uint32_t file_number, TOX_FILE_CONTROL control,
TOX_ERR_FILE_CONTROL *error)
{
Messenger *m = tox;
int ret = file_control(m, friend_number, file_number, control);
if (ret == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_OK);
return 1;
}
switch (ret) {
case -1:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND);
return 0;
case -2:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED);
return 0;
case -3:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_NOT_FOUND);
return 0;
case -4:
/* can't happen */
return 0;
case -5:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_ALREADY_PAUSED);
return 0;
case -6:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_DENIED);
return 0;
case -7:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_NOT_PAUSED);
return 0;
case -8:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_CONTROL_SEND_FAILED);
return 0;
}
/* can't happen */
return 0;
}
void tox_callback_file_control(Tox *tox, tox_file_control_cb *function, void *user_data)
{
Messenger *m = tox;
callback_file_control(m, function, user_data);
}
uint32_t tox_file_send(Tox *tox, uint32_t friend_number, TOX_FILE_KIND kind, uint64_t file_size,
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;
}
Messenger *m = tox;
long int file_num = new_filesender(m, friend_number, kind, file_size, filename, filename_length);
if (file_num >= 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_OK);
return file_num;
}
switch (file_num) {
case -1:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND);
return UINT32_MAX;
case -2:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_NAME_TOO_LONG);
return UINT32_MAX;
case -3:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_TOO_MANY);
return UINT32_MAX;
case -4:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED);
return UINT32_MAX;
}
/* can't happen */
return UINT32_MAX;
}
bool tox_file_send_chunk(Tox *tox, uint32_t friend_number, uint32_t file_number, const uint8_t *data, size_t length,
TOX_ERR_FILE_SEND_CHUNK *error)
{
Messenger *m = tox;
int ret = file_data(m, friend_number, file_number, data, length);
if (ret == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_OK);
return 1;
}
switch (ret) {
case -1:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_FRIEND_NOT_FOUND);
return 0;
case -2:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_FRIEND_NOT_CONNECTED);
return 0;
case -3:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_NOT_FOUND);
return 0;
case -4:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_NOT_TRANSFERRING);
return 0;
case -5:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_TOO_LARGE);
return 0;
case -6:
SET_ERROR_PARAMETER(error, TOX_ERR_FILE_SEND_CHUNK_QUEUE_FULL);
return 0;
}
/* can't happen */
return 0;
}
void tox_callback_file_request_chunk(Tox *tox, tox_file_request_chunk_cb *function, void *user_data)
{
Messenger *m = tox;
callback_file_reqchunk(m, function, user_data);
}
void tox_callback_file_receive(Tox *tox, tox_file_receive_cb *function, void *user_data)
{
Messenger *m = tox;
callback_file_sendrequest(m, function, user_data);
}
void tox_callback_file_receive_chunk(Tox *tox, tox_file_receive_chunk_cb *function, void *user_data)
{
Messenger *m = tox;
callback_file_data(m, function, user_data);
}
static void set_custom_packet_error(int ret, TOX_ERR_SEND_CUSTOM_PACKET *error)
{
switch (ret) {

View File

@ -1485,7 +1485,11 @@ typedef enum TOX_ERR_FILE_CONTROL {
/**
* A PAUSE control was sent, but the file transfer was already paused.
*/
TOX_ERR_FILE_CONTROL_ALREADY_PAUSED
TOX_ERR_FILE_CONTROL_ALREADY_PAUSED,
/**
* Packet failed to send.
*/
TOX_ERR_FILE_CONTROL_SEND_FAILED
} TOX_ERR_FILE_CONTROL;
/**
@ -1612,6 +1616,7 @@ typedef enum TOX_ERR_FILE_SEND {
*
* @return A file number used as an identifier in subsequent callbacks. This
* 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, TOX_FILE_KIND kind, uint64_t file_size,
const uint8_t *filename, size_t filename_length, TOX_ERR_FILE_SEND *error);
@ -1635,12 +1640,21 @@ typedef enum TOX_ERR_FILE_SEND_CHUNK {
* No file transfer with the given file number was found for the given friend.
*/
TOX_ERR_FILE_SEND_CHUNK_NOT_FOUND,
/**
* File transfer was found but isn't in a transferring state: (paused, done,
* broken, etc...) (happens only when not called from the request chunk callback).
*/
TOX_ERR_FILE_SEND_CHUNK_NOT_TRANSFERRING,
/**
* Attempted to send more data than requested. The requested data size is
* adjusted according to maximum transmission unit and the expected end of
* the file. Trying to send more will result in no data being sent.
*/
TOX_ERR_FILE_SEND_CHUNK_TOO_LARGE
TOX_ERR_FILE_SEND_CHUNK_TOO_LARGE,
/**
* Packet queue is full.
*/
TOX_ERR_FILE_SEND_CHUNK_QUEUE_FULL
} TOX_ERR_FILE_SEND_CHUNK;
/**