mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
Make saving and loading the responsibility of Tox rather than Messenger
This commit is contained in:
parent
aa5c782880
commit
744dc2f5da
|
@ -265,54 +265,11 @@ START_TEST(test_dht_state_saveloadsave)
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
START_TEST(test_messenger_state_saveloadsave)
|
|
||||||
{
|
|
||||||
/* validate that:
|
|
||||||
* a) saving stays within the confined space
|
|
||||||
* b) a save()d state can be load()ed back successfully
|
|
||||||
* c) a second save() is of equal size
|
|
||||||
* d) the second save() is of equal content */
|
|
||||||
const size_t extra = 64;
|
|
||||||
const size_t size = messenger_size(m);
|
|
||||||
VLA(uint8_t, buffer, size + 2 * extra);
|
|
||||||
memset(buffer, 0xCD, extra);
|
|
||||||
memset(buffer + extra + size, 0xCD, extra);
|
|
||||||
messenger_save(m, buffer + extra);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < extra; i++) {
|
|
||||||
ck_assert_msg(buffer[i] == 0xCD, "Buffer underwritten from messenger_save() @%u", (unsigned)i);
|
|
||||||
ck_assert_msg(buffer[extra + size + i] == 0xCD, "Buffer overwritten from messenger_save() @%u", (unsigned)i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const int res = messenger_load(m, buffer + extra, size);
|
|
||||||
|
|
||||||
if (res == -1) {
|
|
||||||
ck_assert_msg(res == 0, "Failed to load back stored buffer: res == -1");
|
|
||||||
} else {
|
|
||||||
const size_t offset = res >> 4;
|
|
||||||
const uint8_t *ptr = buffer + extra + offset;
|
|
||||||
ck_assert_msg(res == 0, "Failed to load back stored buffer: 0x%02x%02x%02x%02x%02x%02x%02x%02x @%u/%u, code %d",
|
|
||||||
ptr[-2], ptr[-1], ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5],
|
|
||||||
(unsigned)offset, (unsigned)size, res & 0x0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t size2 = messenger_size(m);
|
|
||||||
ck_assert_msg(size == size2, "Messenger \"grew\" in size from a store/load cycle: %u -> %u",
|
|
||||||
(unsigned)size, (unsigned)size2);
|
|
||||||
|
|
||||||
VLA(uint8_t, buffer2, size2);
|
|
||||||
messenger_save(m, buffer2);
|
|
||||||
|
|
||||||
ck_assert_msg(!memcmp(buffer + extra, buffer2, size), "Messenger state changed by store/load/store cycle");
|
|
||||||
}
|
|
||||||
END_TEST
|
|
||||||
|
|
||||||
static Suite *messenger_suite(void)
|
static Suite *messenger_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("Messenger");
|
Suite *s = suite_create("Messenger");
|
||||||
|
|
||||||
DEFTESTCASE(dht_state_saveloadsave);
|
DEFTESTCASE(dht_state_saveloadsave);
|
||||||
DEFTESTCASE(messenger_state_saveloadsave);
|
|
||||||
|
|
||||||
DEFTESTCASE(getself_name);
|
DEFTESTCASE(getself_name);
|
||||||
DEFTESTCASE(m_get_userstatus_size);
|
DEFTESTCASE(m_get_userstatus_size);
|
||||||
|
|
|
@ -100,10 +100,8 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
/* with optional --ipvx, now it can be 1-4 arguments... */
|
/* with optional --ipvx, now it can be 1-4 arguments... */
|
||||||
if ((argc != argvoffset + 2) && (argc != argvoffset + 4)) {
|
if (argc != argvoffset + 4) {
|
||||||
printf("Usage: %s [--ipv4|--ipv6] ip port public_key (of the DHT bootstrap node)\n", argv[0]);
|
printf("Usage: %s [--ipv4|--ipv6] ip port public_key (of the DHT bootstrap node)\n", argv[0]);
|
||||||
printf("or\n");
|
|
||||||
printf(" %s [--ipv4|--ipv6] Save.bak (to read Save.bak as state file)\n", argv[0]);
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,19 +132,6 @@ int main(int argc, char *argv[])
|
||||||
printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]);
|
printf("Failed to convert \"%s\" into an IP address. Exiting...\n", argv[argvoffset + 1]);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
FILE *file = fopen(argv[argvoffset + 1], "rb");
|
|
||||||
|
|
||||||
if (file == nullptr) {
|
|
||||||
printf("Failed to open \"%s\" - does it exist?\n", argv[argvoffset + 1]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int read;
|
|
||||||
uint8_t buffer[128000];
|
|
||||||
read = fread(buffer, 1, 128000, file);
|
|
||||||
printf("Messenger loaded: %i\n", messenger_load(m, buffer, read));
|
|
||||||
fclose(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_callback_friendrequest(m, print_request);
|
m_callback_friendrequest(m, print_request);
|
||||||
|
|
|
@ -2831,7 +2831,7 @@ uint32_t dht_size(const DHT *dht)
|
||||||
/* Save the DHT in data where data is an array of size dht_size(). */
|
/* Save the DHT in data where data is an array of size dht_size(). */
|
||||||
void dht_save(const DHT *dht, uint8_t *data)
|
void dht_save(const DHT *dht, uint8_t *data)
|
||||||
{
|
{
|
||||||
host_to_lendian32(data, DHT_STATE_COOKIE_GLOBAL);
|
host_to_lendian_bytes32(data, DHT_STATE_COOKIE_GLOBAL);
|
||||||
data += sizeof(uint32_t);
|
data += sizeof(uint32_t);
|
||||||
|
|
||||||
uint8_t *const old_data = data;
|
uint8_t *const old_data = data;
|
||||||
|
@ -2960,7 +2960,7 @@ int dht_load(DHT *dht, const uint8_t *data, uint32_t length)
|
||||||
|
|
||||||
if (length > cookie_len) {
|
if (length > cookie_len) {
|
||||||
uint32_t data32;
|
uint32_t data32;
|
||||||
lendian_to_host32(&data32, data);
|
lendian_bytes_to_host32(&data32, data);
|
||||||
|
|
||||||
if (data32 == DHT_STATE_COOKIE_GLOBAL) {
|
if (data32 == DHT_STATE_COOKIE_GLOBAL) {
|
||||||
return state_load(dht->log, dht_load_state_callback, dht, data + cookie_len,
|
return state_load(dht->log, dht_load_state_callback, dht, data + cookie_len,
|
||||||
|
|
|
@ -2739,10 +2739,6 @@ void do_messenger(Messenger *m, void *userdata)
|
||||||
|
|
||||||
/* new messenger format for load/save, more robust and forward compatible */
|
/* new messenger format for load/save, more robust and forward compatible */
|
||||||
|
|
||||||
#define MESSENGER_STATE_COOKIE_GLOBAL 0x15ed1b1f
|
|
||||||
|
|
||||||
#define MESSENGER_STATE_COOKIE_TYPE 0x01ce
|
|
||||||
|
|
||||||
#define SAVED_FRIEND_REQUEST_SIZE 1024
|
#define SAVED_FRIEND_REQUEST_SIZE 1024
|
||||||
#define NUM_SAVED_PATH_NODES 8
|
#define NUM_SAVED_PATH_NODES 8
|
||||||
|
|
||||||
|
@ -2881,7 +2877,7 @@ static uint32_t m_state_plugins_size(const Messenger *m)
|
||||||
* returns true on success
|
* returns true on success
|
||||||
* returns false on failure
|
* returns false on failure
|
||||||
*/
|
*/
|
||||||
bool m_register_state_plugin(Messenger *m, Messenger_State_Type type, m_state_size_cb size_callback,
|
bool m_register_state_plugin(Messenger *m, State_Type type, m_state_size_cb size_callback,
|
||||||
m_state_load_cb load_callback,
|
m_state_load_cb load_callback,
|
||||||
m_state_save_cb save_callback)
|
m_state_save_cb save_callback)
|
||||||
{
|
{
|
||||||
|
@ -2904,7 +2900,7 @@ bool m_register_state_plugin(Messenger *m, Messenger_State_Type type, m_state_si
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t m_plugin_size(const Messenger *m, Messenger_State_Type type)
|
static uint32_t m_plugin_size(const Messenger *m, State_Type type)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
|
for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
|
||||||
const Messenger_State_Plugin plugin = m->options.state_plugins[i];
|
const Messenger_State_Plugin plugin = m->options.state_plugins[i];
|
||||||
|
@ -2922,30 +2918,18 @@ static uint32_t m_plugin_size(const Messenger *m, Messenger_State_Type type)
|
||||||
/* return size of the messenger data (for saving) */
|
/* return size of the messenger data (for saving) */
|
||||||
uint32_t messenger_size(const Messenger *m)
|
uint32_t messenger_size(const Messenger *m)
|
||||||
{
|
{
|
||||||
const uint32_t size32 = sizeof(uint32_t);
|
return m_state_plugins_size(m);
|
||||||
const uint32_t sizesubhead = size32 * 2;
|
|
||||||
return size32 * 2 // global cookie
|
|
||||||
+ m_state_plugins_size(m)
|
|
||||||
+ sizesubhead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save the messenger in data of size Messenger_size(). */
|
/* Save the messenger in data of size messenger_size(). */
|
||||||
void messenger_save(const Messenger *m, uint8_t *data)
|
uint8_t *messenger_save(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
memset(data, 0, messenger_size(m));
|
|
||||||
|
|
||||||
const uint32_t size32 = sizeof(uint32_t);
|
|
||||||
|
|
||||||
// write cookie
|
|
||||||
memset(data, 0, size32);
|
|
||||||
data += size32;
|
|
||||||
host_to_lendian32(data, MESSENGER_STATE_COOKIE_GLOBAL);
|
|
||||||
data += size32;
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
|
for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
|
||||||
const Messenger_State_Plugin plugin = m->options.state_plugins[i];
|
const Messenger_State_Plugin plugin = m->options.state_plugins[i];
|
||||||
data = plugin.save(m, data);
|
data = plugin.save(m, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// nospam state plugin
|
// nospam state plugin
|
||||||
|
@ -2956,12 +2940,12 @@ static uint32_t nospam_keys_size(const Messenger *m)
|
||||||
|
|
||||||
static State_Load_Status load_nospam_keys(Messenger *m, const uint8_t *data, uint32_t length)
|
static State_Load_Status load_nospam_keys(Messenger *m, const uint8_t *data, uint32_t length)
|
||||||
{
|
{
|
||||||
if (length != m_plugin_size(m, MESSENGER_STATE_TYPE_NOSPAMKEYS)) {
|
if (length != m_plugin_size(m, STATE_TYPE_NOSPAMKEYS)) {
|
||||||
return STATE_LOAD_STATUS_ERROR;
|
return STATE_LOAD_STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t nospam;
|
uint32_t nospam;
|
||||||
lendian_to_host32(&nospam, data);
|
lendian_bytes_to_host32(&nospam, data);
|
||||||
set_nospam(m->fr, nospam);
|
set_nospam(m->fr, nospam);
|
||||||
load_secret_key(m->net_crypto, data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE);
|
load_secret_key(m->net_crypto, data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE);
|
||||||
|
|
||||||
|
@ -2974,11 +2958,11 @@ static State_Load_Status load_nospam_keys(Messenger *m, const uint8_t *data, uin
|
||||||
|
|
||||||
static uint8_t *save_nospam_keys(const Messenger *m, uint8_t *data)
|
static uint8_t *save_nospam_keys(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
const uint32_t len = m_plugin_size(m, MESSENGER_STATE_TYPE_NOSPAMKEYS);
|
const uint32_t len = m_plugin_size(m, STATE_TYPE_NOSPAMKEYS);
|
||||||
assert(sizeof(get_nospam(m->fr)) == sizeof(uint32_t));
|
assert(sizeof(get_nospam(m->fr)) == sizeof(uint32_t));
|
||||||
data = state_write_section_header(data, MESSENGER_STATE_COOKIE_TYPE, len, MESSENGER_STATE_TYPE_NOSPAMKEYS);
|
data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_NOSPAMKEYS);
|
||||||
uint32_t nospam = get_nospam(m->fr);
|
uint32_t nospam = get_nospam(m->fr);
|
||||||
host_to_lendian32(data, nospam);
|
host_to_lendian_bytes32(data, nospam);
|
||||||
save_keys(m->net_crypto, data + sizeof(uint32_t));
|
save_keys(m->net_crypto, data + sizeof(uint32_t));
|
||||||
data += len;
|
data += len;
|
||||||
return data;
|
return data;
|
||||||
|
@ -2992,8 +2976,8 @@ static uint32_t m_dht_size(const Messenger *m)
|
||||||
|
|
||||||
static uint8_t *save_dht(const Messenger *m, uint8_t *data)
|
static uint8_t *save_dht(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
const uint32_t len = m_plugin_size(m, MESSENGER_STATE_TYPE_DHT);
|
const uint32_t len = m_plugin_size(m, STATE_TYPE_DHT);
|
||||||
data = state_write_section_header(data, MESSENGER_STATE_COOKIE_TYPE, len, MESSENGER_STATE_TYPE_DHT);
|
data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_DHT);
|
||||||
dht_save(m->dht, data);
|
dht_save(m->dht, data);
|
||||||
data += len;
|
data += len;
|
||||||
return data;
|
return data;
|
||||||
|
@ -3013,8 +2997,8 @@ static uint32_t saved_friendslist_size(const Messenger *m)
|
||||||
|
|
||||||
static uint8_t *friends_list_save(const Messenger *m, uint8_t *data)
|
static uint8_t *friends_list_save(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
const uint32_t len = m_plugin_size(m, MESSENGER_STATE_TYPE_FRIENDS);
|
const uint32_t len = m_plugin_size(m, STATE_TYPE_FRIENDS);
|
||||||
data = state_write_section_header(data, MESSENGER_STATE_COOKIE_TYPE, len, MESSENGER_STATE_TYPE_FRIENDS);
|
data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_FRIENDS);
|
||||||
|
|
||||||
uint32_t num = 0;
|
uint32_t num = 0;
|
||||||
uint8_t *cur_data = data;
|
uint8_t *cur_data = data;
|
||||||
|
@ -3119,8 +3103,8 @@ static uint32_t name_size(const Messenger *m)
|
||||||
|
|
||||||
static uint8_t *save_name(const Messenger *m, uint8_t *data)
|
static uint8_t *save_name(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
const uint32_t len = m_plugin_size(m, MESSENGER_STATE_TYPE_NAME);
|
const uint32_t len = m_plugin_size(m, STATE_TYPE_NAME);
|
||||||
data = state_write_section_header(data, MESSENGER_STATE_COOKIE_TYPE, len, MESSENGER_STATE_TYPE_NAME);
|
data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_NAME);
|
||||||
memcpy(data, m->name, len);
|
memcpy(data, m->name, len);
|
||||||
data += len;
|
data += len;
|
||||||
return data;
|
return data;
|
||||||
|
@ -3143,8 +3127,8 @@ static uint32_t status_message_size(const Messenger *m)
|
||||||
|
|
||||||
static uint8_t *save_status_message(const Messenger *m, uint8_t *data)
|
static uint8_t *save_status_message(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
const uint32_t len = m_plugin_size(m, MESSENGER_STATE_TYPE_STATUSMESSAGE);
|
const uint32_t len = m_plugin_size(m, STATE_TYPE_STATUSMESSAGE);
|
||||||
data = state_write_section_header(data, MESSENGER_STATE_COOKIE_TYPE, len, MESSENGER_STATE_TYPE_STATUSMESSAGE);
|
data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_STATUSMESSAGE);
|
||||||
memcpy(data, m->statusmessage, len);
|
memcpy(data, m->statusmessage, len);
|
||||||
data += len;
|
data += len;
|
||||||
return data;
|
return data;
|
||||||
|
@ -3167,8 +3151,8 @@ static uint32_t status_size(const Messenger *m)
|
||||||
|
|
||||||
static uint8_t *save_status(const Messenger *m, uint8_t *data)
|
static uint8_t *save_status(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
const uint32_t len = m_plugin_size(m, MESSENGER_STATE_TYPE_STATUS);
|
const uint32_t len = m_plugin_size(m, STATE_TYPE_STATUS);
|
||||||
data = state_write_section_header(data, MESSENGER_STATE_COOKIE_TYPE, len, MESSENGER_STATE_TYPE_STATUS);
|
data = state_write_section_header(data, STATE_COOKIE_TYPE, len, STATE_TYPE_STATUS);
|
||||||
*data = m->userstatus;
|
*data = m->userstatus;
|
||||||
data += len;
|
data += len;
|
||||||
return data;
|
return data;
|
||||||
|
@ -3193,7 +3177,7 @@ static uint8_t *save_tcp_relays(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
Node_format relays[NUM_SAVED_TCP_RELAYS];
|
Node_format relays[NUM_SAVED_TCP_RELAYS];
|
||||||
uint8_t *temp_data = data;
|
uint8_t *temp_data = data;
|
||||||
data = state_write_section_header(temp_data, MESSENGER_STATE_COOKIE_TYPE, 0, MESSENGER_STATE_TYPE_TCP_RELAY);
|
data = state_write_section_header(temp_data, STATE_COOKIE_TYPE, 0, STATE_TYPE_TCP_RELAY);
|
||||||
uint32_t num = copy_connected_tcp_relays(m->net_crypto, relays, NUM_SAVED_TCP_RELAYS);
|
uint32_t num = copy_connected_tcp_relays(m->net_crypto, relays, NUM_SAVED_TCP_RELAYS);
|
||||||
|
|
||||||
if (m->num_loaded_relays > 0) {
|
if (m->num_loaded_relays > 0) {
|
||||||
|
@ -3205,7 +3189,7 @@ static uint8_t *save_tcp_relays(const Messenger *m, uint8_t *data)
|
||||||
|
|
||||||
if (l > 0) {
|
if (l > 0) {
|
||||||
const uint32_t len = l;
|
const uint32_t len = l;
|
||||||
data = state_write_section_header(temp_data, MESSENGER_STATE_COOKIE_TYPE, len, MESSENGER_STATE_TYPE_TCP_RELAY);
|
data = state_write_section_header(temp_data, STATE_COOKIE_TYPE, len, STATE_TYPE_TCP_RELAY);
|
||||||
data += len;
|
data += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3232,14 +3216,14 @@ static uint8_t *save_path_nodes(const Messenger *m, uint8_t *data)
|
||||||
{
|
{
|
||||||
Node_format nodes[NUM_SAVED_PATH_NODES];
|
Node_format nodes[NUM_SAVED_PATH_NODES];
|
||||||
uint8_t *temp_data = data;
|
uint8_t *temp_data = data;
|
||||||
data = state_write_section_header(data, MESSENGER_STATE_COOKIE_TYPE, 0, MESSENGER_STATE_TYPE_PATH_NODE);
|
data = state_write_section_header(data, STATE_COOKIE_TYPE, 0, STATE_TYPE_PATH_NODE);
|
||||||
memset(nodes, 0, sizeof(nodes));
|
memset(nodes, 0, sizeof(nodes));
|
||||||
const unsigned int num = onion_backup_nodes(m->onion_c, nodes, NUM_SAVED_PATH_NODES);
|
const unsigned int num = onion_backup_nodes(m->onion_c, nodes, NUM_SAVED_PATH_NODES);
|
||||||
const int l = pack_nodes(data, NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6), nodes, num);
|
const int l = pack_nodes(data, NUM_SAVED_PATH_NODES * packed_node_size(net_family_tcp_ipv6), nodes, num);
|
||||||
|
|
||||||
if (l > 0) {
|
if (l > 0) {
|
||||||
const uint32_t len = l;
|
const uint32_t len = l;
|
||||||
data = state_write_section_header(temp_data, MESSENGER_STATE_COOKIE_TYPE, len, MESSENGER_STATE_TYPE_PATH_NODE);
|
data = state_write_section_header(temp_data, STATE_COOKIE_TYPE, len, STATE_TYPE_PATH_NODE);
|
||||||
data += len;
|
data += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3261,77 +3245,32 @@ static State_Load_Status load_path_nodes(Messenger *m, const uint8_t *data, uint
|
||||||
return STATE_LOAD_STATUS_CONTINUE;
|
return STATE_LOAD_STATUS_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// end state plugin
|
|
||||||
static uint32_t end_size(const Messenger *m)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t *save_end(const Messenger *m, uint8_t *data)
|
|
||||||
{
|
|
||||||
return state_write_section_header(data, MESSENGER_STATE_COOKIE_TYPE, 0, MESSENGER_STATE_TYPE_END);
|
|
||||||
}
|
|
||||||
|
|
||||||
static State_Load_Status load_end(Messenger *m, const uint8_t *data, uint32_t length)
|
|
||||||
{
|
|
||||||
if (length != 0) {
|
|
||||||
return STATE_LOAD_STATUS_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return STATE_LOAD_STATUS_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void m_register_default_plugins(Messenger *m)
|
static void m_register_default_plugins(Messenger *m)
|
||||||
{
|
{
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_NOSPAMKEYS, nospam_keys_size, load_nospam_keys, save_nospam_keys);
|
m_register_state_plugin(m, STATE_TYPE_NOSPAMKEYS, nospam_keys_size, load_nospam_keys, save_nospam_keys);
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_DHT, m_dht_size, m_dht_load, save_dht);
|
m_register_state_plugin(m, STATE_TYPE_DHT, m_dht_size, m_dht_load, save_dht);
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_FRIENDS, saved_friendslist_size, friends_list_load, friends_list_save);
|
m_register_state_plugin(m, STATE_TYPE_FRIENDS, saved_friendslist_size, friends_list_load, friends_list_save);
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_NAME, name_size, load_name, save_name);
|
m_register_state_plugin(m, STATE_TYPE_NAME, name_size, load_name, save_name);
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_STATUSMESSAGE, status_message_size, load_status_message,
|
m_register_state_plugin(m, STATE_TYPE_STATUSMESSAGE, status_message_size, load_status_message,
|
||||||
save_status_message);
|
save_status_message);
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_STATUS, status_size, load_status, save_status);
|
m_register_state_plugin(m, STATE_TYPE_STATUS, status_size, load_status, save_status);
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_TCP_RELAY, tcp_relay_size, load_tcp_relays, save_tcp_relays);
|
m_register_state_plugin(m, STATE_TYPE_TCP_RELAY, tcp_relay_size, load_tcp_relays, save_tcp_relays);
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_PATH_NODE, path_node_size, load_path_nodes, save_path_nodes);
|
m_register_state_plugin(m, STATE_TYPE_PATH_NODE, path_node_size, load_path_nodes, save_path_nodes);
|
||||||
m_register_state_plugin(m, MESSENGER_STATE_TYPE_END, end_size, load_end, save_end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static State_Load_Status messenger_load_state_callback(void *outer, const uint8_t *data, uint32_t length, uint16_t type)
|
bool messenger_load_state_section(Messenger *m, const uint8_t *data, uint32_t length, uint16_t type,
|
||||||
|
State_Load_Status *status)
|
||||||
{
|
{
|
||||||
Messenger *m = (Messenger *)outer;
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
|
for (uint8_t i = 0; i < m->options.state_plugins_length; ++i) {
|
||||||
const Messenger_State_Plugin *const plugin = &m->options.state_plugins[i];
|
const Messenger_State_Plugin *const plugin = &m->options.state_plugins[i];
|
||||||
|
|
||||||
if (plugin->type == type) {
|
if (plugin->type == type) {
|
||||||
return plugin->load(m, data, length);
|
*status = plugin->load(m, data, length);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER_ERROR(m->log, "Load state: contains unrecognized part (len %u, type %u)\n",
|
return false;
|
||||||
length, type);
|
|
||||||
|
|
||||||
return STATE_LOAD_STATUS_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load the messenger from data of size length. */
|
|
||||||
int messenger_load(Messenger *m, const uint8_t *data, uint32_t length)
|
|
||||||
{
|
|
||||||
uint32_t data32[2];
|
|
||||||
uint32_t cookie_len = sizeof(data32);
|
|
||||||
|
|
||||||
if (length < cookie_len) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(data32, data, sizeof(uint32_t));
|
|
||||||
lendian_to_host32(data32 + 1, data + sizeof(uint32_t));
|
|
||||||
|
|
||||||
if (!data32[0] && (data32[1] == MESSENGER_STATE_COOKIE_GLOBAL)) {
|
|
||||||
return state_load(m->log, messenger_load_state_callback, m, data + cookie_len,
|
|
||||||
length - cookie_len, MESSENGER_STATE_COOKIE_TYPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the number of friends in the instance m.
|
/* Return the number of friends in the instance m.
|
||||||
|
|
|
@ -62,20 +62,8 @@ typedef uint8_t *m_state_save_cb(const Messenger *m, uint8_t *data);
|
||||||
// Returns if there were any erros during loading
|
// Returns if there were any erros during loading
|
||||||
typedef State_Load_Status m_state_load_cb(Messenger *m, const uint8_t *data, uint32_t length);
|
typedef State_Load_Status m_state_load_cb(Messenger *m, const uint8_t *data, uint32_t length);
|
||||||
|
|
||||||
typedef enum Messenger_State_Type {
|
|
||||||
MESSENGER_STATE_TYPE_NOSPAMKEYS = 1,
|
|
||||||
MESSENGER_STATE_TYPE_DHT = 2,
|
|
||||||
MESSENGER_STATE_TYPE_FRIENDS = 3,
|
|
||||||
MESSENGER_STATE_TYPE_NAME = 4,
|
|
||||||
MESSENGER_STATE_TYPE_STATUSMESSAGE = 5,
|
|
||||||
MESSENGER_STATE_TYPE_STATUS = 6,
|
|
||||||
MESSENGER_STATE_TYPE_TCP_RELAY = 10,
|
|
||||||
MESSENGER_STATE_TYPE_PATH_NODE = 11,
|
|
||||||
MESSENGER_STATE_TYPE_END = 255,
|
|
||||||
} Messenger_State_Type;
|
|
||||||
|
|
||||||
typedef struct Messenger_State_Plugin {
|
typedef struct Messenger_State_Plugin {
|
||||||
Messenger_State_Type type;
|
State_Type type;
|
||||||
m_state_size_cb *size;
|
m_state_size_cb *size;
|
||||||
m_state_save_cb *save;
|
m_state_save_cb *save;
|
||||||
m_state_load_cb *load;
|
m_state_load_cb *load;
|
||||||
|
@ -797,17 +785,22 @@ uint32_t messenger_run_interval(const Messenger *m);
|
||||||
* returns true on success
|
* returns true on success
|
||||||
* returns false on error
|
* returns false on error
|
||||||
*/
|
*/
|
||||||
bool m_register_state_plugin(Messenger *m, Messenger_State_Type type, m_state_size_cb size_callback,
|
bool m_register_state_plugin(Messenger *m, State_Type type, m_state_size_cb size_callback,
|
||||||
m_state_load_cb load_callback, m_state_save_cb save_callback);
|
m_state_load_cb load_callback, m_state_save_cb save_callback);
|
||||||
|
|
||||||
/* return size of the messenger data (for saving). */
|
/* return size of the messenger data (for saving). */
|
||||||
uint32_t messenger_size(const Messenger *m);
|
uint32_t messenger_size(const Messenger *m);
|
||||||
|
|
||||||
/* Save the messenger in data (must be allocated memory of size Messenger_size()) */
|
/* Save the messenger in data (must be allocated memory of size at least Messenger_size()) */
|
||||||
void messenger_save(const Messenger *m, uint8_t *data);
|
uint8_t *messenger_save(const Messenger *m, uint8_t *data);
|
||||||
|
|
||||||
/* Load the messenger from data of size length. */
|
/* Load a state section.
|
||||||
int messenger_load(Messenger *m, const uint8_t *data, uint32_t length);
|
*
|
||||||
|
* @param status Result of loading section is stored here if the section is handled.
|
||||||
|
* @return true iff section handled.
|
||||||
|
*/
|
||||||
|
bool messenger_load_state_section(Messenger *m, const uint8_t *data, uint32_t length, uint16_t type,
|
||||||
|
State_Load_Status *status);
|
||||||
|
|
||||||
/* Return the number of friends in the instance m.
|
/* Return the number of friends in the instance m.
|
||||||
* You should use this to determine how much memory to allocate
|
* You should use this to determine how much memory to allocate
|
||||||
|
|
|
@ -2875,6 +2875,22 @@ void send_name_all_groups(Group_Chats *g_c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t conferences_size(const Group_Chats *g_c)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data)
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type,
|
||||||
|
State_Load_Status *status)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create new groupchat instance. */
|
/* Create new groupchat instance. */
|
||||||
Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m)
|
Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m)
|
||||||
{
|
{
|
||||||
|
|
|
@ -448,6 +448,21 @@ int callback_groupchat_peer_delete(Group_Chats *g_c, uint32_t groupnumber, peer_
|
||||||
*/
|
*/
|
||||||
int callback_groupchat_delete(Group_Chats *g_c, uint32_t groupnumber, group_on_delete_cb *function);
|
int callback_groupchat_delete(Group_Chats *g_c, uint32_t groupnumber, group_on_delete_cb *function);
|
||||||
|
|
||||||
|
/* Return size of the conferences data (for saving). */
|
||||||
|
uint32_t conferences_size(const Group_Chats *g_c);
|
||||||
|
|
||||||
|
/* Save the conferences in data (must be allocated memory of size at least conferences_size()) */
|
||||||
|
uint8_t *conferences_save(const Group_Chats *g_c, uint8_t *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a section.
|
||||||
|
*
|
||||||
|
* @param status Result of loading section is stored here if the section is handled.
|
||||||
|
* @return true iff section handled.
|
||||||
|
*/
|
||||||
|
bool conferences_load_state_section(Group_Chats *g_c, const uint8_t *data, uint32_t length, uint16_t type,
|
||||||
|
State_Load_Status *status);
|
||||||
|
|
||||||
/* Create new groupchat instance. */
|
/* Create new groupchat instance. */
|
||||||
Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m);
|
Group_Chats *new_groupchats(Mono_Time *mono_time, Messenger *m);
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,10 @@ int state_load(const Logger *log, state_load_cb *state_load_callback, void *oute
|
||||||
|
|
||||||
while (length >= size_head) {
|
while (length >= size_head) {
|
||||||
uint32_t length_sub;
|
uint32_t length_sub;
|
||||||
lendian_to_host32(&length_sub, data);
|
lendian_bytes_to_host32(&length_sub, data);
|
||||||
|
|
||||||
uint32_t cookie_type;
|
uint32_t cookie_type;
|
||||||
lendian_to_host32(&cookie_type, data + sizeof(uint32_t));
|
lendian_bytes_to_host32(&cookie_type, data + sizeof(uint32_t));
|
||||||
|
|
||||||
data += size_head;
|
data += size_head;
|
||||||
length -= size_head;
|
length -= size_head;
|
||||||
|
@ -63,9 +63,9 @@ int state_load(const Logger *log, state_load_cb *state_load_callback, void *oute
|
||||||
|
|
||||||
uint8_t *state_write_section_header(uint8_t *data, uint16_t cookie_type, uint32_t len, uint32_t section_type)
|
uint8_t *state_write_section_header(uint8_t *data, uint16_t cookie_type, uint32_t len, uint32_t section_type)
|
||||||
{
|
{
|
||||||
host_to_lendian32(data, len);
|
host_to_lendian_bytes32(data, len);
|
||||||
data += sizeof(uint32_t);
|
data += sizeof(uint32_t);
|
||||||
host_to_lendian32(data, (host_tolendian16(cookie_type) << 16) | host_tolendian16(section_type));
|
host_to_lendian_bytes32(data, (host_to_lendian16(cookie_type) << 16) | host_to_lendian16(section_type));
|
||||||
data += sizeof(uint32_t);
|
data += sizeof(uint32_t);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -79,12 +79,12 @@ uint16_t lendian_to_host16(uint16_t lendian)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t host_tolendian16(uint16_t host)
|
uint16_t host_to_lendian16(uint16_t host)
|
||||||
{
|
{
|
||||||
return lendian_to_host16(host);
|
return lendian_to_host16(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
void host_to_lendian32(uint8_t *dest, uint32_t num)
|
void host_to_lendian_bytes32(uint8_t *dest, uint32_t num)
|
||||||
{
|
{
|
||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
num = ((num << 8) & 0xFF00FF00) | ((num >> 8) & 0xFF00FF);
|
num = ((num << 8) & 0xFF00FF00) | ((num >> 8) & 0xFF00FF);
|
||||||
|
@ -93,7 +93,7 @@ void host_to_lendian32(uint8_t *dest, uint32_t num)
|
||||||
memcpy(dest, &num, sizeof(uint32_t));
|
memcpy(dest, &num, sizeof(uint32_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void lendian_to_host32(uint32_t *dest, const uint8_t *lendian)
|
void lendian_bytes_to_host32(uint32_t *dest, const uint8_t *lendian)
|
||||||
{
|
{
|
||||||
uint32_t d;
|
uint32_t d;
|
||||||
memcpy(&d, lendian, sizeof(uint32_t));
|
memcpy(&d, lendian, sizeof(uint32_t));
|
||||||
|
@ -103,3 +103,21 @@ void lendian_to_host32(uint32_t *dest, const uint8_t *lendian)
|
||||||
#endif
|
#endif
|
||||||
*dest = d;
|
*dest = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void host_to_lendian_bytes16(uint8_t *dest, uint16_t num)
|
||||||
|
{
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
num = (num << 8) | (num >> 8);
|
||||||
|
#endif
|
||||||
|
memcpy(dest, &num, sizeof(uint16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void lendian_bytes_to_host16(uint16_t *dest, const uint8_t *lendian)
|
||||||
|
{
|
||||||
|
uint16_t d;
|
||||||
|
memcpy(&d, lendian, sizeof(uint16_t));
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
d = (d << 8) | (d >> 8);
|
||||||
|
#endif
|
||||||
|
*dest = d;
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,22 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define STATE_COOKIE_GLOBAL 0x15ed1b1f
|
||||||
|
|
||||||
|
#define STATE_COOKIE_TYPE 0x01ce
|
||||||
|
|
||||||
|
typedef enum State_Type {
|
||||||
|
STATE_TYPE_NOSPAMKEYS = 1,
|
||||||
|
STATE_TYPE_DHT = 2,
|
||||||
|
STATE_TYPE_FRIENDS = 3,
|
||||||
|
STATE_TYPE_NAME = 4,
|
||||||
|
STATE_TYPE_STATUSMESSAGE = 5,
|
||||||
|
STATE_TYPE_STATUS = 6,
|
||||||
|
STATE_TYPE_TCP_RELAY = 10,
|
||||||
|
STATE_TYPE_PATH_NODE = 11,
|
||||||
|
STATE_TYPE_END = 255,
|
||||||
|
} State_Type;
|
||||||
|
|
||||||
// Returned by the state_load_cb to instruct the loader on what to do next.
|
// Returned by the state_load_cb to instruct the loader on what to do next.
|
||||||
typedef enum State_Load_Status {
|
typedef enum State_Load_Status {
|
||||||
// Continue loading state data sections.
|
// Continue loading state data sections.
|
||||||
|
@ -39,10 +55,13 @@ uint8_t *state_write_section_header(uint8_t *data, uint16_t cookie_type, uint32_
|
||||||
// Utilities for state data serialisation.
|
// Utilities for state data serialisation.
|
||||||
|
|
||||||
uint16_t lendian_to_host16(uint16_t lendian);
|
uint16_t lendian_to_host16(uint16_t lendian);
|
||||||
uint16_t host_tolendian16(uint16_t host);
|
uint16_t host_to_lendian16(uint16_t host);
|
||||||
|
|
||||||
void host_to_lendian32(uint8_t *dest, uint32_t num);
|
void host_to_lendian_bytes32(uint8_t *dest, uint32_t num);
|
||||||
void lendian_to_host32(uint32_t *dest, const uint8_t *lendian);
|
void lendian_bytes_to_host32(uint32_t *dest, const uint8_t *lendian);
|
||||||
|
|
||||||
|
void host_to_lendian_bytes16(uint8_t *dest, uint16_t num);
|
||||||
|
void lendian_bytes_to_host16(uint16_t *dest, const uint8_t *lendian);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
|
@ -328,6 +328,51 @@ bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch)
|
||||||
return TOX_VERSION_IS_API_COMPATIBLE(major, minor, patch);
|
return TOX_VERSION_IS_API_COMPATIBLE(major, minor, patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static State_Load_Status state_load_callback(void *outer, const uint8_t *data, uint32_t length, uint16_t type)
|
||||||
|
{
|
||||||
|
Tox *tox = (Tox *)outer;
|
||||||
|
State_Load_Status status = STATE_LOAD_STATUS_CONTINUE;
|
||||||
|
|
||||||
|
if (messenger_load_state_section(tox->m, data, length, type, &status)
|
||||||
|
|| conferences_load_state_section(tox->m->conferences_object, data, length, type, &status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == STATE_TYPE_END) {
|
||||||
|
if (length != 0) {
|
||||||
|
return STATE_LOAD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATE_LOAD_STATUS_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER_ERROR(tox->m->log, "Load state: contains unrecognized part (len %u, type %u)\n",
|
||||||
|
length, type);
|
||||||
|
|
||||||
|
return STATE_LOAD_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load tox from data of size length. */
|
||||||
|
static int tox_load(Tox *tox, const uint8_t *data, uint32_t length)
|
||||||
|
{
|
||||||
|
uint32_t data32[2];
|
||||||
|
uint32_t cookie_len = sizeof(data32);
|
||||||
|
|
||||||
|
if (length < cookie_len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data32, data, sizeof(uint32_t));
|
||||||
|
lendian_bytes_to_host32(data32 + 1, data + sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (data32[0] != 0 || data32[1] != STATE_COOKIE_GLOBAL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state_load(tox->m->log, state_load_callback, tox, data + cookie_len,
|
||||||
|
length - cookie_len, STATE_COOKIE_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
|
Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
|
||||||
{
|
{
|
||||||
|
@ -488,7 +533,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (load_savedata_tox
|
if (load_savedata_tox
|
||||||
&& messenger_load(m, tox_options_get_savedata_data(opts), tox_options_get_savedata_length(opts)) == -1) {
|
&& tox_load(tox, tox_options_get_savedata_data(opts), tox_options_get_savedata_length(opts)) == -1) {
|
||||||
SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_BAD_FORMAT);
|
SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_BAD_FORMAT);
|
||||||
} else if (load_savedata_sk) {
|
} else if (load_savedata_sk) {
|
||||||
load_secret_key(m->net_crypto, tox_options_get_savedata_data(opts));
|
load_secret_key(m->net_crypto, tox_options_get_savedata_data(opts));
|
||||||
|
@ -537,18 +582,45 @@ void tox_kill(Tox *tox)
|
||||||
free(tox);
|
free(tox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t end_size(void)
|
||||||
|
{
|
||||||
|
return 2 * sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void end_save(uint8_t *data)
|
||||||
|
{
|
||||||
|
state_write_section_header(data, STATE_COOKIE_TYPE, 0, STATE_TYPE_END);
|
||||||
|
}
|
||||||
|
|
||||||
size_t tox_get_savedata_size(const Tox *tox)
|
size_t tox_get_savedata_size(const Tox *tox)
|
||||||
{
|
{
|
||||||
const Messenger *m = tox->m;
|
const Messenger *m = tox->m;
|
||||||
return messenger_size(m);
|
return 2 * sizeof(uint32_t)
|
||||||
|
+ messenger_size(m)
|
||||||
|
+ conferences_size(m->conferences_object)
|
||||||
|
+ end_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void tox_get_savedata(const Tox *tox, uint8_t *savedata)
|
void tox_get_savedata(const Tox *tox, uint8_t *savedata)
|
||||||
{
|
{
|
||||||
if (savedata) {
|
if (savedata == nullptr) {
|
||||||
const Messenger *m = tox->m;
|
return;
|
||||||
messenger_save(m, savedata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(savedata, 0, tox_get_savedata_size(tox));
|
||||||
|
|
||||||
|
const uint32_t size32 = sizeof(uint32_t);
|
||||||
|
|
||||||
|
// write cookie
|
||||||
|
memset(savedata, 0, size32);
|
||||||
|
savedata += size32;
|
||||||
|
host_to_lendian_bytes32(savedata, STATE_COOKIE_GLOBAL);
|
||||||
|
savedata += size32;
|
||||||
|
|
||||||
|
const Messenger *m = tox->m;
|
||||||
|
savedata = messenger_save(m, savedata);
|
||||||
|
savedata = conferences_save(m->conferences_object, savedata);
|
||||||
|
end_save(savedata);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, Tox_Err_Bootstrap *error)
|
bool tox_bootstrap(Tox *tox, const char *host, uint16_t port, const uint8_t *public_key, Tox_Err_Bootstrap *error)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user