mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Updated with upstream
This commit is contained in:
commit
d1fd3e36a6
|
@ -419,6 +419,7 @@ START_TEST(test_client)
|
|||
uint8_t f2_public_key[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t f2_secret_key[crypto_box_SECRETKEYBYTES];
|
||||
crypto_box_keypair(f2_public_key, f2_secret_key);
|
||||
ip_port_tcp_s.port = htons(ports[rand() % NUM_PORTS]);
|
||||
TCP_Client_Connection *conn2 = new_TCP_connection(ip_port_tcp_s, self_public_key, f2_public_key, f2_secret_key, 0);
|
||||
routing_response_handler(conn, response_callback, ((void *)conn) + 2);
|
||||
routing_status_handler(conn, status_callback, (void *)2);
|
||||
|
|
|
@ -29,13 +29,13 @@ unsigned char known_key2[crypto_box_BEFORENMBYTES] = {0x7a, 0xfa, 0x95, 0x45, 0x
|
|||
// same as above, except standard opslimit instead of extra ops limit for test_known_kdf, and hash pw before kdf for compat
|
||||
|
||||
/* cause I'm shameless */
|
||||
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||
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)
|
||||
return;
|
||||
|
||||
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
|
||||
tox_add_friend_norequest(m, public_key);
|
||||
tox_friend_add_norequest(m, public_key, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,109 +55,116 @@ END_TEST
|
|||
|
||||
START_TEST(test_save_friend)
|
||||
{
|
||||
Tox *tox1 = tox_new(0);
|
||||
Tox *tox2 = tox_new(0);
|
||||
Tox *tox1 = tox_new(0, 0, 0, 0);
|
||||
Tox *tox2 = tox_new(0, 0, 0, 0);
|
||||
ck_assert_msg(tox1 || tox2, "Failed to create 2 tox instances");
|
||||
uint32_t to_compare = 974536;
|
||||
tox_callback_friend_request(tox2, accept_friend_request, &to_compare);
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(tox2, address);
|
||||
int test = tox_add_friend(tox1, address, (uint8_t *)"Gentoo", 7);
|
||||
ck_assert_msg(test == 0, "Failed to add friend error code: %i", test);
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(tox2, address);
|
||||
uint32_t test = tox_friend_add(tox1, address, (uint8_t *)"Gentoo", 7, 0);
|
||||
ck_assert_msg(test != UINT32_MAX, "Failed to add friend");
|
||||
|
||||
uint32_t size = tox_encrypted_size(tox1);
|
||||
size_t size = tox_get_savedata_size(tox1);
|
||||
uint8_t data[size];
|
||||
test = tox_encrypted_save(tox1, data, "correcthorsebatterystaple", 25);
|
||||
ck_assert_msg(test == 0, "failed to encrypted save");
|
||||
ck_assert_msg(tox_is_save_encrypted(data) == 1, "magic number missing");
|
||||
tox_get_savedata(tox1, data);
|
||||
size_t size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
uint8_t enc_data[size2];
|
||||
TOX_ERR_ENCRYPTION error1;
|
||||
bool ret = tox_pass_encrypt(data, size, "correcthorsebatterystaple", 25, enc_data, &error1);
|
||||
ck_assert_msg(ret, "failed to encrypted save: %u", error1);
|
||||
ck_assert_msg(tox_is_data_encrypted(enc_data), "magic number missing");
|
||||
|
||||
Tox *tox3 = tox_new(0);
|
||||
test = tox_encrypted_load(tox3, data, size, "correcthorsebatterystaple", 25);
|
||||
ck_assert_msg(test == 0, "failed to encrypted load");
|
||||
uint8_t address2[TOX_CLIENT_ID_SIZE];
|
||||
test = tox_get_client_id(tox3, 0, address2);
|
||||
ck_assert_msg(test == 0, "no friends!");
|
||||
ck_assert_msg(memcmp(address, address2, TOX_CLIENT_ID_SIZE) == 0, "addresses don't match!");
|
||||
TOX_ERR_NEW err2;
|
||||
Tox *tox3 = tox_new(0, enc_data, size2, &err2);
|
||||
ck_assert_msg(err2 == TOX_ERR_NEW_LOAD_ENCRYPTED, "wrong error! %u. should fail with %u", err2,
|
||||
TOX_ERR_NEW_LOAD_ENCRYPTED);
|
||||
uint8_t dec_data[size];
|
||||
TOX_ERR_DECRYPTION err3;
|
||||
ret = tox_pass_decrypt(enc_data, size2, "correcthorsebatterystaple", 25, dec_data, &err3);
|
||||
ck_assert_msg(ret, "failed to decrypt save: %u", err3);
|
||||
tox3 = tox_new(0, dec_data, size, &err2);
|
||||
ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to load from decrypted data: %u", err2);
|
||||
uint8_t address2[TOX_PUBLIC_KEY_SIZE];
|
||||
ret = tox_friend_get_public_key(tox3, 0, address2, 0);
|
||||
ck_assert_msg(ret, "no friends!");
|
||||
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match!");
|
||||
|
||||
size = tox_encrypted_size(tox3);
|
||||
size = tox_get_savedata_size(tox3);
|
||||
uint8_t data2[size];
|
||||
uint8_t key[32 + crypto_box_BEFORENMBYTES];
|
||||
memcpy(key, salt, 32);
|
||||
memcpy(key + 32, known_key2, crypto_box_BEFORENMBYTES);
|
||||
test = tox_encrypted_key_save(tox3, data2, key);
|
||||
ck_assert_msg(test == 0, "failed to encrypted save the second");
|
||||
ck_assert_msg(tox_is_save_encrypted(data2) == 1, "magic number the second missing");
|
||||
tox_get_savedata(tox3, data2);
|
||||
TOX_PASS_KEY key;
|
||||
memcpy(key.salt, salt, 32);
|
||||
memcpy(key.key, known_key2, crypto_box_BEFORENMBYTES);
|
||||
size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
uint8_t encdata2[size2];
|
||||
ret = tox_pass_key_encrypt(data2, size, &key, encdata2, &error1);
|
||||
ck_assert_msg(ret, "failed to key encrypt %u", error1);
|
||||
ck_assert_msg(tox_is_data_encrypted(encdata2), "magic number the second missing");
|
||||
|
||||
// first test tox_encrypted_key_load
|
||||
Tox *tox4 = tox_new(0);
|
||||
test = tox_encrypted_key_load(tox4, data2, size, key);
|
||||
ck_assert_msg(test == 0, "failed to encrypted load the second");
|
||||
uint8_t address4[TOX_CLIENT_ID_SIZE];
|
||||
test = tox_get_client_id(tox4, 0, address4);
|
||||
ck_assert_msg(test == 0, "no friends! the second");
|
||||
ck_assert_msg(memcmp(address, address2, TOX_CLIENT_ID_SIZE) == 0, "addresses don't match! the second");
|
||||
|
||||
// now test compaitibilty with tox_encrypted_load, first manually...
|
||||
uint8_t out1[size], out2[size];
|
||||
printf("Trying to decrypt from pw:\n");
|
||||
uint32_t sz1 = tox_pass_decrypt(data2, size, pw, pwlen, out1);
|
||||
uint32_t sz2 = tox_pass_key_decrypt(data2, size, key, out2);
|
||||
ck_assert_msg(sz1 == sz2, "differing output sizes");
|
||||
ck_assert_msg(memcmp(out1, out2, sz1) == 0, "differing output data");
|
||||
ret = tox_pass_decrypt(encdata2, size2, pw, pwlen, out1, &err3);
|
||||
ck_assert_msg(ret, "failed to pw decrypt %u", err3);
|
||||
ret = tox_pass_key_decrypt(encdata2, size2, &key, out2, &err3);
|
||||
ck_assert_msg(ret, "failed to key decrypt %u", err3);
|
||||
ck_assert_msg(memcmp(out1, out2, size) == 0, "differing output data");
|
||||
|
||||
// and now with the code in use (I only bothered with manually to debug this, and it seems a waste
|
||||
// to remove the manual check now that it's there)
|
||||
Tox *tox5 = tox_new(0);
|
||||
test = tox_encrypted_load(tox5, data2, size, pw, pwlen);
|
||||
ck_assert_msg(test == 0, "failed to encrypted load the third");
|
||||
uint8_t address5[TOX_CLIENT_ID_SIZE];
|
||||
test = tox_get_client_id(tox4, 0, address5);
|
||||
ck_assert_msg(test == 0, "no friends! the third");
|
||||
ck_assert_msg(memcmp(address, address2, TOX_CLIENT_ID_SIZE) == 0, "addresses don't match! the third");
|
||||
Tox *tox4 = tox_new(0, out1, size, &err2);
|
||||
ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to new the third");
|
||||
uint8_t address5[TOX_PUBLIC_KEY_SIZE];
|
||||
ret = tox_friend_get_public_key(tox4, 0, address5, 0);
|
||||
ck_assert_msg(ret, "no friends! the third");
|
||||
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match! the third");
|
||||
|
||||
tox_kill(tox1);
|
||||
tox_kill(tox2);
|
||||
tox_kill(tox3);
|
||||
tox_kill(tox4);
|
||||
tox_kill(tox5);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_keys)
|
||||
{
|
||||
uint8_t key[tox_pass_key_length()];
|
||||
tox_derive_key_from_pass("123qweasdzxc", 12, key);
|
||||
TOX_ERR_ENCRYPTION encerr;
|
||||
TOX_ERR_DECRYPTION decerr;
|
||||
TOX_ERR_KEY_DERIVATION keyerr;
|
||||
TOX_PASS_KEY key;
|
||||
bool ret = tox_derive_key_from_pass("123qweasdzxc", 12, &key, &keyerr);
|
||||
ck_assert_msg(ret, "generic failure 1: %u", keyerr);
|
||||
uint8_t *string = "No Patrick, mayonnaise is not an instrument."; // 44
|
||||
|
||||
uint8_t encrypted[44 + tox_pass_encryption_extra_length()];
|
||||
int sz = tox_pass_key_encrypt(string, 44, key, encrypted);
|
||||
uint8_t encrypted[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
|
||||
ret = tox_pass_key_encrypt(string, 44, &key, encrypted, &encerr);
|
||||
ck_assert_msg(ret, "generic failure 2: %u", encerr);
|
||||
|
||||
uint8_t encrypted2[44 + tox_pass_encryption_extra_length()];
|
||||
int sz2 = tox_pass_encrypt(string, 44, "123qweasdzxc", 12, encrypted2);
|
||||
uint8_t encrypted2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
|
||||
ret = tox_pass_encrypt(string, 44, "123qweasdzxc", 12, encrypted2, &encerr);
|
||||
ck_assert_msg(ret, "generic failure 3: %u", encerr);
|
||||
|
||||
ck_assert_msg(sz == sz2, "an encryption failed");
|
||||
uint8_t out1[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
|
||||
uint8_t out2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
|
||||
|
||||
uint8_t out1[44 + tox_pass_encryption_extra_length()];
|
||||
uint8_t out2[44 + tox_pass_encryption_extra_length()];
|
||||
|
||||
sz = tox_pass_key_decrypt(encrypted, 44 + tox_pass_encryption_extra_length(), key, out1);
|
||||
ck_assert_msg(sz == 44, "sz isn't right");
|
||||
ret = tox_pass_key_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, &key, out1, &decerr);
|
||||
ck_assert_msg(ret, "generic failure 4: %u", decerr);
|
||||
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 1 failed");
|
||||
|
||||
sz2 = tox_pass_decrypt(encrypted2, 44 + tox_pass_encryption_extra_length(), "123qweasdzxc", 12, out2);
|
||||
ck_assert_msg(sz2 == 44, "sz2 isn't right");
|
||||
ret = tox_pass_decrypt(encrypted2, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, "123qweasdzxc", 12, out2, &decerr);
|
||||
ck_assert_msg(ret, "generic failure 5: %u", decerr);
|
||||
ck_assert_msg(memcmp(out2, string, 44) == 0, "decryption 2 failed");
|
||||
|
||||
// test that pass_decrypt can decrypt things from pass_key_encrypt
|
||||
sz = tox_pass_decrypt(encrypted, 44 + tox_pass_encryption_extra_length(), "123qweasdzxc", 12, out1);
|
||||
ck_assert_msg(sz == 44, "sz isn't right");
|
||||
ret = tox_pass_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, "123qweasdzxc", 12, out1, &decerr);
|
||||
ck_assert_msg(ret, "generic failure 6: %u", decerr);
|
||||
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 3 failed");
|
||||
|
||||
uint8_t salt[tox_pass_salt_length()];
|
||||
ck_assert_msg(0 == tox_get_salt(encrypted, salt), "couldn't get salt");
|
||||
uint8_t key2[tox_pass_key_length()];
|
||||
tox_derive_key_with_salt("123qweasdzxc", 12, salt, key2);
|
||||
ck_assert_msg(0 == memcmp(key, key2, tox_pass_key_length()), "salt comparison failed");
|
||||
uint8_t salt[TOX_PASS_SALT_LENGTH];
|
||||
ck_assert_msg(tox_get_salt(encrypted, salt), "couldn't get salt");
|
||||
TOX_PASS_KEY key2;
|
||||
ret = tox_derive_key_with_salt("123qweasdzxc", 12, salt, &key2, &keyerr);
|
||||
ck_assert_msg(ret, "generic failure 7: %u", keyerr);
|
||||
ck_assert_msg(0 == memcmp(&key, &key2, sizeof(TOX_PASS_KEY)), "salt comparison failed");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
@ -165,7 +172,7 @@ Suite *encryptsave_suite(void)
|
|||
{
|
||||
Suite *s = suite_create("encryptsave");
|
||||
|
||||
DEFTESTCASE_SLOW(known_kdf, 60); /* is 5-10 seconds on my computer, but is directly dependent on CPU */
|
||||
DEFTESTCASE_SLOW(known_kdf, 60);
|
||||
DEFTESTCASE_SLOW(save_friend, 20);
|
||||
DEFTESTCASE_SLOW(keys, 30);
|
||||
|
||||
|
|
|
@ -50,10 +50,10 @@ START_TEST(test_m_sendmesage)
|
|||
int bad_len = MAX_CRYPTO_PACKET_SIZE;
|
||||
|
||||
|
||||
ck_assert(m_sendmessage(m, -1, (uint8_t *)message, good_len) == 0);
|
||||
ck_assert(m_sendmessage(m, REALLY_BIG_NUMBER, (uint8_t *)message, good_len) == 0);
|
||||
ck_assert(m_sendmessage(m, 17, (uint8_t *)message, good_len) == 0);
|
||||
ck_assert(m_sendmessage(m, friend_id_num, (uint8_t *)message, bad_len) == 0);
|
||||
ck_assert(m_send_message_generic(m, -1, MESSAGE_NORMAL, (uint8_t *)message, good_len, 0) == -1);
|
||||
ck_assert(m_send_message_generic(m, REALLY_BIG_NUMBER, MESSAGE_NORMAL, (uint8_t *)message, good_len, 0) == -1);
|
||||
ck_assert(m_send_message_generic(m, 17, MESSAGE_NORMAL, (uint8_t *)message, good_len, 0) == -1);
|
||||
ck_assert(m_send_message_generic(m, friend_id_num, MESSAGE_NORMAL, (uint8_t *)message, bad_len, 0) == -2);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
@ -68,10 +68,10 @@ START_TEST(test_m_get_userstatus_size)
|
|||
rc = m_get_statusmessage_size(m, friend_id_num);
|
||||
|
||||
/* this WILL error if the original m_addfriend_norequest() failed */
|
||||
ck_assert_msg((rc > 0 && rc <= MAX_STATUSMESSAGE_LENGTH),
|
||||
"m_get_statusmessage_size is returning out of range values!\n"
|
||||
ck_assert_msg((rc >= 0 && rc <= MAX_STATUSMESSAGE_LENGTH),
|
||||
"m_get_statusmessage_size is returning out of range values! (%i)\n"
|
||||
"(this can be caused by the error of m_addfriend_norequest"
|
||||
" in the beginning of the suite)\n");
|
||||
" in the beginning of the suite)\n", rc);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
@ -337,7 +337,7 @@ int main(int argc, char *argv[])
|
|||
/* IPv6 status from global define */
|
||||
Messenger_Options options = {0};
|
||||
options.ipv6enabled = TOX_ENABLE_IPV6_DEFAULT;
|
||||
m = new_messenger(&options);
|
||||
m = new_messenger(&options, 0);
|
||||
|
||||
/* setup a default friend and friendnum */
|
||||
if (m_addfriend_norequest(m, (uint8_t *)friend_id) < 0)
|
||||
|
|
|
@ -275,7 +275,8 @@ Onions *new_onions(uint16_t port)
|
|||
DHT *dht = new_DHT(new_networking(ip, port));
|
||||
on->onion = new_onion(dht);
|
||||
on->onion_a = new_onion_announce(dht);
|
||||
on->onion_c = new_onion_client(new_net_crypto(dht, 0));
|
||||
TCP_Proxy_Info inf = {0};
|
||||
on->onion_c = new_onion_client(new_net_crypto(dht, &inf));
|
||||
|
||||
if (on->onion && on->onion_a && on->onion_c)
|
||||
return on;
|
||||
|
@ -294,15 +295,70 @@ void kill_onions(Onions *on)
|
|||
{
|
||||
Networking_Core *net = on->onion->dht->net;
|
||||
DHT *dht = on->onion->dht;
|
||||
Net_Crypto *c = on->onion_c->c;
|
||||
kill_onion_client(on->onion_c);
|
||||
kill_onion_announce(on->onion_a);
|
||||
kill_onion(on->onion);
|
||||
kill_net_crypto(c);
|
||||
kill_DHT(dht);
|
||||
kill_networking(net);
|
||||
free(on);
|
||||
}
|
||||
|
||||
#define NUM_ONIONS 50
|
||||
#define NUM_FIRST 7
|
||||
#define NUM_LAST 37
|
||||
|
||||
_Bool first_ip, last_ip;
|
||||
void dht_ip_callback(void *object, int32_t number, IP_Port ip_port)
|
||||
{
|
||||
if (NUM_FIRST == number) {
|
||||
first_ip = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (NUM_LAST == number) {
|
||||
last_ip = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
ck_abort_msg("Error.");
|
||||
}
|
||||
|
||||
_Bool first, last;
|
||||
uint8_t first_dht_pk[crypto_box_PUBLICKEYBYTES];
|
||||
uint8_t last_dht_pk[crypto_box_PUBLICKEYBYTES];
|
||||
|
||||
static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key)
|
||||
{
|
||||
Onions *on = object;
|
||||
uint16_t count = 0;
|
||||
int ret = DHT_addfriend(on->onion->dht, dht_public_key, &dht_ip_callback, object, number, &count);
|
||||
ck_assert_msg(count == 1, "Count not 1");
|
||||
ck_assert_msg(ret == 0, "DHT_addfriend() did not return 0");
|
||||
|
||||
if (NUM_FIRST == number && !first) {
|
||||
first = 1;
|
||||
|
||||
if (memcmp(dht_public_key, last_dht_pk, crypto_box_PUBLICKEYBYTES) != 0) {
|
||||
ck_abort_msg("Error wrong dht key.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (NUM_LAST == number && !last) {
|
||||
last = 1;
|
||||
|
||||
if (memcmp(dht_public_key, first_dht_pk, crypto_box_PUBLICKEYBYTES) != 0) {
|
||||
ck_abort_msg("Error wrong dht key.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ck_abort_msg("Error.");
|
||||
}
|
||||
|
||||
START_TEST(test_announce)
|
||||
{
|
||||
|
@ -340,6 +396,8 @@ START_TEST(test_announce)
|
|||
c_sleep(50);
|
||||
}
|
||||
|
||||
printf("connected\n");
|
||||
|
||||
for (i = 0; i < 25 * 2; ++i) {
|
||||
for (j = 0; j < NUM_ONIONS; ++j) {
|
||||
do_onions(onions[j]);
|
||||
|
@ -348,37 +406,40 @@ START_TEST(test_announce)
|
|||
c_sleep(50);
|
||||
}
|
||||
|
||||
onion_addfriend(onions[7]->onion_c, onions[37]->onion_c->c->self_public_key);
|
||||
int frnum = onion_addfriend(onions[37]->onion_c, onions[7]->onion_c->c->self_public_key);
|
||||
memcpy(first_dht_pk, onions[NUM_FIRST]->onion->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(last_dht_pk, onions[NUM_LAST]->onion->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
printf("adding friend\n");
|
||||
int frnum_f = onion_addfriend(onions[NUM_FIRST]->onion_c, onions[NUM_LAST]->onion_c->c->self_public_key);
|
||||
int frnum = onion_addfriend(onions[NUM_LAST]->onion_c, onions[NUM_FIRST]->onion_c->c->self_public_key);
|
||||
|
||||
onion_dht_pk_callback(onions[NUM_FIRST]->onion_c, frnum_f, &dht_pk_callback, onions[NUM_FIRST], NUM_FIRST);
|
||||
onion_dht_pk_callback(onions[NUM_LAST]->onion_c, frnum, &dht_pk_callback, onions[NUM_LAST], NUM_LAST);
|
||||
|
||||
int ok = -1;
|
||||
|
||||
IP_Port ip_port;
|
||||
|
||||
while (ok == -1) {
|
||||
for (i = 0; i < NUM_ONIONS; ++i) {
|
||||
networking_poll(onions[i]->onion->net);
|
||||
do_onion_client(onions[i]->onion_c);
|
||||
}
|
||||
|
||||
ok = onion_getfriendip(onions[37]->onion_c, frnum, &ip_port);
|
||||
|
||||
c_sleep(50);
|
||||
}
|
||||
|
||||
printf("id discovered\n");
|
||||
|
||||
while (ok != 1) {
|
||||
while (!first || !last) {
|
||||
for (i = 0; i < NUM_ONIONS; ++i) {
|
||||
do_onions(onions[i]);
|
||||
}
|
||||
|
||||
ok = onion_getfriendip(onions[37]->onion_c, frnum, &ip_port);
|
||||
c_sleep(50);
|
||||
}
|
||||
|
||||
printf("Waiting for ips\n");
|
||||
|
||||
while (!first_ip || !last_ip) {
|
||||
for (i = 0; i < NUM_ONIONS; ++i) {
|
||||
do_onions(onions[i]);
|
||||
}
|
||||
|
||||
c_sleep(50);
|
||||
}
|
||||
|
||||
ck_assert_msg(ip_port.port == onions[7]->onion->net->port, "Port in returned ip not correct.");
|
||||
onion_getfriendip(onions[NUM_LAST]->onion_c, frnum, &ip_port);
|
||||
ck_assert_msg(ip_port.port == onions[NUM_FIRST]->onion->net->port, "Port in returned ip not correct.");
|
||||
|
||||
for (i = 0; i < NUM_ONIONS; ++i) {
|
||||
kill_onions(onions[i]);
|
||||
|
@ -391,7 +452,7 @@ Suite *onion_suite(void)
|
|||
Suite *s = suite_create("Onion");
|
||||
|
||||
DEFTESTCASE_SLOW(basic, 5);
|
||||
//DEFTESTCASE_SLOW(announce, 50); //TODO: fix test.
|
||||
DEFTESTCASE_SLOW(announce, 70);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,22 +21,28 @@
|
|||
#define c_sleep(x) usleep(1000*x)
|
||||
#endif
|
||||
|
||||
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
|
||||
tox_add_friend_norequest(m, public_key);
|
||||
tox_friend_add_norequest(m, public_key, 0);
|
||||
}
|
||||
}
|
||||
uint32_t messages_received;
|
||||
|
||||
void print_message(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||
void print_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
|
||||
void *userdata)
|
||||
{
|
||||
if (*((uint32_t *)userdata) != 974536)
|
||||
return;
|
||||
|
||||
if (type != TOX_MESSAGE_TYPE_NORMAL) {
|
||||
ck_abort_msg("Bad type");
|
||||
}
|
||||
|
||||
uint8_t cmp_msg[TOX_MAX_MESSAGE_LENGTH];
|
||||
memset(cmp_msg, 'G', sizeof(cmp_msg));
|
||||
|
||||
|
@ -46,7 +52,7 @@ void print_message(Tox *m, int friendnumber, const uint8_t *string, uint16_t len
|
|||
|
||||
uint32_t name_changes;
|
||||
|
||||
void print_nickchange(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||
void print_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
|
||||
{
|
||||
if (*((uint32_t *)userdata) != 974536)
|
||||
return;
|
||||
|
@ -55,9 +61,19 @@ void print_nickchange(Tox *m, int friendnumber, const uint8_t *string, uint16_t
|
|||
++name_changes;
|
||||
}
|
||||
|
||||
uint32_t status_m_changes;
|
||||
void print_status_m_change(Tox *tox, uint32_t friend_number, const uint8_t *message, size_t length, void *user_data)
|
||||
{
|
||||
if (*((uint32_t *)user_data) != 974536)
|
||||
return;
|
||||
|
||||
if (length == sizeof("Installing Gentoo") && memcmp(message, "Installing Gentoo", sizeof("Installing Gentoo")) == 0)
|
||||
++status_m_changes;
|
||||
}
|
||||
|
||||
uint32_t typing_changes;
|
||||
|
||||
void print_typingchange(Tox *m, int friendnumber, uint8_t typing, void *userdata)
|
||||
void print_typingchange(Tox *m, uint32_t friendnumber, bool typing, void *userdata)
|
||||
{
|
||||
if (*((uint32_t *)userdata) != 974536)
|
||||
return;
|
||||
|
@ -70,12 +86,12 @@ void print_typingchange(Tox *m, int friendnumber, uint8_t typing, void *userdata
|
|||
|
||||
uint32_t custom_packet;
|
||||
|
||||
int handle_custom_packet(Tox *m, int32_t friend_num, const uint8_t *data, uint32_t len, void *object)
|
||||
void handle_custom_packet(Tox *m, uint32_t friend_num, const uint8_t *data, size_t len, void *object)
|
||||
{
|
||||
uint8_t number = *((uint32_t *)object);
|
||||
|
||||
if (len != TOX_MAX_CUSTOM_PACKET_SIZE)
|
||||
return -1;
|
||||
return;
|
||||
|
||||
uint8_t f_data[len];
|
||||
memset(f_data, number, len);
|
||||
|
@ -83,54 +99,168 @@ int handle_custom_packet(Tox *m, int32_t friend_num, const uint8_t *data, uint32
|
|||
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 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (*((uint32_t *)userdata) != 974536)
|
||||
return;
|
||||
|
||||
if (filename_length == sizeof("Gentoo.exe") && memcmp(filename, "Gentoo.exe", sizeof("Gentoo.exe")) == 0)
|
||||
++file_accepted;
|
||||
|
||||
file_size = filesize;
|
||||
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_ACCEPT, NULL, 0);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
sendf_ok = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
uint64_t sending_pos;
|
||||
|
||||
uint8_t file_cmp_id[TOX_FILE_ID_LENGTH];
|
||||
uint8_t filenum;
|
||||
uint32_t file_accepted;
|
||||
uint64_t file_size;
|
||||
void tox_file_receive(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t kind, uint64_t filesize,
|
||||
const uint8_t *filename, size_t filename_length, void *userdata)
|
||||
{
|
||||
if (*((uint32_t *)userdata) != 974536)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (filesize) {
|
||||
sending_pos = size_recv = 1337;
|
||||
|
||||
TOX_ERR_FILE_SEEK err_s;
|
||||
|
||||
if (!tox_file_seek(tox, friend_number, file_number, 1337, &err_s)) {
|
||||
ck_abort_msg("tox_file_seek error");
|
||||
}
|
||||
|
||||
ck_assert_msg(err_s == TOX_ERR_FILE_SEEK_OK, "tox_file_seek wrong error");
|
||||
} else {
|
||||
sending_pos = size_recv = 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);
|
||||
}
|
||||
|
||||
TOX_ERR_FILE_SEEK err_s;
|
||||
|
||||
if (tox_file_seek(tox, friend_number, file_number, 1234, &err_s)) {
|
||||
ck_abort_msg("tox_file_seek no error");
|
||||
}
|
||||
|
||||
ck_assert_msg(err_s == TOX_ERR_FILE_SEEK_DENIED, "tox_file_seek wrong error");
|
||||
}
|
||||
|
||||
uint32_t sendf_ok;
|
||||
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;
|
||||
|
||||
/* First send file num is 0.*/
|
||||
if (file_number == 0 && control == TOX_FILE_CONTROL_RESUME)
|
||||
sendf_ok = 1;
|
||||
}
|
||||
|
||||
uint64_t max_sending;
|
||||
_Bool m_send_reached;
|
||||
uint8_t sending_num;
|
||||
_Bool file_sending_done;
|
||||
void tox_file_chunk_request(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 %llu", position);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
if (file_sending_done) {
|
||||
ck_abort_msg("File sending already done.");
|
||||
}
|
||||
|
||||
file_sending_done = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (position + length > max_sending) {
|
||||
if (m_send_reached) {
|
||||
ck_abort_msg("Requested done file tranfer.");
|
||||
}
|
||||
|
||||
length = max_sending - position;
|
||||
m_send_reached = 1;
|
||||
}
|
||||
|
||||
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, position, 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t num;
|
||||
_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 *)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,33 +268,59 @@ 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");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int connected_t1;
|
||||
void tox_connection_status(Tox *tox, TOX_CONNECTION connection_status, void *user_data)
|
||||
{
|
||||
if (*((uint32_t *)user_data) != 974536)
|
||||
return;
|
||||
|
||||
if (connected_t1 && !connection_status)
|
||||
ck_abort_msg("Tox went offline");
|
||||
|
||||
connected_t1 = connection_status;
|
||||
}
|
||||
|
||||
START_TEST(test_one)
|
||||
{
|
||||
Tox *tox1 = tox_new(0);
|
||||
Tox *tox2 = tox_new(0);
|
||||
Tox *tox1 = tox_new(0, 0, 0, 0);
|
||||
Tox *tox2 = tox_new(0, 0, 0, 0);
|
||||
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(tox1, address);
|
||||
ck_assert_msg(tox_add_friend(tox1, address, (uint8_t *)"m", 1) == TOX_FAERR_OWNKEY, "Adding own address worked.");
|
||||
{
|
||||
TOX_ERR_GET_PORT error;
|
||||
ck_assert_msg(tox_self_get_udp_port(tox1, &error) == 33445, "First Tox instance did not bind to udp port 33445.\n");
|
||||
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
|
||||
}
|
||||
|
||||
tox_get_address(tox2, address);
|
||||
uint8_t message[TOX_MAX_FRIENDREQUEST_LENGTH + 1];
|
||||
ck_assert_msg(tox_add_friend(tox1, address, NULL, 0) == TOX_FAERR_NOMESSAGE, "Sending request with no message worked.");
|
||||
ck_assert_msg(tox_add_friend(tox1, address, message, sizeof(message)) == TOX_FAERR_TOOLONG,
|
||||
"TOX_MAX_FRIENDREQUEST_LENGTH is too big.");
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(tox1, address);
|
||||
TOX_ERR_FRIEND_ADD error;
|
||||
uint32_t ret = tox_friend_add(tox1, address, (uint8_t *)"m", 1, &error);
|
||||
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_OWN_KEY, "Adding own address worked.");
|
||||
|
||||
tox_self_get_address(tox2, address);
|
||||
uint8_t message[TOX_MAX_FRIEND_REQUEST_LENGTH + 1];
|
||||
ret = tox_friend_add(tox1, address, NULL, 0, &error);
|
||||
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_NULL, "Sending request with no message worked.");
|
||||
ret = tox_friend_add(tox1, address, message, 0, &error);
|
||||
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_NO_MESSAGE, "Sending request with no message worked.");
|
||||
ret = tox_friend_add(tox1, address, message, sizeof(message), &error);
|
||||
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_TOO_LONG,
|
||||
"TOX_MAX_FRIEND_REQUEST_LENGTH is too big.");
|
||||
|
||||
address[0]++;
|
||||
ck_assert_msg(tox_add_friend(tox1, address, (uint8_t *)"m", 1) == TOX_FAERR_BADCHECKSUM,
|
||||
ret = tox_friend_add(tox1, address, (uint8_t *)"m", 1, &error);
|
||||
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_BAD_CHECKSUM,
|
||||
"Adding address with bad checksum worked.");
|
||||
|
||||
tox_get_address(tox2, address);
|
||||
ck_assert_msg(tox_add_friend(tox1, address, message, TOX_MAX_FRIENDREQUEST_LENGTH) == 0, "Failed to add friend.");
|
||||
ck_assert_msg(tox_add_friend(tox1, address, message, TOX_MAX_FRIENDREQUEST_LENGTH) == TOX_FAERR_ALREADYSENT,
|
||||
"Adding friend twice worked.");
|
||||
tox_self_get_address(tox2, address);
|
||||
ret = tox_friend_add(tox1, address, message, TOX_MAX_FRIEND_REQUEST_LENGTH, &error);
|
||||
ck_assert_msg(ret == 0 && error == TOX_ERR_FRIEND_ADD_OK, "Failed to add friend.");
|
||||
ret = tox_friend_add(tox1, address, message, TOX_MAX_FRIEND_REQUEST_LENGTH, &error);
|
||||
ck_assert_msg(ret == UINT32_MAX && error == TOX_ERR_FRIEND_ADD_ALREADY_SENT, "Adding friend twice worked.");
|
||||
|
||||
uint8_t name[TOX_MAX_NAME_LENGTH];
|
||||
int i;
|
||||
|
@ -173,22 +329,23 @@ START_TEST(test_one)
|
|||
name[i] = rand();
|
||||
}
|
||||
|
||||
tox_set_name(tox1, name, sizeof(name));
|
||||
ck_assert_msg(tox_get_self_name_size(tox1) == sizeof(name), "Can't set name of TOX_MAX_NAME_LENGTH");
|
||||
tox_self_set_name(tox1, name, sizeof(name), 0);
|
||||
ck_assert_msg(tox_self_get_name_size(tox1) == sizeof(name), "Can't set name of TOX_MAX_NAME_LENGTH");
|
||||
|
||||
size_t save_size = tox_size(tox1);
|
||||
size_t save_size = tox_get_savedata_size(tox1);
|
||||
uint8_t data[save_size];
|
||||
tox_save(tox1, data);
|
||||
tox_get_savedata(tox1, data);
|
||||
|
||||
tox_kill(tox2);
|
||||
tox2 = tox_new(0);
|
||||
ck_assert_msg(tox_load(tox2, data, save_size) == 0, "Load failed");
|
||||
TOX_ERR_NEW err_n;
|
||||
|
||||
size_t length = tox_get_self_name_size(tox2);
|
||||
ck_assert_msg(tox_get_self_name_size(tox2) == sizeof name, "Wrong name size.");
|
||||
tox2 = tox_new(0, data, save_size, &err_n);
|
||||
ck_assert_msg(err_n == TOX_ERR_NEW_OK, "Load failed");
|
||||
|
||||
ck_assert_msg(tox_self_get_name_size(tox2) == sizeof name, "Wrong name size.");
|
||||
|
||||
uint8_t new_name[TOX_MAX_NAME_LENGTH] = { 0 };
|
||||
ck_assert_msg(tox_get_self_name(tox2, new_name) == TOX_MAX_NAME_LENGTH, "Wrong name length");
|
||||
tox_self_get_name(tox2, new_name);
|
||||
ck_assert_msg(memcmp(name, new_name, TOX_MAX_NAME_LENGTH) == 0, "Wrong name");
|
||||
|
||||
tox_kill(tox1);
|
||||
|
@ -199,51 +356,83 @@ END_TEST
|
|||
START_TEST(test_few_clients)
|
||||
{
|
||||
long long unsigned int con_time, cur_time = time(NULL);
|
||||
Tox *tox1 = tox_new(0);
|
||||
Tox *tox2 = tox_new(0);
|
||||
Tox *tox3 = tox_new(0);
|
||||
TOX_ERR_NEW t_n_error;
|
||||
Tox *tox1 = tox_new(0, 0, 0, &t_n_error);
|
||||
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
|
||||
Tox *tox2 = tox_new(0, 0, 0, &t_n_error);
|
||||
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
|
||||
Tox *tox3 = tox_new(0, 0, 0, &t_n_error);
|
||||
ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error");
|
||||
|
||||
ck_assert_msg(tox1 || tox2 || tox3, "Failed to create 3 tox instances");
|
||||
|
||||
{
|
||||
TOX_ERR_GET_PORT error;
|
||||
ck_assert_msg(tox_self_get_udp_port(tox1, &error) == 33445, "First Tox instance did not bind to udp port 33445.\n");
|
||||
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
|
||||
}
|
||||
|
||||
{
|
||||
TOX_ERR_GET_PORT error;
|
||||
ck_assert_msg(tox_self_get_udp_port(tox2, &error) == 33446, "Second Tox instance did not bind to udp port 33446.\n");
|
||||
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
|
||||
}
|
||||
|
||||
{
|
||||
TOX_ERR_GET_PORT error;
|
||||
ck_assert_msg(tox_self_get_udp_port(tox3, &error) == 33447, "Third Tox instance did not bind to udp port 33447.\n");
|
||||
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
|
||||
}
|
||||
|
||||
uint32_t to_compare = 974536;
|
||||
connected_t1 = 0;
|
||||
tox_callback_self_connection_status(tox1, tox_connection_status, &to_compare);
|
||||
tox_callback_friend_request(tox2, accept_friend_request, &to_compare);
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(tox2, address);
|
||||
int test = tox_add_friend(tox3, address, (uint8_t *)"Gentoo", 7);
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(tox2, address);
|
||||
uint32_t test = tox_friend_add(tox3, address, (uint8_t *)"Gentoo", 7, 0);
|
||||
ck_assert_msg(test == 0, "Failed to add friend error code: %i", test);
|
||||
|
||||
uint8_t off = 1;
|
||||
|
||||
while (1) {
|
||||
tox_do(tox1);
|
||||
tox_do(tox2);
|
||||
tox_do(tox3);
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (tox_isconnected(tox1) && tox_isconnected(tox2) && tox_isconnected(tox3) && off) {
|
||||
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
|
||||
con_time = time(NULL);
|
||||
off = 0;
|
||||
if (tox_self_get_connection_status(tox1) && tox_self_get_connection_status(tox2)
|
||||
&& tox_self_get_connection_status(tox3)) {
|
||||
if (off) {
|
||||
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
|
||||
con_time = time(NULL);
|
||||
off = 0;
|
||||
}
|
||||
|
||||
if (tox_friend_get_connection_status(tox2, 0, 0) == TOX_CONNECTION_UDP
|
||||
&& tox_friend_get_connection_status(tox3, 0, 0) == TOX_CONNECTION_UDP)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (tox_get_friend_connection_status(tox2, 0) == 1 && tox_get_friend_connection_status(tox3, 0) == 1)
|
||||
break;
|
||||
|
||||
c_sleep(50);
|
||||
}
|
||||
|
||||
ck_assert_msg(connected_t1, "Tox1 isn't connected. %u", connected_t1);
|
||||
printf("tox clients connected took %llu seconds\n", time(NULL) - con_time);
|
||||
to_compare = 974536;
|
||||
tox_callback_friend_message(tox3, print_message, &to_compare);
|
||||
uint8_t msgs[TOX_MAX_MESSAGE_LENGTH + 1];
|
||||
memset(msgs, 'G', sizeof(msgs));
|
||||
ck_assert_msg(tox_send_message(tox2, 0, msgs, TOX_MAX_MESSAGE_LENGTH + 1) == 0,
|
||||
"TOX_MAX_MESSAGE_LENGTH is too small\n");
|
||||
ck_assert_msg(tox_send_message(tox2, 0, msgs, TOX_MAX_MESSAGE_LENGTH) != 0, "TOX_MAX_MESSAGE_LENGTH is too big\n");
|
||||
TOX_ERR_FRIEND_SEND_MESSAGE errm;
|
||||
tox_friend_send_message(tox2, 0, TOX_MESSAGE_TYPE_NORMAL, msgs, TOX_MAX_MESSAGE_LENGTH + 1, &errm);
|
||||
ck_assert_msg(errm == TOX_ERR_FRIEND_SEND_MESSAGE_TOO_LONG, "TOX_MAX_MESSAGE_LENGTH is too small\n");
|
||||
tox_friend_send_message(tox2, 0, TOX_MESSAGE_TYPE_NORMAL, msgs, TOX_MAX_MESSAGE_LENGTH, &errm);
|
||||
ck_assert_msg(errm == TOX_ERR_FRIEND_SEND_MESSAGE_OK, "TOX_MAX_MESSAGE_LENGTH is too big\n");
|
||||
|
||||
while (1) {
|
||||
messages_received = 0;
|
||||
tox_do(tox1);
|
||||
tox_do(tox2);
|
||||
tox_do(tox3);
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (messages_received)
|
||||
break;
|
||||
|
@ -253,14 +442,16 @@ START_TEST(test_few_clients)
|
|||
|
||||
printf("tox clients messaging succeeded\n");
|
||||
|
||||
tox_callback_name_change(tox3, print_nickchange, &to_compare);
|
||||
tox_set_name(tox2, (uint8_t *)"Gentoo", sizeof("Gentoo"));
|
||||
tox_callback_friend_name(tox3, print_nickchange, &to_compare);
|
||||
TOX_ERR_SET_INFO err_n;
|
||||
bool succ = tox_self_set_name(tox2, (uint8_t *)"Gentoo", sizeof("Gentoo"), &err_n);
|
||||
ck_assert_msg(succ && err_n == TOX_ERR_SET_INFO_OK, "tox_self_set_name failed because %u\n", err_n);
|
||||
|
||||
while (1) {
|
||||
name_changes = 0;
|
||||
tox_do(tox1);
|
||||
tox_do(tox2);
|
||||
tox_do(tox3);
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (name_changes)
|
||||
break;
|
||||
|
@ -268,18 +459,42 @@ START_TEST(test_few_clients)
|
|||
c_sleep(50);
|
||||
}
|
||||
|
||||
ck_assert_msg(tox_friend_get_name_size(tox3, 0, 0) == sizeof("Gentoo"), "Name length not correct");
|
||||
uint8_t temp_name[sizeof("Gentoo")];
|
||||
tox_get_name(tox3, 0, temp_name);
|
||||
tox_friend_get_name(tox3, 0, temp_name, 0);
|
||||
ck_assert_msg(memcmp(temp_name, "Gentoo", sizeof("Gentoo")) == 0, "Name not correct");
|
||||
|
||||
tox_callback_typing_change(tox2, &print_typingchange, &to_compare);
|
||||
tox_set_user_is_typing(tox3, 0, 1);
|
||||
tox_callback_friend_status_message(tox3, print_status_m_change, &to_compare);
|
||||
succ = tox_self_set_status_message(tox2, (uint8_t *)"Installing Gentoo", sizeof("Installing Gentoo"), &err_n);
|
||||
ck_assert_msg(succ && err_n == TOX_ERR_SET_INFO_OK, "tox_self_set_status_message failed because %u\n", err_n);
|
||||
|
||||
while (1) {
|
||||
status_m_changes = 0;
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (status_m_changes)
|
||||
break;
|
||||
|
||||
c_sleep(50);
|
||||
}
|
||||
|
||||
ck_assert_msg(tox_friend_get_status_message_size(tox3, 0, 0) == sizeof("Installing Gentoo"),
|
||||
"status message length not correct");
|
||||
uint8_t temp_status_m[sizeof("Installing Gentoo")];
|
||||
tox_friend_get_status_message(tox3, 0, temp_status_m, 0);
|
||||
ck_assert_msg(memcmp(temp_status_m, "Installing Gentoo", sizeof("Installing Gentoo")) == 0,
|
||||
"status message not correct");
|
||||
|
||||
tox_callback_friend_typing(tox2, &print_typingchange, &to_compare);
|
||||
tox_self_set_typing(tox3, 0, 1, 0);
|
||||
|
||||
while (1) {
|
||||
typing_changes = 0;
|
||||
tox_do(tox1);
|
||||
tox_do(tox2);
|
||||
tox_do(tox3);
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
|
||||
if (typing_changes == 2)
|
||||
|
@ -290,14 +505,14 @@ START_TEST(test_few_clients)
|
|||
c_sleep(50);
|
||||
}
|
||||
|
||||
ck_assert_msg(tox_get_is_typing(tox2, 0) == 1, "Typing fail");
|
||||
tox_set_user_is_typing(tox3, 0, 0);
|
||||
ck_assert_msg(tox_friend_get_typing(tox2, 0, 0) == 1, "Typing fail");
|
||||
tox_self_set_typing(tox3, 0, 0, 0);
|
||||
|
||||
while (1) {
|
||||
typing_changes = 0;
|
||||
tox_do(tox1);
|
||||
tox_do(tox2);
|
||||
tox_do(tox3);
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (typing_changes == 1)
|
||||
break;
|
||||
|
@ -307,23 +522,24 @@ START_TEST(test_few_clients)
|
|||
c_sleep(50);
|
||||
}
|
||||
|
||||
ck_assert_msg(tox_get_is_typing(tox2, 0) == 0, "Typing fail");
|
||||
TOX_ERR_FRIEND_QUERY err_t;
|
||||
ck_assert_msg(tox_friend_get_typing(tox2, 0, &err_t) == 0, "Typing fail");
|
||||
ck_assert_msg(err_t == TOX_ERR_FRIEND_QUERY_OK, "Typing fail");
|
||||
|
||||
uint32_t packet_number = 160;
|
||||
int ret = tox_lossless_packet_registerhandler(tox3, 0, packet_number, &handle_custom_packet, &packet_number);
|
||||
ck_assert_msg(ret == 0, "tox_lossless_packet_registerhandler fail %i", ret);
|
||||
tox_callback_friend_lossless_packet(tox3, &handle_custom_packet, &packet_number);
|
||||
uint8_t data_c[TOX_MAX_CUSTOM_PACKET_SIZE + 1];
|
||||
memset(data_c, ((uint8_t)packet_number), sizeof(data_c));
|
||||
ret = tox_send_lossless_packet(tox2, 0, data_c, sizeof(data_c));
|
||||
ck_assert_msg(ret == -1, "tox_send_lossless_packet bigger fail %i", ret);
|
||||
ret = tox_send_lossless_packet(tox2, 0, data_c, TOX_MAX_CUSTOM_PACKET_SIZE);
|
||||
ck_assert_msg(ret == 0, "tox_send_lossless_packet fail %i", ret);
|
||||
int ret = tox_friend_send_lossless_packet(tox2, 0, data_c, sizeof(data_c), 0);
|
||||
ck_assert_msg(ret == 0, "tox_friend_send_lossless_packet bigger fail %i", ret);
|
||||
ret = tox_friend_send_lossless_packet(tox2, 0, data_c, TOX_MAX_CUSTOM_PACKET_SIZE, 0);
|
||||
ck_assert_msg(ret == 1, "tox_friend_send_lossless_packet fail %i", ret);
|
||||
|
||||
while (1) {
|
||||
custom_packet = 0;
|
||||
tox_do(tox1);
|
||||
tox_do(tox2);
|
||||
tox_do(tox3);
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (custom_packet == 1)
|
||||
break;
|
||||
|
@ -334,19 +550,18 @@ START_TEST(test_few_clients)
|
|||
}
|
||||
|
||||
packet_number = 200;
|
||||
ret = tox_lossy_packet_registerhandler(tox3, 0, packet_number, &handle_custom_packet, &packet_number);
|
||||
ck_assert_msg(ret == 0, "tox_lossy_packet_registerhandler fail %i", ret);
|
||||
tox_callback_friend_lossy_packet(tox3, &handle_custom_packet, &packet_number);
|
||||
memset(data_c, ((uint8_t)packet_number), sizeof(data_c));
|
||||
ret = tox_send_lossy_packet(tox2, 0, data_c, sizeof(data_c));
|
||||
ck_assert_msg(ret == -1, "tox_send_lossy_packet bigger fail %i", ret);
|
||||
ret = tox_send_lossy_packet(tox2, 0, data_c, TOX_MAX_CUSTOM_PACKET_SIZE);
|
||||
ck_assert_msg(ret == 0, "tox_send_lossy_packet fail %i", ret);
|
||||
ret = tox_friend_send_lossy_packet(tox2, 0, data_c, sizeof(data_c), 0);
|
||||
ck_assert_msg(ret == 0, "tox_friend_send_lossy_packet bigger fail %i", ret);
|
||||
ret = tox_friend_send_lossy_packet(tox2, 0, data_c, TOX_MAX_CUSTOM_PACKET_SIZE, 0);
|
||||
ck_assert_msg(ret == 1, "tox_friend_send_lossy_packet fail %i", ret);
|
||||
|
||||
while (1) {
|
||||
custom_packet = 0;
|
||||
tox_do(tox1);
|
||||
tox_do(tox2);
|
||||
tox_do(tox3);
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (custom_packet == 1)
|
||||
break;
|
||||
|
@ -356,45 +571,48 @@ START_TEST(test_few_clients)
|
|||
c_sleep(50);
|
||||
}
|
||||
|
||||
filenum = file_accepted = file_size = file_sent = sendf_ok = size_recv = 0;
|
||||
printf("Starting file transfer test.\n");
|
||||
|
||||
file_accepted = file_size = file_recv = sendf_ok = size_recv = 0;
|
||||
max_sending = UINT64_MAX;
|
||||
long long unsigned int f_time = time(NULL);
|
||||
tox_callback_file_data(tox3, write_file, &to_compare);
|
||||
tox_callback_file_control(tox2, file_print_control, &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_recv_chunk(tox3, write_file, &to_compare);
|
||||
tox_callback_file_recv_control(tox2, file_print_control, &to_compare);
|
||||
tox_callback_file_chunk_request(tox2, tox_file_chunk_request, &to_compare);
|
||||
tox_callback_file_recv_control(tox3, file_print_control, &to_compare);
|
||||
tox_callback_file_recv(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, 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) {
|
||||
file_sent = 0;
|
||||
tox_do(tox1);
|
||||
tox_do(tox2);
|
||||
tox_do(tox3);
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(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
|
||||
&& file_accepted == 1) {
|
||||
break;
|
||||
} else {
|
||||
ck_abort_msg("Something went wrong in file transfer %u %u %u %u %u %u %llu %llu %llu", sendf_ok, file_recv,
|
||||
totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, file_accepted == 1, totalf_size, size_recv,
|
||||
sending_pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (file_sent && size_recv == file_size)
|
||||
break;
|
||||
|
||||
uint32_t tox1_interval = tox_do_interval(tox1);
|
||||
uint32_t tox2_interval = tox_do_interval(tox2);
|
||||
uint32_t tox3_interval = tox_do_interval(tox3);
|
||||
uint32_t tox1_interval = tox_iteration_interval(tox1);
|
||||
uint32_t tox2_interval = tox_iteration_interval(tox2);
|
||||
uint32_t tox3_interval = tox_iteration_interval(tox3);
|
||||
|
||||
if (tox2_interval > tox3_interval) {
|
||||
c_sleep(tox3_interval);
|
||||
|
@ -405,6 +623,105 @@ START_TEST(test_few_clients)
|
|||
|
||||
printf("100MB file sent in %llu seconds\n", time(NULL) - f_time);
|
||||
|
||||
printf("Starting file streaming transfer test.\n");
|
||||
|
||||
file_sending_done = file_accepted = file_size = file_recv = sendf_ok = size_recv = 0;
|
||||
f_time = time(NULL);
|
||||
tox_callback_file_recv_chunk(tox3, write_file, &to_compare);
|
||||
tox_callback_file_recv_control(tox2, file_print_control, &to_compare);
|
||||
tox_callback_file_chunk_request(tox2, tox_file_chunk_request, &to_compare);
|
||||
tox_callback_file_recv_control(tox3, file_print_control, &to_compare);
|
||||
tox_callback_file_recv(tox3, tox_file_receive, &to_compare);
|
||||
totalf_size = UINT64_MAX;
|
||||
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");
|
||||
|
||||
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");
|
||||
|
||||
max_sending = 100 * 1024;
|
||||
m_send_reached = 0;
|
||||
|
||||
while (1) {
|
||||
tox_iterate(tox1);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (file_sending_done) {
|
||||
if (sendf_ok && file_recv && m_send_reached && totalf_size == file_size && size_recv == max_sending
|
||||
&& sending_pos == size_recv && file_accepted == 1) {
|
||||
break;
|
||||
} else {
|
||||
ck_abort_msg("Something went wrong in file transfer %u %u %u %u %u %u %u %llu %llu %llu %llu", sendf_ok, file_recv,
|
||||
m_send_reached, totalf_size == file_size, size_recv == max_sending, sending_pos == size_recv, file_accepted == 1,
|
||||
totalf_size, file_size,
|
||||
size_recv, sending_pos);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tox1_interval = tox_iteration_interval(tox1);
|
||||
uint32_t tox2_interval = tox_iteration_interval(tox2);
|
||||
uint32_t tox3_interval = tox_iteration_interval(tox3);
|
||||
|
||||
if (tox2_interval > tox3_interval) {
|
||||
c_sleep(tox3_interval);
|
||||
} else {
|
||||
c_sleep(tox2_interval);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Starting file 0 transfer test.\n");
|
||||
|
||||
file_sending_done = file_accepted = file_size = file_recv = sendf_ok = size_recv = 0;
|
||||
f_time = time(NULL);
|
||||
tox_callback_file_recv_chunk(tox3, write_file, &to_compare);
|
||||
tox_callback_file_recv_control(tox2, file_print_control, &to_compare);
|
||||
tox_callback_file_chunk_request(tox2, tox_file_chunk_request, &to_compare);
|
||||
tox_callback_file_recv_control(tox3, file_print_control, &to_compare);
|
||||
tox_callback_file_recv(tox3, tox_file_receive, &to_compare);
|
||||
totalf_size = 0;
|
||||
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");
|
||||
|
||||
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);
|
||||
tox_iterate(tox2);
|
||||
tox_iterate(tox3);
|
||||
|
||||
if (file_sending_done) {
|
||||
if (sendf_ok && file_recv && totalf_size == file_size && size_recv == file_size && sending_pos == size_recv
|
||||
&& file_accepted == 1) {
|
||||
break;
|
||||
} else {
|
||||
ck_abort_msg("Something went wrong in file transfer %u %u %u %u %u %u %llu %llu %llu", sendf_ok, file_recv,
|
||||
totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, file_accepted == 1, totalf_size, size_recv,
|
||||
sending_pos);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tox1_interval = tox_iteration_interval(tox1);
|
||||
uint32_t tox2_interval = tox_iteration_interval(tox2);
|
||||
uint32_t tox3_interval = tox_iteration_interval(tox3);
|
||||
|
||||
if (tox2_interval > tox3_interval) {
|
||||
c_sleep(tox3_interval);
|
||||
} else {
|
||||
c_sleep(tox2_interval);
|
||||
}
|
||||
}
|
||||
|
||||
printf("test_few_clients succeeded, took %llu seconds\n", time(NULL) - cur_time);
|
||||
|
||||
tox_kill(tox1);
|
||||
|
@ -424,17 +741,23 @@ START_TEST(test_many_clients)
|
|||
uint32_t to_comp = 974536;
|
||||
|
||||
for (i = 0; i < NUM_TOXES; ++i) {
|
||||
toxes[i] = tox_new(0);
|
||||
toxes[i] = tox_new(0, 0, 0, 0);
|
||||
ck_assert_msg(toxes[i] != 0, "Failed to create tox instances %u", i);
|
||||
tox_callback_friend_request(toxes[i], accept_friend_request, &to_comp);
|
||||
}
|
||||
|
||||
{
|
||||
TOX_ERR_GET_PORT error;
|
||||
ck_assert_msg(tox_self_get_udp_port(toxes[0], &error) == 33445, "First Tox instance did not bind to udp port 33445.\n");
|
||||
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
|
||||
}
|
||||
|
||||
struct {
|
||||
uint16_t tox1;
|
||||
uint16_t tox2;
|
||||
} pairs[NUM_FRIENDS];
|
||||
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
|
||||
for (i = 0; i < NUM_FRIENDS; ++i) {
|
||||
loop_top:
|
||||
|
@ -446,22 +769,24 @@ loop_top:
|
|||
goto loop_top;
|
||||
}
|
||||
|
||||
tox_get_address(toxes[pairs[i].tox1], address);
|
||||
int test = tox_add_friend(toxes[pairs[i].tox2], address, (uint8_t *)"Gentoo", 7);
|
||||
tox_self_get_address(toxes[pairs[i].tox1], address);
|
||||
|
||||
if (test == TOX_FAERR_ALREADYSENT) {
|
||||
TOX_ERR_FRIEND_ADD test;
|
||||
uint32_t num = tox_friend_add(toxes[pairs[i].tox2], address, (uint8_t *)"Gentoo", 7, &test);
|
||||
|
||||
if (test == TOX_ERR_FRIEND_ADD_ALREADY_SENT) {
|
||||
goto loop_top;
|
||||
}
|
||||
|
||||
ck_assert_msg(test >= 0, "Failed to add friend error code: %i", test);
|
||||
ck_assert_msg(num != UINT32_MAX && test == TOX_ERR_FRIEND_ADD_OK, "Failed to add friend error code: %i", test);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint16_t counter = 0;
|
||||
|
||||
for (i = 0; i < NUM_TOXES; ++i) {
|
||||
for (j = 0; j < tox_count_friendlist(toxes[i]); ++j)
|
||||
if (tox_get_friend_connection_status(toxes[i], j) == 1)
|
||||
for (j = 0; j < tox_self_get_friend_list_size(toxes[i]); ++j)
|
||||
if (tox_friend_get_connection_status(toxes[i], j, 0) == TOX_CONNECTION_UDP)
|
||||
++counter;
|
||||
}
|
||||
|
||||
|
@ -470,7 +795,7 @@ loop_top:
|
|||
}
|
||||
|
||||
for (i = 0; i < NUM_TOXES; ++i) {
|
||||
tox_do(toxes[i]);
|
||||
tox_iterate(toxes[i]);
|
||||
}
|
||||
|
||||
c_sleep(50);
|
||||
|
@ -486,13 +811,13 @@ END_TEST
|
|||
|
||||
#define NUM_GROUP_TOX 32
|
||||
|
||||
void g_accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||
void g_accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
{
|
||||
if (*((uint32_t *)userdata) != 234212)
|
||||
return;
|
||||
|
||||
if (length == 7 && memcmp("Gentoo", data, 7) == 0) {
|
||||
tox_add_friend_norequest(m, public_key);
|
||||
tox_friend_add_norequest(m, public_key, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -543,24 +868,30 @@ START_TEST(test_many_group)
|
|||
uint32_t to_comp = 234212;
|
||||
|
||||
for (i = 0; i < NUM_GROUP_TOX; ++i) {
|
||||
toxes[i] = tox_new(0);
|
||||
toxes[i] = tox_new(0, 0, 0, 0);
|
||||
ck_assert_msg(toxes[i] != 0, "Failed to create tox instances %u", i);
|
||||
tox_callback_friend_request(toxes[i], &g_accept_friend_request, &to_comp);
|
||||
tox_callback_group_invite(toxes[i], &print_group_invite_callback, &to_comp);
|
||||
}
|
||||
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(toxes[NUM_GROUP_TOX - 1], address);
|
||||
{
|
||||
TOX_ERR_GET_PORT error;
|
||||
ck_assert_msg(tox_self_get_udp_port(toxes[0], &error) == 33445, "First Tox instance did not bind to udp port 33445.\n");
|
||||
ck_assert_msg(error == TOX_ERR_GET_PORT_OK, "wrong error");
|
||||
}
|
||||
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(toxes[NUM_GROUP_TOX - 1], address);
|
||||
|
||||
for (i = 0; i < NUM_GROUP_TOX; ++i) {
|
||||
ck_assert_msg(tox_add_friend(toxes[i], address, (uint8_t *)"Gentoo", 7) == 0, "Failed to add friend");
|
||||
ck_assert_msg(tox_friend_add(toxes[i], address, (uint8_t *)"Gentoo", 7, 0) == 0, "Failed to add friend");
|
||||
|
||||
tox_get_address(toxes[i], address);
|
||||
tox_self_get_address(toxes[i], address);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
for (i = 0; i < NUM_GROUP_TOX; ++i) {
|
||||
if (tox_get_friend_connection_status(toxes[i], 0) != 1) {
|
||||
if (tox_friend_get_connection_status(toxes[i], 0, 0) != TOX_CONNECTION_UDP) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -569,7 +900,7 @@ START_TEST(test_many_group)
|
|||
break;
|
||||
|
||||
for (i = 0; i < NUM_GROUP_TOX; ++i) {
|
||||
tox_do(toxes[i]);
|
||||
tox_iterate(toxes[i]);
|
||||
}
|
||||
|
||||
c_sleep(50);
|
||||
|
@ -587,7 +918,7 @@ START_TEST(test_many_group)
|
|||
|
||||
while (1) {
|
||||
for (i = 0; i < NUM_GROUP_TOX; ++i) {
|
||||
tox_do(toxes[i]);
|
||||
tox_iterate(toxes[i]);
|
||||
}
|
||||
|
||||
if (!invite_counter) {
|
||||
|
@ -625,7 +956,7 @@ START_TEST(test_many_group)
|
|||
|
||||
for (j = 0; j < 20; ++j) {
|
||||
for (i = 0; i < NUM_GROUP_TOX; ++i) {
|
||||
tox_do(toxes[i]);
|
||||
tox_iterate(toxes[i]);
|
||||
}
|
||||
|
||||
c_sleep(50);
|
||||
|
@ -638,7 +969,7 @@ START_TEST(test_many_group)
|
|||
|
||||
for (j = 0; j < 10; ++j) {
|
||||
for (i = 0; i < NUM_GROUP_TOX; ++i) {
|
||||
tox_do(toxes[i]);
|
||||
tox_iterate(toxes[i]);
|
||||
}
|
||||
|
||||
c_sleep(50);
|
||||
|
|
|
@ -55,10 +55,10 @@ typedef struct _Status {
|
|||
/* My default settings */
|
||||
static ToxAvCSettings muhcaps;
|
||||
|
||||
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
{
|
||||
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
|
||||
tox_add_friend_norequest(m, public_key);
|
||||
tox_friend_add_norequest(m, public_key, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,7 +232,7 @@ void register_callbacks(ToxAv *av, void *data)
|
|||
*/
|
||||
#define CALL_AND_START_LOOP(AliceCallType, BobCallType) \
|
||||
{ int step = 0, running = 1; while (running) {\
|
||||
tox_do(bootstrap_node); tox_do(Alice); tox_do(Bob); \
|
||||
tox_iterate(bootstrap_node); tox_iterate(Alice); tox_iterate(Bob); \
|
||||
toxav_do(status_control.Bob.av); toxav_do(status_control.Alice.av); \
|
||||
switch ( step ) {\
|
||||
case 0: /* Alice */ printf("Alice is calling...\n");\
|
||||
|
@ -250,33 +250,35 @@ if (status_control.Alice.status == Ended && status_control.Bob.status == Ended)
|
|||
START_TEST(test_AV_flows)
|
||||
{
|
||||
long long unsigned int cur_time = time(NULL);
|
||||
Tox *bootstrap_node = tox_new(0);
|
||||
Tox *Alice = tox_new(0);
|
||||
Tox *Bob = tox_new(0);
|
||||
Tox *bootstrap_node = tox_new(0, 0, 0, 0);
|
||||
Tox *Alice = tox_new(0, 0, 0, 0);
|
||||
Tox *Bob = tox_new(0, 0, 0, 0);
|
||||
|
||||
ck_assert_msg(bootstrap_node || Alice || Bob, "Failed to create 3 tox instances");
|
||||
|
||||
uint32_t to_compare = 974536;
|
||||
tox_callback_friend_request(Alice, accept_friend_request, &to_compare);
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(Alice, address);
|
||||
int test = tox_add_friend(Bob, address, (uint8_t *)"gentoo", 7);
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(Alice, address);
|
||||
uint32_t test = tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, 0);
|
||||
|
||||
ck_assert_msg(test == 0, "Failed to add friend error code: %i", test);
|
||||
|
||||
uint8_t off = 1;
|
||||
|
||||
while (1) {
|
||||
tox_do(bootstrap_node);
|
||||
tox_do(Alice);
|
||||
tox_do(Bob);
|
||||
tox_iterate(bootstrap_node);
|
||||
tox_iterate(Alice);
|
||||
tox_iterate(Bob);
|
||||
|
||||
if (tox_isconnected(bootstrap_node) && tox_isconnected(Alice) && tox_isconnected(Bob) && off) {
|
||||
if (tox_self_get_connection_status(bootstrap_node) && tox_self_get_connection_status(Alice)
|
||||
&& tox_self_get_connection_status(Bob)
|
||||
&& off) {
|
||||
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
|
||||
off = 0;
|
||||
}
|
||||
|
||||
if (tox_get_friend_connection_status(Alice, 0) == 1 && tox_get_friend_connection_status(Bob, 0) == 1)
|
||||
if (tox_friend_get_connection_status(Alice, 0, 0) && tox_friend_get_connection_status(Bob, 0, 0))
|
||||
break;
|
||||
|
||||
c_sleep(20);
|
||||
|
@ -483,9 +485,9 @@ START_TEST(test_AV_flows)
|
|||
int running = 1;
|
||||
|
||||
while (running) {
|
||||
tox_do(bootstrap_node);
|
||||
tox_do(Alice);
|
||||
tox_do(Bob);
|
||||
tox_iterate(bootstrap_node);
|
||||
tox_iterate(Alice);
|
||||
tox_iterate(Bob);
|
||||
|
||||
switch ( step ) {
|
||||
case 0: /* Alice */
|
||||
|
@ -524,9 +526,9 @@ START_TEST(test_AV_flows)
|
|||
int running = 1;
|
||||
|
||||
while (running) {
|
||||
tox_do(bootstrap_node);
|
||||
tox_do(Alice);
|
||||
tox_do(Bob);
|
||||
tox_iterate(bootstrap_node);
|
||||
tox_iterate(Alice);
|
||||
tox_iterate(Bob);
|
||||
|
||||
toxav_do(status_control.Alice.av);
|
||||
toxav_do(status_control.Bob.av);
|
||||
|
@ -569,9 +571,9 @@ START_TEST(test_AV_flows)
|
|||
int running = 1;
|
||||
|
||||
while (running) {
|
||||
tox_do(bootstrap_node);
|
||||
tox_do(Alice);
|
||||
tox_do(Bob);
|
||||
tox_iterate(bootstrap_node);
|
||||
tox_iterate(Alice);
|
||||
tox_iterate(Bob);
|
||||
|
||||
toxav_do(status_control.Alice.av);
|
||||
toxav_do(status_control.Bob.av);
|
||||
|
|
|
@ -58,10 +58,10 @@ typedef struct _Status {
|
|||
|
||||
Status status_control;
|
||||
|
||||
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||
void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
{
|
||||
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
|
||||
tox_add_friend_norequest(m, public_key);
|
||||
tox_friend_add_norequest(m, public_key, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,12 +264,12 @@ START_TEST(test_AV_three_calls)
|
|||
// void test_AV_three_calls()
|
||||
{
|
||||
long long unsigned int cur_time = time(NULL);
|
||||
Tox *bootstrap_node = tox_new(0);
|
||||
Tox *caller = tox_new(0);
|
||||
Tox *bootstrap_node = tox_new(0, 0, 0, 0);
|
||||
Tox *caller = tox_new(0, 0, 0, 0);
|
||||
Tox *callees[3] = {
|
||||
tox_new(0),
|
||||
tox_new(0),
|
||||
tox_new(0),
|
||||
tox_new(0, 0, 0, 0),
|
||||
tox_new(0, 0, 0, 0),
|
||||
tox_new(0, 0, 0, 0),
|
||||
};
|
||||
|
||||
|
||||
|
@ -284,37 +284,37 @@ START_TEST(test_AV_three_calls)
|
|||
for ( i = 0; i < 3; i ++ ) {
|
||||
uint32_t to_compare = 974536;
|
||||
tox_callback_friend_request(callees[i], accept_friend_request, &to_compare);
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(callees[i], address);
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(callees[i], address);
|
||||
|
||||
int test = tox_add_friend(caller, address, (uint8_t *)"gentoo", 7);
|
||||
uint32_t test = tox_friend_add(caller, address, (uint8_t *)"gentoo", 7, 0);
|
||||
ck_assert_msg( test == i, "Failed to add friend error code: %i", test);
|
||||
}
|
||||
|
||||
uint8_t off = 1;
|
||||
|
||||
while (1) {
|
||||
tox_do(bootstrap_node);
|
||||
tox_do(caller);
|
||||
tox_iterate(bootstrap_node);
|
||||
tox_iterate(caller);
|
||||
|
||||
for (i = 0; i < 3; i ++) {
|
||||
tox_do(callees[i]);
|
||||
tox_iterate(callees[i]);
|
||||
}
|
||||
|
||||
|
||||
if (tox_isconnected(bootstrap_node) &&
|
||||
tox_isconnected(caller) &&
|
||||
tox_isconnected(callees[0]) &&
|
||||
tox_isconnected(callees[1]) &&
|
||||
tox_isconnected(callees[2]) && off) {
|
||||
if (tox_self_get_connection_status(bootstrap_node) &&
|
||||
tox_self_get_connection_status(caller) &&
|
||||
tox_self_get_connection_status(callees[0]) &&
|
||||
tox_self_get_connection_status(callees[1]) &&
|
||||
tox_self_get_connection_status(callees[2]) && off) {
|
||||
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
|
||||
off = 0;
|
||||
}
|
||||
|
||||
|
||||
if (tox_get_friend_connection_status(caller, 0) == 1 &&
|
||||
tox_get_friend_connection_status(caller, 1) == 1 &&
|
||||
tox_get_friend_connection_status(caller, 2) == 1 )
|
||||
if (tox_friend_get_connection_status(caller, 0, 0) &&
|
||||
tox_friend_get_connection_status(caller, 1, 0) &&
|
||||
tox_friend_get_connection_status(caller, 2, 0) )
|
||||
break;
|
||||
|
||||
c_sleep(20);
|
||||
|
@ -351,11 +351,11 @@ START_TEST(test_AV_three_calls)
|
|||
while (call_running[0] || call_running[1] || call_running[2]) {
|
||||
pthread_mutex_lock(&muhmutex);
|
||||
|
||||
tox_do(bootstrap_node);
|
||||
tox_do(caller);
|
||||
tox_do(callees[0]);
|
||||
tox_do(callees[1]);
|
||||
tox_do(callees[2]);
|
||||
tox_iterate(bootstrap_node);
|
||||
tox_iterate(caller);
|
||||
tox_iterate(callees[0]);
|
||||
tox_iterate(callees[1]);
|
||||
tox_iterate(callees[2]);
|
||||
|
||||
for ( i = 0; i < 3; i++ )
|
||||
toxav_do(status_control.calls[0].Caller.av);
|
||||
|
|
|
@ -472,7 +472,7 @@ AC_C_BIGENDIAN
|
|||
# Checks for library functions.
|
||||
AC_FUNC_FORK
|
||||
AC_CHECK_FUNCS([gettimeofday memset socket strchr malloc])
|
||||
if (test "x$WIN32" != "xyes") && (test "x$MACH" != "xyes") && (test "x$DISABLE_RT" != "xyes"); then
|
||||
if (test "x$WIN32" != "xyes") && (test "x$MACH" != "xyes") && (test "x${host_os#*openbsd}" == "x$host_os") && (test "x$DISABLE_RT" != "xyes"); then
|
||||
AC_CHECK_LIB(rt, clock_gettime,
|
||||
[
|
||||
RT_LIBS="-lrt"
|
||||
|
|
631
docs/Avatars.md
631
docs/Avatars.md
|
@ -1,631 +0,0 @@
|
|||
# User avatars in Tox
|
||||
|
||||
|
||||
|
||||
## Introduction and rationale
|
||||
|
||||
User avatars are small icons or images used to identify users in the friend
|
||||
list; they exist in virtually all VoIP and IM protocols and provide an easy
|
||||
way for one user to identify another in the friend list.
|
||||
|
||||
This document describes the implementation of avatars in the Tox protocol,
|
||||
according to the following design considerations:
|
||||
|
||||
- Avatars are handled as private information, i.e., they are only exchanged
|
||||
over Tox encrypted channels among previously authenticated friends.
|
||||
|
||||
- The library treats all images as blobs and does not interpret or
|
||||
understand image formats. It only ensures that the avatar data sent by
|
||||
a user is correctly received by the other. The client application is
|
||||
responsible for validating, decoding, resizing, and presenting the
|
||||
image to the user.
|
||||
|
||||
- There is a strict limit of 16 KiB to the avatar raw data size -- this
|
||||
seems suitable for practical use as, for example, the raw data of an
|
||||
uncompressed 64 x 64 pixels 24 bpp RGB bitmap is 12288 bytes long; the
|
||||
data limit provides enough space for larger bitmaps if the usual
|
||||
compressed formats are used.
|
||||
|
||||
**Notice:** As designed, this limit can be changed in the future without
|
||||
breaking the protocol compatibility, but clients using the original
|
||||
limit will reject larger avatars.
|
||||
|
||||
- The protocol MUST provide means to allow caching and avoid unnecessary
|
||||
data transfers.
|
||||
|
||||
- Avatars are transferred between clients in a background operation.
|
||||
|
||||
- Avatars are served on a "best effort" basis, without breaking clients
|
||||
which do not support them.
|
||||
|
||||
- The protocol MUST resist to malicious users.
|
||||
|
||||
- The protocol MUST work with both UDP and TCP networks.
|
||||
|
||||
|
||||
The Single Tox Standard Draft v.0.1.0 recommends implementing avatars as
|
||||
a purely client-side feature through a procedure that can be summarized as
|
||||
sending a specially named file as a file transfer request and accepting
|
||||
it silently. This procedure can be improved to provide the previously stated
|
||||
design considerations, but this requires a higher integration with the core
|
||||
protocol. Moving this feature to the core protocol also:
|
||||
|
||||
- provides a simpler and cleaner interface for client applications;
|
||||
|
||||
- hides protocol complexities from the client;
|
||||
|
||||
- avoids code duplication and ad-hoc protocols in the clients;
|
||||
|
||||
- avoids incompatibility between client implementations;
|
||||
|
||||
- allows important optimizations, such as lightweight notification of
|
||||
removed and updated avatars;
|
||||
|
||||
- plays well with cache schemes;
|
||||
|
||||
- makes avatar transfer essentially a background operation.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## High level description
|
||||
|
||||
|
||||
This is a very high level description. The usage patterns expected from
|
||||
client applications are described in the section "Using Avatars in Client
|
||||
Applications", and a low level protocol description is available in the
|
||||
section "Internal Protocol Description").
|
||||
The avatar exchange is implemented with the following new elements in the
|
||||
Tox protocol:
|
||||
|
||||
- **Avatar Information Notifications** are events which may be sent by
|
||||
a user to another anytime, but are usually sent after one of them
|
||||
connects to the network, changes his avatar, or in reply to an **avatar
|
||||
information request**. They are delivered by a very lightweight message
|
||||
but with enough information to allow a user to validate or discard an
|
||||
avatar from the local cache and to decide if it is interesting to request
|
||||
the avatar data from the peer.
|
||||
|
||||
This event contains two data fields: (1) the image format, and (2) the
|
||||
cryptographic hash of the current image data. The image format may be
|
||||
NONE (for users who have no avatar or removed their avatars) or PNG. The
|
||||
cryptographic hash is intended to be compared with the hash of the
|
||||
currently cached avatar (if any) in order to check if it is still up to
|
||||
date.
|
||||
|
||||
- **Avatar Information Requests** are very lightweight messages sent by a
|
||||
user asking for an **avatar information notification**. They may be sent
|
||||
as part of the login process or when the client thinks the currently
|
||||
cached avatar is outdated. The receiver may or may not answer to this
|
||||
request. This message contains no data fields.
|
||||
|
||||
- An **Avatar Data Request** is sent by a user asking another for his
|
||||
complete avatar data. It is sent only when the requesting user decides
|
||||
the avatar does not exist in the local cache or is outdated. The receiver
|
||||
may or may not answer to this request. This message contains no data
|
||||
fields.
|
||||
|
||||
- An **Avatar Data Notification** is an event signaling the client that
|
||||
the complete avatar image data of another user is available. The actual
|
||||
data transfer is implemented using several data and control messages,
|
||||
but the details are hidden from the client applications. This event can
|
||||
only arrive in reply to an **avatar data request**.
|
||||
|
||||
This event contains three data fields: (1) the image format, (2) the
|
||||
cryptographic hash of the image data, and (3) the raw image data. If the
|
||||
image format is NONE (i.e. no avatar), the hash is zeroed and the image
|
||||
data is empty. The raw image data is locally validated and ensured to
|
||||
match the hash (the event is **not** triggered otherwise).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
To implement this feature, the following public symbols were added. The
|
||||
complete API documentation is available in `tox.h`.
|
||||
|
||||
|
||||
```
|
||||
#define TOX_AVATAR_MAX_DATA_LENGTH 16384
|
||||
#define TOX_HASH_LENGTH 32
|
||||
|
||||
|
||||
/* Data formats for user avatar images */
|
||||
typedef enum {
|
||||
TOX_AVATAR_FORMAT_NONE,
|
||||
TOX_AVATAR_FORMAT_PNG
|
||||
}
|
||||
TOX_AVATAR_FORMAT;
|
||||
|
||||
|
||||
|
||||
/* Set the user avatar image data. */
|
||||
int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length);
|
||||
|
||||
/* Remove the user avatar image data. */
|
||||
int tox_unset_avatar(Tox *tox);
|
||||
|
||||
/* Get avatar data from the current user. */
|
||||
int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash);
|
||||
|
||||
/* Generate a cryptographic hash of the given data (usually a cached avatar). */
|
||||
int tox_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen);
|
||||
|
||||
/* Request avatar information from a friend. */
|
||||
int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber);
|
||||
|
||||
/* Send an unrequested avatar information to a friend. */
|
||||
int tox_send_avatar_info(Tox *tox, const int32_t friendnumber);
|
||||
|
||||
/* Request the avatar data from a friend. */
|
||||
int tox_request_avatar_data(const Tox *tox, const int32_t friendnumber);
|
||||
|
||||
/* Set the callback function for avatar data. */
|
||||
void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t*, void *), void *userdata);
|
||||
|
||||
/* Set the callback function for avatar data. */
|
||||
void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t*, uint8_t*, uint32_t, void *), void *userdata);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Using Avatars in Client Applications
|
||||
|
||||
|
||||
### General recommendations
|
||||
|
||||
- Clients MUST NOT imply the availability of avatars in other users.
|
||||
Avatars are an optional feature and not all users and clients may
|
||||
support them.
|
||||
|
||||
- Clients MUST NOT block waiting for avatar information and avatar data
|
||||
packets.
|
||||
|
||||
- Clients MUST treat avatar data as insecure and potentially malicious.
|
||||
For example, users may accidentally use corrupted images as avatars,
|
||||
a malicious user may send a specially crafted image to exploit a known
|
||||
vulnerability in an image decoding library, etc. It is recommended to
|
||||
handle the avatar image data in the same way as an image downloaded
|
||||
from an unknown Internet source.
|
||||
|
||||
- The peers MUST NOT assume any coupling between the operations of
|
||||
receiving an avatar information packet, sending unrequested avatar
|
||||
information packets, requesting avatar data, or receiving avatar data.
|
||||
|
||||
For example, the following situations are valid:
|
||||
|
||||
* A text-mode client may send avatars to other users, but never
|
||||
request them.
|
||||
|
||||
* A client may not understand a particular image format and ignore
|
||||
avatars using it, but request and handle other formats.
|
||||
|
||||
* A client on a slow mobile network may ask for avatar information to
|
||||
ensure its cached avatars are still valid, but not request avatar
|
||||
data. The same client may start asking for avatar data once it
|
||||
connects through a fast network.
|
||||
|
||||
- Clients SHOULD implement a local cache of avatars and not request
|
||||
avatar data from other peers unless necessary.
|
||||
|
||||
- When avatar information is received, the client should delete the
|
||||
avatar if the new avatar format is NONE or compare the hash received
|
||||
from the peer with the hash of the currently cached avatar. If they
|
||||
differ, send an avatar data request.
|
||||
|
||||
- If the cached avatar is older than a given threshold, the client may
|
||||
also send an avatar info request to that friend once he is online and
|
||||
mark the avatar as updated *before* any avatar information is received
|
||||
(to not spam the peer with such requests).
|
||||
|
||||
- When an avatar data notification is received, the client must update
|
||||
the cached avatar with the new one.
|
||||
|
||||
- Clients should resize or crop the image such that it better adapts
|
||||
to the client's user interface.
|
||||
|
||||
- If the user already has an avatar defined in the client configuration,
|
||||
it must be set before connecting to the network to avoid spurious avatar
|
||||
change notifications and unnecessary data transfers.
|
||||
|
||||
- If no avatar data is available for a given friend, the client should
|
||||
show a placeholder image.
|
||||
|
||||
|
||||
|
||||
### Interoperability and sharing avatars among different clients
|
||||
|
||||
**This section is a tentative recommendation of how clients should store
|
||||
avatars to ensure local interoperability, and should be revised if this
|
||||
code is accepted into Tox core.**
|
||||
|
||||
It is desirable that the user avatar and the cached friends' avatars could be
|
||||
shared among different Tox clients in the same system, in the spirit of the
|
||||
proposed Single Tox Standard. This not only makes switching from one client
|
||||
to another easier, but also minimizes the need of data transfers, as avatars
|
||||
already downloaded by other clients can be reused.
|
||||
|
||||
Given the Tox data directory described in STS Draft v0.1.0:
|
||||
|
||||
- Avatars are stored in a directory called "avatars" and named
|
||||
as "xxxxx.png", where "xxxxx" is the complete public key (but not friend
|
||||
address!) encoded as an uppercase hexadecimal string and "png" is the
|
||||
extension for the PNG avatar. As new image formats may be used in the
|
||||
future, clients should ensure no other file "xxxxx.*" exists. No file
|
||||
should be kept for a user who has no avatar.
|
||||
|
||||
- The client's own avatar is not special and is stored like any other. This
|
||||
is partially for simplicity, and partially in anticipation of profiles.
|
||||
|
||||
- The avatar should be stored as it was received, before any modifications by
|
||||
the client for display purposes.
|
||||
|
||||
- The hash, as calculated by toxcore and passed into the data callback,
|
||||
should be saved in "avatars/xxxxx.hash" where "xxxxx" means the
|
||||
same thing as for avatars. (The filename is longer than the file :) )
|
||||
|
||||
**To be discussed:** User keys are usually presented in Tox clients as
|
||||
upper case strings, but lower case file names are more usual.
|
||||
|
||||
|
||||
Example for Linux and other Unix systems, assuming a user called "gildor":
|
||||
|
||||
Tox data directory: /home/gildor/.config/tox/
|
||||
Tox data file: /home/gildor/.config/tox/data
|
||||
Avatar data dir: /home/gildor/.config/tox/avatars/
|
||||
Gildor's avatar: /home/gildor/.config/tox/avatars/446F4E6F744D6564646C65496E546865416666616972734F6657697A61726473.png
|
||||
Elrond's avatar: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.png
|
||||
Elrond's hash: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.hash
|
||||
Elladan's avatar: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.png
|
||||
Elladan's hash: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.hash
|
||||
Elrohir's avatar /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.png
|
||||
Elrohir's hash: /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.hash
|
||||
|
||||
This recommendation is partially implemented by "testing/test_avatars.c".
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Common operations
|
||||
|
||||
These are minimal examples of how to perform common operations with avatar
|
||||
functions. For a complete working example, see `testing/test_avatars.c`.
|
||||
|
||||
|
||||
#### Setting an avatar for the current user
|
||||
|
||||
In this example, `load_data_file` is just a hypothetical function that loads
|
||||
data from a file into the buffer and sets the length accordingly.
|
||||
|
||||
uint8_t buf[TOX_AVATAR_MAX_DATA_LENGTH];
|
||||
uint32_t len;
|
||||
|
||||
if (load_data_file("avatar.png", buf, &len) == 0)
|
||||
if (tox_set_avatar(tox, TOX_AVATAR_FORMAT_PNG, buf, len) != 0)
|
||||
fprintf(stderr, "Failed to set avatar.\n");
|
||||
|
||||
If the user is connected, this function will also notify all connected
|
||||
friends about the avatar change.
|
||||
|
||||
If the user already has an avatar defined in the client configuration, it
|
||||
must be set before connecting to the network to avoid spurious avatar change
|
||||
notifications and unnecessary data transfers.
|
||||
|
||||
|
||||
|
||||
|
||||
#### Removing the avatar from the current user
|
||||
|
||||
To remove the current avatar, an application must call
|
||||
|
||||
tox_unset_avatar(tox);
|
||||
|
||||
the effect is the same as setting the avatar format to `TOX_AVATAR_FORMAT_NONE`
|
||||
with no data:
|
||||
|
||||
tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0);
|
||||
|
||||
If the user is connected, this function will also notify all connected
|
||||
friends about the avatar change.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Receiving avatar information from friends
|
||||
|
||||
All avatar information is passed to a callback function with the prototype:
|
||||
|
||||
void function(Tox *tox, int32_t friendnumber, uint8_t format,
|
||||
uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
|
||||
|
||||
As in this example:
|
||||
|
||||
static void avatar_info_cb(Tox *tox, int32_t friendnumber, uint8_t format,
|
||||
uint8_t *hash, void *userdata)
|
||||
{
|
||||
printf("Receiving avatar information from friend %d. Format = %d\n",
|
||||
friendnumber, format);
|
||||
printf("Data hash: ");
|
||||
hex_printf(hash, TOX_HASH_LENGTH); /* Hypothetical function */
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
And, somewhere in the Tox initialization calls, set it as the callback to be
|
||||
triggered when an avatar information event arrives:
|
||||
|
||||
tox_callback_avatar_info(tox, avatar_info_cb, NULL);
|
||||
|
||||
|
||||
A typical client will test the currently cached avatar against the hash given
|
||||
in the avatar information event and, if needed, request the avatar data.
|
||||
|
||||
|
||||
|
||||
#### Receiving avatar data from friends
|
||||
|
||||
Avatar data events are only delivered in reply to avatar data requests, which
|
||||
**should** only be sent after getting the user avatar information (format
|
||||
and hash) from an avatar information event and checking it against a local
|
||||
cache.
|
||||
|
||||
For this, an application must define an avatar information callback which
|
||||
checks the local avatar cache and emits an avatar data request if necessary:
|
||||
|
||||
static void avatar_info_cb(Tox *tox, int32_t friendnumber, uint8_t format,
|
||||
uint8_t *hash, void *userdata)
|
||||
{
|
||||
printf("Receiving avatar information from friend %d. Format = %d\n",
|
||||
friendnumber, format);
|
||||
if (format = TOX_AVATAR_FORMAT_NONE) {
|
||||
/* User has no avatar or removed the avatar */
|
||||
delete_avatar_from_cache(tox, friendnumber);
|
||||
} else {
|
||||
/* Use the received hash to check if the cached avatar is
|
||||
still up to date. */
|
||||
if (!is_user_cached_avatar_updated(tox, friendnumber, hash)) {
|
||||
/* User avatar is outdated, send data request */
|
||||
tox_request_avatar_data(tox, friendnumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Then define an avatar data callback to store the received data in the local
|
||||
cache:
|
||||
|
||||
static void avatar_data_cb(Tox *tox, int32_t friendnumber, uint8_t format,
|
||||
uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
|
||||
{
|
||||
if (format = TOX_AVATAR_FORMAT_NONE) {
|
||||
/* User has no avatar or removed the avatar */
|
||||
delete_avatar_from_cache(tox, friendnumber);
|
||||
} else {
|
||||
save_avatar_data_to_cache(tox, friendnumber, format, hash,
|
||||
data, datalen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
And, finally, register both callbacks somewhere in the Tox initialization
|
||||
calls:
|
||||
|
||||
tox_callback_avatar_info(tox, avatar_info_cb, NULL);
|
||||
tox_callback_avatar_data(tox, avatar_data_cb, NULL);
|
||||
|
||||
|
||||
In the previous examples, implementation of the functions to check, store,
|
||||
and retrieve data from the cache were omitted for brevity. These functions
|
||||
will also need to get the friend public key (client id) from the friend
|
||||
number and, usually, convert it from a byte string to a hexadecimal
|
||||
string. A complete, yet more complex, example is available in the file
|
||||
`testing/test_avatars.c`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Internal Protocol Description
|
||||
|
||||
### New packet types
|
||||
|
||||
The avatar transfer protocol adds the following new packet types and ids:
|
||||
|
||||
PACKET_ID_AVATAR_INFO_REQ = 52
|
||||
PACKET_ID_AVATAR_INFO = 53
|
||||
PACKET_ID_AVATAR_DATA_CONTROL = 54
|
||||
PACKET_ID_AVATAR_DATA_START = 55
|
||||
PACKET_ID_AVATAR_DATA_PUSH = 56
|
||||
|
||||
|
||||
|
||||
|
||||
### Requesting avatar information
|
||||
|
||||
To request avatar information, a user must send a packet of type
|
||||
`PACKET_ID_AVATAR_INFO_REQ`. This packet has no data fields. Upon
|
||||
receiving this packet, a client which supports avatars should answer with
|
||||
a `PACKET_ID_AVATAR_INFO`. The sender must accept that the friend may
|
||||
not answer at all.
|
||||
|
||||
|
||||
|
||||
|
||||
### Receiving avatar information
|
||||
|
||||
Avatar information arrives in a packet of type `PACKET_ID_AVATAR_INFO` with
|
||||
the following structure:
|
||||
|
||||
PACKET_ID_AVATAR_INFO (53)
|
||||
Packet data size: 33 bytes
|
||||
[1: uint8_t format][32: uint8_t hash]
|
||||
|
||||
where 'format' is the image data format, one of the following:
|
||||
|
||||
0 = AVATAR_FORMAT_NONE (no avatar set)
|
||||
1 = AVATAR_FORMAT_PNG
|
||||
|
||||
and 'hash' is the SHA-256 message digest of the avatar data.
|
||||
|
||||
This packet may be sent at any time and no previous request is required.
|
||||
Clients should send this packet upon connection or when a friend
|
||||
connects, in the same way Tox sends name, status and action information.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Requesting avatar data
|
||||
|
||||
Transmission of avatar data is a multi-step procedure using three new packet
|
||||
types.
|
||||
|
||||
- Packet `PACKET_ID_AVATAR_DATA_CONTROL` has the format:
|
||||
|
||||
PACKET_ID_AVATAR_DATA_CONTROL (54)
|
||||
Packet data size: 1 byte
|
||||
[1: uint8_t op]
|
||||
|
||||
where 'op' is a code signaling either an operation request or a status
|
||||
return, the semantics of which are explained below. The following values are
|
||||
defined:
|
||||
|
||||
0 = AVATAR_DATACONTROL_REQ
|
||||
1 = AVATAR_DATACONTROL_ERROR
|
||||
|
||||
|
||||
- Packet `PACKET_ID_AVATAR_DATA_START` has the following format:
|
||||
|
||||
PACKET_ID_AVATAR_DATA_START (55)
|
||||
Packet data size: 37 bytes
|
||||
[1: uint8_t format][32: uint8_t hash][1: uint32_t data_length]
|
||||
|
||||
|
||||
where 'format' is the image format, with the same values accepted for
|
||||
the field 'format' in packet type `PACKET_ID_AVATAR_INFO`, 'hash' is
|
||||
the SHA-256 cryptographic hash of the avatar raw data, and 'data_length'
|
||||
is the total number of bytes the raw avatar data.
|
||||
|
||||
|
||||
- Packet `PACKET_ID_AVATAR_DATA_PUSH` has no format structure, just up
|
||||
to `AVATAR_DATA_MAX_CHUNK_SIZE` bytes of raw avatar image data; this
|
||||
value is defined according to the maximum amount of data a Tox encrypted
|
||||
packet can hold.
|
||||
|
||||
|
||||
|
||||
The following procedure assumes that a client "A" is requesting avatar data
|
||||
from a client "B":
|
||||
|
||||
- "A" must initialize its control structures and mark its data transfer
|
||||
as not yet started. Then it requests avatar data from "B" by sending a
|
||||
packet `PACKET_ID_AVATAR_DATA_CONTROL` with 'op' set to
|
||||
`AVATAR_DATACONTROL_REQ`.
|
||||
|
||||
- If "B" accepts this transfer, it answers by sending a
|
||||
`PACKET_ID_AVATAR_DATA_START` with the fields 'format', 'hash', and
|
||||
'data_length' set to the respective values of the current avatar.
|
||||
If "B" has no avatar set, 'format' must be `AVATAR_FORMAT_NONE`, 'hash'
|
||||
must be zeroed and 'data_length' must be zero.
|
||||
|
||||
If "B" does not accept sending the avatar, it may send a packet
|
||||
`PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to
|
||||
`AVATAR_DATACONTROL_ERROR` or simply ignore this request. "A" must cope
|
||||
with this.
|
||||
|
||||
If "B" has an avatar, it sends a variable number of
|
||||
`PACKET_ID_AVATAR_DATA_PUSH` packets with the avatar data in a single
|
||||
shot.
|
||||
|
||||
- Upon receiving a `PACKET_ID_AVATAR_DATA_START`, "A" checks if it
|
||||
has sent a data request to "B". If not, it simply ignores the packet.
|
||||
|
||||
If "A" really requested avatar data and the format is `AVATAR_FORMAT_NONE`,
|
||||
it triggers the avatar data callback, and clears all the temporary data,
|
||||
finishing the process. For other formats, "A" just waits for packets
|
||||
of type `PACKET_ID_AVATAR_DATA_PUSH`.
|
||||
|
||||
- Upon receiving a `PACKET_ID_AVATAR_DATA_PUSH`, "A" checks if it really
|
||||
sent an avatar data request and if the `PACKET_ID_AVATAR_DATA_START` was
|
||||
already received. If these conditions were met, it checks if the total
|
||||
length of the data already stored in the receiving buffer plus the data
|
||||
present in the push packet is still less or equal than
|
||||
`TOX_AVATAR_MAX_DATA_LENGTH`. If that is not the case, it replies with a
|
||||
`PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to
|
||||
`AVATAR_DATACONTROL_ERROR`.
|
||||
|
||||
If valid, "A" updates the 'bytes_received' counter and concatenates the
|
||||
newly arrived data to the buffer.
|
||||
|
||||
Then "A" checks if all the data has already been received, by comparing the
|
||||
counter 'bytes_received' with the field 'total_length'. If they are
|
||||
equal, "A" takes a SHA-256 hash of the data and compares it with the
|
||||
hash stored in the field 'hash' received with the first
|
||||
`PACKET_ID_AVATAR_DATA_START`.
|
||||
|
||||
If the hashes match, the avatar data was correctly received, and "A"
|
||||
triggers the avatar data callback and clears all the temporary data,
|
||||
finishing the process.
|
||||
|
||||
If not all data was received, "A" simply waits for more data.
|
||||
|
||||
Client "A" is always responsible for controlling the transfer and
|
||||
validating the data received. "B" doesn't need to keep any state for the
|
||||
protocol, have full control over the data sent and should implement
|
||||
some transfer limit for the data it sends.
|
||||
|
||||
- Any peer receiving a `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op'
|
||||
set to `AVATAR_DATACONTROL_ERROR` clears any existing control state and
|
||||
aborts sending or receiving data.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Security considerations
|
||||
|
||||
The major security implication of background data transfers of large objects,
|
||||
like avatars, is the possibility of exhausting the network resources of a
|
||||
client. This problem is exacerbated when there is the possibility of an
|
||||
amplification attack, as happens, for example, when sending a very small
|
||||
avatar request message will force the user to reply with a larger avatar
|
||||
data message.
|
||||
|
||||
The present proposal mitigates this situation by:
|
||||
|
||||
- only transferring data between previously authenticated friends,
|
||||
|
||||
- enforcing strict limits on the avatar data size,
|
||||
|
||||
- providing an alternate, smaller message for cooperative users to refresh
|
||||
avatar information when nothing has changed (`PACKET_ID_AVATAR_INFO`),
|
||||
|
||||
- having a per-friend data transfer limit. As the current protocol still
|
||||
allows a user to request avatar data again and again, the implementation
|
||||
limits the amount of data a particular user can request for some time. The
|
||||
exact values are defined in constants `AVATAR_DATA_TRANSFER_LIMIT` and
|
||||
`AVATAR_DATA_TRANSFER_TIMEOUT` in file `Messenger.c`.
|
||||
|
||||
- making the requester responsible for storing partial data and state
|
||||
information
|
||||
|
||||
Another problem present in avatars is the possibility of a friend sending
|
||||
a maliciously crafted image intended to exploit vulnerabilities in image
|
||||
decoders. Without an intermediate server to recompress, validate, and
|
||||
convert the images to neutral formats, the client applications must handle
|
||||
this situation by themselves using stable and secure image libraries and
|
||||
imposing limits on the maximum amount of system resources the decoding
|
||||
process can take. Images coming from Tox friends must be treated in the same
|
||||
way as images coming from random Internet sources.
|
48
docs/TODO
48
docs/TODO
|
@ -1,48 +0,0 @@
|
|||
TODO list.
|
||||
|
||||
[IN PROGRESS] Add what is left to do to the TODO list.
|
||||
|
||||
Networking:
|
||||
[NEEDS TESTING] UPnP port forwarding. (https://github.com/irungentoo/toxcore/pull/969)
|
||||
[NOT STARTED] NAT-PMP port forwarding.
|
||||
|
||||
DHT:
|
||||
[ALMOST DONE] Metadata collection prevention. (docs/Prevent_Tracking.txt)
|
||||
[IN PROGRESS] Hardening against attacks.
|
||||
[IN PROGRESS] Optimizing the code.
|
||||
|
||||
[DONE] Friend only group chats
|
||||
[DONE] Networking base.
|
||||
[MOSTLY DONE] Syncing chat state between clients (nicknames, list of who is in chat, etc...)
|
||||
[NOT STARTED] Private messages. (and adding friends from group chats using those private messages.)
|
||||
[NOT STARTED] File transfers.
|
||||
|
||||
[IN PROGRESS] Audio/Video
|
||||
[DONE] encoding/streaming/decoding
|
||||
[DONE] Call initiation
|
||||
[DONE] Encryption
|
||||
[IN PROGRESS] Auditing.
|
||||
[NEEDS TESTING] Video packet splitting.
|
||||
[IN PROGRESS] Prevent audio skew (seems to be easily solvable client side.)
|
||||
[IN PROGRESS] Group chats, audio done.
|
||||
|
||||
Friend_requests.c:
|
||||
[NOT STARTED] What happens when a friend request is received needs to be changed.
|
||||
[NOT STARTED] Add multiple nospam functionality.
|
||||
|
||||
[DONE] File transfers
|
||||
[NOT STARTED] Offline messaging
|
||||
[IN PROGRESS] Friends list syncing
|
||||
[DONE] IPV6 support
|
||||
|
||||
[IN PROGRESS] Make toxcore thread safe.
|
||||
|
||||
[NOT STARTED] Make the core save/datafile portable across client versions/different processor architectures.
|
||||
|
||||
[MOSTLY DONE] A way for people to connect to people on Tox if they are behind a bad NAT that
|
||||
blocks UDP (or is just unpunchable) (docs/TCP_Network.txt) (Current way doesn't scale very well.)
|
||||
|
||||
[DONE] Encrypted Saves. (see: toxencryptsave)
|
||||
|
||||
|
||||
[NOT STARTED] Security audit from professionals
|
46
docs/TODO.md
Normal file
46
docs/TODO.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Toxcore todo list.
|
||||
Welcome to the Toxcore todo list, this is very likely out of date, but either way it's a good jumping off point if
|
||||
you're looking to see where core is going, or where it could use a little love.
|
||||
|
||||
There are 3 sections; In Progress, TODO, and Done. These tasks are somewhat sorted by priority, but that shouldn't be
|
||||
taken to mean that this is the order tasks will (or should) be completed in.
|
||||
|
||||
## In Progress
|
||||
- [ ] [IN PROGRESS] Audio/Video
|
||||
- [X] [DONE] encoding/streaming/decoding
|
||||
- [X] [DONE] Call initiation
|
||||
- [X] [DONE] Encryption
|
||||
- [ ] [NEEDS TESTING] Video packet splitting.
|
||||
- [ ] [IN PROGRESS] Auditing.
|
||||
- [ ] [IN PROGRESS] Prevent audio skew (seems to be easily solvable client side.)
|
||||
- [ ] [IN PROGRESS] Group chats, audio done.
|
||||
- [ ] Networking:
|
||||
- [ ] [NEEDS TESTING] UPnP port forwarding. ([#969](https://github.com/irungentoo/toxcore/pull/969))
|
||||
- [ ] [TODO] NAT-PMP port forwarding.
|
||||
- [ ] DHT:
|
||||
- [ ] [ALMOST DONE] Metadata collection prevention. (docs/Prevent_Tracking.txt)
|
||||
- [ ] [IN PROGRESS] Hardening against attacks.
|
||||
- [ ] [IN PROGRESS] Optimizing the code.
|
||||
- [ ] [DONE] Friend only group chats
|
||||
- [X] [DONE] Networking base.
|
||||
- [X] [MOSTLY DONE] Syncing chat state between clients (nicknames, list of who is in chat, etc...)
|
||||
- [ ] [TODO] Group private messages. (and adding friends from group chats using those private messages.)
|
||||
- [ ] [TODO] Group file transfers.
|
||||
- [ ] [IN PROGRESS] Friends list syncing
|
||||
- [ ] [IN PROGRESS] Make toxcore thread safe.
|
||||
- [ ] [MOSTLY DONE] A way for people to connect to people on Tox if they are behind a bad NAT that blocks UDP (or is
|
||||
just unpunchable) ([docs/TCP_Network.txt](TCP_Network.txt)) (Current way doesn't scale very well.)
|
||||
|
||||
## TODO
|
||||
- [ ] [TODO] Make the core save/datafile portable across client versions/different processor architectures.
|
||||
- [ ] [TODO] Friend_requests.c:
|
||||
- [ ] [TODO] What happens when a friend request is received needs to be changed.
|
||||
- [ ] [DONE?] Add multiple nospam functionality. ([#1317](https://github.com/irungentoo/toxcore/pull/1317))
|
||||
|
||||
- [ ] [TODO] Offline messaging
|
||||
- [ ] [TODO] Security audit from professionals
|
||||
|
||||
## Done
|
||||
- [X] [DONE] File transfers
|
||||
- [X] [DONE] IPV6 support
|
||||
- [X] [DONE] Encrypted Saves. (see: toxencryptsave)
|
|
@ -106,21 +106,6 @@ tox_shell_LDADD = $(LIBSODIUM_LDFLAGS) \
|
|||
-lutil
|
||||
|
||||
|
||||
noinst_PROGRAMS += test_avatars
|
||||
|
||||
test_avatars_SOURCES = ../testing/test_avatars.c
|
||||
|
||||
test_avatars_CFLAGS = $(LIBSODIUM_CFLAGS) \
|
||||
$(NACL_CFLAGS)
|
||||
|
||||
test_avatars_LDADD = $(LIBSODIUM_LDFLAGS) \
|
||||
$(NACL_LDFLAGS) \
|
||||
libtoxcore.la \
|
||||
$(LIBSODIUM_LIBS) \
|
||||
$(NACL_OBJECTS) \
|
||||
$(NACL_LIBS)
|
||||
|
||||
|
||||
noinst_PROGRAMS += irc_syncbot
|
||||
|
||||
irc_syncbot_SOURCES = ../testing/irc_syncbot.c
|
||||
|
|
|
@ -56,17 +56,18 @@
|
|||
|
||||
#endif
|
||||
|
||||
void print_message(Messenger *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||
void print_message(Messenger *m, uint32_t friendnumber, unsigned int type, const uint8_t *string, size_t length,
|
||||
void *userdata)
|
||||
{
|
||||
printf("Message with length %u received from %u: %s \n", length, friendnumber, string);
|
||||
m_sendmessage(m, friendnumber, (uint8_t *)"Test1", 6);
|
||||
printf("Message with length %lu received from %u: %s \n", length, friendnumber, string);
|
||||
m_send_message_generic(m, friendnumber, type, (uint8_t *)"Test1", 6, 0);
|
||||
}
|
||||
|
||||
/* FIXME needed as print_request has to match the interface expected by
|
||||
* networking_requesthandler and so cannot take a Messenger * */
|
||||
static Messenger *m;
|
||||
|
||||
void print_request(Messenger *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||
void print_request(Messenger *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
{
|
||||
printf("Friend request received from: \n");
|
||||
printf("ClientID: ");
|
||||
|
@ -79,7 +80,7 @@ void print_request(Messenger *m, const uint8_t *public_key, const uint8_t *data,
|
|||
printf("%hhX", public_key[j]);
|
||||
}
|
||||
|
||||
printf("\nOf length: %u with data: %s \n", length, data);
|
||||
printf("\nOf length: %lu with data: %s \n", length, data);
|
||||
|
||||
if (length != sizeof("Install Gentoo")) {
|
||||
return;
|
||||
|
@ -112,7 +113,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
Messenger_Options options = {0};
|
||||
options.ipv6enabled = ipv6enabled;
|
||||
m = new_messenger(&options);
|
||||
m = new_messenger(&options, 0);
|
||||
|
||||
if ( !m ) {
|
||||
fputs("Failed to allocate messenger datastructure\n", stderr);
|
||||
|
@ -184,7 +185,7 @@ int main(int argc, char *argv[])
|
|||
getname(m, num, name);
|
||||
printf("%s\n", name);
|
||||
|
||||
m_sendmessage(m, num, (uint8_t *)"Test", 5);
|
||||
m_send_message_generic(m, num, MESSAGE_NORMAL, (uint8_t *)"Test", 5, 0);
|
||||
do_messenger(m);
|
||||
c_sleep(30);
|
||||
FILE *file = fopen("Save.bak", "wb");
|
||||
|
|
|
@ -98,7 +98,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
for (i = r_len - 1; i != 0 && buffer[i] != '='; --i);
|
||||
|
||||
uint8_t tox_id[TOX_FRIEND_ADDRESS_SIZE];
|
||||
uint8_t tox_id[TOX_ADDRESS_SIZE];
|
||||
|
||||
if (tox_decrypt_dns3_TXT(d, tox_id, buffer + i + 1, r_len - (i + 1), request_id) != 0)
|
||||
return -1;
|
||||
|
@ -106,7 +106,7 @@ int main(int argc, char *argv[])
|
|||
printf("The Tox id for username %s is:\n", argv[3]);
|
||||
|
||||
//unsigned int i;
|
||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
||||
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
|
||||
printf("%02hhX", tox_id[i]);
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,8 @@ static void callback_group_invite(Tox *tox, int fid, uint8_t type, const uint8_t
|
|||
current_group = tox_join_groupchat(tox, fid, data, length);
|
||||
}
|
||||
|
||||
void callback_friend_message(Tox *tox, int fid, const uint8_t *message, uint16_t length, void *userdata)
|
||||
void callback_friend_message(Tox *tox, uint32_t fid, TOX_MESSAGE_TYPE type, const uint8_t *message, size_t length,
|
||||
void *userdata)
|
||||
{
|
||||
if (length == 1 && *message == 'c') {
|
||||
if (tox_del_groupchat(tox, current_group) == 0)
|
||||
|
@ -203,7 +204,7 @@ void send_irc_group(Tox *tox, uint8_t *msg, uint16_t len)
|
|||
|
||||
Tox *init_tox(int argc, char *argv[])
|
||||
{
|
||||
uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */
|
||||
uint8_t ipv6enabled = 1; /* x */
|
||||
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
|
||||
|
||||
if (argvoffset < 0)
|
||||
|
@ -215,12 +216,12 @@ Tox *init_tox(int argc, char *argv[])
|
|||
exit(0);
|
||||
}
|
||||
|
||||
Tox *tox = tox_new(0);
|
||||
Tox *tox = tox_new(0, 0, 0, 0);
|
||||
|
||||
if (!tox)
|
||||
exit(1);
|
||||
|
||||
tox_set_name(tox, (uint8_t *)IRC_NAME, sizeof(IRC_NAME) - 1);
|
||||
tox_self_set_name(tox, (uint8_t *)IRC_NAME, sizeof(IRC_NAME) - 1, 0);
|
||||
tox_callback_friend_message(tox, &callback_friend_message, 0);
|
||||
tox_callback_group_invite(tox, &callback_group_invite, 0);
|
||||
tox_callback_group_message(tox, ©_groupmessage, 0);
|
||||
|
@ -228,7 +229,7 @@ Tox *init_tox(int argc, char *argv[])
|
|||
|
||||
uint16_t port = atoi(argv[argvoffset + 2]);
|
||||
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
|
||||
int res = tox_bootstrap_from_address(tox, argv[argvoffset + 1], port, binary_string);
|
||||
tox_bootstrap(tox, argv[argvoffset + 1], port, binary_string, 0);
|
||||
free(binary_string);
|
||||
|
||||
char temp_id[128];
|
||||
|
@ -239,10 +240,10 @@ Tox *init_tox(int argc, char *argv[])
|
|||
}
|
||||
|
||||
uint8_t *bin_id = hex_string_to_bin(temp_id);
|
||||
int num = tox_add_friend(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo") - 1);
|
||||
uint32_t num = tox_friend_add(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo") - 1, 0);
|
||||
free(bin_id);
|
||||
|
||||
if (num < 0) {
|
||||
if (num == UINT32_MAX) {
|
||||
printf("\nSomething went wrong when adding friend.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
@ -342,7 +343,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
tox_do(tox);
|
||||
tox_iterate(tox);
|
||||
usleep(1000 * 50);
|
||||
}
|
||||
|
||||
|
|
273
testing/nTox.c
273
testing/nTox.c
|
@ -105,55 +105,52 @@ int x, y;
|
|||
int conversation_default = 0;
|
||||
|
||||
typedef struct {
|
||||
uint8_t id[TOX_CLIENT_ID_SIZE];
|
||||
uint8_t id[TOX_PUBLIC_KEY_SIZE];
|
||||
uint8_t accepted;
|
||||
} Friend_request;
|
||||
|
||||
Friend_request pending_requests[256];
|
||||
uint8_t num_requests = 0;
|
||||
|
||||
#define NUM_FILE_SENDERS 256
|
||||
#define NUM_FILE_SENDERS 64
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
uint16_t friendnum;
|
||||
uint8_t filenumber;
|
||||
uint8_t nextpiece[1024];
|
||||
uint16_t piecelength;
|
||||
uint32_t friendnum;
|
||||
uint32_t filenumber;
|
||||
} File_Sender;
|
||||
File_Sender file_senders[NUM_FILE_SENDERS];
|
||||
uint8_t numfilesenders;
|
||||
|
||||
void send_filesenders(Tox *m)
|
||||
void tox_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, size_t length,
|
||||
void *user_data)
|
||||
{
|
||||
uint32_t i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
|
||||
if (file_senders[i].file == 0)
|
||||
continue;
|
||||
|
||||
while (1) {
|
||||
if (tox_file_send_data(m, file_senders[i].friendnum, file_senders[i].filenumber, file_senders[i].nextpiece,
|
||||
file_senders[i].piecelength) == -1)
|
||||
break;
|
||||
|
||||
file_senders[i].piecelength = fread(file_senders[i].nextpiece, 1, tox_file_data_size(m, file_senders[i].friendnum),
|
||||
file_senders[i].file);
|
||||
|
||||
if (file_senders[i].piecelength == 0) {
|
||||
/* This is slow */
|
||||
if (file_senders[i].file && file_senders[i].friendnum == friend_number && file_senders[i].filenumber == file_number) {
|
||||
if (length == 0) {
|
||||
fclose(file_senders[i].file);
|
||||
file_senders[i].file = 0;
|
||||
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenumber, 3, 0, 0);
|
||||
char msg[512];
|
||||
sprintf(msg, "[t] %u file transfer: %u completed", file_senders[i].friendnum, file_senders[i].filenumber);
|
||||
new_lines(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
fseek(file_senders[i].file, position, SEEK_SET);
|
||||
uint8_t data[length];
|
||||
int len = fread(data, 1, length, file_senders[i].file);
|
||||
tox_file_send_chunk(tox, friend_number, file_number, position, data, len, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
int add_filesender(Tox *m, uint16_t friendnum, char *filename)
|
||||
|
||||
|
||||
uint32_t add_filesender(Tox *m, uint16_t friendnum, char *filename)
|
||||
{
|
||||
FILE *tempfile = fopen(filename, "r");
|
||||
FILE *tempfile = fopen(filename, "rb");
|
||||
|
||||
if (tempfile == 0)
|
||||
return -1;
|
||||
|
@ -161,15 +158,13 @@ int add_filesender(Tox *m, uint16_t friendnum, char *filename)
|
|||
fseek(tempfile, 0, SEEK_END);
|
||||
uint64_t filesize = ftell(tempfile);
|
||||
fseek(tempfile, 0, SEEK_SET);
|
||||
int filenum = tox_new_file_sender(m, friendnum, filesize, (uint8_t *)filename, strlen(filename) + 1);
|
||||
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;
|
||||
|
||||
file_senders[numfilesenders].file = tempfile;
|
||||
file_senders[numfilesenders].piecelength = fread(file_senders[numfilesenders].nextpiece, 1, tox_file_data_size(m,
|
||||
file_senders[numfilesenders].friendnum),
|
||||
file_senders[numfilesenders].file);
|
||||
file_senders[numfilesenders].friendnum = friendnum;
|
||||
file_senders[numfilesenders].filenumber = filenum;
|
||||
++numfilesenders;
|
||||
|
@ -179,19 +174,19 @@ int add_filesender(Tox *m, uint16_t friendnum, char *filename)
|
|||
|
||||
|
||||
#define FRADDR_TOSTR_CHUNK_LEN 8
|
||||
#define FRADDR_TOSTR_BUFSIZE (TOX_FRIEND_ADDRESS_SIZE * 2 + TOX_FRIEND_ADDRESS_SIZE / FRADDR_TOSTR_CHUNK_LEN + 1)
|
||||
#define FRADDR_TOSTR_BUFSIZE (TOX_ADDRESS_SIZE * 2 + TOX_ADDRESS_SIZE / FRADDR_TOSTR_CHUNK_LEN + 1)
|
||||
|
||||
static void fraddr_to_str(uint8_t *id_bin, char *id_str)
|
||||
{
|
||||
uint32_t i, delta = 0, pos_extra, sum_extra = 0;
|
||||
|
||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
|
||||
for (i = 0; i < TOX_ADDRESS_SIZE; i++) {
|
||||
sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]);
|
||||
|
||||
if ((i + 1) == TOX_CLIENT_ID_SIZE)
|
||||
if ((i + 1) == TOX_PUBLIC_KEY_SIZE)
|
||||
pos_extra = 2 * (i + 1) + delta;
|
||||
|
||||
if (i >= TOX_CLIENT_ID_SIZE)
|
||||
if (i >= TOX_PUBLIC_KEY_SIZE)
|
||||
sum_extra |= id_bin[i];
|
||||
|
||||
if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) {
|
||||
|
@ -210,14 +205,15 @@ void get_id(Tox *m, char *data)
|
|||
{
|
||||
sprintf(data, "[i] ID: ");
|
||||
int offset = strlen(data);
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(m, address);
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, address);
|
||||
fraddr_to_str(address, data + offset);
|
||||
}
|
||||
|
||||
int getfriendname_terminated(Tox *m, int friendnum, char *namebuf)
|
||||
{
|
||||
int res = tox_get_name(m, friendnum, (uint8_t *)namebuf);
|
||||
tox_friend_get_name(m, friendnum, (uint8_t *)namebuf, NULL);
|
||||
int res = tox_friend_get_name_size(m, friendnum, NULL);
|
||||
|
||||
if (res >= 0)
|
||||
namebuf[res] = 0;
|
||||
|
@ -249,13 +245,13 @@ void new_lines(char *line)
|
|||
|
||||
|
||||
const char ptrn_friend[] = "[i] Friend %i: %s\n+ id: %s";
|
||||
const int id_str_len = TOX_FRIEND_ADDRESS_SIZE * 2 + 3;
|
||||
const int id_str_len = TOX_ADDRESS_SIZE * 2 + 3;
|
||||
void print_friendlist(Tox *m)
|
||||
{
|
||||
new_lines("[i] Friend List:");
|
||||
|
||||
char name[TOX_MAX_NAME_LENGTH + 1];
|
||||
uint8_t fraddr_bin[TOX_FRIEND_ADDRESS_SIZE];
|
||||
uint8_t fraddr_bin[TOX_ADDRESS_SIZE];
|
||||
char fraddr_str[FRADDR_TOSTR_BUFSIZE];
|
||||
|
||||
/* account for the longest name and the longest "base" string and number (int) and id_str */
|
||||
|
@ -264,7 +260,7 @@ void print_friendlist(Tox *m)
|
|||
uint32_t i = 0;
|
||||
|
||||
while (getfriendname_terminated(m, i, name) != -1) {
|
||||
if (!tox_get_client_id(m, i, fraddr_bin))
|
||||
if (tox_friend_get_public_key(m, i, fraddr_bin, NULL))
|
||||
fraddr_to_str(fraddr_bin, fraddr_str);
|
||||
else
|
||||
sprintf(fraddr_str, "???");
|
||||
|
@ -347,50 +343,59 @@ void line_eval(Tox *m, char *line)
|
|||
}
|
||||
|
||||
unsigned char *bin_string = hex_string_to_bin(temp_id);
|
||||
int num = tox_add_friend(m, bin_string, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
|
||||
TOX_ERR_FRIEND_ADD error;
|
||||
uint32_t num = tox_friend_add(m, bin_string, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"), &error);
|
||||
free(bin_string);
|
||||
char numstring[100];
|
||||
|
||||
switch (num) {
|
||||
case TOX_FAERR_TOOLONG:
|
||||
switch (error) {
|
||||
case TOX_ERR_FRIEND_ADD_TOO_LONG:
|
||||
sprintf(numstring, "[i] Message is too long.");
|
||||
break;
|
||||
|
||||
case TOX_FAERR_NOMESSAGE:
|
||||
case TOX_ERR_FRIEND_ADD_NO_MESSAGE:
|
||||
sprintf(numstring, "[i] Please add a message to your request.");
|
||||
break;
|
||||
|
||||
case TOX_FAERR_OWNKEY:
|
||||
case TOX_ERR_FRIEND_ADD_OWN_KEY:
|
||||
sprintf(numstring, "[i] That appears to be your own ID.");
|
||||
break;
|
||||
|
||||
case TOX_FAERR_ALREADYSENT:
|
||||
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
|
||||
sprintf(numstring, "[i] Friend request already sent.");
|
||||
break;
|
||||
|
||||
case TOX_FAERR_UNKNOWN:
|
||||
sprintf(numstring, "[i] Undefined error when adding friend.");
|
||||
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
|
||||
sprintf(numstring, "[i] Address has a bad checksum.");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (num >= 0) {
|
||||
sprintf(numstring, "[i] Added friend as %d.", num);
|
||||
save_data(m);
|
||||
} else
|
||||
sprintf(numstring, "[i] Unknown error %i.", num);
|
||||
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
|
||||
sprintf(numstring, "[i] New nospam set.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_MALLOC:
|
||||
sprintf(numstring, "[i] malloc error.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_NULL:
|
||||
sprintf(numstring, "[i] message was NULL.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_OK:
|
||||
sprintf(numstring, "[i] Added friend as %d.", num);
|
||||
save_data(m);
|
||||
break;
|
||||
}
|
||||
|
||||
new_lines(numstring);
|
||||
} else if (inpt_command == 'd') {
|
||||
tox_do(m);
|
||||
tox_iterate(m);
|
||||
} else if (inpt_command == 'm') { //message command: /m friendnumber messsage
|
||||
char *posi[1];
|
||||
int num = strtoul(line + prompt_offset, posi, 0);
|
||||
|
||||
if (**posi != 0) {
|
||||
if (tox_send_message(m, num, (uint8_t *) *posi + 1, strlen(*posi + 1)) < 1) {
|
||||
if (tox_friend_send_message(m, num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) *posi + 1, strlen(*posi + 1), NULL) < 1) {
|
||||
char sss[256];
|
||||
sprintf(sss, "[i] could not send message to friend num %u", num);
|
||||
new_lines(sss);
|
||||
|
@ -410,14 +415,14 @@ void line_eval(Tox *m, char *line)
|
|||
}
|
||||
|
||||
name[i - 3] = 0;
|
||||
tox_set_name(m, name, i - 2);
|
||||
tox_self_set_name(m, name, i - 2, NULL);
|
||||
char numstring[100];
|
||||
sprintf(numstring, "[i] changed nick to %s", (char *)name);
|
||||
new_lines(numstring);
|
||||
} else if (inpt_command == 'l') {
|
||||
print_friendlist(m);
|
||||
} else if (inpt_command == 's') {
|
||||
uint8_t status[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||
uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
size_t i, len = strlen(line);
|
||||
|
||||
for (i = 3; i < len; i++) {
|
||||
|
@ -427,7 +432,7 @@ void line_eval(Tox *m, char *line)
|
|||
}
|
||||
|
||||
status[i - 3] = 0;
|
||||
tox_set_status_message(m, status, strlen((char *)status));
|
||||
tox_self_set_status_message(m, status, strlen((char *)status), NULL);
|
||||
char numstring[100];
|
||||
sprintf(numstring, "[i] changed status to %s", (char *)status);
|
||||
new_lines(numstring);
|
||||
|
@ -439,9 +444,9 @@ void line_eval(Tox *m, char *line)
|
|||
sprintf(numchar, "[i] you either didn't receive that request or you already accepted it");
|
||||
new_lines(numchar);
|
||||
} else {
|
||||
int num = tox_add_friend_norequest(m, pending_requests[numf].id);
|
||||
uint32_t num = tox_friend_add_norequest(m, pending_requests[numf].id, NULL);
|
||||
|
||||
if (num != -1) {
|
||||
if (num != UINT32_MAX) {
|
||||
pending_requests[numf].accepted = 1;
|
||||
sprintf(numchar, "[i] friend request %u accepted as friend no. %d", numf, num);
|
||||
new_lines(numchar);
|
||||
|
@ -475,9 +480,9 @@ void line_eval(Tox *m, char *line)
|
|||
} while ((c != 'y') && (c != 'n') && (c != EOF));
|
||||
|
||||
if (c == 'y') {
|
||||
int res = tox_del_friend(m, numf);
|
||||
int res = tox_friend_delete(m, numf, NULL);
|
||||
|
||||
if (res == 0)
|
||||
if (res)
|
||||
sprintf(msg, "[i] [%i: %s] is no longer your friend", numf, fname);
|
||||
else
|
||||
sprintf(msg, "[i] failed to remove friend");
|
||||
|
@ -602,7 +607,7 @@ void line_eval(Tox *m, char *line)
|
|||
if (conversation_default != 0) {
|
||||
if (conversation_default > 0) {
|
||||
int friendnumber = conversation_default - 1;
|
||||
uint32_t res = tox_send_message(m, friendnumber, (uint8_t *)line, strlen(line));
|
||||
uint32_t res = tox_friend_send_message(m, friendnumber, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *)line, strlen(line), NULL);
|
||||
|
||||
if (res == 0) {
|
||||
char sss[128];
|
||||
|
@ -863,20 +868,21 @@ void do_refresh()
|
|||
refresh();
|
||||
}
|
||||
|
||||
void print_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||
void print_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
{
|
||||
new_lines("[i] received friend request with message:");
|
||||
new_lines((char *)data);
|
||||
char numchar[100];
|
||||
sprintf(numchar, "[i] accept request with /a %u", num_requests);
|
||||
new_lines(numchar);
|
||||
memcpy(pending_requests[num_requests].id, public_key, TOX_CLIENT_ID_SIZE);
|
||||
memcpy(pending_requests[num_requests].id, public_key, TOX_PUBLIC_KEY_SIZE);
|
||||
pending_requests[num_requests].accepted = 0;
|
||||
++num_requests;
|
||||
do_refresh();
|
||||
}
|
||||
|
||||
void print_message(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||
void print_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
|
||||
void *userdata)
|
||||
{
|
||||
/* ensure null termination */
|
||||
uint8_t null_string[length + 1];
|
||||
|
@ -885,7 +891,7 @@ void print_message(Tox *m, int friendnumber, const uint8_t *string, uint16_t len
|
|||
print_formatted_message(m, (char *)null_string, friendnumber, 0);
|
||||
}
|
||||
|
||||
void print_nickchange(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||
void print_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
|
||||
{
|
||||
char name[TOX_MAX_NAME_LENGTH + 1];
|
||||
|
||||
|
@ -901,7 +907,7 @@ void print_nickchange(Tox *m, int friendnumber, const uint8_t *string, uint16_t
|
|||
}
|
||||
}
|
||||
|
||||
void print_statuschange(Tox *m, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||
void print_statuschange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
|
||||
{
|
||||
char name[TOX_MAX_NAME_LENGTH + 1];
|
||||
|
||||
|
@ -919,7 +925,7 @@ void print_statuschange(Tox *m, int friendnumber, const uint8_t *string, uint16_
|
|||
|
||||
static char *data_file_name = NULL;
|
||||
|
||||
static int load_data(Tox *m)
|
||||
static Tox *load_data()
|
||||
{
|
||||
FILE *data_file = fopen(data_file_name, "r");
|
||||
|
||||
|
@ -936,7 +942,7 @@ static int load_data(Tox *m)
|
|||
return 0;
|
||||
}
|
||||
|
||||
tox_load(m, data, size);
|
||||
Tox *m = tox_new(0, data, size, NULL);
|
||||
|
||||
if (fclose(data_file) < 0) {
|
||||
perror("[!] fclose failed");
|
||||
|
@ -944,10 +950,10 @@ static int load_data(Tox *m)
|
|||
/* return 0; */
|
||||
}
|
||||
|
||||
return 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return tox_new(0, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static int save_data(Tox *m)
|
||||
|
@ -960,9 +966,9 @@ static int save_data(Tox *m)
|
|||
}
|
||||
|
||||
int res = 1;
|
||||
size_t size = tox_size(m);
|
||||
size_t size = tox_get_savedata_size(m);
|
||||
uint8_t data[size];
|
||||
tox_save(m, data);
|
||||
tox_get_savedata(m, data);
|
||||
|
||||
if (fwrite(data, sizeof(uint8_t), size, data_file) != size) {
|
||||
fputs("[!] could not write data file (1)!", stderr);
|
||||
|
@ -977,13 +983,10 @@ static int save_data(Tox *m)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int load_data_or_init(Tox *m, char *path)
|
||||
static int save_data_file(Tox *m, char *path)
|
||||
{
|
||||
data_file_name = path;
|
||||
|
||||
if (load_data(m))
|
||||
return 1;
|
||||
|
||||
if (save_data(m))
|
||||
return 1;
|
||||
|
||||
|
@ -1125,58 +1128,94 @@ void print_groupnamelistchange(Tox *m, int groupnumber, int peernumber, uint8_t
|
|||
print_groupchatpeers(m, groupnumber);
|
||||
}
|
||||
}
|
||||
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 file_request_accept(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t type, uint64_t file_size,
|
||||
const uint8_t *filename, size_t filename_length, void *user_data)
|
||||
{
|
||||
if (type != TOX_FILE_KIND_DATA) {
|
||||
new_lines("Refused invalid file type.");
|
||||
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
char msg[512];
|
||||
sprintf(msg, "[t] %u is sending us: %s of size %llu", friendnumber, filename, (long long unsigned int)filesize);
|
||||
sprintf(msg, "[t] %u is sending us: %s of size %llu", friend_number, filename, (long long unsigned int)file_size);
|
||||
new_lines(msg);
|
||||
|
||||
if (tox_file_send_control(m, friendnumber, 1, filenumber, 0, 0, 0) == 0) {
|
||||
sprintf(msg, "Accepted file transfer. (saving file as: %u.%u.bin)", friendnumber, filenumber);
|
||||
if (tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, 0)) {
|
||||
sprintf(msg, "Accepted file transfer. (saving file as: %u.%u.bin)", friend_number, file_number);
|
||||
new_lines(msg);
|
||||
} else
|
||||
new_lines("Could not accept file transfer.");
|
||||
}
|
||||
|
||||
void file_print_control(Tox *m, int friendnumber, uint8_t send_recieve, 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 *user_data)
|
||||
{
|
||||
char msg[512] = {0};
|
||||
|
||||
if (control_type == 0)
|
||||
sprintf(msg, "[t] %u accepted file transfer: %u", friendnumber, filenumber);
|
||||
else if (control_type == 3)
|
||||
sprintf(msg, "[t] %u file transfer: %u completed", friendnumber, filenumber);
|
||||
else
|
||||
sprintf(msg, "[t] control %u received", control_type);
|
||||
|
||||
sprintf(msg, "[t] control %u received", control);
|
||||
new_lines(msg);
|
||||
|
||||
if (control == TOX_FILE_CONTROL_CANCEL) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
|
||||
/* This is slow */
|
||||
if (file_senders[i].file && file_senders[i].friendnum == friend_number && file_senders[i].filenumber == file_number) {
|
||||
fclose(file_senders[i].file);
|
||||
file_senders[i].file = 0;
|
||||
char msg[512];
|
||||
sprintf(msg, "[t] %u file transfer: %u cancelled", file_senders[i].friendnum, file_senders[i].filenumber);
|
||||
new_lines(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_file(Tox *m, int friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata)
|
||||
void write_file(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
|
||||
size_t length, void *user_data)
|
||||
{
|
||||
char filename[256];
|
||||
sprintf(filename, "%u.%u.bin", friendnumber, filenumber);
|
||||
FILE *pFile = fopen(filename, "a");
|
||||
|
||||
if (tox_file_data_remaining(m, friendnumber, filenumber, 1) == 0) {
|
||||
//file_control(m, friendnumber, 1, filenumber, 3, 0, 0);
|
||||
if (length == 0) {
|
||||
char msg[512];
|
||||
sprintf(msg, "[t] %u file transfer: %u completed", friendnumber, filenumber);
|
||||
new_lines(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
char filename[256];
|
||||
sprintf(filename, "%u.%u.bin", friendnumber, filenumber);
|
||||
FILE *pFile = fopen(filename, "r+b");
|
||||
|
||||
if (pFile == NULL)
|
||||
pFile = fopen(filename, "wb");
|
||||
|
||||
fseek(pFile, position, SEEK_SET);
|
||||
|
||||
if (fwrite(data, length, 1, pFile) != 1)
|
||||
new_lines("Error writing to file");
|
||||
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
void print_online(Tox *tox, uint32_t friendnumber, TOX_CONNECTION status, void *userdata)
|
||||
{
|
||||
if (status)
|
||||
printf("\nOther went online.\n");
|
||||
else {
|
||||
printf("\nOther went offline.\n");
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_FILE_SENDERS; ++i)
|
||||
if (file_senders[i].file != 0 && file_senders[i].friendnum == friendnumber) {
|
||||
fclose(file_senders[i].file);
|
||||
file_senders[i].file = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char timeout_getch(Tox *m)
|
||||
{
|
||||
char c;
|
||||
int slpval = tox_do_interval(m);
|
||||
int slpval = tox_iteration_interval(m);
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
|
@ -1214,7 +1253,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
/* let user override default by cmdline */
|
||||
uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */
|
||||
uint8_t ipv6enabled = 1; /* x */
|
||||
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
|
||||
|
||||
if (argvoffset < 0)
|
||||
|
@ -1231,25 +1270,28 @@ int main(int argc, char *argv[])
|
|||
if (!strcmp(argv[argc - 2], "-f"))
|
||||
filename = argv[argc - 1];
|
||||
|
||||
m = tox_new(0);
|
||||
data_file_name = filename;
|
||||
m = load_data();
|
||||
|
||||
if ( !m ) {
|
||||
fputs("Failed to allocate Messenger datastructure", stderr);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
load_data_or_init(m, filename);
|
||||
save_data_file(m, filename);
|
||||
|
||||
tox_callback_friend_request(m, print_request, NULL);
|
||||
tox_callback_friend_message(m, print_message, NULL);
|
||||
tox_callback_name_change(m, print_nickchange, NULL);
|
||||
tox_callback_status_message(m, print_statuschange, NULL);
|
||||
tox_callback_friend_name(m, print_nickchange, NULL);
|
||||
tox_callback_friend_status_message(m, print_statuschange, NULL);
|
||||
tox_callback_group_invite(m, print_invite, NULL);
|
||||
tox_callback_group_message(m, print_groupmessage, NULL);
|
||||
tox_callback_file_data(m, write_file, NULL);
|
||||
tox_callback_file_control(m, file_print_control, NULL);
|
||||
tox_callback_file_send_request(m, file_request_accept, NULL);
|
||||
tox_callback_file_recv_chunk(m, write_file, NULL);
|
||||
tox_callback_file_recv_control(m, file_print_control, NULL);
|
||||
tox_callback_file_recv(m, file_request_accept, NULL);
|
||||
tox_callback_file_chunk_request(m, tox_file_chunk_request, NULL);
|
||||
tox_callback_group_namelist_change(m, print_groupnamelistchange, NULL);
|
||||
tox_callback_friend_connection_status(m, print_online, NULL);
|
||||
|
||||
initscr();
|
||||
noecho();
|
||||
|
@ -1263,7 +1305,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
uint16_t port = atoi(argv[argvoffset + 2]);
|
||||
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
|
||||
int res = tox_bootstrap_from_address(m, argv[argvoffset + 1], port, binary_string);
|
||||
int res = tox_bootstrap(m, argv[argvoffset + 1], port, binary_string, NULL);
|
||||
|
||||
if (!res) {
|
||||
printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]);
|
||||
|
@ -1275,7 +1317,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
new_lines("[i] change username with /n");
|
||||
uint8_t name[TOX_MAX_NAME_LENGTH + 1];
|
||||
uint16_t namelen = tox_get_self_name(m, name);
|
||||
tox_self_get_name(m, name);
|
||||
uint16_t namelen = tox_self_get_name_size(m);
|
||||
name[namelen] = 0;
|
||||
|
||||
if (namelen > 0) {
|
||||
|
@ -1288,7 +1331,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
while (1) {
|
||||
if (on == 0) {
|
||||
if (tox_isconnected(m)) {
|
||||
if (tox_self_get_connection_status(m)) {
|
||||
new_lines("[i] connected to DHT");
|
||||
on = 1;
|
||||
} else {
|
||||
|
@ -1296,15 +1339,12 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (timestamp0 + 10 < timestamp1) {
|
||||
timestamp0 = timestamp1;
|
||||
tox_bootstrap_from_address(m, argv[argvoffset + 1], port, binary_string);
|
||||
tox_bootstrap(m, argv[argvoffset + 1], port, binary_string, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
send_filesenders(m);
|
||||
tox_do(m);
|
||||
tox_iterate(m);
|
||||
do_refresh();
|
||||
|
||||
int c = timeout_getch(m);
|
||||
|
@ -1320,11 +1360,12 @@ int main(int argc, char *argv[])
|
|||
} else if (c == 8 || c == 127) {
|
||||
input_line[strlen(input_line) - 1] = '\0';
|
||||
} else if (isalnum(c) || ispunct(c) || c == ' ') {
|
||||
strcpy(input_line, appender(input_line, (char) c));
|
||||
appender(input_line, (char) c);
|
||||
}
|
||||
}
|
||||
|
||||
free(binary_string);
|
||||
save_data_file(m, filename);
|
||||
tox_kill(m);
|
||||
endwin();
|
||||
return 0;
|
||||
|
|
|
@ -1,755 +0,0 @@
|
|||
/*
|
||||
* A bot to test Tox avatars
|
||||
*
|
||||
* Usage: ./test_avatars <data dir>
|
||||
*
|
||||
* Connects to the Tox network, publishes our avatar, requests our friends
|
||||
* avatars and, if available, saves them to a local cache.
|
||||
* This bot automatically accepts any friend request.
|
||||
*
|
||||
*
|
||||
* Data dir MUST have:
|
||||
*
|
||||
* - A file named "data" (named accordingly to STS Draft v0.1.0) with
|
||||
* user id, friends, bootstrap data, etc. from a previously configured
|
||||
* Tox session; use a client (eg. toxic) to configure it, add friends,
|
||||
* etc.
|
||||
*
|
||||
* Data dir MAY have:
|
||||
*
|
||||
* - A directory named "avatars" with the user's avatar and cached avatars.
|
||||
* The user avatar must be named in the format: "<uppercase pub key>.png"
|
||||
*
|
||||
*
|
||||
* The bot will answer to these commands:
|
||||
*
|
||||
* !debug-on - Enable extended debug messages
|
||||
* !debug-off - Disenable extended debug messages
|
||||
* !set-avatar - Set our avatar from "avatars/<USERID>.png"
|
||||
* !remove-avatar - Remove our avatar
|
||||
*
|
||||
*/
|
||||
|
||||
#define DATA_FILE_NAME "data"
|
||||
#define AVATAR_DIR_NAME "avatars"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "../toxcore/tox.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
|
||||
/* Basic debug utils */
|
||||
|
||||
#define DEBUG(format, ...) debug_printf("DEBUG: %s:%d %s: " format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
|
||||
static bool print_debug_msgs = true;
|
||||
|
||||
static void debug_printf(const char *fmt, ...)
|
||||
{
|
||||
if (print_debug_msgs == true) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------ Avatar cache managenment functions ------------ */
|
||||
|
||||
typedef struct {
|
||||
uint8_t format;
|
||||
char *suffix;
|
||||
} avatar_format_data_t;
|
||||
|
||||
static const avatar_format_data_t avatar_formats[] = {
|
||||
/* In order of preference */
|
||||
{ TOX_AVATAR_FORMAT_PNG, "png" },
|
||||
{ TOX_AVATAR_FORMAT_NONE, NULL }, /* Must be the last one */
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void set_avatar(Tox *tox, const char *base_dir);
|
||||
|
||||
|
||||
static char *get_avatar_suffix_from_format(uint8_t format)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; avatar_formats[i].format != TOX_AVATAR_FORMAT_NONE; i++)
|
||||
if (avatar_formats[i].format == format)
|
||||
return avatar_formats[i].suffix;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Load avatar data from a file into a memory buffer 'buf'.
|
||||
* buf must have at least TOX_MAX_AVATAR_DATA_LENGTH bytes
|
||||
* Returns the length of the data sucess or < 0 on error
|
||||
*/
|
||||
static int load_avatar_data(char *fname, uint8_t *buf)
|
||||
{
|
||||
FILE *fp = fopen(fname, "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
return -1; /* Error */
|
||||
|
||||
size_t n = fread(buf, 1, TOX_AVATAR_MAX_DATA_LENGTH, fp);
|
||||
int ret;
|
||||
|
||||
if (ferror(fp) != 0 || n == 0)
|
||||
ret = -1; /* Error */
|
||||
else
|
||||
ret = n;
|
||||
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Save avatar data to a file */
|
||||
static int save_avatar_data(char *fname, uint8_t *data, uint32_t len)
|
||||
{
|
||||
FILE *fp = fopen(fname, "wb");
|
||||
|
||||
if (fp == NULL)
|
||||
return -1; /* Error */
|
||||
|
||||
int ret = 0; /* Ok */
|
||||
|
||||
if (fwrite(data, 1, len, fp) != len)
|
||||
ret = -1; /* Error */
|
||||
|
||||
if (fclose(fp) != 0)
|
||||
ret = -1; /* Error */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void byte_to_hex_str(const uint8_t *buf, const size_t buflen, char *dst)
|
||||
{
|
||||
const char *hex_chars = "0123456789ABCDEF";
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
|
||||
while (i < buflen) {
|
||||
dst[j++] = hex_chars[(buf[i] >> 4) & 0xf];
|
||||
dst[j++] = hex_chars[buf[i] & 0xf];
|
||||
i++;
|
||||
}
|
||||
|
||||
dst[j++] = '\0';
|
||||
}
|
||||
|
||||
/* Make the cache file name for an avatar of the given format for the given
|
||||
* public key.
|
||||
*/
|
||||
static int make_avatar_file_name(char *dst, size_t dst_len, const char *base_dir,
|
||||
const uint8_t format, uint8_t *public_key)
|
||||
{
|
||||
char public_key_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
|
||||
byte_to_hex_str(public_key, TOX_PUBLIC_KEY_SIZE, public_key_str);
|
||||
|
||||
const char *suffix = get_avatar_suffix_from_format(format);
|
||||
|
||||
if (suffix == NULL)
|
||||
return -1; /* Error */
|
||||
|
||||
int n = snprintf(dst, dst_len, "%s/%s/%s.%s", base_dir, AVATAR_DIR_NAME,
|
||||
public_key_str, suffix);
|
||||
dst[dst_len - 1] = '\0';
|
||||
|
||||
if (n >= dst_len)
|
||||
return -1; /* Error: Output truncated */
|
||||
|
||||
return 0; /* Ok */
|
||||
}
|
||||
|
||||
|
||||
/* Load a cached avatar into the buffer 'data' (which must be at least
|
||||
* TOX_MAX_AVATAR_DATA_LENGTH bytes long). Gets the file name from client
|
||||
* id and the given data format.
|
||||
* Returns 0 on success, or -1 on error.
|
||||
*/
|
||||
static int load_user_avatar(Tox *tox, char *base_dir, int friendnum,
|
||||
uint8_t format, uint8_t *hash, uint8_t *data, uint32_t *datalen)
|
||||
{
|
||||
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
|
||||
|
||||
if (tox_get_client_id(tox, friendnum, addr) != 0) {
|
||||
DEBUG("Bad client id, friendnumber=%d", friendnum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
int ret = make_avatar_file_name(path, sizeof(path), base_dir, format, addr);
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG("Can't create an file name for this user/avatar.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = load_avatar_data(path, data);
|
||||
|
||||
if (ret < 0) {
|
||||
DEBUG("Failed to load avatar data.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*datalen = ret;
|
||||
tox_hash(hash, data, *datalen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save a user avatar into the cache. Gets the file name from the public key
|
||||
* and the given data format.
|
||||
* Returns 0 on success, or -1 on error.
|
||||
*/
|
||||
static int save_user_avatar(Tox *tox, char *base_dir, int friendnum,
|
||||
uint8_t format, uint8_t *data, uint32_t datalen)
|
||||
{
|
||||
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
|
||||
|
||||
if (tox_get_client_id(tox, friendnum, addr) != 0) {
|
||||
DEBUG("Bad client id, friendnumber=%d", friendnum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
int ret = make_avatar_file_name(path, sizeof(path), base_dir, format, addr);
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG("Can't create a file name for this user/avatar");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return save_avatar_data(path, data, datalen);
|
||||
}
|
||||
|
||||
/* Delete all cached avatars for a given user */
|
||||
static int delete_user_avatar(Tox *tox, char *base_dir, int friendnum)
|
||||
{
|
||||
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
|
||||
|
||||
if (tox_get_client_id(tox, friendnum, addr) != 0) {
|
||||
DEBUG("Bad client id, friendnumber=%d", friendnum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
|
||||
/* This iteration is dumb and inefficient */
|
||||
int i;
|
||||
|
||||
for (i = 0; avatar_formats[i].format != TOX_AVATAR_FORMAT_NONE; i++) {
|
||||
int ret = make_avatar_file_name(path, sizeof(path), base_dir,
|
||||
avatar_formats[i].format, addr);
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG("Failed to create avatar path for friend #%d, format %d\n",
|
||||
friendnum, avatar_formats[i].format);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlink(path) == 0)
|
||||
printf("Avatar file %s deleted.\n", path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------ Protocol callbacks ------------ */
|
||||
|
||||
static void friend_status_cb(Tox *tox, int n, uint8_t status, void *ud)
|
||||
{
|
||||
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
|
||||
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
|
||||
|
||||
if (tox_get_client_id(tox, n, addr) == 0) {
|
||||
byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str);
|
||||
printf("Receiving status from %s: %u\n", addr_str, status);
|
||||
}
|
||||
}
|
||||
|
||||
static void friend_avatar_info_cb(Tox *tox, int32_t n, uint8_t format, uint8_t *hash, void *ud)
|
||||
{
|
||||
char *base_dir = (char *) ud;
|
||||
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
|
||||
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
|
||||
char hash_str[2 * TOX_HASH_LENGTH + 1];
|
||||
|
||||
if (tox_get_client_id(tox, n, addr) == 0) {
|
||||
byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str);
|
||||
printf("Receiving avatar information from %s.\n", addr_str);
|
||||
} else {
|
||||
DEBUG("tox_get_client_id failed");
|
||||
printf("Receiving avatar information from friend number %u.\n", n);
|
||||
}
|
||||
|
||||
byte_to_hex_str(hash, TOX_HASH_LENGTH, hash_str);
|
||||
DEBUG("format=%u, hash=%s", format, hash_str);
|
||||
|
||||
if (format == TOX_AVATAR_FORMAT_NONE) {
|
||||
printf(" -> User do not have an avatar.\n");
|
||||
/* User have no avatar anymore, delete it from our cache */
|
||||
delete_user_avatar(tox, base_dir, n);
|
||||
} else {
|
||||
/* Check the hash of the currently cached user avatar
|
||||
* WARNING: THIS IS ONLY AN EXAMPLE!
|
||||
*
|
||||
* Real clients should keep the hashes in memory (eg. in the object
|
||||
* used to represent a friend in the friend list) and do not access
|
||||
* the file system or do anything resource intensive in reply of
|
||||
* these events.
|
||||
*/
|
||||
uint32_t cur_av_len;
|
||||
uint8_t cur_av_data[TOX_AVATAR_MAX_DATA_LENGTH];
|
||||
uint8_t cur_av_hash[TOX_HASH_LENGTH];
|
||||
int ret;
|
||||
|
||||
ret = load_user_avatar(tox, base_dir, n, format, cur_av_hash, cur_av_data, &cur_av_len);
|
||||
|
||||
if (ret != 0
|
||||
&& memcpy(cur_av_hash, hash, TOX_HASH_LENGTH) != 0) {
|
||||
printf(" -> Cached avatar is outdated. Requesting avatar data.\n");
|
||||
tox_request_avatar_data(tox, n);
|
||||
} else {
|
||||
printf(" -> Cached avatar is still updated.\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void friend_avatar_data_cb(Tox *tox, int32_t n, uint8_t format,
|
||||
uint8_t *hash, uint8_t *data, uint32_t datalen, void *ud)
|
||||
{
|
||||
char *base_dir = (char *) ud;
|
||||
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
|
||||
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
|
||||
char hash_str[2 * TOX_HASH_LENGTH + 1];
|
||||
|
||||
if (tox_get_client_id(tox, n, addr) == 0) {
|
||||
byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str);
|
||||
printf("Receiving avatar data from %s.\n", addr_str);
|
||||
} else {
|
||||
DEBUG("tox_get_client_id failed");
|
||||
printf("Receiving avatar data from friend number %u.\n", n);
|
||||
}
|
||||
|
||||
byte_to_hex_str(hash, TOX_HASH_LENGTH, hash_str);
|
||||
DEBUG("format=%u, datalen=%d, hash=%s\n", format, datalen, hash_str);
|
||||
|
||||
delete_user_avatar(tox, base_dir, n);
|
||||
|
||||
if (format != TOX_AVATAR_FORMAT_NONE) {
|
||||
int ret = save_user_avatar(tox, base_dir, n, format, data, datalen);
|
||||
|
||||
if (ret == 0)
|
||||
printf(" -> Avatar updated in the cache.\n");
|
||||
else
|
||||
printf(" -> Failed to save user avatar.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void friend_msg_cb(Tox *tox, int n, const uint8_t *msg, uint16_t len, void *ud)
|
||||
{
|
||||
const char *base_dir = (char *) ud;
|
||||
const char *msg_str = (char *) msg;
|
||||
uint8_t addr[TOX_PUBLIC_KEY_SIZE];
|
||||
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
|
||||
|
||||
if (tox_get_client_id(tox, n, addr) == 0) {
|
||||
byte_to_hex_str(addr, TOX_FRIEND_ADDRESS_SIZE, addr_str);
|
||||
printf("Receiving message from %s:\n %s\n", addr_str, msg);
|
||||
}
|
||||
|
||||
/* Handle bot commands for the tests */
|
||||
char *reply_ptr = NULL;
|
||||
|
||||
if (strstr(msg_str, "!debug-on") != NULL) {
|
||||
print_debug_msgs = true;
|
||||
reply_ptr = "Debug enabled.";
|
||||
} else if (strstr(msg_str, "!debug-off") != NULL) {
|
||||
print_debug_msgs = false;
|
||||
reply_ptr = "Debug disabled.";
|
||||
} else if (strstr(msg_str, "!set-avatar") != NULL) {
|
||||
set_avatar(tox, base_dir);
|
||||
reply_ptr = "Setting image avatar";
|
||||
} else if (strstr(msg_str, "!remove-avatar") != NULL) {
|
||||
int r = tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0);
|
||||
DEBUG("tox_set_avatar returned %d", r);
|
||||
reply_ptr = "Removing avatar";
|
||||
}
|
||||
|
||||
/* Add more useful commands here: add friend, etc. */
|
||||
|
||||
char reply[TOX_MAX_MESSAGE_LENGTH];
|
||||
int reply_len;
|
||||
|
||||
if (reply_ptr)
|
||||
reply_len = snprintf(reply, sizeof(reply), "%s", reply_ptr);
|
||||
else
|
||||
reply_len = snprintf(reply, sizeof(reply),
|
||||
"No command found in message: %s", msg);
|
||||
|
||||
reply[sizeof(reply) - 1] = '\0';
|
||||
printf(" -> Reply: %s\n", reply);
|
||||
tox_send_message(tox, n, (uint8_t *) reply, reply_len);
|
||||
}
|
||||
|
||||
|
||||
static void friend_request_cb(Tox *tox, const uint8_t *public_key,
|
||||
const uint8_t *data, uint16_t length, void *ud)
|
||||
{
|
||||
char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1];
|
||||
byte_to_hex_str(public_key, TOX_PUBLIC_KEY_SIZE, addr_str);
|
||||
printf("Accepting friend request from %s.\n %s\n", addr_str, data);
|
||||
tox_add_friend_norequest(tox, public_key);
|
||||
}
|
||||
|
||||
|
||||
static void set_avatar(Tox *tox, const char *base_dir)
|
||||
{
|
||||
uint8_t addr[TOX_FRIEND_ADDRESS_SIZE];
|
||||
char path[PATH_MAX];
|
||||
uint8_t buf[2 * TOX_AVATAR_MAX_DATA_LENGTH];
|
||||
|
||||
tox_get_address(tox, addr);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
if (avatar_formats[i].format == TOX_AVATAR_FORMAT_NONE) {
|
||||
tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0);
|
||||
printf("No avatar file found, setting to NONE.\n");
|
||||
break;
|
||||
} else {
|
||||
int ret = make_avatar_file_name(path, sizeof(path), base_dir,
|
||||
avatar_formats[i].format, addr);
|
||||
|
||||
if (ret < 0) {
|
||||
printf("Failed to generate avatar file name.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int len = load_avatar_data(path, buf);
|
||||
|
||||
if (len < 0) {
|
||||
printf("Failed to load avatar data from file: %s\n", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len > TOX_AVATAR_MAX_DATA_LENGTH) {
|
||||
printf("Avatar file %s is too big (more than %d bytes)",
|
||||
path, TOX_AVATAR_MAX_DATA_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = tox_set_avatar(tox, avatar_formats[i].format, buf, len);
|
||||
DEBUG("tox_set_avatar returned=%d", ret);
|
||||
|
||||
if (ret == 0)
|
||||
printf("Setting avatar from %s (%d bytes).\n", path, len);
|
||||
else
|
||||
printf("Error setting avatar from %s.\n", path);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void print_avatar_info(Tox *tox)
|
||||
{
|
||||
uint8_t format;
|
||||
uint8_t data[TOX_AVATAR_MAX_DATA_LENGTH];
|
||||
uint8_t hash[TOX_HASH_LENGTH];
|
||||
uint32_t data_length;
|
||||
char hash_str[2 * TOX_HASH_LENGTH + 1];
|
||||
|
||||
int ret = tox_get_self_avatar(tox, &format, data, &data_length, sizeof(data), hash);
|
||||
DEBUG("tox_get_self_avatar returned %d", ret);
|
||||
DEBUG("format: %d, data_length: %d", format, data_length);
|
||||
byte_to_hex_str(hash, TOX_HASH_LENGTH, hash_str);
|
||||
DEBUG("hash: %s", hash_str);
|
||||
}
|
||||
|
||||
|
||||
/* ------------ Initialization functions ------------ */
|
||||
|
||||
/* Create directory to store tha avatars. Returns 0 if it was sucessfuly
|
||||
* created or already existed. Returns -1 on error.
|
||||
*/
|
||||
static int create_avatar_diretory(const char *base_dir)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int n = snprintf(path, sizeof(path), "%s/%s", base_dir, AVATAR_DIR_NAME);
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
|
||||
if (n >= sizeof(path))
|
||||
return -1;
|
||||
|
||||
if (mkdir(path, 0755) == 0) {
|
||||
return 0; /* Done */
|
||||
} else if (errno == EEXIST) {
|
||||
/* Check if the existing path is a directory */
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st) != 0) {
|
||||
perror("stat()ing avatar directory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1; /* Error */
|
||||
}
|
||||
|
||||
|
||||
static void *load_bootstrap_data(const char *base_dir, uint32_t *len)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int n = snprintf(path, sizeof(path), "%s/%s", base_dir, DATA_FILE_NAME);
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
|
||||
if (n >= sizeof(path)) {
|
||||
printf("Load error: path %s too long\n", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We should be using POSIX functions here, but let's try to be
|
||||
* compatible with Windows.
|
||||
*/
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
|
||||
if (fp == NULL) {
|
||||
printf("fatal error: file %s not found.\n", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_END) != 0) {
|
||||
printf("seek fail\n");
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32_t flen = ftell(fp);
|
||||
|
||||
if (flen < 8 || flen > 2e6) {
|
||||
printf("Fatal error: file %s have %u bytes. Out of acceptable range.\n", path, flen);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_SET) != 0) {
|
||||
printf("seek fail\n");
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *buf = malloc(flen);
|
||||
|
||||
if (buf == NULL) {
|
||||
printf("malloc failed, %u bytes", flen);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*len = fread(buf, 1, flen, fp);
|
||||
fclose(fp);
|
||||
|
||||
if (*len != flen) {
|
||||
printf("fatal: %s have %u bytes, read only %u\n", path, flen, *len);
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("bootstrap data loaded from %s (%u bytes)\n", path, flen);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int save_bootstrap_data(Tox *tox, const char *base_dir)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int n = snprintf(path, sizeof(path), "%s/%s", base_dir, DATA_FILE_NAME);
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
|
||||
if (n >= sizeof(path)) {
|
||||
printf("Save error: path %s too long\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path_tmp[PATH_MAX];
|
||||
n = snprintf(path_tmp, sizeof(path_tmp), "%s.tmp", path);
|
||||
path_tmp[sizeof(path_tmp) - 1] = '\0';
|
||||
|
||||
if (n >= sizeof(path_tmp)) {
|
||||
printf("error: path %s too long\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t len = tox_size(tox);
|
||||
|
||||
if (len < 8 || len > 2e6) {
|
||||
printf("save data length == %u, out of acceptable range\n", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *buf = malloc(len);
|
||||
|
||||
if (buf == NULL) {
|
||||
printf("save data: malloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tox_save(tox, buf);
|
||||
|
||||
FILE *fp = fopen(path_tmp, "wb");
|
||||
|
||||
if (fp == NULL) {
|
||||
printf("Error saving data: can't open %s\n", path_tmp);
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(buf, 1, len, fp) != len) {
|
||||
printf("Error writing data to %s\n", path_tmp);
|
||||
free(buf);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
if (fclose(fp) != 0) {
|
||||
printf("Error writing data to %s\n", path_tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rename(path_tmp, path) != 0) {
|
||||
printf("Error renaming %s to %s\n", path_tmp, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Bootstrap data saved to %s\n", path);
|
||||
return 0; /* Done */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (argc != 2) {
|
||||
printf("usage: %s <data dir>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *base_dir = argv[1];
|
||||
|
||||
if (create_avatar_diretory(base_dir) != 0)
|
||||
printf("Error creating avatar directory.\n");
|
||||
|
||||
Tox *tox = tox_new(NULL);
|
||||
|
||||
uint32_t len;
|
||||
void *data = load_bootstrap_data(base_dir, &len);
|
||||
|
||||
if (data == NULL)
|
||||
return 1;
|
||||
|
||||
ret = tox_load(tox, data, len);
|
||||
free(data);
|
||||
|
||||
if (ret == 0) {
|
||||
printf("Tox initialized\n");
|
||||
} else {
|
||||
printf("Fatal: tox_load returned %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tox_callback_connection_status(tox, friend_status_cb, NULL);
|
||||
tox_callback_friend_message(tox, friend_msg_cb, base_dir);
|
||||
tox_callback_friend_request(tox, friend_request_cb, NULL);
|
||||
tox_callback_avatar_info(tox, friend_avatar_info_cb, base_dir);
|
||||
tox_callback_avatar_data(tox, friend_avatar_data_cb, base_dir);
|
||||
|
||||
uint8_t addr[TOX_FRIEND_ADDRESS_SIZE];
|
||||
char addr_str[2 * TOX_FRIEND_ADDRESS_SIZE + 1];
|
||||
tox_get_address(tox, addr);
|
||||
byte_to_hex_str(addr, TOX_FRIEND_ADDRESS_SIZE, addr_str);
|
||||
printf("Using local tox address: %s\n", addr_str);
|
||||
|
||||
#ifdef TEST_SET_RESET_AVATAR
|
||||
printf("Printing default avatar information:\n");
|
||||
print_avatar_info(tox);
|
||||
|
||||
printf("Setting a new avatar:\n");
|
||||
set_avatar(tox, base_dir);
|
||||
print_avatar_info(tox);
|
||||
|
||||
printf("Removing the avatar we just set:\n");
|
||||
tox_avatar(tox, TOX_AVATARFORMAT_NONE, NULL, 0);
|
||||
print_avatar_info(tox);
|
||||
|
||||
printf("Setting that avatar again:\n");
|
||||
#endif /* TEST_SET_RESET_AVATAR */
|
||||
|
||||
set_avatar(tox, base_dir);
|
||||
print_avatar_info(tox);
|
||||
|
||||
bool waiting = true;
|
||||
time_t last_save = time(0);
|
||||
|
||||
while (1) {
|
||||
if (tox_isconnected(tox) && waiting) {
|
||||
printf("DHT connected.\n");
|
||||
waiting = false;
|
||||
}
|
||||
|
||||
tox_do(tox);
|
||||
|
||||
time_t now = time(0);
|
||||
|
||||
if (now - last_save > 120) {
|
||||
save_bootstrap_data(tox, base_dir);
|
||||
last_save = now;
|
||||
}
|
||||
|
||||
usleep(500000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -45,15 +45,16 @@
|
|||
|
||||
#define c_sleep(x) usleep(1000*x)
|
||||
|
||||
void print_online(Tox *tox, int friendnumber, uint8_t status, void *userdata)
|
||||
void print_online(Tox *tox, uint32_t friendnumber, TOX_CONNECTION status, void *userdata)
|
||||
{
|
||||
if (status == 1)
|
||||
if (status)
|
||||
printf("\nOther went online.\n");
|
||||
else
|
||||
printf("\nOther went offline.\n");
|
||||
}
|
||||
|
||||
void print_message(Tox *tox, int friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||
void print_message(Tox *tox, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
|
||||
void *userdata)
|
||||
{
|
||||
int master = *((int *)userdata);
|
||||
write(master, string, length);
|
||||
|
@ -62,7 +63,7 @@ void print_message(Tox *tox, int friendnumber, const uint8_t *string, uint16_t l
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */
|
||||
uint8_t ipv6enabled = 1; /* x */
|
||||
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
|
||||
|
||||
if (argvoffset < 0)
|
||||
|
@ -94,14 +95,14 @@ int main(int argc, char *argv[])
|
|||
printf("error setting flags\n");
|
||||
}
|
||||
|
||||
Tox *tox = tox_new(0);
|
||||
tox_callback_connection_status(tox, print_online, NULL);
|
||||
Tox *tox = tox_new(0, 0, 0, 0);
|
||||
tox_callback_friend_connection_status(tox, print_online, NULL);
|
||||
tox_callback_friend_message(tox, print_message, master);
|
||||
|
||||
|
||||
uint16_t port = atoi(argv[argvoffset + 2]);
|
||||
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
|
||||
int res = tox_bootstrap_from_address(tox, argv[argvoffset + 1], port, binary_string);
|
||||
int res = tox_bootstrap(tox, argv[argvoffset + 1], port, binary_string, 0);
|
||||
free(binary_string);
|
||||
|
||||
if (!res) {
|
||||
|
@ -109,11 +110,11 @@ int main(int argc, char *argv[])
|
|||
exit(1);
|
||||
}
|
||||
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(tox, address);
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(tox, address);
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
|
||||
for (i = 0; i < TOX_ADDRESS_SIZE; i++) {
|
||||
printf("%02X", address[i]);
|
||||
}
|
||||
|
||||
|
@ -125,10 +126,10 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
uint8_t *bin_id = hex_string_to_bin(temp_id);
|
||||
int num = tox_add_friend(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
|
||||
uint32_t num = tox_friend_add(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"), 0);
|
||||
free(bin_id);
|
||||
|
||||
if (num < 0) {
|
||||
if (num == UINT32_MAX) {
|
||||
printf("\nSomething went wrong when adding friend.\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -136,22 +137,22 @@ int main(int argc, char *argv[])
|
|||
uint8_t notconnected = 1;
|
||||
|
||||
while (1) {
|
||||
if (tox_isconnected(tox) && notconnected) {
|
||||
if (tox_self_get_connection_status(tox) && notconnected) {
|
||||
printf("\nDHT connected.\n");
|
||||
notconnected = 0;
|
||||
}
|
||||
|
||||
while (tox_get_friend_connection_status(tox, num) == 1) {
|
||||
while (tox_friend_get_connection_status(tox, num, 0)) {
|
||||
uint8_t buf[TOX_MAX_MESSAGE_LENGTH];
|
||||
ret = read(*master, buf, sizeof(buf));
|
||||
|
||||
if (ret <= 0)
|
||||
break;
|
||||
|
||||
tox_send_message(tox, num, buf, ret);
|
||||
tox_friend_send_message(tox, num, TOX_MESSAGE_TYPE_NORMAL, buf, ret, 0);
|
||||
}
|
||||
|
||||
tox_do(tox);
|
||||
tox_iterate(tox);
|
||||
c_sleep(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,43 +46,39 @@
|
|||
#define NUM_FILE_SENDERS 256
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
uint16_t friendnum;
|
||||
uint8_t filenumber;
|
||||
uint8_t nextpiece[1400];
|
||||
uint16_t piecelength;
|
||||
uint32_t friendnum;
|
||||
uint32_t filenumber;
|
||||
} File_t;
|
||||
File_t file_senders[NUM_FILE_SENDERS];
|
||||
File_t file_recv[NUM_FILE_SENDERS];
|
||||
uint8_t numfilesenders;
|
||||
|
||||
void send_filesenders(Tox *m)
|
||||
void tox_file_chunk_request(Tox *tox, uint32_t friend_number, uint32_t file_number, uint64_t position, size_t length,
|
||||
void *user_data)
|
||||
{
|
||||
uint32_t i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
|
||||
if (file_senders[i].file == 0)
|
||||
continue;
|
||||
|
||||
while (1) {
|
||||
if (tox_file_send_data(m, file_senders[i].friendnum, file_senders[i].filenumber, file_senders[i].nextpiece,
|
||||
file_senders[i].piecelength) != 0)
|
||||
break;
|
||||
|
||||
file_senders[i].piecelength = fread(file_senders[i].nextpiece, 1, tox_file_data_size(m, file_senders[i].friendnum),
|
||||
file_senders[i].file);
|
||||
|
||||
if (file_senders[i].piecelength == 0) {
|
||||
/* This is slow */
|
||||
if (file_senders[i].file && file_senders[i].friendnum == friend_number && file_senders[i].filenumber == file_number) {
|
||||
if (length == 0) {
|
||||
fclose(file_senders[i].file);
|
||||
file_senders[i].file = 0;
|
||||
|
||||
printf("[t] %u file transfer: %u completed %i\n", file_senders[i].friendnum, file_senders[i].filenumber,
|
||||
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenumber, TOX_FILECONTROL_FINISHED, 0, 0));
|
||||
printf("[t] %u file transfer: %u completed\n", file_senders[i].friendnum, file_senders[i].filenumber);
|
||||
break;
|
||||
}
|
||||
|
||||
fseek(file_senders[i].file, position, SEEK_SET);
|
||||
uint8_t data[length];
|
||||
int len = fread(data, 1, length, file_senders[i].file);
|
||||
tox_file_send_chunk(tox, friend_number, file_number, position, data, len, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
int add_filesender(Tox *m, uint16_t friendnum, char *filename)
|
||||
|
||||
|
||||
uint32_t add_filesender(Tox *m, uint16_t friendnum, char *filename)
|
||||
{
|
||||
FILE *tempfile = fopen(filename, "rb");
|
||||
|
||||
|
@ -92,22 +88,20 @@ int add_filesender(Tox *m, uint16_t friendnum, char *filename)
|
|||
fseek(tempfile, 0, SEEK_END);
|
||||
uint64_t filesize = ftell(tempfile);
|
||||
fseek(tempfile, 0, SEEK_SET);
|
||||
int filenum = tox_new_file_sender(m, friendnum, filesize, (uint8_t *)filename, strlen(filename) + 1);
|
||||
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;
|
||||
|
||||
file_senders[numfilesenders].file = tempfile;
|
||||
file_senders[numfilesenders].piecelength = fread(file_senders[numfilesenders].nextpiece, 1, tox_file_data_size(m,
|
||||
file_senders[numfilesenders].friendnum),
|
||||
file_senders[numfilesenders].file);
|
||||
file_senders[numfilesenders].friendnum = friendnum;
|
||||
file_senders[numfilesenders].filenumber = filenum;
|
||||
++numfilesenders;
|
||||
return filenum;
|
||||
}
|
||||
|
||||
void kill_filesender(Tox *m, uint8_t filenum)
|
||||
void kill_filesender(Tox *m, uint32_t filenum)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
|
@ -130,9 +124,15 @@ int not_sending()
|
|||
|
||||
static char path[1024];
|
||||
|
||||
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 file_request_accept(Tox *tox, uint32_t friend_number, uint32_t file_number, uint32_t type, uint64_t file_size,
|
||||
const uint8_t *filename, size_t filename_length, void *user_data)
|
||||
{
|
||||
if (type != TOX_FILE_KIND_DATA) {
|
||||
printf("Refused invalid file type.");
|
||||
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
char fullpath[1024];
|
||||
uint32_t i;
|
||||
uint16_t rm = 0;
|
||||
|
@ -151,58 +151,87 @@ void file_request_accept(Tox *m, int friendnumber, uint8_t filenumber, uint64_t
|
|||
|
||||
if (tempfile != 0) {
|
||||
fclose(tempfile);
|
||||
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_KILL, 0, 0);
|
||||
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
file_recv[filenumber].file = fopen(fullpath, "wb");
|
||||
uint8_t file_index = (file_number >> 16) - 1;
|
||||
file_recv[file_index].file = fopen(fullpath, "wb");
|
||||
|
||||
if (file_recv[filenumber].file == 0) {
|
||||
tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_KILL, 0, 0);
|
||||
if (file_recv[file_index].file == 0) {
|
||||
tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_CANCEL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tox_file_send_control(m, friendnumber, 1, filenumber, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
|
||||
if (tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, 0)) {
|
||||
printf("Accepted file transfer. (file: %s)\n", fullpath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void file_print_control(Tox *m, int friendnumber, uint8_t recieve_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 *user_data)
|
||||
{
|
||||
if (recieve_send == 1 && (control_type == TOX_FILECONTROL_KILL || control_type == TOX_FILECONTROL_FINISHED)) {
|
||||
kill_filesender(m, filenumber);
|
||||
if (file_number < (1 << 15) && (control == TOX_FILE_CONTROL_CANCEL)) {
|
||||
kill_filesender(tox, file_number);
|
||||
return;
|
||||
}
|
||||
|
||||
if (recieve_send == 0 && (control_type == TOX_FILECONTROL_KILL || control_type == TOX_FILECONTROL_FINISHED)) {
|
||||
fclose(file_recv[filenumber].file);
|
||||
if (file_number > (1 << 15) && (control == TOX_FILE_CONTROL_CANCEL)) {
|
||||
uint8_t file_index = (file_number >> 16) - 1;
|
||||
fclose(file_recv[file_index].file);
|
||||
printf("File closed\n");
|
||||
file_recv[filenumber].file = 0;
|
||||
file_recv[file_index].file = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void write_file(Tox *m, int friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata)
|
||||
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 (file_recv[filenumber].file != 0)
|
||||
if (fwrite(data, length, 1, file_recv[filenumber].file) != 1)
|
||||
uint8_t file_index = (filenumber >> 16) - 1;
|
||||
|
||||
if (length == 0) {
|
||||
fclose(file_recv[file_index].file);
|
||||
printf("File closed\n");
|
||||
file_recv[file_index].file = 0;
|
||||
printf("%u file transfer: %u completed\n", friendnumber, filenumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_recv[file_index].file != 0) {
|
||||
fseek(file_recv[file_index].file, position, SEEK_SET);
|
||||
|
||||
if (fwrite(data, length, 1, file_recv[file_index].file) != 1)
|
||||
printf("Error writing data\n");
|
||||
}
|
||||
}
|
||||
|
||||
void print_online(Tox *tox, int friendnumber, uint8_t status, void *userdata)
|
||||
void print_online(Tox *tox, uint32_t friendnumber, TOX_CONNECTION status, void *userdata)
|
||||
{
|
||||
if (status == 1)
|
||||
if (status)
|
||||
printf("\nOther went online.\n");
|
||||
else
|
||||
else {
|
||||
printf("\nOther went offline.\n");
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NUM_FILE_SENDERS; ++i) {
|
||||
if (file_senders[i].file != 0) {
|
||||
fclose(file_senders[i].file);
|
||||
file_senders[i].file = 0;
|
||||
}
|
||||
|
||||
if (file_recv[i].file != 0) {
|
||||
fclose(file_recv[i].file);
|
||||
file_recv[i].file = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
uint8_t ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; /* x */
|
||||
uint8_t ipv6enabled = 1; /* x */
|
||||
int argvoffset = cmdline_parsefor_ipv46(argc, argv, &ipv6enabled);
|
||||
|
||||
if (argvoffset < 0)
|
||||
|
@ -214,15 +243,16 @@ int main(int argc, char *argv[])
|
|||
exit(0);
|
||||
}
|
||||
|
||||
Tox *tox = tox_new(0);
|
||||
tox_callback_file_data(tox, write_file, NULL);
|
||||
tox_callback_file_control(tox, file_print_control, NULL);
|
||||
tox_callback_file_send_request(tox, file_request_accept, NULL);
|
||||
tox_callback_connection_status(tox, print_online, NULL);
|
||||
Tox *tox = tox_new(0, 0, 0, 0);
|
||||
tox_callback_file_recv_chunk(tox, write_file, NULL);
|
||||
tox_callback_file_recv_control(tox, file_print_control, NULL);
|
||||
tox_callback_file_recv(tox, file_request_accept, NULL);
|
||||
tox_callback_file_chunk_request(tox, tox_file_chunk_request, NULL);
|
||||
tox_callback_friend_connection_status(tox, print_online, NULL);
|
||||
|
||||
uint16_t port = atoi(argv[argvoffset + 2]);
|
||||
unsigned char *binary_string = hex_string_to_bin(argv[argvoffset + 3]);
|
||||
int res = tox_bootstrap_from_address(tox, argv[argvoffset + 1], port, binary_string);
|
||||
int res = tox_bootstrap(tox, argv[argvoffset + 1], port, binary_string, 0);
|
||||
free(binary_string);
|
||||
|
||||
if (!res) {
|
||||
|
@ -230,11 +260,11 @@ int main(int argc, char *argv[])
|
|||
exit(1);
|
||||
}
|
||||
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(tox, address);
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(tox, address);
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
|
||||
for (i = 0; i < TOX_ADDRESS_SIZE; i++) {
|
||||
printf("%02X", address[i]);
|
||||
}
|
||||
|
||||
|
@ -246,10 +276,10 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
uint8_t *bin_id = hex_string_to_bin(temp_id);
|
||||
int num = tox_add_friend(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"));
|
||||
uint32_t num = tox_friend_add(tox, bin_id, (uint8_t *)"Install Gentoo", sizeof("Install Gentoo"), 0);
|
||||
free(bin_id);
|
||||
|
||||
if (num < 0) {
|
||||
if (num == UINT32_MAX) {
|
||||
printf("\nSomething went wrong when adding friend.\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -260,12 +290,12 @@ int main(int argc, char *argv[])
|
|||
uint8_t notconnected = 1;
|
||||
|
||||
while (1) {
|
||||
if (tox_isconnected(tox) && notconnected) {
|
||||
if (tox_self_get_connection_status(tox) && notconnected) {
|
||||
printf("\nDHT connected.\n");
|
||||
notconnected = 0;
|
||||
}
|
||||
|
||||
if (not_sending() && tox_get_friend_connection_status(tox, num)) {
|
||||
if (not_sending() && tox_friend_get_connection_status(tox, num, 0)) {
|
||||
d = opendir(path);
|
||||
|
||||
if (d) {
|
||||
|
@ -290,8 +320,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
send_filesenders(tox);
|
||||
tox_do(tox);
|
||||
tox_iterate(tox);
|
||||
c_sleep(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -211,21 +211,21 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
|
|||
uint32_t sampling_rate,
|
||||
void *user_data)
|
||||
{
|
||||
CallControl* cc = user_data;
|
||||
frame* f = malloc(sizeof(frame) + sample_count * sizeof(int16_t));
|
||||
memcpy(f->data, pcm, sample_count);
|
||||
f->size = sample_count/channels;
|
||||
// CallControl* cc = user_data;
|
||||
// frame* f = malloc(sizeof(frame) + sample_count * sizeof(int16_t));
|
||||
// memcpy(f->data, pcm, sample_count);
|
||||
// f->size = sample_count/channels;
|
||||
//
|
||||
// pthread_mutex_lock(cc->arb_mutex);
|
||||
// free(rb_write(cc->arb, f));
|
||||
// pthread_mutex_unlock(cc->arb_mutex);
|
||||
|
||||
pthread_mutex_lock(cc->arb_mutex);
|
||||
free(rb_write(cc->arb, f));
|
||||
pthread_mutex_unlock(cc->arb_mutex);
|
||||
|
||||
// Pa_WriteStream(adout, pcm, sample_count/channels);
|
||||
Pa_WriteStream(adout, pcm, sample_count/channels);
|
||||
}
|
||||
void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||
void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
|
||||
{
|
||||
if (length == 7 && memcmp("gentoo", data, 7) == 0) {
|
||||
tox_add_friend_norequest(m, public_key);
|
||||
assert(tox_friend_add_norequest(m, public_key, NULL) != ~0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,11 +237,17 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
|
|||
Tox* Alice;
|
||||
Tox* Bob;
|
||||
|
||||
*bootstrap = tox_new(0);
|
||||
Alice = tox_new(0);
|
||||
Bob = tox_new(0);
|
||||
|
||||
assert(bootstrap && Alice && Bob);
|
||||
{
|
||||
TOX_ERR_NEW error;
|
||||
*bootstrap = tox_new(NULL, NULL, 0, &error);
|
||||
assert(error == TOX_ERR_NEW_OK);
|
||||
|
||||
Alice = tox_new(NULL, NULL, 0, &error);
|
||||
assert(error == TOX_ERR_NEW_OK);
|
||||
|
||||
Bob = tox_new(NULL, NULL, 0, &error);
|
||||
assert(error == TOX_ERR_NEW_OK);
|
||||
}
|
||||
|
||||
printf("Created 3 instances of Tox\n");
|
||||
|
||||
|
@ -249,26 +255,30 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
|
|||
long long unsigned int cur_time = time(NULL);
|
||||
|
||||
uint32_t to_compare = 974536;
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
uint8_t address[TOX_ADDRESS_SIZE];
|
||||
|
||||
tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
|
||||
tox_get_address(Alice, address);
|
||||
tox_self_get_address(Alice, address);
|
||||
|
||||
assert(tox_add_friend(Bob, address, (uint8_t *)"gentoo", 7) >= 0);
|
||||
|
||||
assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != ~0);
|
||||
|
||||
uint8_t off = 1;
|
||||
|
||||
while (1) {
|
||||
tox_do(*bootstrap);
|
||||
tox_do(Alice);
|
||||
tox_do(Bob);
|
||||
tox_iterate(*bootstrap);
|
||||
tox_iterate(Alice);
|
||||
tox_iterate(Bob);
|
||||
|
||||
if (tox_isconnected(*bootstrap) && tox_isconnected(Alice) && tox_isconnected(Bob) && off) {
|
||||
if (tox_self_get_connection_status(*bootstrap) &&
|
||||
tox_self_get_connection_status(Alice) &&
|
||||
tox_self_get_connection_status(Bob) && off) {
|
||||
printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
|
||||
off = 0;
|
||||
}
|
||||
|
||||
if (tox_get_friend_connection_status(Alice, 0) == 1 && tox_get_friend_connection_status(Bob, 0) == 1)
|
||||
if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
|
||||
tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP)
|
||||
break;
|
||||
|
||||
c_sleep(20);
|
||||
|
@ -300,11 +310,11 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
|
|||
}
|
||||
int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV)
|
||||
{
|
||||
tox_do(bootstrap);
|
||||
tox_do(toxav_get_tox(AliceAV));
|
||||
tox_do(toxav_get_tox(BobAV));
|
||||
tox_iterate(bootstrap);
|
||||
tox_iterate(toxav_get_tox(AliceAV));
|
||||
tox_iterate(toxav_get_tox(BobAV));
|
||||
|
||||
return MIN(tox_do_interval(toxav_get_tox(AliceAV)), tox_do_interval(toxav_get_tox(BobAV)));
|
||||
return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV)));
|
||||
}
|
||||
void* iterate_toxav (void * data)
|
||||
{
|
||||
|
|
|
@ -23,9 +23,11 @@
|
|||
#endif
|
||||
|
||||
#include "group.h"
|
||||
#include "../toxcore/util.h"
|
||||
#include "../toxcore/logger.h"
|
||||
|
||||
#define GROUP_JBUF_SIZE 6
|
||||
#define GROUP_JBUF_DEAD_SECONDS 4
|
||||
|
||||
typedef struct {
|
||||
uint16_t sequnum;
|
||||
|
@ -39,6 +41,7 @@ typedef struct {
|
|||
uint32_t capacity;
|
||||
uint16_t bottom;
|
||||
uint16_t top;
|
||||
uint64_t last_queued_time;
|
||||
} Group_JitterBuffer;
|
||||
|
||||
static Group_JitterBuffer *create_queue(unsigned int capacity)
|
||||
|
@ -90,11 +93,19 @@ static int queue(Group_JitterBuffer *q, Group_Audio_Packet *pk)
|
|||
|
||||
unsigned int num = sequnum % q->size;
|
||||
|
||||
if (!is_timeout(q->last_queued_time, GROUP_JBUF_DEAD_SECONDS)) {
|
||||
if ((uint32_t)(sequnum - q->bottom) > (1 << 15)) {
|
||||
/* Drop old packet. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((uint32_t)(sequnum - q->bottom) > q->size) {
|
||||
clear_queue(q);
|
||||
q->bottom = sequnum - q->capacity;
|
||||
q->queue[num] = pk;
|
||||
q->top = sequnum + 1;
|
||||
q->last_queued_time = unix_time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -106,6 +117,7 @@ static int queue(Group_JitterBuffer *q, Group_Audio_Packet *pk)
|
|||
if ((sequnum - q->bottom) >= (q->top - q->bottom))
|
||||
q->top = sequnum + 1;
|
||||
|
||||
q->last_queued_time = unix_time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,10 +88,10 @@ static int invoke_callback(MSICall* call, MSICallbackID cb);
|
|||
static MSICall *get_call ( MSISession *session, uint32_t friend_id );
|
||||
MSICall *new_call ( MSISession *session, uint32_t friend_id );
|
||||
void kill_call ( MSICall *call );
|
||||
void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data);
|
||||
void on_peer_status(Messenger *m, uint32_t friend_id, uint8_t status, void *data);
|
||||
void handle_push ( MSICall *call, const MSIMessage *msg );
|
||||
void handle_pop ( MSICall *call, const MSIMessage *msg );
|
||||
void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object );
|
||||
void handle_msi_packet ( Messenger *m, uint32_t friend_id, const uint8_t *data, uint16_t length, void *object );
|
||||
|
||||
|
||||
/**
|
||||
|
@ -572,7 +572,7 @@ CLEAR_CONTAINER:
|
|||
free(call);
|
||||
session->calls = NULL;
|
||||
}
|
||||
void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data)
|
||||
void on_peer_status(Messenger* m, uint32_t friend_id, uint8_t status, void* data)
|
||||
{
|
||||
(void)m;
|
||||
MSISession *session = data;
|
||||
|
@ -744,7 +744,7 @@ void handle_pop ( MSICall *call, const MSIMessage *msg )
|
|||
|
||||
kill_call ( call );
|
||||
}
|
||||
void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object )
|
||||
void handle_msi_packet ( Messenger* m, uint32_t friend_id, const uint8_t* data, uint16_t length, void* object )
|
||||
{
|
||||
LOGGER_DEBUG("Got msi message");
|
||||
|
||||
|
|
28
toxav/rtp.c
28
toxav/rtp.c
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "../toxcore/logger.h"
|
||||
#include "../toxcore/util.h"
|
||||
#include "../toxcore/Messenger.h"
|
||||
|
||||
#include "rtp.h"
|
||||
#include <stdlib.h>
|
||||
|
@ -77,11 +78,9 @@ RTPMessage *msg_parse ( const uint8_t *data, int length );
|
|||
uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload );
|
||||
uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload );
|
||||
void build_header ( RTPSession* session, RTPHeader* header );
|
||||
void send_rtcp_report ( RTCPSession* session, Messenger* m, int32_t friendnumber );
|
||||
int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object );
|
||||
int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object );
|
||||
|
||||
|
||||
void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber );
|
||||
int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
|
||||
int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
|
||||
|
||||
|
||||
RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
|
||||
|
@ -121,7 +120,7 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
|
|||
return NULL;
|
||||
}
|
||||
|
||||
retu->rtcp_session->prefix = 222 + payload_type % 192;
|
||||
retu->rtcp_session->prefix = payload_type + 2;
|
||||
retu->rtcp_session->pl_stats = rb_new(4);
|
||||
retu->rtcp_session->rtp_session = retu;
|
||||
|
||||
|
@ -201,15 +200,15 @@ int rtp_start_receiving(RTPSession* session)
|
|||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
if (custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix,
|
||||
if (m_callback_rtp_packet(session->m, session->dest, session->prefix,
|
||||
handle_rtp_packet, session) == -1) {
|
||||
LOGGER_WARNING("Failed to register rtp receive handler");
|
||||
return -1;
|
||||
}
|
||||
if (custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp_session->prefix,
|
||||
if (m_callback_rtp_packet(session->m, session->dest, session->rtcp_session->prefix,
|
||||
handle_rtcp_packet, session->rtcp_session) == -1) {
|
||||
LOGGER_WARNING("Failed to register rtcp receive handler");
|
||||
custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL);
|
||||
m_callback_rtp_packet(session->m, session->dest, session->prefix, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -220,8 +219,8 @@ int rtp_stop_receiving(RTPSession* session)
|
|||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL);
|
||||
custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
|
||||
m_callback_rtp_packet(session->m, session->dest, session->prefix, NULL, NULL);
|
||||
m_callback_rtp_packet(session->m, session->dest, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -251,6 +250,7 @@ int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length )
|
|||
|
||||
memcpy ( it, data, length );
|
||||
|
||||
|
||||
if ( -1 == send_custom_lossy_packet(session->m, session->dest, parsed, parsed_len) ) {
|
||||
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
|
||||
return -1;
|
||||
|
@ -514,7 +514,7 @@ void build_header ( RTPSession *session, RTPHeader *header )
|
|||
|
||||
header->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
|
||||
}
|
||||
void send_rtcp_report(RTCPSession* session, Messenger* m, int32_t friendnumber)
|
||||
void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber)
|
||||
{
|
||||
if (session->last_expected_packets == 0)
|
||||
return;
|
||||
|
@ -538,7 +538,7 @@ void send_rtcp_report(RTCPSession* session, Messenger* m, int32_t friendnumber)
|
|||
session->last_sent_report_ts = current_time_monotonic();
|
||||
}
|
||||
}
|
||||
int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object )
|
||||
int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
|
||||
{
|
||||
RTPSession *session = object;
|
||||
RTPMessage *msg;
|
||||
|
@ -574,7 +574,7 @@ int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data,
|
|||
queue_message(session, msg);
|
||||
return 0;
|
||||
}
|
||||
int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object )
|
||||
int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
|
||||
{
|
||||
if (length < 9)
|
||||
return -1;
|
||||
|
|
|
@ -904,7 +904,7 @@ ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
|
|||
goto END;
|
||||
}
|
||||
|
||||
if (m_get_friend_connectionstatus(av->m, friend_number) != 1) {
|
||||
if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
|
||||
rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
|
||||
goto END;
|
||||
}
|
||||
|
|
|
@ -78,8 +78,8 @@ int id_closest(const uint8_t *id, const uint8_t *id1, const uint8_t *id2)
|
|||
|
||||
for (i = 0; i < CLIENT_ID_SIZE; ++i) {
|
||||
|
||||
distance1 = abs(((int8_t *)id)[i] ^ ((int8_t *)id1)[i]);
|
||||
distance2 = abs(((int8_t *)id)[i] ^ ((int8_t *)id2)[i]);
|
||||
distance1 = id[i] ^ id1[i];
|
||||
distance2 = id[i] ^ id2[i];
|
||||
|
||||
if (distance1 < distance2)
|
||||
return 1;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
lib_LTLIBRARIES += libtoxcore.la
|
||||
|
||||
libtoxcore_la_include_HEADERS = \
|
||||
../toxcore/tox.h
|
||||
../toxcore/tox.h \
|
||||
../toxcore/tox_old.h
|
||||
|
||||
libtoxcore_la_includedir = $(includedir)/tox
|
||||
|
||||
|
@ -45,6 +46,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \
|
|||
../toxcore/TCP_client.c \
|
||||
../toxcore/TCP_server.h \
|
||||
../toxcore/TCP_server.c \
|
||||
../toxcore/TCP_connection.h \
|
||||
../toxcore/TCP_connection.c \
|
||||
../toxcore/list.c \
|
||||
../toxcore/list.h \
|
||||
../toxcore/misc_tools.h
|
||||
|
|
1841
toxcore/Messenger.c
1841
toxcore/Messenger.c
File diff suppressed because it is too large
Load Diff
|
@ -33,28 +33,25 @@
|
|||
#define MAX_NAME_LENGTH 128
|
||||
/* TODO: this must depend on other variable. */
|
||||
#define MAX_STATUSMESSAGE_LENGTH 1007
|
||||
#define AVATAR_MAX_DATA_LENGTH 16384
|
||||
#define AVATAR_HASH_LENGTH crypto_hash_sha256_BYTES
|
||||
|
||||
|
||||
#define FRIEND_ADDRESS_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + sizeof(uint16_t))
|
||||
|
||||
/* NOTE: Packet ids below 17 must never be used. */
|
||||
#define PACKET_ID_SHARE_RELAYS 17
|
||||
enum {
|
||||
MESSAGE_NORMAL,
|
||||
MESSAGE_ACTION
|
||||
};
|
||||
|
||||
/* NOTE: Packet ids below 20 must never be used. */
|
||||
#define PACKET_ID_SHARE_RELAYS 23
|
||||
#define PACKET_ID_ONLINE 24
|
||||
#define PACKET_ID_OFFLINE 25
|
||||
#define PACKET_ID_NICKNAME 48
|
||||
#define PACKET_ID_STATUSMESSAGE 49
|
||||
#define PACKET_ID_USERSTATUS 50
|
||||
#define PACKET_ID_TYPING 51
|
||||
#define PACKET_ID_AVATAR_INFO_REQ 52
|
||||
#define PACKET_ID_AVATAR_INFO 53
|
||||
#define PACKET_ID_AVATAR_DATA_CONTROL 54
|
||||
#define PACKET_ID_AVATAR_DATA_START 55
|
||||
#define PACKET_ID_AVATAR_DATA_PUSH 56
|
||||
#define PACKET_ID_RECEIPT 63
|
||||
#define PACKET_ID_MESSAGE 64
|
||||
#define PACKET_ID_ACTION 65
|
||||
#define PACKET_ID_ACTION (PACKET_ID_MESSAGE + MESSAGE_ACTION) /* 65 */
|
||||
#define PACKET_ID_MSI 69
|
||||
#define PACKET_ID_FILE_SENDREQUEST 80
|
||||
#define PACKET_ID_FILE_CONTROL 81
|
||||
|
@ -71,13 +68,22 @@
|
|||
/* All packets starting with a byte in this range can be used for anything. */
|
||||
#define PACKET_ID_LOSSLESS_RANGE_START 160
|
||||
#define PACKET_ID_LOSSLESS_RANGE_SIZE 32
|
||||
#define PACKET_LOSSY_AV_RESERVED 8 /* Number of lossy packet types at start of range reserved for A/V. */
|
||||
|
||||
typedef struct {
|
||||
uint8_t ipv6enabled;
|
||||
uint8_t udp_disabled;
|
||||
TCP_Proxy_Info proxy_info;
|
||||
uint16_t port_range[2];
|
||||
} Messenger_Options;
|
||||
|
||||
|
||||
struct Receipts {
|
||||
uint32_t packet_num;
|
||||
uint32_t msg_id;
|
||||
struct Receipts *next;
|
||||
};
|
||||
|
||||
/* Status definitions. */
|
||||
enum {
|
||||
NOFRIEND,
|
||||
|
@ -95,25 +101,23 @@ enum {
|
|||
FAERR_NOMESSAGE = -2,
|
||||
FAERR_OWNKEY = -3,
|
||||
FAERR_ALREADYSENT = -4,
|
||||
FAERR_UNKNOWN = -5,
|
||||
FAERR_BADCHECKSUM = -6,
|
||||
FAERR_SETNEWNOSPAM = -7,
|
||||
FAERR_NOMEM = -8
|
||||
};
|
||||
|
||||
|
||||
/* Default start timeout in seconds between friend requests. */
|
||||
#define FRIENDREQUEST_TIMEOUT 5;
|
||||
|
||||
/* Interval between the sending of tcp relay information */
|
||||
#define FRIEND_SHARE_RELAYS_INTERVAL (5 * 60)
|
||||
|
||||
/* Must be < MAX_CRYPTO_DATA_SIZE */
|
||||
#define AVATAR_DATA_MAX_CHUNK_SIZE (MAX_CRYPTO_DATA_SIZE-1)
|
||||
|
||||
/* Per-friend data limit for avatar data requests */
|
||||
#define AVATAR_DATA_TRANSFER_LIMIT (10*AVATAR_MAX_DATA_LENGTH)
|
||||
#define AVATAR_DATA_TRANSFER_TIMEOUT (60) /* 164kB every 60 seconds is not a lot */
|
||||
|
||||
enum {
|
||||
CONNECTION_NONE,
|
||||
CONNECTION_TCP,
|
||||
CONNECTION_UDP
|
||||
};
|
||||
|
||||
/* USERSTATUS -
|
||||
* Represents userstatuses someone can have.
|
||||
|
@ -127,56 +131,33 @@ typedef enum {
|
|||
}
|
||||
USERSTATUS;
|
||||
|
||||
/* AVATAR_FORMAT -
|
||||
* Data formats for user avatar images
|
||||
*/
|
||||
typedef enum {
|
||||
AVATAR_FORMAT_NONE = 0,
|
||||
AVATAR_FORMAT_PNG
|
||||
}
|
||||
AVATAR_FORMAT;
|
||||
|
||||
/* AVATAR_DATACONTROL
|
||||
* To control avatar data requests (PACKET_ID_AVATAR_DATA_CONTROL)
|
||||
*/
|
||||
typedef enum {
|
||||
AVATAR_DATACONTROL_REQ,
|
||||
AVATAR_DATACONTROL_ERROR
|
||||
}
|
||||
AVATAR_DATACONTROL;
|
||||
|
||||
typedef struct {
|
||||
uint8_t started;
|
||||
AVATAR_FORMAT format;
|
||||
uint8_t hash[AVATAR_HASH_LENGTH];
|
||||
uint32_t total_length;
|
||||
uint32_t bytes_received;
|
||||
uint8_t data[AVATAR_MAX_DATA_LENGTH];
|
||||
}
|
||||
AVATAR_RECEIVEDATA;
|
||||
|
||||
typedef struct {
|
||||
/* Fields only used to limit the network usage from a given friend */
|
||||
uint32_t bytes_sent; /* Total bytes send to this user */
|
||||
uint64_t last_reset; /* Time the data counter was last reset */
|
||||
}
|
||||
AVATAR_SENDDATA;
|
||||
|
||||
#define FILE_ID_LENGTH 32
|
||||
|
||||
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 */
|
||||
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. */
|
||||
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,
|
||||
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
|
||||
|
||||
|
@ -184,10 +165,15 @@ enum {
|
|||
FILECONTROL_ACCEPT,
|
||||
FILECONTROL_PAUSE,
|
||||
FILECONTROL_KILL,
|
||||
FILECONTROL_FINISHED,
|
||||
FILECONTROL_RESUME_BROKEN
|
||||
FILECONTROL_SEEK
|
||||
};
|
||||
|
||||
enum {
|
||||
FILEKIND_DATA,
|
||||
FILEKIND_AVATAR
|
||||
};
|
||||
|
||||
|
||||
typedef struct Messenger Messenger;
|
||||
|
||||
typedef struct {
|
||||
|
@ -201,36 +187,31 @@ typedef struct {
|
|||
uint8_t name[MAX_NAME_LENGTH];
|
||||
uint16_t name_length;
|
||||
uint8_t name_sent; // 0 if we didn't send our name to this friend 1 if we have.
|
||||
uint8_t *statusmessage;
|
||||
uint8_t statusmessage[MAX_STATUSMESSAGE_LENGTH];
|
||||
uint16_t statusmessage_length;
|
||||
uint8_t statusmessage_sent;
|
||||
USERSTATUS userstatus;
|
||||
uint8_t userstatus_sent;
|
||||
uint8_t avatar_info_sent;
|
||||
uint8_t user_istyping;
|
||||
uint8_t user_istyping_sent;
|
||||
uint8_t is_typing;
|
||||
uint16_t info_size; // Length of the info.
|
||||
uint32_t message_id; // a semi-unique id used in read receipts.
|
||||
uint8_t receives_read_receipts; // shall we send read receipts to this person?
|
||||
uint32_t friendrequest_nospam; // The nospam number used in the friend request.
|
||||
uint64_t ping_lastrecv;//TODO remove
|
||||
uint64_t last_seen_time;
|
||||
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];
|
||||
|
||||
AVATAR_SENDDATA avatar_send_data;
|
||||
AVATAR_RECEIVEDATA *avatar_recv_data; // We are receiving avatar data from this friend.
|
||||
|
||||
struct {
|
||||
int (*function)(Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t len, void *object);
|
||||
int (*function)(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t len, void *object);
|
||||
void *object;
|
||||
} lossy_packethandlers[PACKET_ID_LOSSY_RANGE_SIZE];
|
||||
} lossy_rtp_packethandlers[PACKET_LOSSY_AV_RESERVED];
|
||||
|
||||
struct {
|
||||
int (*function)(Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t len, void *object);
|
||||
void *object;
|
||||
} lossless_packethandlers[PACKET_ID_LOSSLESS_RANGE_SIZE];
|
||||
struct Receipts *receipts_start;
|
||||
struct Receipts *receipts_end;
|
||||
} Friend;
|
||||
|
||||
|
||||
|
@ -255,11 +236,6 @@ struct Messenger {
|
|||
|
||||
USERSTATUS userstatus;
|
||||
|
||||
AVATAR_FORMAT avatar_format;
|
||||
uint8_t *avatar_data;
|
||||
uint32_t avatar_data_length;
|
||||
uint8_t avatar_hash[AVATAR_HASH_LENGTH];
|
||||
|
||||
Friend *friendlist;
|
||||
uint32_t numfriends;
|
||||
|
||||
|
@ -271,45 +247,49 @@ struct Messenger {
|
|||
uint8_t has_added_relays; // If the first connection has occurred in do_messenger
|
||||
Node_format loaded_relays[NUM_SAVED_TCP_RELAYS]; // Relays loaded from config
|
||||
|
||||
void (*friend_message)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
|
||||
void (*friend_message)(struct Messenger *m, uint32_t, unsigned int, const uint8_t *, size_t, void *);
|
||||
void *friend_message_userdata;
|
||||
void (*friend_action)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
|
||||
void *friend_action_userdata;
|
||||
void (*friend_namechange)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
|
||||
void (*friend_namechange)(struct Messenger *m, uint32_t, const uint8_t *, size_t, void *);
|
||||
void *friend_namechange_userdata;
|
||||
void (*friend_statusmessagechange)(struct Messenger *m, int32_t, const uint8_t *, uint16_t, void *);
|
||||
void (*friend_statusmessagechange)(struct Messenger *m, uint32_t, const uint8_t *, size_t, void *);
|
||||
void *friend_statusmessagechange_userdata;
|
||||
void (*friend_userstatuschange)(struct Messenger *m, int32_t, uint8_t, void *);
|
||||
void (*friend_userstatuschange)(struct Messenger *m, uint32_t, unsigned int, void *);
|
||||
void *friend_userstatuschange_userdata;
|
||||
void (*friend_typingchange)(struct Messenger *m, int32_t, uint8_t, void *);
|
||||
void (*friend_typingchange)(struct Messenger *m, uint32_t, _Bool, void *);
|
||||
void *friend_typingchange_userdata;
|
||||
void (*read_receipt)(struct Messenger *m, int32_t, uint32_t, void *);
|
||||
void (*read_receipt)(struct Messenger *m, uint32_t, uint32_t, void *);
|
||||
void *read_receipt_userdata;
|
||||
void (*friend_statuschange)(struct Messenger *m, int32_t, uint8_t, void *);
|
||||
void *friend_statuschange_userdata;
|
||||
void (*friend_connectionstatuschange)(struct Messenger *m, int32_t, uint8_t, void *);
|
||||
void (*friend_connectionstatuschange)(struct Messenger *m, uint32_t, unsigned int, void *);
|
||||
void *friend_connectionstatuschange_userdata;
|
||||
void (*friend_connectionstatuschange_internal)(struct Messenger *m, int32_t, uint8_t, void *);
|
||||
void (*friend_connectionstatuschange_internal)(struct Messenger *m, uint32_t, uint8_t, void *);
|
||||
void *friend_connectionstatuschange_internal_userdata;
|
||||
void *avatar_info_recv_userdata;
|
||||
void (*avatar_info_recv)(struct Messenger *m, int32_t, uint8_t, uint8_t *, void *);
|
||||
void *avatar_data_recv_userdata;
|
||||
void (*avatar_data_recv)(struct Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *, uint32_t, void *);
|
||||
|
||||
void *group_chat_object; /* Set by new_groupchats()*/
|
||||
void (*group_invite)(struct Messenger *m, int32_t, const uint8_t *, uint16_t);
|
||||
void (*group_message)(struct Messenger *m, int32_t, const uint8_t *, uint16_t);
|
||||
void (*group_invite)(struct Messenger *m, uint32_t, const uint8_t *, uint16_t);
|
||||
void (*group_message)(struct Messenger *m, uint32_t, const uint8_t *, uint16_t);
|
||||
|
||||
void (*file_sendrequest)(struct Messenger *m, int32_t, uint8_t, uint64_t, const uint8_t *, uint16_t, void *);
|
||||
void (*file_sendrequest)(struct Messenger *m, uint32_t, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t,
|
||||
void *);
|
||||
void *file_sendrequest_userdata;
|
||||
void (*file_filecontrol)(struct Messenger *m, int32_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, int32_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, int32_t, const uint8_t *, uint16_t, void *);
|
||||
void (*msi_packet)(struct Messenger *m, uint32_t, const uint8_t *, uint16_t, void *);
|
||||
void *msi_packet_userdata;
|
||||
|
||||
void (*lossy_packethandler)(struct Messenger *m, uint32_t, const uint8_t *, size_t, void *);
|
||||
void *lossy_packethandler_userdata;
|
||||
void (*lossless_packethandler)(struct Messenger *m, uint32_t, const uint8_t *, size_t, void *);
|
||||
void *lossless_packethandler_userdata;
|
||||
|
||||
void (*core_connection_change)(struct Messenger *m, unsigned int, void *);
|
||||
void *core_connection_change_userdata;
|
||||
unsigned int last_connection_status;
|
||||
|
||||
Messenger_Options options;
|
||||
};
|
||||
|
||||
|
@ -329,7 +309,6 @@ void getaddress(const Messenger *m, uint8_t *address);
|
|||
* return -2 if no message (message length must be >= 1 byte).
|
||||
* return -3 if user's own key.
|
||||
* return -4 if friend request already sent or already a friend.
|
||||
* return -5 for unknown error.
|
||||
* return -6 if bad checksum in address.
|
||||
* return -7 if the friend was already there but the nospam was different.
|
||||
* (the nospam for that friend was set to the new one).
|
||||
|
@ -340,7 +319,10 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u
|
|||
|
||||
/* Add a friend without sending a friendrequest.
|
||||
* return the friend number if success.
|
||||
* return -1 if failure.
|
||||
* return -3 if user's own key.
|
||||
* return -4 if friend request already sent or already a friend.
|
||||
* return -6 if bad checksum in address.
|
||||
* return -8 if increasing the friend list size fails.
|
||||
*/
|
||||
int32_t m_addfriend_norequest(Messenger *m, const uint8_t *real_pk);
|
||||
|
||||
|
@ -371,8 +353,9 @@ int m_delfriend(Messenger *m, int32_t friendnumber);
|
|||
|
||||
/* Checks friend's connecting status.
|
||||
*
|
||||
* return 1 if friend is connected to us (Online).
|
||||
* return 0 if friend is not connected to us (Offline).
|
||||
* return CONNECTION_UDP (2) if friend is directly connected to us (Online UDP).
|
||||
* return CONNECTION_TCP (1) if friend is connected to us (Online TCP).
|
||||
* return CONNECTION_NONE (0) if friend is not connected to us (Offline).
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber);
|
||||
|
@ -384,33 +367,20 @@ int m_get_friend_connectionstatus(const Messenger *m, int32_t friendnumber);
|
|||
*/
|
||||
int m_friend_exists(const Messenger *m, int32_t friendnumber);
|
||||
|
||||
/* Send a text chat message to an online friend.
|
||||
/* Send a message of type to an online friend.
|
||||
*
|
||||
* return the message id if packet was successfully put into the send queue.
|
||||
* return 0 if it was not.
|
||||
* return -1 if friend not valid.
|
||||
* return -2 if too large.
|
||||
* return -3 if friend not online.
|
||||
* return -4 if send failed (because queue is full).
|
||||
* return -5 if bad type.
|
||||
* return 0 if success.
|
||||
*
|
||||
* You will want to retain the return value, it will be passed to your read_receipt callback
|
||||
* if one is received.
|
||||
* m_sendmessage_withid will send a message with the id of your choosing,
|
||||
* however we can generate an id for you by calling plain m_sendmessage.
|
||||
* the value in message_id will be passed to your read_receipt callback when the other receives the message.
|
||||
*/
|
||||
uint32_t m_sendmessage(Messenger *m, int32_t friendnumber, const uint8_t *message, uint32_t length);
|
||||
uint32_t m_sendmessage_withid(Messenger *m, int32_t friendnumber, uint32_t theid, const uint8_t *message,
|
||||
uint32_t length);
|
||||
int m_send_message_generic(Messenger *m, int32_t friendnumber, uint8_t type, const uint8_t *message, uint32_t length,
|
||||
uint32_t *message_id);
|
||||
|
||||
/* Send an action to an online friend.
|
||||
*
|
||||
* return the message id if packet was successfully put into the send queue.
|
||||
* return 0 if it was not.
|
||||
*
|
||||
* You will want to retain the return value, it will be passed to your read_receipt callback
|
||||
* if one is received.
|
||||
* m_sendaction_withid will send an action message with the id of your choosing,
|
||||
* however we can generate an id for you by calling plain m_sendaction.
|
||||
*/
|
||||
uint32_t m_sendaction(Messenger *m, int32_t friendnumber, const uint8_t *action, uint32_t length);
|
||||
uint32_t m_sendaction_withid(const Messenger *m, int32_t friendnumber, uint32_t theid, const uint8_t *action,
|
||||
uint32_t length);
|
||||
|
||||
/* Set the name and name_length of a friend.
|
||||
* name must be a string of maximum MAX_NAME_LENGTH length.
|
||||
|
@ -479,7 +449,7 @@ int m_get_self_statusmessage_size(const Messenger *m);
|
|||
* retruns -1 on failure.
|
||||
*/
|
||||
int m_copy_statusmessage(const Messenger *m, int32_t friendnumber, uint8_t *buf, uint32_t maxlen);
|
||||
int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf, uint32_t maxlen);
|
||||
int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf);
|
||||
|
||||
/* return one of USERSTATUS values.
|
||||
* Values unknown to your application should be represented as USERSTATUS_NONE.
|
||||
|
@ -490,115 +460,8 @@ uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber);
|
|||
uint8_t m_get_self_userstatus(const Messenger *m);
|
||||
|
||||
|
||||
/* Set the user avatar image data.
|
||||
* This should be made before connecting, so we will not announce that the user have no avatar
|
||||
* before setting and announcing a new one, forcing the peers to re-download it.
|
||||
*
|
||||
* Notice that the library treats the image as raw data and does not interpret it by any way.
|
||||
*
|
||||
* Arguments:
|
||||
* format - Avatar image format or NONE for user with no avatar (see AVATAR_FORMAT);
|
||||
* data - pointer to the avatar data (may be NULL it the format is NONE);
|
||||
* length - length of image data. Must be <= MAX_AVATAR_DATA_LENGTH.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int m_set_avatar(Messenger *m, uint8_t format, const uint8_t *data, uint32_t length);
|
||||
|
||||
/* Unsets the user avatar.
|
||||
|
||||
returns 0 on success (currently always returns 0) */
|
||||
int m_unset_avatar(Messenger *m);
|
||||
|
||||
/* Get avatar data from the current user.
|
||||
* Copies the current user avatar data to the destination buffer and sets the image format
|
||||
* accordingly.
|
||||
*
|
||||
* If the avatar format is NONE, the buffer 'buf' isleft uninitialized, 'hash' is zeroed, and
|
||||
* 'length' is set to zero.
|
||||
*
|
||||
* If any of the pointers format, buf, length, and hash are NULL, that particular field will be ignored.
|
||||
*
|
||||
* Arguments:
|
||||
* format - destination pointer to the avatar image format (see AVATAR_FORMAT);
|
||||
* buf - destination buffer to the image data. Must have at least 'maxlen' bytes;
|
||||
* length - destination pointer to the image data length;
|
||||
* maxlen - length of the destination buffer 'buf';
|
||||
* hash - destination pointer to the avatar hash (it must be exactly AVATAR_HASH_LENGTH bytes long).
|
||||
*
|
||||
* returns 0 on success;
|
||||
* returns -1 on failure.
|
||||
*
|
||||
*/
|
||||
int m_get_self_avatar(const Messenger *m, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen,
|
||||
uint8_t *hash);
|
||||
|
||||
/* Generates a cryptographic hash of the given data.
|
||||
* This function may be used by clients for any purpose, but is provided primarily for
|
||||
* validating cached avatars.
|
||||
* This function is a wrapper to internal message-digest functions.
|
||||
*
|
||||
* Arguments:
|
||||
* hash - destination buffer for the hash data, it must be exactly crypto_hash_sha256_BYTES bytes long.
|
||||
* data - data to be hashed;
|
||||
* datalen - length of the data;
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int m_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen);
|
||||
|
||||
/* Generates a cryptographic hash of the given avatar data.
|
||||
* This function is a wrapper to m_hash and specifically provided
|
||||
* to generate hashes from user avatars that may be memcmp()ed with the values returned by the
|
||||
* other avatar functions. It is specially important to validate cached avatars.
|
||||
*
|
||||
* Arguments:
|
||||
* hash - destination buffer for the hash data, it must be exactly AVATAR_HASH_LENGTH bytes long.
|
||||
* data - avatar image data;
|
||||
* datalen - length of the avatar image data; it must be <= MAX_AVATAR_DATA_LENGTH.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int m_avatar_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen);
|
||||
|
||||
/* Request avatar information from a friend.
|
||||
* Asks a friend to provide their avatar information (image format and hash). The friend may
|
||||
* or may not answer this request and, if answered, the information will be provided through
|
||||
* the callback 'avatar_info'.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int m_request_avatar_info(const Messenger *m, const int32_t friendnumber);
|
||||
|
||||
/* Send an unrequested avatar information to a friend.
|
||||
* Sends our avatar format and hash to a friend; he/she can use this information to validate
|
||||
* an avatar from the cache and may (or not) reply with an avatar data request.
|
||||
*
|
||||
* Notice: it is NOT necessary to send these notification after changing the avatar or
|
||||
* connecting. The library already does this.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int m_send_avatar_info(const Messenger *m, const int32_t friendnumber);
|
||||
|
||||
|
||||
/* Request the avatar data from a friend.
|
||||
* Ask a friend to send their avatar data. The friend may or may not answer this request and,
|
||||
* if answered, the information will be provided in callback 'avatar_data'.
|
||||
*
|
||||
* returns 0 on sucess
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int m_request_avatar_data(const Messenger *m, const int32_t friendnumber);
|
||||
|
||||
|
||||
/* returns timestamp of last time friendnumber was seen online, or 0 if never seen.
|
||||
* returns -1 on error.
|
||||
/* returns timestamp of last time friendnumber was seen online or 0 if never seen.
|
||||
* if friendnumber is invalid this function will return UINT64_MAX.
|
||||
*/
|
||||
uint64_t m_get_last_online(const Messenger *m, int32_t friendnumber);
|
||||
|
||||
|
@ -615,58 +478,48 @@ int m_set_usertyping(Messenger *m, int32_t friendnumber, uint8_t is_typing);
|
|||
* returns 0 if friend is not typing.
|
||||
* returns 1 if friend is typing.
|
||||
*/
|
||||
uint8_t m_get_istyping(const Messenger *m, int32_t friendnumber);
|
||||
|
||||
/* Sets whether we send read receipts for friendnumber.
|
||||
* This function is not lazy, and it will fail if yesno is not (0 or 1).
|
||||
*/
|
||||
void m_set_sends_receipts(Messenger *m, int32_t friendnumber, int yesno);
|
||||
int m_get_istyping(const Messenger *m, int32_t friendnumber);
|
||||
|
||||
/* Set the function that will be executed when a friend request is received.
|
||||
* Function format is function(uint8_t * public_key, uint8_t * data, uint16_t length)
|
||||
* Function format is function(uint8_t * public_key, uint8_t * data, size_t length)
|
||||
*/
|
||||
void m_callback_friendrequest(Messenger *m, void (*function)(Messenger *m, const uint8_t *, const uint8_t *, uint16_t,
|
||||
void m_callback_friendrequest(Messenger *m, void (*function)(Messenger *m, const uint8_t *, const uint8_t *, size_t,
|
||||
void *), void *userdata);
|
||||
|
||||
/* Set the function that will be executed when a message from a friend is received.
|
||||
* Function format is: function(int32_t friendnumber, uint8_t * message, uint32_t length)
|
||||
* Function format is: function(uint32_t friendnumber, unsigned int type, uint8_t * message, uint32_t length)
|
||||
*/
|
||||
void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set the function that will be executed when an action from a friend is received.
|
||||
* Function format is: function(int32_t friendnumber, uint8_t * action, uint32_t length)
|
||||
*/
|
||||
void m_callback_action(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, uint32_t, unsigned int, const uint8_t *,
|
||||
size_t, void *), void *userdata);
|
||||
|
||||
/* Set the callback for name changes.
|
||||
* Function(int32_t friendnumber, uint8_t *newname, uint16_t length)
|
||||
* Function(uint32_t friendnumber, uint8_t *newname, size_t length)
|
||||
* You are not responsible for freeing newname.
|
||||
*/
|
||||
void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
|
||||
void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, uint32_t, const uint8_t *, size_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set the callback for status message changes.
|
||||
* Function(int32_t friendnumber, uint8_t *newstatus, uint16_t length)
|
||||
* Function(uint32_t friendnumber, uint8_t *newstatus, size_t length)
|
||||
*
|
||||
* You are not responsible for freeing newstatus
|
||||
*/
|
||||
void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
|
||||
void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, uint32_t, const uint8_t *, size_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set the callback for status type changes.
|
||||
* Function(int32_t friendnumber, USERSTATUS kind)
|
||||
* Function(uint32_t friendnumber, USERSTATUS kind)
|
||||
*/
|
||||
void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *), void *userdata);
|
||||
void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, uint32_t, unsigned int, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set the callback for typing changes.
|
||||
* Function(int32_t friendnumber, uint8_t is_typing)
|
||||
* Function(uint32_t friendnumber, uint8_t is_typing)
|
||||
*/
|
||||
void m_callback_typingchange(Messenger *m, void(*function)(Messenger *m, int32_t, uint8_t, void *), void *userdata);
|
||||
void m_callback_typingchange(Messenger *m, void(*function)(Messenger *m, uint32_t, _Bool, void *), void *userdata);
|
||||
|
||||
/* Set the callback for read receipts.
|
||||
* Function(int32_t friendnumber, uint32_t receipt)
|
||||
* Function(uint32_t friendnumber, uint32_t receipt)
|
||||
*
|
||||
* If you are keeping a record of returns from m_sendmessage,
|
||||
* receipt might be one of those values, meaning the message
|
||||
|
@ -674,10 +527,10 @@ void m_callback_typingchange(Messenger *m, void(*function)(Messenger *m, int32_t
|
|||
* Since core doesn't track ids for you, receipt may not correspond to any message.
|
||||
* In that case, you should discard it.
|
||||
*/
|
||||
void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int32_t, uint32_t, void *), void *userdata);
|
||||
void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, void *), void *userdata);
|
||||
|
||||
/* Set the callback for connection status changes.
|
||||
* function(int32_t friendnumber, uint8_t status)
|
||||
* function(uint32_t friendnumber, uint8_t status)
|
||||
*
|
||||
* Status:
|
||||
* 0 -- friend went offline after being previously online.
|
||||
|
@ -687,62 +540,25 @@ void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int32_
|
|||
* being previously online" part.
|
||||
* It's assumed that when adding friends, their connection status is offline.
|
||||
*/
|
||||
void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *),
|
||||
void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, uint32_t, unsigned int, void *),
|
||||
void *userdata);
|
||||
/* Same as previous but for internal A/V core usage only */
|
||||
void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *),
|
||||
void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, uint32_t, uint8_t, void *),
|
||||
void *userdata);
|
||||
|
||||
|
||||
/* Set the callback function for avatar information.
|
||||
* This callback will be called when avatar information are received from friends. These events
|
||||
* can arrive at anytime, but are usually received uppon connection and in reply of avatar
|
||||
* information requests.
|
||||
*
|
||||
* Function format is:
|
||||
* function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata)
|
||||
*
|
||||
* where 'format' is the avatar image format (see AVATAR_FORMAT) and 'hash' is the hash of
|
||||
* the avatar data for caching purposes and it is exactly AVATAR_HASH_LENGTH long. If the
|
||||
* image format is NONE, the hash is zeroed.
|
||||
*
|
||||
/* Set the callback for typing changes.
|
||||
* Function(unsigned int connection_status (0 = not connected, 1 = TCP only, 2 = UDP + TCP))
|
||||
*/
|
||||
void m_callback_avatar_info(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, void *),
|
||||
void *userdata);
|
||||
|
||||
|
||||
/* Set the callback function for avatar data.
|
||||
* This callback will be called when the complete avatar data was correctly received from a
|
||||
* friend. This only happens in reply of a avatar data request (see tox_request_avatar_data);
|
||||
*
|
||||
* Function format is:
|
||||
* function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
|
||||
*
|
||||
* where 'format' is the avatar image format (see AVATAR_FORMAT); 'hash' is the
|
||||
* locally-calculated cryptographic hash of the avatar data and it is exactly
|
||||
* AVATAR_HASH_LENGTH long; 'data' is the avatar image data and 'datalen' is the length
|
||||
* of such data.
|
||||
*
|
||||
* If format is NONE, 'data' is NULL, 'datalen' is zero, and the hash is zeroed. The hash is
|
||||
* always validated locally with the function tox_avatar_hash and ensured to match the image
|
||||
* data, so this value can be safely used to compare with cached avatars.
|
||||
*
|
||||
* WARNING: users MUST treat all avatar image data received from another peer as untrusted and
|
||||
* potentially malicious. The library only ensures that the data which arrived is the same the
|
||||
* other user sent, and does not interpret or validate any image data.
|
||||
*/
|
||||
void m_callback_avatar_data(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *,
|
||||
uint32_t, void *), void *userdata);
|
||||
|
||||
|
||||
void m_callback_core_connection(Messenger *m, void (*function)(Messenger *m, unsigned int, void *), void *userdata);
|
||||
|
||||
/**********GROUP CHATS************/
|
||||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Messenger *m, int32_t friendnumber, uint8_t *data, uint16_t length)
|
||||
* Function(Messenger *m, uint32_t friendnumber, uint8_t *data, uint16_t length)
|
||||
*/
|
||||
void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t));
|
||||
void m_callback_group_invite(Messenger *m, void (*function)(Messenger *m, uint32_t, const uint8_t *, uint16_t));
|
||||
|
||||
/* Send a group invite packet.
|
||||
*
|
||||
|
@ -756,58 +572,97 @@ int send_group_invite_packet(const Messenger *m, int32_t friendnumber, const uin
|
|||
|
||||
/* Set the callback for file send requests.
|
||||
*
|
||||
* Function(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length, void *userdata)
|
||||
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint32_t filetype, uint64_t filesize, uint8_t *filename, size_t filename_length, void *userdata)
|
||||
*/
|
||||
void callback_file_sendrequest(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint64_t, const uint8_t *,
|
||||
uint16_t, void *), void *userdata);
|
||||
void callback_file_sendrequest(Messenger *m, void (*function)(Messenger *m, uint32_t, uint32_t, uint32_t, uint64_t,
|
||||
const uint8_t *, size_t, void *), void *userdata);
|
||||
|
||||
|
||||
/* Set the callback for file control requests.
|
||||
*
|
||||
* Function(Tox *tox, int32_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, int32_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, int32_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, int32_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);
|
||||
|
||||
/* Send a file send request.
|
||||
* Maximum filename length is 255 bytes.
|
||||
* return 1 on success
|
||||
* return 0 on failure
|
||||
/* Set the callback for file request chunk.
|
||||
*
|
||||
* Function(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void *userdata)
|
||||
*
|
||||
*/
|
||||
int file_sendrequest(const Messenger *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
|
||||
const uint8_t *filename, uint16_t filename_length);
|
||||
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
|
||||
* return -1 on failure
|
||||
* return -1 if friend not found.
|
||||
* return -2 if filename length invalid.
|
||||
* return -3 if no more file sending slots left.
|
||||
* return -4 if could not send packet (friend offline).
|
||||
*
|
||||
*/
|
||||
int new_filesender(const Messenger *m, int32_t friendnumber, uint64_t filesize, const uint8_t *filename,
|
||||
uint16_t filename_length);
|
||||
long int new_filesender(const Messenger *m, int32_t friendnumber, uint32_t file_type, uint64_t filesize,
|
||||
const uint8_t *file_id, 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 a seek 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 not receiving file.
|
||||
* return -5 if file status wrong.
|
||||
* return -6 if position bad.
|
||||
* return -8 if packet failed to send.
|
||||
*/
|
||||
int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uint64_t position);
|
||||
|
||||
/* 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 bad data size.
|
||||
* return -6 if packet queue full.
|
||||
* return -7 if wrong position.
|
||||
*/
|
||||
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, uint64_t position, const uint8_t *data,
|
||||
uint16_t length);
|
||||
|
||||
/* Give the number of bytes left to be sent/received.
|
||||
*
|
||||
|
@ -822,9 +677,9 @@ uint64_t file_dataremaining(const Messenger *m, int32_t friendnumber, uint8_t fi
|
|||
|
||||
/* Set the callback for msi packets.
|
||||
*
|
||||
* Function(Messenger *m, int32_t friendnumber, uint8_t *data, uint16_t length, void *userdata)
|
||||
* Function(Messenger *m, uint32_t friendnumber, uint8_t *data, uint16_t length, void *userdata)
|
||||
*/
|
||||
void m_callback_msi_packet(Messenger *m, void (*function)(Messenger *m, int32_t, const uint8_t *, uint16_t, void *),
|
||||
void m_callback_msi_packet(Messenger *m, void (*function)(Messenger *m, uint32_t, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Send an msi packet.
|
||||
|
@ -834,20 +689,29 @@ void m_callback_msi_packet(Messenger *m, void (*function)(Messenger *m, int32_t,
|
|||
*/
|
||||
int m_msi_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length);
|
||||
|
||||
/**********************************************/
|
||||
|
||||
/* Set handlers for custom lossy packets (RTP packets for example.)
|
||||
/* Set handlers for lossy rtp packets.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int custom_lossy_packet_registerhandler(Messenger *m, int32_t friendnumber, uint8_t byte,
|
||||
int (*packet_handler_callback)(Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t len, void *object),
|
||||
void *object);
|
||||
int m_callback_rtp_packet(Messenger *m, int32_t friendnumber, uint8_t byte, int (*packet_handler_callback)(Messenger *m,
|
||||
uint32_t friendnumber, const uint8_t *data, uint16_t len, void *object), void *object);
|
||||
|
||||
/**********************************************/
|
||||
|
||||
/* Set handlers for custom lossy packets.
|
||||
*
|
||||
*/
|
||||
void custom_lossy_packet_registerhandler(Messenger *m, void (*packet_handler_callback)(Messenger *m,
|
||||
uint32_t friendnumber, const uint8_t *data, size_t len, void *object), void *object);
|
||||
|
||||
/* High level function to send custom lossy packets.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return -1 if friend invalid.
|
||||
* return -2 if length wrong.
|
||||
* return -3 if first byte invalid.
|
||||
* return -4 if friend offline.
|
||||
* return -5 if packet failed to send because of other error.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length);
|
||||
|
@ -855,28 +719,36 @@ int send_custom_lossy_packet(const Messenger *m, int32_t friendnumber, const uin
|
|||
|
||||
/* Set handlers for custom lossless packets.
|
||||
*
|
||||
* byte must be in PACKET_ID_LOSSLESS_RANGE_START PACKET_ID_LOSSLESS_RANGE_SIZE range.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int custom_lossless_packet_registerhandler(Messenger *m, int32_t friendnumber, uint8_t byte,
|
||||
int (*packet_handler_callback)(Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t len, void *object),
|
||||
void *object);
|
||||
void custom_lossless_packet_registerhandler(Messenger *m, void (*packet_handler_callback)(Messenger *m,
|
||||
uint32_t friendnumber, const uint8_t *data, size_t len, void *object), void *object);
|
||||
|
||||
/* High level function to send custom lossless packets.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return -1 if friend invalid.
|
||||
* return -2 if length wrong.
|
||||
* return -3 if first byte invalid.
|
||||
* return -4 if friend offline.
|
||||
* return -5 if packet failed to send because of other error.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int send_custom_lossless_packet(const Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length);
|
||||
|
||||
/**********************************************/
|
||||
|
||||
enum {
|
||||
MESSENGER_ERROR_NONE,
|
||||
MESSENGER_ERROR_PORT,
|
||||
MESSENGER_ERROR_OTHER
|
||||
};
|
||||
|
||||
/* Run this at startup.
|
||||
* return allocated instance of Messenger on success.
|
||||
* return 0 if there are problems.
|
||||
*
|
||||
* if error is not NULL it will be set to one of the values in the enum above.
|
||||
*/
|
||||
Messenger *new_messenger(Messenger_Options *options);
|
||||
Messenger *new_messenger(Messenger_Options *options, unsigned int *error);
|
||||
|
||||
/* Run this before closing shop
|
||||
* Free all datastructures.
|
||||
|
@ -891,7 +763,7 @@ void do_messenger(Messenger *m);
|
|||
*
|
||||
* returns time (in ms) before the next do_messenger() needs to be run on success.
|
||||
*/
|
||||
uint32_t messenger_run_interval(Messenger *m);
|
||||
uint32_t messenger_run_interval(const Messenger *m);
|
||||
|
||||
/* SAVING AND LOADING FUNCTIONS: */
|
||||
|
||||
|
@ -917,6 +789,6 @@ uint32_t get_num_online_friends(const Messenger *m);
|
|||
* Otherwise, returns the number of elements copied.
|
||||
* If the array was too small, the contents
|
||||
* of out_list will be truncated to list_size. */
|
||||
uint32_t copy_friendlist(const Messenger *m, int32_t *out_list, uint32_t list_size);
|
||||
uint32_t copy_friendlist(const Messenger *m, uint32_t *out_list, uint32_t list_size);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -346,6 +346,18 @@ static _Bool add_priority(TCP_Client_Connection *con, const uint8_t *packet, uin
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void wipe_priority_list(TCP_Client_Connection *con)
|
||||
{
|
||||
TCP_Priority_List *p = con->priority_queue_start;
|
||||
|
||||
while (p) {
|
||||
TCP_Priority_List *pp = p;
|
||||
p = p->next;
|
||||
free(pp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* return 1 on success.
|
||||
* return 0 if could not send packet.
|
||||
* return -1 on failure (connection must be killed).
|
||||
|
@ -948,6 +960,7 @@ void kill_TCP_connection(TCP_Client_Connection *TCP_connection)
|
|||
if (TCP_connection == NULL)
|
||||
return;
|
||||
|
||||
wipe_priority_list(TCP_connection);
|
||||
kill_sock(TCP_connection->sock);
|
||||
memset(TCP_connection, 0, sizeof(TCP_Client_Connection));
|
||||
free(TCP_connection);
|
||||
|
|
|
@ -78,8 +78,10 @@ typedef struct {
|
|||
uint64_t ping_response_id;
|
||||
uint64_t ping_request_id;
|
||||
|
||||
//TODO: remove
|
||||
void *net_crypto_pointer;
|
||||
uint32_t net_crypto_location;
|
||||
|
||||
struct {
|
||||
uint8_t status; /* 0 if not used, 1 if other is offline, 2 if other is online. */
|
||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
|
||||
|
@ -96,6 +98,10 @@ typedef struct {
|
|||
|
||||
int (*onion_callback)(void *object, const uint8_t *data, uint16_t length);
|
||||
void *onion_callback_object;
|
||||
|
||||
/* Can be used by user. */
|
||||
void *custom_object;
|
||||
uint32_t custom_uint;
|
||||
} TCP_Client_Connection;
|
||||
|
||||
/* Create new TCP connection to ip_port/public_key
|
||||
|
|
968
toxcore/TCP_connection.c
Normal file
968
toxcore/TCP_connection.c
Normal file
|
@ -0,0 +1,968 @@
|
|||
/* TCP_connection.c
|
||||
*
|
||||
* Handles TCP relay connections between two Tox clients.
|
||||
*
|
||||
* Copyright (C) 2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "TCP_connection.h"
|
||||
|
||||
/* Set the size of the array to num.
|
||||
*
|
||||
* return -1 if realloc fails.
|
||||
* return 0 if it succeeds.
|
||||
*/
|
||||
#define realloc_tox_array(array, element_size, num, temp_pointer) (num ? (temp_pointer = realloc(array, ((num) * (element_size))), temp_pointer ? (array = temp_pointer, 0) : (-1) ) : (free(array), array = NULL, 0))
|
||||
|
||||
|
||||
/* return 1 if the connections_number is not valid.
|
||||
* return 0 if the connections_number is valid.
|
||||
*/
|
||||
static _Bool connections_number_not_valid(const TCP_Connections *tcp_c, int connections_number)
|
||||
{
|
||||
if ((unsigned int)connections_number >= tcp_c->connections_length)
|
||||
return 1;
|
||||
|
||||
if (tcp_c->connections == NULL)
|
||||
return 1;
|
||||
|
||||
if (tcp_c->connections[connections_number].status == TCP_CONN_NONE)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return 1 if the tcp_connections_number is not valid.
|
||||
* return 0 if the tcp_connections_number is valid.
|
||||
*/
|
||||
static _Bool tcp_connections_number_not_valid(const TCP_Connections *tcp_c, int tcp_connections_number)
|
||||
{
|
||||
if ((unsigned int)tcp_connections_number >= tcp_c->tcp_connections_length)
|
||||
return 1;
|
||||
|
||||
if (tcp_c->tcp_connections == NULL)
|
||||
return 1;
|
||||
|
||||
if (tcp_c->tcp_connections[tcp_connections_number].status == TCP_CONN_NONE)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a new empty connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return connections_number on success.
|
||||
*/
|
||||
static int create_connection(TCP_Connections *tcp_c)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < tcp_c->connections_length; ++i) {
|
||||
if (tcp_c->connections[i].status == TCP_CONN_NONE)
|
||||
return i;
|
||||
}
|
||||
|
||||
int id = -1;
|
||||
|
||||
TCP_Connection_to *temp_pointer;
|
||||
|
||||
if (realloc_tox_array(tcp_c->connections, sizeof(TCP_Connection_to), tcp_c->connections_length + 1,
|
||||
temp_pointer) == 0) {
|
||||
id = tcp_c->connections_length;
|
||||
++tcp_c->connections_length;
|
||||
memset(&(tcp_c->connections[id]), 0, sizeof(TCP_Connection_to));
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Create a new empty tcp connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return tcp_connections_number on success.
|
||||
*/
|
||||
static int create_tcp_connection(TCP_Connections *tcp_c)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
|
||||
if (tcp_c->tcp_connections[i].status == TCP_CONN_NONE)
|
||||
return i;
|
||||
}
|
||||
|
||||
int id = -1;
|
||||
|
||||
TCP_con *temp_pointer;
|
||||
|
||||
if (realloc_tox_array(tcp_c->tcp_connections, sizeof(TCP_con), tcp_c->tcp_connections_length + 1, temp_pointer) == 0) {
|
||||
id = tcp_c->tcp_connections_length;
|
||||
++tcp_c->tcp_connections_length;
|
||||
memset(&(tcp_c->tcp_connections[id]), 0, sizeof(TCP_con));
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Wipe a connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
static int wipe_connection(TCP_Connections *tcp_c, int connections_number)
|
||||
{
|
||||
if (connections_number_not_valid(tcp_c, connections_number))
|
||||
return -1;
|
||||
|
||||
uint32_t i;
|
||||
memset(&(tcp_c->connections[connections_number]), 0 , sizeof(TCP_Connection_to));
|
||||
|
||||
for (i = tcp_c->connections_length; i != 0; --i) {
|
||||
if (tcp_c->connections[i - 1].status != TCP_CONN_NONE)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tcp_c->connections_length != i) {
|
||||
tcp_c->connections_length = i;
|
||||
TCP_Connection_to *temp_pointer;
|
||||
realloc_tox_array(tcp_c->connections, sizeof(TCP_Connection_to), tcp_c->connections_length, temp_pointer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wipe a connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
static int wipe_tcp_connection(TCP_Connections *tcp_c, int tcp_connections_number)
|
||||
{
|
||||
if (tcp_connections_number_not_valid(tcp_c, tcp_connections_number))
|
||||
return -1;
|
||||
|
||||
uint32_t i;
|
||||
memset(&(tcp_c->tcp_connections[tcp_connections_number]), 0 , sizeof(TCP_con));
|
||||
|
||||
for (i = tcp_c->tcp_connections_length; i != 0; --i) {
|
||||
if (tcp_c->tcp_connections[i - 1].status != TCP_CONN_NONE)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tcp_c->tcp_connections_length != i) {
|
||||
tcp_c->tcp_connections_length = i;
|
||||
TCP_con *temp_pointer;
|
||||
realloc_tox_array(tcp_c->tcp_connections, sizeof(TCP_con), tcp_c->tcp_connections_length, temp_pointer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static TCP_Connection_to *get_connection(const TCP_Connections *tcp_c, int connections_number)
|
||||
{
|
||||
if (connections_number_not_valid(tcp_c, connections_number))
|
||||
return 0;
|
||||
|
||||
return &tcp_c->connections[connections_number];
|
||||
}
|
||||
|
||||
static TCP_con *get_tcp_connection(const TCP_Connections *tcp_c, int tcp_connections_number)
|
||||
{
|
||||
if (tcp_connections_number_not_valid(tcp_c, tcp_connections_number))
|
||||
return 0;
|
||||
|
||||
return &tcp_c->tcp_connections[tcp_connections_number];
|
||||
}
|
||||
|
||||
/* Send a packet to the TCP connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int send_packet_tcp_connection(TCP_Connections *tcp_c, int connections_number, const uint8_t *packet, uint16_t length)
|
||||
{
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
|
||||
|
||||
if (!con_to) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//TODO: detect and kill bad relays.
|
||||
//TODO: thread safety?
|
||||
unsigned int i;
|
||||
int ret = -1;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
|
||||
uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
|
||||
uint8_t status = con_to->connections[i].status;
|
||||
uint8_t connection_id = con_to->connections[i].connection_id;
|
||||
|
||||
if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_ONLINE) {
|
||||
tcp_con_num -= 1;
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
|
||||
|
||||
if (!tcp_con) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = send_data(tcp_con->connection, connection_id, packet, length);
|
||||
|
||||
if (ret == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 1) {
|
||||
return 0;
|
||||
} else {
|
||||
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
|
||||
uint32_t tcp_con_num = con_to->connections[i].tcp_connection;
|
||||
uint8_t status = con_to->connections[i].status;
|
||||
uint8_t connection_id = con_to->connections[i].connection_id;
|
||||
|
||||
if (tcp_con_num && status == TCP_CONNECTIONS_STATUS_REGISTERED) {
|
||||
tcp_con_num -= 1;
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_con_num);
|
||||
|
||||
if (!tcp_con) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = send_oob_packet(tcp_con->connection, con_to->public_key, packet, length);
|
||||
|
||||
if (ret == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 1) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a random TCP connection number for use in send_tcp_onion_request.
|
||||
*
|
||||
* TODO: This number is just the index of an array that the elements can
|
||||
* change without warning.
|
||||
*
|
||||
* return TCP connection number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int get_random_tcp_conn_number(TCP_Connections *tcp_c)
|
||||
{
|
||||
unsigned int i, r = rand();
|
||||
|
||||
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
|
||||
if (tcp_c->tcp_connections[(i + r) % tcp_c->tcp_connections_length].status == TCP_CONN_CONNECTED) {
|
||||
return ((i + r) % tcp_c->tcp_connections_length);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Send an onion packet via the TCP relay corresponding to tcp_connections_number.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int tcp_send_onion_request(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *data,
|
||||
uint16_t length)
|
||||
{
|
||||
if (tcp_connections_number >= tcp_c->tcp_connections_length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tcp_c->tcp_connections[tcp_connections_number].status == TCP_CONN_CONNECTED) {
|
||||
int ret = send_onion_request(tcp_c->tcp_connections[tcp_connections_number].connection, data, length);
|
||||
|
||||
if (ret == 1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Send an oob packet via the TCP relay corresponding to tcp_connections_number.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int tcp_send_oob_packet(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *public_key,
|
||||
const uint8_t *packet, uint16_t length)
|
||||
{
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
int ret = send_oob_packet(tcp_con->connection, public_key, packet, length);
|
||||
|
||||
if (ret == 1)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the callback for TCP data packets.
|
||||
*/
|
||||
void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_data_callback)(void *object, int id,
|
||||
const uint8_t *data, uint16_t length), void *object)
|
||||
{
|
||||
tcp_c->tcp_data_callback = tcp_data_callback;
|
||||
tcp_c->tcp_data_callback_object = object;
|
||||
}
|
||||
|
||||
/* Set the callback for TCP onion packets.
|
||||
*/
|
||||
void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_oob_callback)(void *object,
|
||||
const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length), void *object)
|
||||
{
|
||||
tcp_c->tcp_oob_callback = tcp_oob_callback;
|
||||
tcp_c->tcp_oob_callback_object = object;
|
||||
}
|
||||
|
||||
/* Set the callback for TCP oob data packets.
|
||||
*/
|
||||
void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_onion_callback)(void *object,
|
||||
const uint8_t *data, uint16_t length), void *object)
|
||||
{
|
||||
tcp_c->tcp_onion_callback = tcp_onion_callback;
|
||||
tcp_c->tcp_onion_callback_object = object;
|
||||
}
|
||||
|
||||
|
||||
/* Find the TCP connection with public_key.
|
||||
*
|
||||
* return connections_number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int find_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp_c->connections_length; ++i) {
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, i);
|
||||
|
||||
if (con_to) {
|
||||
if (memcmp(con_to->public_key, public_key, crypto_box_PUBLICKEYBYTES) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find the TCP connection to a relay with relay_pk.
|
||||
*
|
||||
* return connections_number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int find_tcp_connection_relay(TCP_Connections *tcp_c, const uint8_t *relay_pk)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
|
||||
|
||||
if (tcp_con) {
|
||||
if (memcmp(tcp_con->connection->public_key, relay_pk, crypto_box_PUBLICKEYBYTES) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create a new TCP connection to public_key.
|
||||
*
|
||||
* id is the id in the callbacks for that connection.
|
||||
*
|
||||
* return connections_number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int id)
|
||||
{
|
||||
if (find_tcp_connection_to(tcp_c, public_key) != -1)
|
||||
return -1;
|
||||
|
||||
int connections_number = create_connection(tcp_c);
|
||||
|
||||
if (connections_number == -1)
|
||||
return -1;
|
||||
|
||||
TCP_Connection_to *con_to = &tcp_c->connections[connections_number];
|
||||
|
||||
con_to->status = TCP_CONN_VALID;
|
||||
memcpy(con_to->public_key, public_key, crypto_box_PUBLICKEYBYTES);
|
||||
con_to->id = id;
|
||||
|
||||
return connections_number;
|
||||
}
|
||||
|
||||
/* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number)
|
||||
{
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
|
||||
|
||||
if (!con_to)
|
||||
return -1;
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
|
||||
if (con_to->connections[i].tcp_connection) {
|
||||
unsigned int tcp_connections_number = con_to->connections[i].tcp_connection - 1;
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
continue;
|
||||
|
||||
if (tcp_con->status == TCP_CONN_CONNECTED) {
|
||||
send_disconnect_request(tcp_con->connection, con_to->connections[i].connection_id);
|
||||
}
|
||||
|
||||
--tcp_con->lock_count;
|
||||
}
|
||||
}
|
||||
|
||||
return wipe_connection(tcp_c, connections_number);
|
||||
}
|
||||
|
||||
static _Bool tcp_connection_in_conn(TCP_Connection_to *con_to, int tcp_connections_number)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
|
||||
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return index on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int add_tcp_connection_to_conn(TCP_Connection_to *con_to, int tcp_connections_number)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (tcp_connection_in_conn(con_to, tcp_connections_number))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
|
||||
if (con_to->connections[i].tcp_connection == 0) {
|
||||
con_to->connections[i].tcp_connection = tcp_connections_number + 1;
|
||||
con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE;
|
||||
con_to->connections[i].connection_id = 0;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* return index on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int rm_tcp_connection_from_conn(TCP_Connection_to *con_to, int tcp_connections_number)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
|
||||
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
|
||||
con_to->connections[i].tcp_connection = 0;
|
||||
con_to->connections[i].status = TCP_CONNECTIONS_STATUS_NONE;
|
||||
con_to->connections[i].connection_id = 0;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* return index on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int set_tcp_connection_status(TCP_Connection_to *con_to, int tcp_connections_number, unsigned int status,
|
||||
uint8_t connection_id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_TCP_CONNECTIONS; ++i) {
|
||||
if (con_to->connections[i].tcp_connection == (tcp_connections_number + 1)) {
|
||||
con_to->connections[i].status = status;
|
||||
con_to->connections[i].connection_id = connection_id;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Kill a TCP relay connection.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int kill_tcp_relay_connection(TCP_Connections *tcp_c, int tcp_connections_number)
|
||||
{
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp_c->connections_length; ++i) {
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, i);
|
||||
|
||||
if (con_to) {
|
||||
rm_tcp_connection_from_conn(con_to, tcp_connections_number);
|
||||
}
|
||||
}
|
||||
|
||||
kill_TCP_connection(tcp_con->connection);
|
||||
|
||||
return wipe_tcp_connection(tcp_c, tcp_connections_number);
|
||||
}
|
||||
|
||||
/* Send a TCP routing request.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int send_tcp_relay_routing_request(TCP_Connections *tcp_c, int tcp_connections_number, uint8_t *public_key)
|
||||
{
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
if (send_routing_request(tcp_con->connection, public_key) != 1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcp_response_callback(void *object, uint8_t connection_id, const uint8_t *public_key)
|
||||
{
|
||||
TCP_Client_Connection *TCP_client_con = object;
|
||||
TCP_Connections *tcp_c = TCP_client_con->custom_object;
|
||||
|
||||
unsigned int tcp_connections_number = TCP_client_con->custom_uint;
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
int connections_number = find_tcp_connection_to(tcp_c, public_key);
|
||||
|
||||
if (connections_number == -1)
|
||||
return -1;
|
||||
|
||||
set_tcp_connection_number(tcp_con->connection, connection_id, connections_number);
|
||||
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
|
||||
|
||||
if (con_to == NULL)
|
||||
return -1;
|
||||
|
||||
if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcp_status_callback(void *object, uint32_t number, uint8_t connection_id, uint8_t status)
|
||||
{
|
||||
TCP_Client_Connection *TCP_client_con = object;
|
||||
TCP_Connections *tcp_c = TCP_client_con->custom_object;
|
||||
|
||||
unsigned int tcp_connections_number = TCP_client_con->custom_uint;
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, number);
|
||||
|
||||
if (!con_to || !tcp_con)
|
||||
return -1;
|
||||
|
||||
if (status == 1) {
|
||||
if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_REGISTERED, connection_id) == -1)
|
||||
return -1;
|
||||
|
||||
} else if (status == 2) {
|
||||
if (set_tcp_connection_status(con_to, tcp_connections_number, TCP_CONNECTIONS_STATUS_ONLINE, connection_id) == -1)
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcp_data_callback(void *object, uint32_t number, uint8_t connection_id, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
|
||||
if (length == 0)
|
||||
return -1;
|
||||
|
||||
TCP_Client_Connection *TCP_client_con = object;
|
||||
TCP_Connections *tcp_c = TCP_client_con->custom_object;
|
||||
|
||||
unsigned int tcp_connections_number = TCP_client_con->custom_uint;
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, number);
|
||||
|
||||
if (!con_to)
|
||||
return -1;
|
||||
|
||||
if (tcp_c->tcp_data_callback)
|
||||
tcp_c->tcp_data_callback(tcp_c->tcp_data_callback_object, con_to->id, data, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcp_oob_callback(void *object, const uint8_t *public_key, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
if (length == 0)
|
||||
return -1;
|
||||
|
||||
TCP_Client_Connection *TCP_client_con = object;
|
||||
TCP_Connections *tcp_c = TCP_client_con->custom_object;
|
||||
|
||||
unsigned int tcp_connections_number = TCP_client_con->custom_uint;
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
/* TODO: optimize */
|
||||
int connections_number = find_tcp_connection_to(tcp_c, public_key);
|
||||
|
||||
if (connections_number == -1) {
|
||||
if (tcp_c->tcp_oob_callback)
|
||||
tcp_c->tcp_oob_callback(tcp_c->tcp_oob_callback_object, public_key, tcp_connections_number, data, length);
|
||||
} else {
|
||||
return tcp_data_callback(object, connections_number, 0, data, length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcp_onion_callback(void *object, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
TCP_Connections *tcp_c = object;
|
||||
|
||||
if (tcp_c->tcp_onion_callback)
|
||||
tcp_c->tcp_onion_callback(tcp_c->tcp_onion_callback_object, data, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set callbacks for the TCP relay connection.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
static int tcp_relay_set_callbacks(TCP_Connections *tcp_c, int tcp_connections_number)
|
||||
{
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
TCP_Client_Connection *con = tcp_con->connection;
|
||||
|
||||
con->custom_object = tcp_c;
|
||||
con->custom_uint = tcp_connections_number;
|
||||
onion_response_handler(con, &tcp_onion_callback, tcp_c);
|
||||
routing_response_handler(con, &tcp_response_callback, con);
|
||||
routing_status_handler(con, &tcp_status_callback, con);
|
||||
routing_data_handler(con, &tcp_data_callback, con);
|
||||
oob_data_handler(con, &tcp_oob_callback, con);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcp_relay_on_online(TCP_Connections *tcp_c, int tcp_connections_number)
|
||||
{
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp_c->connections_length; ++i) {
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, i);
|
||||
|
||||
if (con_to) {
|
||||
if (tcp_connection_in_conn(con_to, tcp_connections_number)) {
|
||||
send_tcp_relay_routing_request(tcp_c, tcp_connections_number, con_to->public_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tcp_relay_set_callbacks(tcp_c, tcp_connections_number);
|
||||
tcp_con->status = TCP_CONN_CONNECTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_tcp_relay(TCP_Connections *tcp_c, IP_Port ip_port, const uint8_t *relay_pk)
|
||||
{
|
||||
if (ip_port.ip.family == TCP_INET) {
|
||||
ip_port.ip.family = AF_INET;
|
||||
} else if (ip_port.ip.family == TCP_INET6) {
|
||||
ip_port.ip.family = AF_INET6;
|
||||
}
|
||||
|
||||
if (ip_port.ip.family != AF_INET && ip_port.ip.family != AF_INET6)
|
||||
return -1;
|
||||
|
||||
int tcp_connections_number = create_tcp_connection(tcp_c);
|
||||
|
||||
if (tcp_connections_number == -1)
|
||||
return -1;
|
||||
|
||||
TCP_con *tcp_con = &tcp_c->tcp_connections[tcp_connections_number];
|
||||
|
||||
|
||||
tcp_con->connection = new_TCP_connection(ip_port, relay_pk, tcp_c->dht->self_public_key, tcp_c->dht->self_secret_key,
|
||||
&tcp_c->proxy_info);
|
||||
|
||||
if (!tcp_con->connection)
|
||||
return -1;
|
||||
|
||||
tcp_con->status = TCP_CONN_VALID;
|
||||
|
||||
return tcp_connections_number;
|
||||
}
|
||||
|
||||
/* Add a TCP relay to the instance.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_tcp_relay_global(TCP_Connections *tcp_c, IP_Port ip_port, const uint8_t *relay_pk)
|
||||
{
|
||||
int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk);
|
||||
|
||||
if (tcp_connections_number != -1)
|
||||
return -1;
|
||||
|
||||
if (add_tcp_relay(tcp_c, ip_port, relay_pk) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add a TCP relay tied to a connection.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_tcp_number_relay_connection(TCP_Connections *tcp_c, int connections_number, unsigned int tcp_connections_number)
|
||||
{
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
|
||||
|
||||
if (!con_to)
|
||||
return -1;
|
||||
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
if (add_tcp_connection_to_conn(con_to, tcp_connections_number) == -1)
|
||||
return -1;
|
||||
|
||||
++tcp_con->lock_count;
|
||||
|
||||
if (tcp_con->status == TCP_CONN_CONNECTED) {
|
||||
send_tcp_relay_routing_request(tcp_c, tcp_connections_number, con_to->public_key);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add a TCP relay tied to a connection.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, IP_Port ip_port, const uint8_t *relay_pk)
|
||||
{
|
||||
TCP_Connection_to *con_to = get_connection(tcp_c, connections_number);
|
||||
|
||||
if (!con_to)
|
||||
return -1;
|
||||
|
||||
int tcp_connections_number = find_tcp_connection_relay(tcp_c, relay_pk);
|
||||
|
||||
if (tcp_connections_number != -1) {
|
||||
return add_tcp_number_relay_connection(tcp_c, connections_number, tcp_connections_number);
|
||||
} else {
|
||||
int tcp_connections_number = add_tcp_relay(tcp_c, ip_port, relay_pk);
|
||||
|
||||
if (add_tcp_connection_to_conn(con_to, tcp_connections_number) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, tcp_connections_number);
|
||||
|
||||
if (!tcp_con)
|
||||
return -1;
|
||||
|
||||
++tcp_con->lock_count;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy a maximum of max_num TCP relays we are connected to to tcp_relays.
|
||||
* NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6.
|
||||
*
|
||||
* return number of relays copied to tcp_relays on success.
|
||||
* return 0 on failure.
|
||||
*/
|
||||
unsigned int tcp_copy_connected_relays(TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num)
|
||||
{
|
||||
unsigned int i, copied = 0;
|
||||
|
||||
for (i = 0; (i < tcp_c->tcp_connections_length) && (copied < max_num); ++i) {
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
|
||||
|
||||
if (!tcp_con) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tcp_con->status == TCP_CONN_CONNECTED) {
|
||||
memcpy(tcp_relays[copied].public_key, tcp_con->connection->public_key, crypto_box_PUBLICKEYBYTES);
|
||||
tcp_relays[copied].ip_port = tcp_con->connection->ip_port;
|
||||
|
||||
if (tcp_relays[copied].ip_port.ip.family == AF_INET) {
|
||||
tcp_relays[copied].ip_port.ip.family = TCP_INET;
|
||||
} else if (tcp_relays[copied].ip_port.ip.family == AF_INET6) {
|
||||
tcp_relays[copied].ip_port.ip.family = TCP_INET6;
|
||||
}
|
||||
|
||||
++copied;
|
||||
}
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
TCP_Connections *new_tcp_connections(DHT *dht)
|
||||
{
|
||||
if (dht == NULL)
|
||||
return NULL;
|
||||
|
||||
TCP_Connections *temp = calloc(1, sizeof(TCP_Connections));
|
||||
|
||||
if (temp == NULL)
|
||||
return NULL;
|
||||
|
||||
temp->dht = dht;
|
||||
return temp;
|
||||
}
|
||||
|
||||
static void do_tcp_conns(TCP_Connections *tcp_c)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
|
||||
|
||||
if (tcp_con) {
|
||||
do_TCP_connection(tcp_con->connection);
|
||||
|
||||
/* callbacks can change TCP connection address. */
|
||||
tcp_con = get_tcp_connection(tcp_c, i);
|
||||
|
||||
if (tcp_con->connection->status == TCP_CLIENT_DISCONNECTED) {
|
||||
kill_tcp_relay_connection(tcp_c, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tcp_con->status == TCP_CONN_VALID && tcp_con->connection->status == TCP_CLIENT_CONFIRMED) {
|
||||
tcp_relay_on_online(tcp_c, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void kill_nonused_tcp(TCP_Connections *tcp_c)
|
||||
{
|
||||
unsigned int i, num_online = 0;
|
||||
|
||||
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
|
||||
TCP_con *tcp_con = get_tcp_connection(tcp_c, i);
|
||||
|
||||
if (tcp_con) {
|
||||
if (tcp_con->status == TCP_CONN_CONNECTED) {
|
||||
if (!tcp_con->lock_count && num_online >= MAX_FRIEND_TCP_CONNECTIONS) {
|
||||
kill_tcp_relay_connection(tcp_c, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
++num_online;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_tcp_connections(TCP_Connections *tcp_c)
|
||||
{
|
||||
//TODO reconnect to TCP relays if disconnects happen.
|
||||
do_tcp_conns(tcp_c);
|
||||
kill_nonused_tcp(tcp_c);
|
||||
}
|
||||
|
||||
void kill_tcp_connections(TCP_Connections *tcp_c)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < tcp_c->tcp_connections_length; ++i) {
|
||||
kill_TCP_connection(tcp_c->tcp_connections[i].connection);
|
||||
}
|
||||
|
||||
free(tcp_c->tcp_connections);
|
||||
free(tcp_c->connections);
|
||||
free(tcp_c);
|
||||
}
|
||||
|
||||
|
180
toxcore/TCP_connection.h
Normal file
180
toxcore/TCP_connection.h
Normal file
|
@ -0,0 +1,180 @@
|
|||
/* TCP_connection.h
|
||||
*
|
||||
* Handles TCP relay connections between two Tox clients.
|
||||
*
|
||||
* Copyright (C) 2015 Tox project All Rights Reserved.
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TCP_CONNECTION_H
|
||||
#define TCP_CONNECTION_H
|
||||
|
||||
#include "TCP_client.h"
|
||||
|
||||
#define TCP_CONN_NONE 0
|
||||
#define TCP_CONN_VALID 1
|
||||
#define TCP_CONN_CONNECTED 2
|
||||
|
||||
#define TCP_CONNECTIONS_STATUS_NONE 0
|
||||
#define TCP_CONNECTIONS_STATUS_REGISTERED 1
|
||||
#define TCP_CONNECTIONS_STATUS_ONLINE 2
|
||||
|
||||
#define MAX_FRIEND_TCP_CONNECTIONS 4
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t status;
|
||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES]; /* The dht public key of the peer */
|
||||
|
||||
struct {
|
||||
uint32_t tcp_connection;
|
||||
unsigned int status;
|
||||
unsigned int connection_id;
|
||||
} connections[MAX_FRIEND_TCP_CONNECTIONS];
|
||||
|
||||
int id; /* id used in callbacks. */
|
||||
} TCP_Connection_to;
|
||||
|
||||
typedef struct {
|
||||
uint8_t status;
|
||||
TCP_Client_Connection *connection;
|
||||
uint32_t lock_count;
|
||||
} TCP_con;
|
||||
|
||||
typedef struct {
|
||||
DHT *dht;
|
||||
|
||||
TCP_Connection_to *connections;
|
||||
uint32_t connections_length; /* Length of connections array. */
|
||||
|
||||
TCP_con *tcp_connections;
|
||||
uint32_t tcp_connections_length; /* Length of tcp_connections array. */
|
||||
|
||||
int (*tcp_data_callback)(void *object, int id, const uint8_t *data, uint16_t length);
|
||||
void *tcp_data_callback_object;
|
||||
|
||||
int (*tcp_oob_callback)(void *object, const uint8_t *public_key, unsigned int tcp_connections_number,
|
||||
const uint8_t *data, uint16_t length);
|
||||
void *tcp_oob_callback_object;
|
||||
|
||||
int (*tcp_onion_callback)(void *object, const uint8_t *data, uint16_t length);
|
||||
void *tcp_onion_callback_object;
|
||||
|
||||
TCP_Proxy_Info proxy_info;
|
||||
} TCP_Connections;
|
||||
|
||||
/* Send a packet to the TCP connection.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return 0 on success.
|
||||
*/
|
||||
int send_packet_tcp_connection(TCP_Connections *tcp_c, int connections_number, const uint8_t *packet, uint16_t length);
|
||||
|
||||
/* Return a random TCP connection number for use in send_tcp_onion_request.
|
||||
*
|
||||
* TODO: This number is just the index of an array that the elements can
|
||||
* change without warning.
|
||||
*
|
||||
* return TCP connection number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int get_random_tcp_conn_number(TCP_Connections *tcp_c);
|
||||
|
||||
/* Send an onion packet via the TCP relay corresponding to tcp_connections_number.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int tcp_send_onion_request(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *data,
|
||||
uint16_t length);
|
||||
|
||||
/* Send an oob packet via the TCP relay corresponding to tcp_connections_number.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int tcp_send_oob_packet(TCP_Connections *tcp_c, unsigned int tcp_connections_number, const uint8_t *public_key,
|
||||
const uint8_t *packet, uint16_t length);
|
||||
|
||||
/* Set the callback for TCP data packets.
|
||||
*/
|
||||
void set_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_data_callback)(void *object, int id,
|
||||
const uint8_t *data, uint16_t length), void *object);
|
||||
|
||||
/* Set the callback for TCP onion packets.
|
||||
*/
|
||||
void set_onion_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_onion_callback)(void *object,
|
||||
const uint8_t *data, uint16_t length), void *object);
|
||||
|
||||
/* Set the callback for TCP oob data packets.
|
||||
*/
|
||||
void set_oob_packet_tcp_connection_callback(TCP_Connections *tcp_c, int (*tcp_oob_callback)(void *object,
|
||||
const uint8_t *public_key, unsigned int tcp_connections_number, const uint8_t *data, uint16_t length), void *object);
|
||||
|
||||
/* Create a new TCP connection to public_key.
|
||||
*
|
||||
* id is the id in the callbacks for that connection.
|
||||
*
|
||||
* return connections_number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int new_tcp_connection_to(TCP_Connections *tcp_c, const uint8_t *public_key, int id);
|
||||
|
||||
/* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int kill_tcp_connection_to(TCP_Connections *tcp_c, int connections_number);
|
||||
|
||||
/* Add a TCP relay tied to a connection.
|
||||
*
|
||||
* NOTE: This can only be used during the tcp_oob_callback.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_tcp_number_relay_connection(TCP_Connections *tcp_c, int connections_number,
|
||||
unsigned int tcp_connections_number);
|
||||
|
||||
/* Add a TCP relay tied to a connection.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_tcp_relay_connection(TCP_Connections *tcp_c, int connections_number, IP_Port ip_port, const uint8_t *relay_pk);
|
||||
|
||||
/* Add a TCP relay to the instance.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int add_tcp_relay_global(TCP_Connections *tcp_c, IP_Port ip_port, const uint8_t *relay_pk);
|
||||
|
||||
/* Copy a maximum of max_num TCP relays we are connected to to tcp_relays.
|
||||
* NOTE that the family of the copied ip ports will be set to TCP_INET or TCP_INET6.
|
||||
*
|
||||
* return number of relays copied to tcp_relays on success.
|
||||
* return 0 on failure.
|
||||
*/
|
||||
unsigned int tcp_copy_connected_relays(TCP_Connections *tcp_c, Node_format *tcp_relays, uint16_t max_num);
|
||||
|
||||
TCP_Connections *new_tcp_connections(DHT *dht);
|
||||
void do_tcp_connections(TCP_Connections *tcp_c);
|
||||
void kill_tcp_connections(TCP_Connections *tcp_c);
|
||||
|
||||
#endif
|
||||
|
|
@ -925,6 +925,10 @@ static sock_t new_listening_TCP_socket(int family, uint16_t port)
|
|||
ok = set_socket_dualstack(sock);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
ok = set_socket_reuseaddr(sock);
|
||||
}
|
||||
|
||||
ok = ok && bind_to_port(sock, family, port) && (listen(sock, TCP_MAX_BACKLOG) == 0);
|
||||
|
||||
if (!ok) {
|
||||
|
@ -1187,7 +1191,7 @@ static void do_TCP_epoll(TCP_Server *TCP_server)
|
|||
|
||||
for (n = 0; n < nfds; ++n) {
|
||||
sock_t sock = events[n].data.u64 & 0xFFFFFFFF;
|
||||
int status = (events[n].data.u64 >> 32) & 0xFFFF, index = (events[n].data.u64 >> 48);
|
||||
int status = (events[n].data.u64 >> 32) & 0xFF, index = (events[n].data.u64 >> 40);
|
||||
|
||||
if ((events[n].events & EPOLLERR) || (events[n].events & EPOLLHUP) || (events[n].events & EPOLLRDHUP)) {
|
||||
switch (status) {
|
||||
|
@ -1241,7 +1245,7 @@ static void do_TCP_epoll(TCP_Server *TCP_server)
|
|||
|
||||
struct epoll_event ev = {
|
||||
.events = EPOLLIN | EPOLLET | EPOLLRDHUP,
|
||||
.data.u64 = sock_new | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 48)
|
||||
.data.u64 = sock_new | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40)
|
||||
};
|
||||
|
||||
if (epoll_ctl(TCP_server->efd, EPOLL_CTL_ADD, sock_new, &ev) == -1) {
|
||||
|
@ -1258,7 +1262,7 @@ static void do_TCP_epoll(TCP_Server *TCP_server)
|
|||
|
||||
if ((index_new = do_incoming(TCP_server, index)) != -1) {
|
||||
events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP;
|
||||
events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 48);
|
||||
events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_UNCONFIRMED << 32) | ((uint64_t)index_new << 40);
|
||||
|
||||
if (epoll_ctl(TCP_server->efd, EPOLL_CTL_MOD, sock, &events[n]) == -1) {
|
||||
kill_TCP_connection(&TCP_server->unconfirmed_connection_queue[index_new]);
|
||||
|
@ -1274,7 +1278,7 @@ static void do_TCP_epoll(TCP_Server *TCP_server)
|
|||
|
||||
if ((index_new = do_unconfirmed(TCP_server, index)) != -1) {
|
||||
events[n].events = EPOLLIN | EPOLLET | EPOLLRDHUP;
|
||||
events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 48);
|
||||
events[n].data.u64 = sock | ((uint64_t)TCP_SOCKET_CONFIRMED << 32) | ((uint64_t)index_new << 40);
|
||||
|
||||
if (epoll_ctl(TCP_server->efd, EPOLL_CTL_MOD, sock, &events[n]) == -1) {
|
||||
//remove from confirmed connections
|
||||
|
|
|
@ -194,19 +194,10 @@ void new_symmetric_key(uint8_t *key)
|
|||
randombytes(key, crypto_box_KEYBYTES);
|
||||
}
|
||||
|
||||
static uint8_t base_nonce[crypto_box_NONCEBYTES];
|
||||
static uint8_t nonce_set = 0;
|
||||
|
||||
/* Gives a nonce guaranteed to be different from previous ones.*/
|
||||
void new_nonce(uint8_t *nonce)
|
||||
{
|
||||
if (nonce_set == 0) {
|
||||
random_nonce(base_nonce);
|
||||
nonce_set = 1;
|
||||
}
|
||||
|
||||
increment_nonce(base_nonce);
|
||||
memcpy(nonce, base_nonce, crypto_box_NONCEBYTES);
|
||||
random_nonce(nonce);
|
||||
}
|
||||
|
||||
/* Create a request to peer.
|
||||
|
|
|
@ -122,7 +122,7 @@ void new_nonce(uint8_t *nonce);
|
|||
|
||||
#define CRYPTO_PACKET_FRIEND_REQ 32 /* Friend request crypto packet ID. */
|
||||
#define CRYPTO_PACKET_HARDENING 48 /* Hardening crypto packet ID. */
|
||||
#define CRYPTO_PACKET_FAKEID 156
|
||||
#define CRYPTO_PACKET_DHTPK 156
|
||||
#define CRYPTO_PACKET_NAT_PING 254 /* NAT ping crypto packet ID. */
|
||||
|
||||
/* Create a request to peer.
|
||||
|
|
|
@ -224,23 +224,32 @@ static int handle_status(void *object, int number, uint8_t status)
|
|||
if (!friend_con)
|
||||
return -1;
|
||||
|
||||
_Bool call_cb = 0;
|
||||
|
||||
if (status) { /* Went online. */
|
||||
call_cb = 1;
|
||||
friend_con->status = FRIENDCONN_STATUS_CONNECTED;
|
||||
friend_con->ping_lastrecv = unix_time();
|
||||
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
|
||||
} else { /* Went offline. */
|
||||
if (friend_con->status != FRIENDCONN_STATUS_CONNECTING) {
|
||||
call_cb = 1;
|
||||
friend_con->dht_ping_lastrecv = unix_time();
|
||||
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
|
||||
}
|
||||
|
||||
friend_con->status = FRIENDCONN_STATUS_CONNECTING;
|
||||
friend_con->crypt_connection_id = -1;
|
||||
friend_con->dht_ping_lastrecv = unix_time();
|
||||
onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
if (call_cb) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
|
||||
if (friend_con->callbacks[i].status_callback)
|
||||
friend_con->callbacks[i].status_callback(friend_con->callbacks[i].status_callback_object,
|
||||
friend_con->callbacks[i].status_callback_id, status);
|
||||
for (i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
|
||||
if (friend_con->callbacks[i].status_callback)
|
||||
friend_con->callbacks[i].status_callback(friend_con->callbacks[i].status_callback_object,
|
||||
friend_con->callbacks[i].status_callback_id, status);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -42,7 +42,7 @@ uint32_t get_nospam(const Friend_Requests *fr)
|
|||
|
||||
|
||||
/* Set the function that will be executed when a friend request is received. */
|
||||
void callback_friendrequest(Friend_Requests *fr, void (*function)(void *, const uint8_t *, const uint8_t *, uint16_t,
|
||||
void callback_friendrequest(Friend_Requests *fr, void (*function)(void *, const uint8_t *, const uint8_t *, size_t,
|
||||
void *), void *object, void *userdata)
|
||||
{
|
||||
fr->handle_friendrequest = function;
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
typedef struct {
|
||||
uint32_t nospam;
|
||||
void (*handle_friendrequest)(void *, const uint8_t *, const uint8_t *, uint16_t, void *);
|
||||
void (*handle_friendrequest)(void *, const uint8_t *, const uint8_t *, size_t, void *);
|
||||
uint8_t handle_friendrequest_isset;
|
||||
void *handle_friendrequest_object;
|
||||
void *handle_friendrequest_userdata;
|
||||
|
@ -59,9 +59,9 @@ uint32_t get_nospam(const Friend_Requests *fr);
|
|||
int remove_request_received(Friend_Requests *fr, const uint8_t *real_pk);
|
||||
|
||||
/* Set the function that will be executed when a friend request for us is received.
|
||||
* Function format is function(uint8_t * public_key, uint8_t * data, uint16_t length, void * userdata)
|
||||
* Function format is function(uint8_t * public_key, uint8_t * data, size_t length, void * userdata)
|
||||
*/
|
||||
void callback_friendrequest(Friend_Requests *fr, void (*function)(void *, const uint8_t *, const uint8_t *, uint16_t,
|
||||
void callback_friendrequest(Friend_Requests *fr, void (*function)(void *, const uint8_t *, const uint8_t *, size_t,
|
||||
void *), void *object, void *userdata);
|
||||
|
||||
/* Set the function used to check if a friend request should be displayed to the user or not.
|
||||
|
|
|
@ -518,7 +518,7 @@ static int delpeer(Group_Chats *g_c, int groupnumber, int peer_index)
|
|||
|
||||
static int setnick(Group_Chats *g_c, int groupnumber, int peer_index, const uint8_t *nick, uint16_t nick_len)
|
||||
{
|
||||
if (nick_len > MAX_NAME_LENGTH || nick_len == 0)
|
||||
if (nick_len > MAX_NAME_LENGTH)
|
||||
return -1;
|
||||
|
||||
Group_c *g = get_group_c(g_c, groupnumber);
|
||||
|
@ -528,10 +528,12 @@ static int setnick(Group_Chats *g_c, int groupnumber, int peer_index, const uint
|
|||
|
||||
/* same name as already stored? */
|
||||
if (g->group[peer_index].nick_len == nick_len)
|
||||
if (!memcmp(g->group[peer_index].nick, nick, nick_len))
|
||||
if (nick_len == 0 || !memcmp(g->group[peer_index].nick, nick, nick_len))
|
||||
return 0;
|
||||
|
||||
memcpy(g->group[peer_index].nick, nick, nick_len);
|
||||
if (nick_len)
|
||||
memcpy(g->group[peer_index].nick, nick, nick_len);
|
||||
|
||||
g->group[peer_index].nick_len = nick_len;
|
||||
|
||||
if (g_c->peer_namelistchange)
|
||||
|
@ -1234,7 +1236,7 @@ int group_title_get(const Group_Chats *g_c, int groupnumber, uint8_t *title, uin
|
|||
return max_length;
|
||||
}
|
||||
|
||||
static void handle_friend_invite_packet(Messenger *m, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
static void handle_friend_invite_packet(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
Group_Chats *g_c = m->group_chat_object;
|
||||
|
||||
|
|
|
@ -722,7 +722,7 @@ static int handle_request_packet(Packets_Array *send_array, const uint8_t *data,
|
|||
|
||||
if (n == data[0]) {
|
||||
if (send_array->buffer[num]) {
|
||||
send_array->buffer[num]->time = 0;
|
||||
send_array->buffer[num]->sent = 0;
|
||||
}
|
||||
|
||||
++data;
|
||||
|
@ -803,12 +803,49 @@ static int send_data_packet_helper(Net_Crypto *c, int crypt_connection_id, uint3
|
|||
uint8_t packet[sizeof(uint32_t) + sizeof(uint32_t) + padding_length + length];
|
||||
memcpy(packet, &buffer_start, sizeof(uint32_t));
|
||||
memcpy(packet + sizeof(uint32_t), &num, sizeof(uint32_t));
|
||||
memset(packet + (sizeof(uint32_t) * 2), 0, padding_length);
|
||||
memset(packet + (sizeof(uint32_t) * 2), PACKET_ID_PADDING, padding_length);
|
||||
memcpy(packet + (sizeof(uint32_t) * 2) + padding_length, data, length);
|
||||
|
||||
return send_data_packet(c, crypt_connection_id, packet, sizeof(packet));
|
||||
}
|
||||
|
||||
static int reset_max_speed_reached(Net_Crypto *c, int crypt_connection_id)
|
||||
{
|
||||
Crypto_Connection *conn = get_crypto_connection(c, crypt_connection_id);
|
||||
|
||||
if (conn == 0)
|
||||
return -1;
|
||||
|
||||
/* If last packet send failed, try to send packet again.
|
||||
If sending it fails we won't be able to send the new packet. */
|
||||
if (conn->maximum_speed_reached) {
|
||||
Packet_Data *dt = NULL;
|
||||
uint32_t packet_num = conn->send_array.buffer_end - 1;
|
||||
int ret = get_data_pointer(&conn->send_array, &dt, packet_num);
|
||||
|
||||
uint8_t send_failed = 0;
|
||||
|
||||
if (ret == 1) {
|
||||
if (!dt->sent) {
|
||||
if (send_data_packet_helper(c, crypt_connection_id, conn->recv_array.buffer_start, packet_num, dt->data,
|
||||
dt->length) != 0) {
|
||||
send_failed = 1;
|
||||
} else {
|
||||
dt->sent = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!send_failed) {
|
||||
conn->maximum_speed_reached = 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return -1 if data could not be put in packet queue.
|
||||
* return positive packet number if data was put into the queue.
|
||||
*/
|
||||
|
@ -823,39 +860,16 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons
|
|||
if (conn == 0)
|
||||
return -1;
|
||||
|
||||
uint64_t temp_time = current_time_monotonic();
|
||||
|
||||
/* If last packet send failed, try to send packet again.
|
||||
If sending it fails we won't be able to send the new packet. */
|
||||
if (conn->maximum_speed_reached) {
|
||||
Packet_Data *dt = NULL;
|
||||
uint32_t packet_num = conn->send_array.buffer_end - 1;
|
||||
int ret = get_data_pointer(&conn->send_array, &dt, packet_num);
|
||||
reset_max_speed_reached(c, crypt_connection_id);
|
||||
|
||||
uint8_t send_failed = 0;
|
||||
|
||||
if (ret == 1) {
|
||||
if (!dt->time) {
|
||||
if (send_data_packet_helper(c, crypt_connection_id, conn->recv_array.buffer_start, packet_num, dt->data,
|
||||
dt->length) != 0) {
|
||||
if (congestion_control) {
|
||||
return -1;
|
||||
} else {
|
||||
send_failed = 1;
|
||||
}
|
||||
} else {
|
||||
dt->time = temp_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!send_failed) {
|
||||
conn->maximum_speed_reached = 0;
|
||||
}
|
||||
if (conn->maximum_speed_reached && congestion_control) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Packet_Data dt;
|
||||
dt.time = 0;
|
||||
dt.sent = 0;
|
||||
dt.length = length;
|
||||
memcpy(dt.data, data, length);
|
||||
pthread_mutex_lock(&conn->mutex);
|
||||
|
@ -873,7 +887,7 @@ static int64_t send_lossless_packet(Net_Crypto *c, int crypt_connection_id, cons
|
|||
Packet_Data *dt1 = NULL;
|
||||
|
||||
if (get_data_pointer(&conn->send_array, &dt1, packet_num) == 1)
|
||||
dt1->time = temp_time;
|
||||
dt1->sent = 1;
|
||||
} else {
|
||||
conn->maximum_speed_reached = 1;
|
||||
LOGGER_ERROR("send_data_packet failed\n");
|
||||
|
@ -983,15 +997,15 @@ static int send_requested_packets(Net_Crypto *c, int crypt_connection_id, uint16
|
|||
continue;
|
||||
}
|
||||
|
||||
if (dt->time != 0) {
|
||||
if (dt->sent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dt->time = current_time_monotonic();
|
||||
|
||||
if (send_data_packet_helper(c, crypt_connection_id, conn->recv_array.buffer_start, packet_num, dt->data,
|
||||
dt->length) == 0)
|
||||
dt->length) == 0) {
|
||||
dt->sent = 1;
|
||||
++num_sent;
|
||||
}
|
||||
|
||||
if (num_sent >= max_num)
|
||||
break;
|
||||
|
@ -1156,7 +1170,7 @@ static int handle_data_packet_helper(const Net_Crypto *c, int crypt_connection_i
|
|||
uint8_t *real_data = data + (sizeof(uint32_t) * 2);
|
||||
uint16_t real_length = len - (sizeof(uint32_t) * 2);
|
||||
|
||||
while (real_data[0] == 0) { /* Remove Padding */
|
||||
while (real_data[0] == PACKET_ID_PADDING) { /* Remove Padding */
|
||||
++real_data;
|
||||
--real_length;
|
||||
|
||||
|
@ -1189,7 +1203,6 @@ static int handle_data_packet_helper(const Net_Crypto *c, int crypt_connection_i
|
|||
set_buffer_end(&conn->recv_array, num);
|
||||
} else if (real_data[0] >= CRYPTO_RESERVED_PACKETS && real_data[0] < PACKET_ID_LOSSY_RANGE_START) {
|
||||
Packet_Data dt;
|
||||
dt.time = current_time_monotonic();
|
||||
dt.length = real_length;
|
||||
memcpy(dt.data, real_data, real_length);
|
||||
|
||||
|
@ -2529,7 +2542,7 @@ static void send_crypto_packets(Net_Crypto *c)
|
|||
if (conn->last_packets_left_set == 0) {
|
||||
conn->last_packets_left_set = temp_time;
|
||||
conn->packets_left = CRYPTO_MIN_QUEUE_LENGTH;
|
||||
} else if (((1000.0 / conn->packet_send_rate) + conn->last_packets_left_set) < temp_time) {
|
||||
} else if (((uint64_t)((1000.0 / conn->packet_send_rate) + 0.5) + conn->last_packets_left_set) < temp_time) {
|
||||
uint32_t num_packets = conn->packet_send_rate * ((double)(temp_time - conn->last_packets_left_set) / 1000.0) + 0.5;
|
||||
|
||||
if (conn->packets_left > num_packets * 4 + CRYPTO_MIN_QUEUE_LENGTH) {
|
||||
|
@ -2575,6 +2588,13 @@ static void send_crypto_packets(Net_Crypto *c)
|
|||
}
|
||||
}
|
||||
|
||||
/* Return 1 if max speed was reached for this connection (no more data can be physically through the pipe).
|
||||
* Return 0 if it wasn't reached.
|
||||
*/
|
||||
_Bool max_speed_reached(Net_Crypto *c, int crypt_connection_id)
|
||||
{
|
||||
return reset_max_speed_reached(c, crypt_connection_id) != 0;
|
||||
}
|
||||
|
||||
/* returns the number of packet slots left in the sendbuffer.
|
||||
* return 0 if failure.
|
||||
|
@ -2586,7 +2606,13 @@ uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connecti
|
|||
if (conn == 0)
|
||||
return 0;
|
||||
|
||||
return conn->packets_left;
|
||||
uint32_t max_packets = CRYPTO_PACKET_BUFFER_SIZE - num_packets_array(&conn->send_array);
|
||||
|
||||
if (conn->packets_left < max_packets) {
|
||||
return conn->packets_left;
|
||||
} else {
|
||||
return max_packets;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sends a lossless cryptopacket.
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
#define MAX_NUM_SENDPACKET_TRIES 8
|
||||
|
||||
/* The timeout of no received UDP packets before the direct UDP connection is considered dead. */
|
||||
#define UDP_DIRECT_TIMEOUT (MAX_NUM_SENDPACKET_TRIES * CRYPTO_SEND_PACKET_INTERVAL)
|
||||
#define UDP_DIRECT_TIMEOUT ((MAX_NUM_SENDPACKET_TRIES * CRYPTO_SEND_PACKET_INTERVAL) / 2)
|
||||
|
||||
#define PACKET_ID_PADDING 0 /* Denotes padding */
|
||||
#define PACKET_ID_REQUEST 1 /* Used to request unreceived packets */
|
||||
|
@ -89,7 +89,7 @@
|
|||
#define CONGESTION_QUEUE_ARRAY_SIZE 24
|
||||
|
||||
typedef struct {
|
||||
uint64_t time;
|
||||
_Bool sent;
|
||||
uint16_t length;
|
||||
uint8_t data[MAX_CRYPTO_DATA_SIZE];
|
||||
} Packet_Data;
|
||||
|
@ -317,6 +317,11 @@ int nc_dht_pk_callback(Net_Crypto *c, int crypt_connection_id, void (*function)(
|
|||
*/
|
||||
uint32_t crypto_num_free_sendqueue_slots(const Net_Crypto *c, int crypt_connection_id);
|
||||
|
||||
/* Return 1 if max speed was reached for this connection (no more data can be physically through the pipe).
|
||||
* Return 0 if it wasn't reached.
|
||||
*/
|
||||
_Bool max_speed_reached(Net_Crypto *c, int crypt_connection_id);
|
||||
|
||||
/* Sends a lossless cryptopacket.
|
||||
*
|
||||
* return -1 if data could not be put in packet queue.
|
||||
|
|
|
@ -172,6 +172,17 @@ int set_socket_nosigpipe(sock_t sock)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Enable SO_REUSEADDR on socket.
|
||||
*
|
||||
* return 1 on success
|
||||
* return 0 on failure
|
||||
*/
|
||||
int set_socket_reuseaddr(sock_t sock)
|
||||
{
|
||||
int set = 1;
|
||||
return (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set)) == 0);
|
||||
}
|
||||
|
||||
/* Set socket to dual (IPv4 + IPv6 socket)
|
||||
*
|
||||
* return 1 on success
|
||||
|
@ -466,6 +477,14 @@ static void at_shutdown(void)
|
|||
}
|
||||
*/
|
||||
|
||||
/* Initialize networking.
|
||||
* Added for reverse compatibility with old new_networking calls.
|
||||
*/
|
||||
Networking_Core *new_networking(IP ip, uint16_t port)
|
||||
{
|
||||
return new_networking_ex(ip, port, port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM), 0);
|
||||
}
|
||||
|
||||
/* Initialize networking.
|
||||
* Bind to ip and port.
|
||||
* ip must be in network order EX: 127.0.0.1 = (7F000001).
|
||||
|
@ -473,9 +492,31 @@ static void at_shutdown(void)
|
|||
*
|
||||
* return Networking_Core object if no problems
|
||||
* return NULL if there are problems.
|
||||
*
|
||||
* If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other.
|
||||
*/
|
||||
Networking_Core *new_networking(IP ip, uint16_t port)
|
||||
Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to, unsigned int *error)
|
||||
{
|
||||
/* If both from and to are 0, use default port range
|
||||
* If one is 0 and the other is non-0, use the non-0 value as only port
|
||||
* If from > to, swap
|
||||
*/
|
||||
if (port_from == 0 && port_to == 0) {
|
||||
port_from = TOX_PORTRANGE_FROM;
|
||||
port_to = TOX_PORTRANGE_TO;
|
||||
} else if (port_from == 0 && port_to != 0) {
|
||||
port_from = port_to;
|
||||
} else if (port_from != 0 && port_to == 0) {
|
||||
port_to = port_from;
|
||||
} else if (port_from > port_to) {
|
||||
uint16_t temp = port_from;
|
||||
port_from = port_to;
|
||||
port_to = temp;
|
||||
}
|
||||
|
||||
if (error)
|
||||
*error = 2;
|
||||
|
||||
/* maybe check for invalid IPs like 224+.x.y.z? if there is any IP set ever */
|
||||
if (ip.family != AF_INET && ip.family != AF_INET6) {
|
||||
#ifdef DEBUG
|
||||
|
@ -505,6 +546,10 @@ Networking_Core *new_networking(IP ip, uint16_t port)
|
|||
fprintf(stderr, "Failed to get a socket?! %u, %s\n", errno, strerror(errno));
|
||||
#endif
|
||||
free(temp);
|
||||
|
||||
if (error)
|
||||
*error = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -521,12 +566,20 @@ Networking_Core *new_networking(IP ip, uint16_t port)
|
|||
/* iOS UDP sockets are weird and apparently can SIGPIPE */
|
||||
if (!set_socket_nosigpipe(temp->sock)) {
|
||||
kill_networking(temp);
|
||||
|
||||
if (error)
|
||||
*error = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set socket nonblocking. */
|
||||
if (!set_socket_nonblock(temp->sock)) {
|
||||
kill_networking(temp);
|
||||
|
||||
if (error)
|
||||
*error = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -600,11 +653,11 @@ Networking_Core *new_networking(IP ip, uint16_t port)
|
|||
* some clients might not test return of tox_new(), blindly assuming that
|
||||
* it worked ok (which it did previously without a successful bind)
|
||||
*/
|
||||
uint16_t port_to_try = port;
|
||||
uint16_t port_to_try = port_from;
|
||||
*portptr = htons(port_to_try);
|
||||
int tries;
|
||||
|
||||
for (tries = TOX_PORTRANGE_FROM; tries <= TOX_PORTRANGE_TO; tries++) {
|
||||
for (tries = port_from; tries <= port_to; tries++) {
|
||||
int res = bind(temp->sock, (struct sockaddr *)&addr, addrsize);
|
||||
|
||||
if (!res) {
|
||||
|
@ -618,22 +671,28 @@ Networking_Core *new_networking(IP ip, uint16_t port)
|
|||
if (tries > 0)
|
||||
errno = 0;
|
||||
|
||||
if (error)
|
||||
*error = 0;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
port_to_try++;
|
||||
|
||||
if (port_to_try > TOX_PORTRANGE_TO)
|
||||
port_to_try = TOX_PORTRANGE_FROM;
|
||||
if (port_to_try > port_to)
|
||||
port_to_try = port_from;
|
||||
|
||||
*portptr = htons(port_to_try);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Failed to bind socket: %u, %s (IP/Port: %s:%u\n", errno,
|
||||
strerror(errno), ip_ntoa(&ip), port);
|
||||
#endif
|
||||
LOGGER_ERROR("Failed to bind socket: %u, %s IP: %s port_from: %u port_to: %u", errno, strerror(errno),
|
||||
ip_ntoa(&ip), port_from, port_to);
|
||||
|
||||
kill_networking(temp);
|
||||
|
||||
if (error)
|
||||
*error = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -334,6 +334,13 @@ int set_socket_nonblock(sock_t sock);
|
|||
*/
|
||||
int set_socket_nosigpipe(sock_t sock);
|
||||
|
||||
/* Enable SO_REUSEADDR on socket.
|
||||
*
|
||||
* return 1 on success
|
||||
* return 0 on failure
|
||||
*/
|
||||
int set_socket_reuseaddr(sock_t sock);
|
||||
|
||||
/* Set socket to dual (IPv4 + IPv6 socket)
|
||||
*
|
||||
* return 1 on success
|
||||
|
@ -362,8 +369,11 @@ void networking_poll(Networking_Core *net);
|
|||
*
|
||||
* return Networking_Core object if no problems
|
||||
* return NULL if there are problems.
|
||||
*
|
||||
* If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other.
|
||||
*/
|
||||
Networking_Core *new_networking(IP ip, uint16_t port);
|
||||
Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to, unsigned int *error);
|
||||
|
||||
/* Function to cleanup networking stuff (doesn't do much right now). */
|
||||
void kill_networking(Networking_Core *net);
|
||||
|
|
|
@ -858,7 +858,7 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin
|
|||
|
||||
uint8_t packet[MAX_CRYPTO_REQUEST_SIZE];
|
||||
len = create_request(onion_c->dht->self_public_key, onion_c->dht->self_secret_key, packet,
|
||||
onion_c->friends_list[friend_num].dht_public_key, temp, sizeof(temp), CRYPTO_PACKET_FAKEID);
|
||||
onion_c->friends_list[friend_num].dht_public_key, temp, sizeof(temp), CRYPTO_PACKET_DHTPK);
|
||||
|
||||
if (len == -1)
|
||||
return -1;
|
||||
|
@ -1351,78 +1351,10 @@ static void do_announce(Onion_Client *onion_c)
|
|||
}
|
||||
}
|
||||
|
||||
void do_onion_client(Onion_Client *onion_c)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (onion_c->last_run == unix_time())
|
||||
return;
|
||||
|
||||
populate_path_nodes(onion_c);
|
||||
|
||||
do_announce(onion_c);
|
||||
|
||||
if (onion_isconnected(onion_c)) {
|
||||
for (i = 0; i < onion_c->num_friends; ++i) {
|
||||
do_friend(onion_c, i);
|
||||
}
|
||||
} else {
|
||||
populate_path_nodes_tcp(onion_c);
|
||||
}
|
||||
|
||||
onion_c->last_run = unix_time();
|
||||
}
|
||||
|
||||
Onion_Client *new_onion_client(Net_Crypto *c)
|
||||
{
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
Onion_Client *onion_c = calloc(1, sizeof(Onion_Client));
|
||||
|
||||
if (onion_c == NULL)
|
||||
return NULL;
|
||||
|
||||
if (ping_array_init(&onion_c->announce_ping_array, ANNOUNCE_ARRAY_SIZE, ANNOUNCE_TIMEOUT) != 0) {
|
||||
free(onion_c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
onion_c->dht = c->dht;
|
||||
onion_c->net = c->dht->net;
|
||||
onion_c->c = c;
|
||||
new_symmetric_key(onion_c->secret_symmetric_key);
|
||||
crypto_box_keypair(onion_c->temp_public_key, onion_c->temp_secret_key);
|
||||
networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_announce_response, onion_c);
|
||||
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_data_response, onion_c);
|
||||
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, &handle_dhtpk_announce, onion_c);
|
||||
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_FAKEID, &handle_dht_dhtpk, onion_c);
|
||||
tcp_onion_response_handler(onion_c->c, &handle_tcp_onion, onion_c);
|
||||
|
||||
return onion_c;
|
||||
}
|
||||
|
||||
void kill_onion_client(Onion_Client *onion_c)
|
||||
{
|
||||
if (onion_c == NULL)
|
||||
return;
|
||||
|
||||
ping_array_free_all(&onion_c->announce_ping_array);
|
||||
realloc_onion_friends(onion_c, 0);
|
||||
networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, NULL, NULL);
|
||||
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, NULL, NULL);
|
||||
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, NULL, NULL);
|
||||
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_FAKEID, NULL, NULL);
|
||||
tcp_onion_response_handler(onion_c->c, NULL, NULL);
|
||||
memset(onion_c, 0, sizeof(Onion_Client));
|
||||
free(onion_c);
|
||||
}
|
||||
|
||||
|
||||
/* return 0 if we are not connected to the network.
|
||||
* return 1 if we are.
|
||||
*/
|
||||
int onion_isconnected(const Onion_Client *onion_c)
|
||||
static int onion_isconnected(const Onion_Client *onion_c)
|
||||
{
|
||||
unsigned int i, num = 0, announced = 0;
|
||||
|
||||
|
@ -1457,3 +1389,102 @@ int onion_isconnected(const Onion_Client *onion_c)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ONION_CONNECTION_SECONDS 2
|
||||
|
||||
/* return 0 if we are not connected to the network.
|
||||
* return 1 if we are connected with TCP only.
|
||||
* return 2 if we are also connected with UDP.
|
||||
*/
|
||||
unsigned int onion_connection_status(const Onion_Client *onion_c)
|
||||
{
|
||||
if (onion_c->onion_connected >= ONION_CONNECTION_SECONDS) {
|
||||
if (onion_c->UDP_connected) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void do_onion_client(Onion_Client *onion_c)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (onion_c->last_run == unix_time())
|
||||
return;
|
||||
|
||||
populate_path_nodes(onion_c);
|
||||
|
||||
do_announce(onion_c);
|
||||
|
||||
if (onion_isconnected(onion_c)) {
|
||||
if (onion_c->onion_connected < ONION_CONNECTION_SECONDS * 2) {
|
||||
++onion_c->onion_connected;
|
||||
}
|
||||
|
||||
onion_c->UDP_connected = DHT_non_lan_connected(onion_c->dht);
|
||||
} else {
|
||||
populate_path_nodes_tcp(onion_c);
|
||||
|
||||
if (onion_c->onion_connected != 0) {
|
||||
--onion_c->onion_connected;
|
||||
}
|
||||
}
|
||||
|
||||
if (onion_connection_status(onion_c)) {
|
||||
for (i = 0; i < onion_c->num_friends; ++i) {
|
||||
do_friend(onion_c, i);
|
||||
}
|
||||
}
|
||||
|
||||
onion_c->last_run = unix_time();
|
||||
}
|
||||
|
||||
Onion_Client *new_onion_client(Net_Crypto *c)
|
||||
{
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
Onion_Client *onion_c = calloc(1, sizeof(Onion_Client));
|
||||
|
||||
if (onion_c == NULL)
|
||||
return NULL;
|
||||
|
||||
if (ping_array_init(&onion_c->announce_ping_array, ANNOUNCE_ARRAY_SIZE, ANNOUNCE_TIMEOUT) != 0) {
|
||||
free(onion_c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
onion_c->dht = c->dht;
|
||||
onion_c->net = c->dht->net;
|
||||
onion_c->c = c;
|
||||
new_symmetric_key(onion_c->secret_symmetric_key);
|
||||
crypto_box_keypair(onion_c->temp_public_key, onion_c->temp_secret_key);
|
||||
networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_announce_response, onion_c);
|
||||
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_data_response, onion_c);
|
||||
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, &handle_dhtpk_announce, onion_c);
|
||||
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, &handle_dht_dhtpk, onion_c);
|
||||
tcp_onion_response_handler(onion_c->c, &handle_tcp_onion, onion_c);
|
||||
|
||||
return onion_c;
|
||||
}
|
||||
|
||||
void kill_onion_client(Onion_Client *onion_c)
|
||||
{
|
||||
if (onion_c == NULL)
|
||||
return;
|
||||
|
||||
ping_array_free_all(&onion_c->announce_ping_array);
|
||||
realloc_onion_friends(onion_c, 0);
|
||||
networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, NULL, NULL);
|
||||
networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, NULL, NULL);
|
||||
oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, NULL, NULL);
|
||||
cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, NULL, NULL);
|
||||
tcp_onion_response_handler(onion_c->c, NULL, NULL);
|
||||
memset(onion_c, 0, sizeof(Onion_Client));
|
||||
free(onion_c);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
/* Onion data packet ids. */
|
||||
#define ONION_DATA_FRIEND_REQ CRYPTO_PACKET_FRIEND_REQ
|
||||
#define ONION_DATA_DHTPK CRYPTO_PACKET_FAKEID
|
||||
#define ONION_DATA_DHTPK CRYPTO_PACKET_DHTPK
|
||||
|
||||
typedef struct {
|
||||
uint8_t public_key[crypto_box_PUBLICKEYBYTES];
|
||||
|
@ -157,6 +157,9 @@ typedef struct {
|
|||
} Onion_Data_Handlers[256];
|
||||
|
||||
uint64_t last_packet_recv;
|
||||
|
||||
unsigned int onion_connected;
|
||||
_Bool UDP_connected;
|
||||
} Onion_Client;
|
||||
|
||||
|
||||
|
@ -278,8 +281,9 @@ void kill_onion_client(Onion_Client *onion_c);
|
|||
|
||||
|
||||
/* return 0 if we are not connected to the network.
|
||||
* return 1 if we are.
|
||||
* return 1 if we are connected with TCP only.
|
||||
* return 2 if we are also connected with UDP.
|
||||
*/
|
||||
int onion_isconnected(const Onion_Client *onion_c);
|
||||
unsigned int onion_connection_status(const Onion_Client *onion_c);
|
||||
|
||||
#endif
|
||||
|
|
2053
toxcore/tox.c
2053
toxcore/tox.c
File diff suppressed because it is too large
Load Diff
2730
toxcore/tox.h
2730
toxcore/tox.h
File diff suppressed because it is too large
Load Diff
173
toxcore/tox_old.h
Normal file
173
toxcore/tox_old.h
Normal file
|
@ -0,0 +1,173 @@
|
|||
/**********GROUP CHAT FUNCTIONS ************/
|
||||
|
||||
/* Group chat types for tox_callback_group_invite function.
|
||||
*
|
||||
* TOX_GROUPCHAT_TYPE_TEXT groupchats must be accepted with the tox_join_groupchat() function.
|
||||
* The function to accept TOX_GROUPCHAT_TYPE_AV is in toxav.
|
||||
*/
|
||||
enum {
|
||||
TOX_GROUPCHAT_TYPE_TEXT,
|
||||
TOX_GROUPCHAT_TYPE_AV
|
||||
};
|
||||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Tox *tox, int32_t friendnumber, uint8_t type, const uint8_t *data, uint16_t length, void *userdata)
|
||||
*
|
||||
* data of length is what needs to be passed to join_groupchat().
|
||||
*
|
||||
* for what type means see the enum right above this comment.
|
||||
*/
|
||||
void tox_callback_group_invite(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, const uint8_t *, uint16_t,
|
||||
void *), void *userdata);
|
||||
|
||||
/* Set the callback for group messages.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int peernumber, const uint8_t * message, uint16_t length, void *userdata)
|
||||
*/
|
||||
void tox_callback_group_message(Tox *tox, void (*function)(Tox *tox, int, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set the callback for group actions.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int peernumber, const uint8_t * action, uint16_t length, void *userdata)
|
||||
*/
|
||||
void tox_callback_group_action(Tox *tox, void (*function)(Tox *tox, int, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Set callback function for title changes.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int peernumber, uint8_t * title, uint8_t length, void *userdata)
|
||||
* if peernumber == -1, then author is unknown (e.g. initial joining the group)
|
||||
*/
|
||||
void tox_callback_group_title(Tox *tox, void (*function)(Tox *tox, int, int, const uint8_t *, uint8_t,
|
||||
void *), void *userdata);
|
||||
|
||||
/* Set callback function for peer name list changes.
|
||||
*
|
||||
* It gets called every time the name list changes(new peer/name, deleted peer)
|
||||
* Function(Tox *tox, int groupnumber, int peernumber, TOX_CHAT_CHANGE change, void *userdata)
|
||||
*/
|
||||
typedef enum {
|
||||
TOX_CHAT_CHANGE_PEER_ADD,
|
||||
TOX_CHAT_CHANGE_PEER_DEL,
|
||||
TOX_CHAT_CHANGE_PEER_NAME,
|
||||
} TOX_CHAT_CHANGE;
|
||||
|
||||
void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int, int, uint8_t, void *),
|
||||
void *userdata);
|
||||
|
||||
/* Creates a new groupchat and puts it in the chats array.
|
||||
*
|
||||
* return group number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int tox_add_groupchat(Tox *tox);
|
||||
|
||||
/* Delete a groupchat from the chats array.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 if failure.
|
||||
*/
|
||||
int tox_del_groupchat(Tox *tox, int groupnumber);
|
||||
|
||||
/* Copy the name of peernumber who is in groupnumber to name.
|
||||
* name must be at least TOX_MAX_NAME_LENGTH long.
|
||||
*
|
||||
* return length of name if success
|
||||
* return -1 if failure
|
||||
*/
|
||||
int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t *name);
|
||||
|
||||
/* Copy the public key of peernumber who is in groupnumber to public_key.
|
||||
* public_key must be TOX_PUBLIC_KEY_SIZE long.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_group_peer_pubkey(const Tox *tox, int groupnumber, int peernumber, uint8_t *public_key);
|
||||
|
||||
/* invite friendnumber to groupnumber
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_invite_friend(Tox *tox, int32_t friendnumber, int groupnumber);
|
||||
|
||||
/* Join a group (you need to have been invited first.) using data of length obtained
|
||||
* in the group invite callback.
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length);
|
||||
|
||||
/* send a group message
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint16_t length);
|
||||
|
||||
/* send a group action
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint16_t length);
|
||||
|
||||
/* set the group's title, limited to MAX_NAME_LENGTH
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_set_title(Tox *tox, int groupnumber, const uint8_t *title, uint8_t length);
|
||||
|
||||
/* Get group title from groupnumber and put it in title.
|
||||
* title needs to be a valid memory location with a max_length size of at least MAX_NAME_LENGTH (128) bytes.
|
||||
*
|
||||
* return length of copied title if success.
|
||||
* return -1 if failure.
|
||||
*/
|
||||
int tox_group_get_title(Tox *tox, int groupnumber, uint8_t *title, uint32_t max_length);
|
||||
|
||||
/* Check if the current peernumber corresponds to ours.
|
||||
*
|
||||
* return 1 if the peernumber corresponds to ours.
|
||||
* return 0 on failure.
|
||||
*/
|
||||
unsigned int tox_group_peernumber_is_ours(const Tox *tox, int groupnumber, int peernumber);
|
||||
|
||||
/* Return the number of peers in the group chat on success.
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_number_peers(const Tox *tox, int groupnumber);
|
||||
|
||||
/* List all the peers in the group chat.
|
||||
*
|
||||
* Copies the names of the peers to the name[length][TOX_MAX_NAME_LENGTH] array.
|
||||
*
|
||||
* Copies the lengths of the names to lengths[length]
|
||||
*
|
||||
* returns the number of peers on success.
|
||||
*
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int tox_group_get_names(const Tox *tox, int groupnumber, uint8_t names[][TOX_MAX_NAME_LENGTH], uint16_t lengths[],
|
||||
uint16_t length);
|
||||
|
||||
/* Return the number of chats in the instance m.
|
||||
* You should use this to determine how much memory to allocate
|
||||
* for copy_chatlist. */
|
||||
uint32_t tox_count_chatlist(const Tox *tox);
|
||||
|
||||
/* Copy a list of valid chat IDs into the array out_list.
|
||||
* If out_list is NULL, returns 0.
|
||||
* Otherwise, returns the number of elements copied.
|
||||
* If the array was too small, the contents
|
||||
* of out_list will be truncated to list_size. */
|
||||
uint32_t tox_get_chatlist(const Tox *tox, int32_t *out_list, uint32_t list_size);
|
||||
|
||||
/* return the type of groupchat (TOX_GROUPCHAT_TYPE_) that groupnumber is.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return type on success.
|
||||
*/
|
||||
int tox_group_get_type(const Tox *tox, int groupnumber);
|
||||
|
237
toxcore/tox_old_code.h
Normal file
237
toxcore/tox_old_code.h
Normal file
|
@ -0,0 +1,237 @@
|
|||
/**********GROUP CHAT FUNCTIONS: WARNING Group chats will be rewritten so this might change ************/
|
||||
|
||||
/* Set the callback for group invites.
|
||||
*
|
||||
* Function(Tox *tox, int32_t friendnumber, uint8_t type, uint8_t *data, uint16_t length, void *userdata)
|
||||
*
|
||||
* data of length is what needs to be passed to join_groupchat().
|
||||
*/
|
||||
void tox_callback_group_invite(Tox *tox, void (*function)(Messenger *tox, int32_t, uint8_t, const uint8_t *, uint16_t,
|
||||
void *), void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
g_callback_group_invite(m->group_chat_object, function, userdata);
|
||||
}
|
||||
|
||||
/* Set the callback for group messages.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int peernumber, uint8_t * message, uint16_t length, void *userdata)
|
||||
*/
|
||||
void tox_callback_group_message(Tox *tox, void (*function)(Messenger *tox, int, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
g_callback_group_message(m->group_chat_object, function, userdata);
|
||||
}
|
||||
|
||||
/* Set the callback for group actions.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int peernumber, uint8_t * action, uint16_t length, void *userdata)
|
||||
*/
|
||||
void tox_callback_group_action(Tox *tox, void (*function)(Messenger *tox, int, int, const uint8_t *, uint16_t, void *),
|
||||
void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
g_callback_group_action(m->group_chat_object, function, userdata);
|
||||
}
|
||||
|
||||
/* Set callback function for title changes.
|
||||
*
|
||||
* Function(Tox *tox, int groupnumber, int peernumber, uint8_t * title, uint8_t length, void *userdata)
|
||||
* if peernumber == -1, then author is unknown (e.g. initial joining the group)
|
||||
*/
|
||||
void tox_callback_group_title(Tox *tox, void (*function)(Messenger *tox, int, int, const uint8_t *, uint8_t,
|
||||
void *), void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
g_callback_group_title(m->group_chat_object, function, userdata);
|
||||
}
|
||||
|
||||
/* Set callback function for peer name list changes.
|
||||
*
|
||||
* It gets called every time the name list changes(new peer/name, deleted peer)
|
||||
* Function(Tox *tox, int groupnumber, void *userdata)
|
||||
*/
|
||||
void tox_callback_group_namelist_change(Tox *tox, void (*function)(Tox *tox, int, int, uint8_t, void *), void *userdata)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
g_callback_group_namelistchange(m->group_chat_object, function, userdata);
|
||||
}
|
||||
|
||||
/* Creates a new groupchat and puts it in the chats array.
|
||||
*
|
||||
* return group number on success.
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int tox_add_groupchat(Tox *tox)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return add_groupchat(m->group_chat_object, GROUPCHAT_TYPE_TEXT);
|
||||
}
|
||||
|
||||
/* Delete a groupchat from the chats array.
|
||||
*
|
||||
* return 0 on success.
|
||||
* return -1 if failure.
|
||||
*/
|
||||
int tox_del_groupchat(Tox *tox, int groupnumber)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return del_groupchat(m->group_chat_object, groupnumber);
|
||||
}
|
||||
|
||||
/* Copy the name of peernumber who is in groupnumber to name.
|
||||
* name must be at least MAX_NICK_BYTES long.
|
||||
*
|
||||
* return length of name if success
|
||||
* return -1 if failure
|
||||
*/
|
||||
int tox_group_peername(const Tox *tox, int groupnumber, int peernumber, uint8_t *name)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return group_peername(m->group_chat_object, groupnumber, peernumber, name);
|
||||
}
|
||||
|
||||
/* Copy the public key of peernumber who is in groupnumber to public_key.
|
||||
* public_key must be crypto_box_PUBLICKEYBYTES long.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_group_peer_pubkey(const Tox *tox, int groupnumber, int peernumber, uint8_t *public_key)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return group_peer_pubkey(m->group_chat_object, groupnumber, peernumber, public_key);
|
||||
}
|
||||
|
||||
/* invite friendnumber to groupnumber
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_invite_friend(Tox *tox, int32_t friendnumber, int groupnumber)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return invite_friend(m->group_chat_object, friendnumber, groupnumber);
|
||||
}
|
||||
|
||||
/* Join a group (you need to have been invited first.) using data of length obtained
|
||||
* in the group invite callback.
|
||||
*
|
||||
* returns group number on success
|
||||
* returns -1 on failure.
|
||||
*/
|
||||
int tox_join_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return join_groupchat(m->group_chat_object, friendnumber, GROUPCHAT_TYPE_TEXT, data, length);
|
||||
}
|
||||
|
||||
/* send a group message
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_message_send(Tox *tox, int groupnumber, const uint8_t *message, uint16_t length)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return group_message_send(m->group_chat_object, groupnumber, message, length);
|
||||
}
|
||||
|
||||
/* send a group action
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_action_send(Tox *tox, int groupnumber, const uint8_t *action, uint16_t length)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return group_action_send(m->group_chat_object, groupnumber, action, length);
|
||||
}
|
||||
|
||||
/* set the group's title, limited to MAX_NAME_LENGTH
|
||||
* return 0 on success
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_set_title(Tox *tox, int groupnumber, const uint8_t *title, uint8_t length)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return group_title_send(m->group_chat_object, groupnumber, title, length);
|
||||
}
|
||||
|
||||
/* Get group title from groupnumber and put it in title.
|
||||
* title needs to be a valid memory location with a max_length size of at least MAX_NAME_LENGTH (128) bytes.
|
||||
*
|
||||
* return length of copied title if success.
|
||||
* return -1 if failure.
|
||||
*/
|
||||
int tox_group_get_title(Tox *tox, int groupnumber, uint8_t *title, uint32_t max_length)
|
||||
{
|
||||
Messenger *m = tox;
|
||||
return group_title_get(m->group_chat_object, groupnumber, title, max_length);
|
||||
}
|
||||
|
||||
/* Check if the current peernumber corresponds to ours.
|
||||
*
|
||||
* return 1 if the peernumber corresponds to ours.
|
||||
* return 0 on failure.
|
||||
*/
|
||||
unsigned int tox_group_peernumber_is_ours(const Tox *tox, int groupnumber, int peernumber)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return group_peernumber_is_ours(m->group_chat_object, groupnumber, peernumber);
|
||||
}
|
||||
|
||||
/* Return the number of peers in the group chat on success.
|
||||
* return -1 on failure
|
||||
*/
|
||||
int tox_group_number_peers(const Tox *tox, int groupnumber)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return group_number_peers(m->group_chat_object, groupnumber);
|
||||
}
|
||||
|
||||
/* List all the peers in the group chat.
|
||||
*
|
||||
* Copies the names of the peers to the name[length][MAX_NICK_BYTES] array.
|
||||
*
|
||||
* Copies the lengths of the names to lengths[length]
|
||||
*
|
||||
* returns the number of peers on success.
|
||||
*
|
||||
* return -1 on failure.
|
||||
*/
|
||||
int tox_group_get_names(const Tox *tox, int groupnumber, uint8_t names[][TOX_MAX_NAME_LENGTH], uint16_t lengths[],
|
||||
uint16_t length)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return group_names(m->group_chat_object, groupnumber, names, lengths, length);
|
||||
}
|
||||
|
||||
/* Return the number of chats in the instance m.
|
||||
* You should use this to determine how much memory to allocate
|
||||
* for copy_chatlist. */
|
||||
uint32_t tox_count_chatlist(const Tox *tox)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return count_chatlist(m->group_chat_object);
|
||||
}
|
||||
|
||||
/* Copy a list of valid chat IDs into the array out_list.
|
||||
* If out_list is NULL, returns 0.
|
||||
* Otherwise, returns the number of elements copied.
|
||||
* If the array was too small, the contents
|
||||
* of out_list will be truncated to list_size. */
|
||||
uint32_t tox_get_chatlist(const Tox *tox, int32_t *out_list, uint32_t list_size)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return copy_chatlist(m->group_chat_object, out_list, list_size);
|
||||
}
|
||||
|
||||
/* return the type of groupchat (TOX_GROUPCHAT_TYPE_) that groupnumber is.
|
||||
*
|
||||
* return -1 on failure.
|
||||
* return type on success.
|
||||
*/
|
||||
int tox_group_get_type(const Tox *tox, int groupnumber)
|
||||
{
|
||||
const Messenger *m = tox;
|
||||
return group_get_type(m->group_chat_object, groupnumber);
|
||||
}
|
2
toxencryptsave/defines.h
Normal file
2
toxencryptsave/defines.h
Normal file
|
@ -0,0 +1,2 @@
|
|||
#define TOX_ENC_SAVE_MAGIC_NUMBER "toxEsave"
|
||||
#define TOX_ENC_SAVE_MAGIC_LENGTH 8
|
|
@ -26,8 +26,9 @@
|
|||
#endif
|
||||
|
||||
#include "toxencryptsave.h"
|
||||
#include "defines.h"
|
||||
#include "../toxcore/crypto_core.h"
|
||||
#include "../toxcore/tox.h"
|
||||
#define SET_ERROR_PARAMETER(param, x) {if(param) {*param = x;}}
|
||||
|
||||
#ifdef VANILLA_NACL
|
||||
#include "crypto_pwhash_scryptsalsa208sha256/crypto_pwhash_scryptsalsa208sha256.h"
|
||||
|
@ -35,81 +36,70 @@
|
|||
#include <crypto_hash_sha256.h>
|
||||
#endif
|
||||
|
||||
#define TOX_PASS_ENCRYPTION_EXTRA_LENGTH (crypto_box_MACBYTES + crypto_box_NONCEBYTES \
|
||||
+ crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
|
||||
#if TOX_PASS_SALT_LENGTH != crypto_pwhash_scryptsalsa208sha256_SALTBYTES
|
||||
#error TOX_PASS_SALT_LENGTH is assumed to be equal to crypto_pwhash_scryptsalsa208sha256_SALTBYTES
|
||||
#endif
|
||||
|
||||
#define TOX_PASS_KEY_LENGTH (crypto_pwhash_scryptsalsa208sha256_SALTBYTES + crypto_box_KEYBYTES)
|
||||
#if TOX_PASS_KEY_LENGTH != crypto_box_KEYBYTES
|
||||
#error TOX_PASS_KEY_LENGTH is assumed to be equal to crypto_box_KEYBYTES
|
||||
#endif
|
||||
|
||||
int tox_pass_encryption_extra_length()
|
||||
{
|
||||
return TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
}
|
||||
#if TOX_PASS_ENCRYPTION_EXTRA_LENGTH != (crypto_box_MACBYTES + crypto_box_NONCEBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
|
||||
#error TOX_PASS_ENCRYPTION_EXTRA_LENGTH is assumed to be equal to (crypto_box_MACBYTES + crypto_box_NONCEBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
|
||||
#endif
|
||||
|
||||
int tox_pass_key_length()
|
||||
{
|
||||
return TOX_PASS_KEY_LENGTH;
|
||||
}
|
||||
|
||||
int tox_pass_salt_length()
|
||||
{
|
||||
return crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
|
||||
}
|
||||
|
||||
/* This "module" provides functions analogous to tox_load and tox_save in toxcore
|
||||
* Clients should consider alerting their users that, unlike plain data, if even one bit
|
||||
/* Clients should consider alerting their users that, unlike plain data, if even one bit
|
||||
* becomes corrupted, the data will be entirely unrecoverable.
|
||||
* Ditto if they forget their password, there is no way to recover the data.
|
||||
*/
|
||||
|
||||
/* return size of the messenger data (for encrypted saving). */
|
||||
uint32_t tox_encrypted_size(const Tox *tox)
|
||||
{
|
||||
return tox_size(tox) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
}
|
||||
|
||||
/* This retrieves the salt used to encrypt the given data, which can then be passed to
|
||||
* derive_key_with_salt to produce the same key as was previously used. Any encrpyted
|
||||
* data with this module can be used as input.
|
||||
*
|
||||
* returns -1 if the magic number is wrong
|
||||
* returns 0 otherwise (no guarantee about validity of data)
|
||||
* returns true if magic number matches
|
||||
* success does not say anything about the validity of the data, only that data of
|
||||
* the appropriate size was copied
|
||||
*/
|
||||
int tox_get_salt(uint8_t *data, uint8_t *salt)
|
||||
bool tox_get_salt(const uint8_t *data, uint8_t *salt)
|
||||
{
|
||||
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
data += TOX_ENC_SAVE_MAGIC_LENGTH;
|
||||
memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generates a secret symmetric key from the given passphrase. out_key must be at least
|
||||
* TOX_PASS_KEY_LENGTH bytes long.
|
||||
* Be sure to not compromise the key! Only keep it in memory, do not write to disk.
|
||||
* This function is fairly cheap, but irungentoo insists that you be allowed to
|
||||
* cache the result if you want, to minimize computation for repeated encryptions.
|
||||
* The password is zeroed after key derivation.
|
||||
* The key should only be used with the other functions in this module, as it
|
||||
* includes a salt.
|
||||
* Note that this function is not deterministic; to derive the same key from a
|
||||
* password, you also must know the random salt that was used. See below.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key)
|
||||
bool tox_derive_key_from_pass(uint8_t *passphrase, size_t pplength, TOX_PASS_KEY *out_key,
|
||||
TOX_ERR_KEY_DERIVATION *error)
|
||||
{
|
||||
uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
|
||||
randombytes(salt, sizeof salt);
|
||||
return tox_derive_key_with_salt(passphrase, pplength, salt, out_key);
|
||||
return tox_derive_key_with_salt(passphrase, pplength, salt, out_key, error);
|
||||
}
|
||||
|
||||
/* Same as above, except with use the given salt for deterministic key derivation.
|
||||
* The salt must be tox_salt_length() bytes in length.
|
||||
* The salt must be TOX_PASS_SALT_LENGTH bytes in length.
|
||||
*/
|
||||
int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *salt, uint8_t *out_key)
|
||||
bool tox_derive_key_with_salt(uint8_t *passphrase, size_t pplength, uint8_t *salt, TOX_PASS_KEY *out_key,
|
||||
TOX_ERR_KEY_DERIVATION *error)
|
||||
{
|
||||
if (pplength == 0)
|
||||
return -1;
|
||||
if (pplength == 0 || !passphrase || !salt || !out_key) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t passkey[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(passkey, passphrase, pplength);
|
||||
|
@ -125,27 +115,33 @@ int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *sa
|
|||
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */
|
||||
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) {
|
||||
/* out of memory most likely */
|
||||
return -1;
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_FAILED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sodium_memzero(passkey, crypto_hash_sha256_BYTES); /* wipe plaintext pw */
|
||||
memcpy(out_key, salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
|
||||
memcpy(out_key + crypto_pwhash_scryptsalsa208sha256_SALTBYTES, key, crypto_box_KEYBYTES);
|
||||
return 0;
|
||||
memcpy(out_key->salt, salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
|
||||
memcpy(out_key->key, key, crypto_box_KEYBYTES);
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_OK);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Encrypt arbitrary with a key produced by tox_derive_key_from_pass. The output
|
||||
/* Encrypt arbitrary with a key produced by tox_derive_key_*. The output
|
||||
* array must be at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
|
||||
* key must be TOX_PASS_KEY_LENGTH bytes.
|
||||
* If you already have a symmetric key from somewhere besides this module, simply
|
||||
* call encrypt_data_symmetric in toxcore/crypto_core directly.
|
||||
*
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *key, uint8_t *out)
|
||||
bool tox_pass_key_encrypt(const uint8_t *data, size_t data_len, const TOX_PASS_KEY *key, uint8_t *out,
|
||||
TOX_ERR_ENCRYPTION *error)
|
||||
{
|
||||
if (data_len == 0 || !data || !key || !out) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the output data consists of, in order:
|
||||
* salt, nonce, mac, enc_data
|
||||
* where the mac is automatically prepended by the encrypt()
|
||||
|
@ -159,8 +155,7 @@ int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *
|
|||
out += TOX_ENC_SAVE_MAGIC_LENGTH;
|
||||
|
||||
/* then add the rest prefix */
|
||||
memcpy(out, key, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
|
||||
key += crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
|
||||
memcpy(out, key->salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
|
||||
out += crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
|
||||
|
||||
uint8_t nonce[crypto_box_NONCEBYTES];
|
||||
|
@ -169,177 +164,133 @@ int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *
|
|||
out += crypto_box_NONCEBYTES;
|
||||
|
||||
/* now encrypt */
|
||||
if (encrypt_data_symmetric(key, nonce, data, data_len, out)
|
||||
if (encrypt_data_symmetric(key->key, nonce, data, data_len, out)
|
||||
!= data_len + crypto_box_MACBYTES) {
|
||||
return -1;
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_OK);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Encrypts the given data with the given passphrase. The output array must be
|
||||
* at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
|
||||
* to tox_derive_key_from_pass and tox_pass_key_encrypt.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_pass_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *passphrase, uint32_t pplength, uint8_t *out)
|
||||
bool tox_pass_encrypt(const uint8_t *data, size_t data_len, uint8_t *passphrase, size_t pplength, uint8_t *out,
|
||||
TOX_ERR_ENCRYPTION *error)
|
||||
{
|
||||
uint8_t key[TOX_PASS_KEY_LENGTH];
|
||||
TOX_PASS_KEY key;
|
||||
TOX_ERR_KEY_DERIVATION _error;
|
||||
|
||||
if (tox_derive_key_from_pass(passphrase, pplength, key) == -1)
|
||||
return -1;
|
||||
if (!tox_derive_key_from_pass(passphrase, pplength, &key, &_error)) {
|
||||
if (_error == TOX_ERR_KEY_DERIVATION_NULL) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL);
|
||||
} else if (_error == TOX_ERR_KEY_DERIVATION_FAILED) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED);
|
||||
}
|
||||
|
||||
return tox_pass_key_encrypt(data, data_len, key, out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save the messenger data encrypted with the given password.
|
||||
* data must be at least tox_encrypted_size().
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_encrypted_save(const Tox *tox, uint8_t *data, uint8_t *passphrase, uint32_t pplength)
|
||||
{
|
||||
/* first get plain save data */
|
||||
uint32_t temp_size = tox_size(tox);
|
||||
uint8_t temp_data[temp_size];
|
||||
tox_save(tox, temp_data);
|
||||
|
||||
/* now encrypt */
|
||||
return tox_pass_encrypt(temp_data, temp_size, passphrase, pplength, data);
|
||||
}
|
||||
|
||||
/* Save the messenger data encrypted with the given key from tox_derive_key.
|
||||
* data must be at least tox_encrypted_size().
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key)
|
||||
{
|
||||
/* first get plain save data */
|
||||
uint32_t temp_size = tox_size(tox);
|
||||
uint8_t temp_data[temp_size];
|
||||
tox_save(tox, temp_data);
|
||||
|
||||
/* encrypt */
|
||||
return tox_pass_key_encrypt(temp_data, temp_size, key, data);
|
||||
return tox_pass_key_encrypt(data, data_len, &key, out, error);
|
||||
}
|
||||
|
||||
/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by
|
||||
* tox_derive_key_from_pass.
|
||||
*
|
||||
* returns the length of the output data (== data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) on success
|
||||
* returns -1 on failure
|
||||
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
|
||||
*
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *key, uint8_t *out)
|
||||
bool tox_pass_key_decrypt(const uint8_t *data, size_t length, const TOX_PASS_KEY *key, uint8_t *out,
|
||||
TOX_ERR_DECRYPTION *error)
|
||||
{
|
||||
if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH
|
||||
|| 0 != memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH))
|
||||
return -1;
|
||||
if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!data || !key || !out) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data += TOX_ENC_SAVE_MAGIC_LENGTH;
|
||||
data += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; // salt only affects key derivation
|
||||
|
||||
size_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
|
||||
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
//uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
|
||||
uint8_t nonce[crypto_box_NONCEBYTES];
|
||||
|
||||
//memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
|
||||
key += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; // ignore the salt, which is only needed for kdf
|
||||
data += crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
|
||||
memcpy(nonce, data, crypto_box_NONCEBYTES);
|
||||
data += crypto_box_NONCEBYTES;
|
||||
|
||||
/* decrypt the data */
|
||||
if (decrypt_data_symmetric(key, nonce, data, decrypt_length + crypto_box_MACBYTES, out)
|
||||
if (decrypt_data_symmetric(key->key, nonce, data, decrypt_length + crypto_box_MACBYTES, out)
|
||||
!= decrypt_length) {
|
||||
return -1;
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_FAILED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return decrypt_length;
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_OK);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Decrypts the given data with the given passphrase. The output array must be
|
||||
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
|
||||
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
|
||||
* to tox_pass_key_decrypt.
|
||||
*
|
||||
* returns the length of the output data (== data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) on success
|
||||
* returns -1 on failure
|
||||
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
|
||||
*
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength, uint8_t *out)
|
||||
bool tox_pass_decrypt(const uint8_t *data, size_t length, uint8_t *passphrase, size_t pplength, uint8_t *out,
|
||||
TOX_ERR_DECRYPTION *error)
|
||||
{
|
||||
uint8_t passkey[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(passkey, passphrase, pplength);
|
||||
if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH || pplength == 0) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!data || !passphrase || !out) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
|
||||
memcpy(salt, data + TOX_ENC_SAVE_MAGIC_LENGTH, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
|
||||
|
||||
/* derive the key */
|
||||
uint8_t key[crypto_box_KEYBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
|
||||
TOX_PASS_KEY key;
|
||||
|
||||
if (crypto_pwhash_scryptsalsa208sha256(
|
||||
key + crypto_pwhash_scryptsalsa208sha256_SALTBYTES,
|
||||
crypto_box_KEYBYTES, (char *)passkey, sizeof(passkey), salt,
|
||||
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */
|
||||
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) {
|
||||
if (!tox_derive_key_with_salt(passphrase, pplength, salt, &key, NULL)) {
|
||||
/* out of memory most likely */
|
||||
return -1;
|
||||
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sodium_memzero(passkey, crypto_hash_sha256_BYTES); /* wipe plaintext pw */
|
||||
|
||||
return tox_pass_key_decrypt(data, length, key, out);
|
||||
}
|
||||
|
||||
/* Load the messenger from encrypted data of size length.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength)
|
||||
{
|
||||
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
uint8_t temp_data[decrypt_length];
|
||||
|
||||
if (tox_pass_decrypt(data, length, passphrase, pplength, temp_data)
|
||||
!= decrypt_length)
|
||||
return -1;
|
||||
|
||||
return tox_load(tox, temp_data, decrypt_length);
|
||||
}
|
||||
|
||||
/* Load the messenger from encrypted data of size length, with key from tox_derive_key.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_encrypted_key_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *key)
|
||||
{
|
||||
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
uint8_t temp_data[decrypt_length];
|
||||
|
||||
if (tox_pass_key_decrypt(data, length, key, temp_data)
|
||||
!= decrypt_length)
|
||||
return -1;
|
||||
|
||||
return tox_load(tox, temp_data, decrypt_length);
|
||||
return tox_pass_key_decrypt(data, length, &key, out, error);
|
||||
}
|
||||
|
||||
/* Determines whether or not the given data is encrypted (by checking the magic number)
|
||||
*
|
||||
* returns 1 if it is encrypted
|
||||
* returns 0 otherwise
|
||||
*/
|
||||
int tox_is_data_encrypted(const uint8_t *data)
|
||||
bool tox_is_data_encrypted(const uint8_t *data)
|
||||
{
|
||||
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tox_is_save_encrypted(const uint8_t *data)
|
||||
{
|
||||
return tox_is_data_encrypted(data);
|
||||
}
|
||||
|
|
|
@ -29,27 +29,20 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef __TOX_DEFINED__
|
||||
#define __TOX_DEFINED__
|
||||
#ifndef TOX_DEFINED
|
||||
#define TOX_DEFINED
|
||||
typedef struct Tox Tox;
|
||||
struct Tox_Options;
|
||||
#endif
|
||||
|
||||
// these functions provide access to these defines in toxencryptsave.c, which
|
||||
// otherwise aren't actually available in clients...
|
||||
int tox_pass_encryption_extra_length();
|
||||
#define TOX_PASS_SALT_LENGTH 32
|
||||
#define TOX_PASS_KEY_LENGTH 32
|
||||
#define TOX_PASS_ENCRYPTION_EXTRA_LENGTH 80
|
||||
|
||||
int tox_pass_key_length();
|
||||
|
||||
int tox_pass_salt_length();
|
||||
|
||||
/* return size of the messenger data (for encrypted Messenger saving). */
|
||||
uint32_t tox_encrypted_size(const Tox *tox);
|
||||
|
||||
/* This "module" provides functions analogous to tox_load and tox_save in toxcore,
|
||||
* as well as functions for encryption of arbitrary client data (e.g. chat logs).
|
||||
*
|
||||
* It is conceptually organized into two parts. The first part are the functions
|
||||
/* This module is conceptually organized into two parts. The first part are the functions
|
||||
* with "key" in the name. To use these functions, first derive an encryption key
|
||||
* from a password with tox_derive_key_from_pass, and use the returned key to
|
||||
* encrypt the data. The second part takes the password itself instead of the key,
|
||||
|
@ -59,13 +52,83 @@ uint32_t tox_encrypted_size(const Tox *tox);
|
|||
* favor using the first part intead of the second part.
|
||||
*
|
||||
* The encrypted data is prepended with a magic number, to aid validity checking
|
||||
* (no guarantees are made of course).
|
||||
* (no guarantees are made of course). Any data to be decrypted must start with
|
||||
* the magic number.
|
||||
*
|
||||
* Clients should consider alerting their users that, unlike plain data, if even one bit
|
||||
* becomes corrupted, the data will be entirely unrecoverable.
|
||||
* Ditto if they forget their password, there is no way to recover the data.
|
||||
*/
|
||||
|
||||
/* Since apparently no one actually bothered to learn about the module previously,
|
||||
* the recently removed functions tox_encrypted_new and tox_get_encrypted_savedata
|
||||
* may be trivially replaced by calls to tox_pass_decrypt -> tox_new or
|
||||
* tox_get_savedata -> tox_pass_encrypt as appropriate. The removed functions
|
||||
* were never more than 5 line wrappers of the other public API functions anyways.
|
||||
* (As has always been, tox_pass_decrypt and tox_pass_encrypt are interchangeable
|
||||
* with tox_pass_key_decrypt and tox_pass_key_encrypt, as the client program requires.)
|
||||
*/
|
||||
|
||||
typedef enum TOX_ERR_KEY_DERIVATION {
|
||||
TOX_ERR_KEY_DERIVATION_OK,
|
||||
/**
|
||||
* Some input data, or maybe the output pointer, was null.
|
||||
*/
|
||||
TOX_ERR_KEY_DERIVATION_NULL,
|
||||
/**
|
||||
* The crypto lib was unable to derive a key from the given passphrase,
|
||||
* which is usually a lack of memory issue. The functions accepting keys
|
||||
* do not produce this error.
|
||||
*/
|
||||
TOX_ERR_KEY_DERIVATION_FAILED
|
||||
} TOX_ERR_KEY_DERIVATION;
|
||||
|
||||
typedef enum TOX_ERR_ENCRYPTION {
|
||||
TOX_ERR_ENCRYPTION_OK,
|
||||
/**
|
||||
* Some input data, or maybe the output pointer, was null.
|
||||
*/
|
||||
TOX_ERR_ENCRYPTION_NULL,
|
||||
/**
|
||||
* The crypto lib was unable to derive a key from the given passphrase,
|
||||
* which is usually a lack of memory issue. The functions accepting keys
|
||||
* do not produce this error.
|
||||
*/
|
||||
TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED,
|
||||
/**
|
||||
* The encryption itself failed.
|
||||
*/
|
||||
TOX_ERR_ENCRYPTION_FAILED
|
||||
} TOX_ERR_ENCRYPTION;
|
||||
|
||||
typedef enum TOX_ERR_DECRYPTION {
|
||||
TOX_ERR_DECRYPTION_OK,
|
||||
/**
|
||||
* Some input data, or maybe the output pointer, was null.
|
||||
*/
|
||||
TOX_ERR_DECRYPTION_NULL,
|
||||
/**
|
||||
* The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes
|
||||
*/
|
||||
TOX_ERR_DECRYPTION_INVALID_LENGTH,
|
||||
/**
|
||||
* The input data is missing the magic number (i.e. wasn't created by this
|
||||
* module, or is corrupted)
|
||||
*/
|
||||
TOX_ERR_DECRYPTION_BAD_FORMAT,
|
||||
/**
|
||||
* The crypto lib was unable to derive a key from the given passphrase,
|
||||
* which is usually a lack of memory issue. The functions accepting keys
|
||||
* do not produce this error.
|
||||
*/
|
||||
TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED,
|
||||
/**
|
||||
* The encrypted byte array could not be decrypted. Either the data was
|
||||
* corrupt or the password/key was incorrect.
|
||||
*/
|
||||
TOX_ERR_DECRYPTION_FAILED
|
||||
} TOX_ERR_DECRYPTION;
|
||||
|
||||
|
||||
/******************************* BEGIN PART 2 *******************************
|
||||
* For simplicty, the second part of the module is presented first. The API for
|
||||
|
@ -75,41 +138,25 @@ uint32_t tox_encrypted_size(const Tox *tox);
|
|||
*/
|
||||
|
||||
/* Encrypts the given data with the given passphrase. The output array must be
|
||||
* at least data_len + tox_pass_encryption_extra_length() bytes long. This delegates
|
||||
* at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
|
||||
* to tox_derive_key_from_pass and tox_pass_key_encrypt.
|
||||
*
|
||||
* tox_encrypted_save() is a good example of how to use this function.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_pass_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *passphrase, uint32_t pplength, uint8_t *out);
|
||||
bool tox_pass_encrypt(const uint8_t *data, size_t data_len, uint8_t *passphrase, size_t pplength, uint8_t *out,
|
||||
TOX_ERR_ENCRYPTION *error);
|
||||
|
||||
/* Save the messenger data encrypted with the given password.
|
||||
* data must be at least tox_encrypted_size().
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_encrypted_save(const Tox *tox, uint8_t *data, uint8_t *passphrase, uint32_t pplength);
|
||||
|
||||
/* Decrypts the given data with the given passphrase. The output array must be
|
||||
* at least data_len - tox_pass_encryption_extra_length() bytes long. This delegates
|
||||
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
|
||||
* to tox_pass_key_decrypt.
|
||||
*
|
||||
* tox_encrypted_load() is a good example of how to use this function.
|
||||
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
|
||||
*
|
||||
* returns the length of the output data (== data_len - tox_pass_encryption_extra_length()) on success
|
||||
* returns -1 on failure
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength, uint8_t *out);
|
||||
|
||||
/* Load the messenger from encrypted data of size length.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength);
|
||||
bool tox_pass_decrypt(const uint8_t *data, size_t length, uint8_t *passphrase, size_t pplength, uint8_t *out,
|
||||
TOX_ERR_DECRYPTION *error);
|
||||
|
||||
|
||||
/******************************* BEGIN PART 1 *******************************
|
||||
|
@ -117,8 +164,16 @@ int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *
|
|||
* intensive than part one. The first 3 functions are for key handling.
|
||||
*/
|
||||
|
||||
/* This key structure's internals should not be used by any client program, even
|
||||
* if they are straightforward here.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t salt[TOX_PASS_SALT_LENGTH];
|
||||
uint8_t key[TOX_PASS_KEY_LENGTH];
|
||||
} TOX_PASS_KEY;
|
||||
|
||||
/* Generates a secret symmetric key from the given passphrase. out_key must be at least
|
||||
* tox_pass_key_length() bytes long.
|
||||
* TOX_PASS_KEY_LENGTH bytes long.
|
||||
* Be sure to not compromise the key! Only keep it in memory, do not write to disk.
|
||||
* The password is zeroed after key derivation.
|
||||
* The key should only be used with the other functions in this module, as it
|
||||
|
@ -126,68 +181,53 @@ int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *
|
|||
* Note that this function is not deterministic; to derive the same key from a
|
||||
* password, you also must know the random salt that was used. See below.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key);
|
||||
bool tox_derive_key_from_pass(uint8_t *passphrase, size_t pplength, TOX_PASS_KEY *out_key,
|
||||
TOX_ERR_KEY_DERIVATION *error);
|
||||
|
||||
/* Same as above, except with use the given salt for deterministic key derivation.
|
||||
* The salt must be tox_salt_length() bytes in length.
|
||||
/* Same as above, except use the given salt for deterministic key derivation.
|
||||
* The salt must be TOX_PASS_SALT_LENGTH bytes in length.
|
||||
*/
|
||||
int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *salt, uint8_t *out_key);
|
||||
bool tox_derive_key_with_salt(uint8_t *passphrase, size_t pplength, uint8_t *salt, TOX_PASS_KEY *out_key,
|
||||
TOX_ERR_KEY_DERIVATION *error);
|
||||
|
||||
/* This retrieves the salt used to encrypt the given data, which can then be passed to
|
||||
* derive_key_with_salt to produce the same key as was previously used. Any encrpyted
|
||||
* data with this module can be used as input.
|
||||
*
|
||||
* returns -1 if the magic number is wrong
|
||||
* returns 0 otherwise (no guarantee about validity of data)
|
||||
* returns true if magic number matches
|
||||
* success does not say anything about the validity of the data, only that data of
|
||||
* the appropriate size was copied
|
||||
*/
|
||||
int tox_get_salt(uint8_t *data, uint8_t *salt);
|
||||
bool tox_get_salt(const uint8_t *data, uint8_t *salt);
|
||||
|
||||
/* Now come the functions that are analogous to the part 2 functions. */
|
||||
|
||||
/* Encrypt arbitrary with a key produced by tox_derive_key_. The output
|
||||
* array must be at least data_len + tox_pass_encryption_extra_length() bytes long.
|
||||
* key must be tox_pass_key_length() bytes.
|
||||
/* Encrypt arbitrary with a key produced by tox_derive_key_*. The output
|
||||
* array must be at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
|
||||
* key must be TOX_PASS_KEY_LENGTH bytes.
|
||||
* If you already have a symmetric key from somewhere besides this module, simply
|
||||
* call encrypt_data_symmetric in toxcore/crypto_core directly.
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *key, uint8_t *out);
|
||||
|
||||
/* Save the messenger data encrypted with the given key from tox_derive_key.
|
||||
* data must be at least tox_encrypted_size().
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key);
|
||||
bool tox_pass_key_encrypt(const uint8_t *data, size_t data_len, const TOX_PASS_KEY *key, uint8_t *out,
|
||||
TOX_ERR_ENCRYPTION *error);
|
||||
|
||||
/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by
|
||||
* tox_derive_key_from_pass.
|
||||
*
|
||||
* returns the length of the output data (== data_len - tox_pass_encryption_extra_length()) on success
|
||||
* returns -1 on failure
|
||||
*/
|
||||
int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *key, uint8_t *out);
|
||||
|
||||
/* Load the messenger from encrypted data of size length, with key from tox_derive_key.
|
||||
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
|
||||
*
|
||||
* returns 0 on success
|
||||
* returns -1 on failure
|
||||
* returns true on success
|
||||
*/
|
||||
int tox_encrypted_key_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *key);
|
||||
bool tox_pass_key_decrypt(const uint8_t *data, size_t length, const TOX_PASS_KEY *key, uint8_t *out,
|
||||
TOX_ERR_DECRYPTION *error);
|
||||
|
||||
/* Determines whether or not the given data is encrypted (by checking the magic number)
|
||||
*
|
||||
* returns 1 if it is encrypted
|
||||
* returns 0 otherwise
|
||||
*/
|
||||
int tox_is_data_encrypted(const uint8_t *data);
|
||||
int tox_is_save_encrypted(const uint8_t *data); // poorly-named alias for backwards compat (oh irony...)
|
||||
bool tox_is_data_encrypted(const uint8_t *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user