From 0207fcdfb0e43d425abb026b08c074a6822e0349 Mon Sep 17 00:00:00 2001 From: irungentoo Date: Tue, 10 Mar 2015 17:31:50 -0400 Subject: [PATCH] Implementation of new api file transfers. Everything should work except resuming. --- auto_tests/tox_test.c | 139 ++++++++---- toxcore/Messenger.c | 500 +++++++++++++++++++++++++++--------------- toxcore/Messenger.h | 74 +++++-- toxcore/tox.c | 167 ++++++++++++++ toxcore/tox.h | 18 +- 5 files changed, 646 insertions(+), 252 deletions(-) diff --git a/auto_tests/tox_test.c b/auto_tests/tox_test.c index d7a44fb2..077e51fb 100644 --- a/auto_tests/tox_test.c +++ b/auto_tests/tox_test.c @@ -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); diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index ee76b103..08b62660 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -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); } } } diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index ca6b4896..5215f989 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -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. * diff --git a/toxcore/tox.c b/toxcore/tox.c index fc6ffc2b..bcb871fc 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -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) { diff --git a/toxcore/tox.h b/toxcore/tox.h index 23d37cd9..b8cac443 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -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; /**