mirror of
https://github.com/Tha14/toxic.git
synced 2024-11-22 21:03:02 +01:00
Refactor DHT bootstrap code
- Separate node list loading from connecting - Put code in its own source file - Rename a few functions
This commit is contained in:
parent
98154b3cba
commit
1f02bb2be5
2
Makefile
2
Makefile
@ -14,7 +14,7 @@ LDFLAGS = $(USER_LDFLAGS)
|
|||||||
OBJ = chat.o chat_commands.o configdir.o execute.o file_transfers.o notify.o
|
OBJ = chat.o chat_commands.o configdir.o execute.o file_transfers.o notify.o
|
||||||
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
|
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
|
||||||
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
|
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
|
||||||
OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.o
|
OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.o bootstrap.o
|
||||||
|
|
||||||
# Check on wich system we are running
|
# Check on wich system we are running
|
||||||
UNAME_S = $(shell uname -s)
|
UNAME_S = $(shell uname -s)
|
||||||
|
151
src/bootstrap.c
Normal file
151
src/bootstrap.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/* bootstrap.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic 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.
|
||||||
|
*
|
||||||
|
* Toxic 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 Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern struct arg_opts arg_opts;
|
||||||
|
|
||||||
|
|
||||||
|
/* Time to wait between bootstrap attempts */
|
||||||
|
#define TRY_BOOTSTRAP_INTERVAL 5
|
||||||
|
|
||||||
|
|
||||||
|
#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */
|
||||||
|
#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
||||||
|
#define MAXNODES 50
|
||||||
|
#define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7)
|
||||||
|
|
||||||
|
static struct toxNodes {
|
||||||
|
size_t lines;
|
||||||
|
char nodes[MAXNODES][NODELEN];
|
||||||
|
uint16_t ports[MAXNODES];
|
||||||
|
char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE];
|
||||||
|
} toxNodes;
|
||||||
|
|
||||||
|
/* Load the DHT nodelist to memory.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if nodelist file cannot be opened.
|
||||||
|
* Return -2 if nodelist file does not contain any valid node entries.
|
||||||
|
*/
|
||||||
|
int load_DHT_nodelist(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
const char *filename = !arg_opts.nodes_path[0] ? PACKAGE_DATADIR "/DHTnodes" : arg_opts.nodes_path;
|
||||||
|
|
||||||
|
FILE *fp = fopen(filename, "r");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char line[MAX_NODE_LINE];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
|
||||||
|
size_t line_len = strlen(line);
|
||||||
|
|
||||||
|
if (line_len >= MIN_NODE_LINE && line_len <= MAX_NODE_LINE) {
|
||||||
|
const char *name = strtok(line, " ");
|
||||||
|
const char *port_str = strtok(NULL, " ");
|
||||||
|
const char *key_ascii = strtok(NULL, " ");
|
||||||
|
|
||||||
|
if (name == NULL || port_str == NULL || key_ascii == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
long int port = strtol(port_str, NULL, 10);
|
||||||
|
|
||||||
|
if (port <= 0 || port > MAX_PORT_RANGE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
size_t key_len = strlen(key_ascii);
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
|
||||||
|
if (key_len < TOX_PUBLIC_KEY_SIZE * 2 || name_len >= NODELEN)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
|
||||||
|
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
|
||||||
|
toxNodes.ports[toxNodes.lines] = port;
|
||||||
|
|
||||||
|
/* remove possible trailing newline from key string */
|
||||||
|
char real_ascii_key[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||||
|
memcpy(real_ascii_key, key_ascii, TOX_PUBLIC_KEY_SIZE * 2);
|
||||||
|
key_len = TOX_PUBLIC_KEY_SIZE * 2;
|
||||||
|
real_ascii_key[key_len] = '\0';
|
||||||
|
|
||||||
|
if (hex_string_to_bin(real_ascii_key, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
toxNodes.lines++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (toxNodes.lines == 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connects to a random DHT node listed in the DHTnodes file. */
|
||||||
|
static void DHT_bootstrap(Tox *m)
|
||||||
|
{
|
||||||
|
if (toxNodes.lines == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t node = rand() % toxNodes.lines;
|
||||||
|
|
||||||
|
TOX_ERR_BOOTSTRAP err;
|
||||||
|
tox_bootstrap(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]);
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_add_tcp_relay(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manages connection to the Tox DHT network. */
|
||||||
|
void do_tox_connection(Tox *m)
|
||||||
|
{
|
||||||
|
static uint64_t last_bootstrap_time = 0;
|
||||||
|
bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE;
|
||||||
|
|
||||||
|
if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) {
|
||||||
|
DHT_bootstrap(m);
|
||||||
|
last_bootstrap_time = get_unix_time();
|
||||||
|
}
|
||||||
|
}
|
32
src/bootstrap.h
Normal file
32
src/bootstrap.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* bootstrap.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic 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.
|
||||||
|
*
|
||||||
|
* Toxic 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 Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Manages connection to the Tox DHT network. */
|
||||||
|
void do_tox_connection(Tox *m);
|
||||||
|
|
||||||
|
/* Load the DHT nodelist to memory.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if nodelist file cannot be opened.
|
||||||
|
* Return -2 if nodelist file does not contain any valid node entries.
|
||||||
|
*/
|
||||||
|
int load_DHT_nodelist(void);
|
193
src/toxic.c
193
src/toxic.c
@ -60,6 +60,7 @@
|
|||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "term_mplex.h"
|
#include "term_mplex.h"
|
||||||
#include "name_lookup.h"
|
#include "name_lookup.h"
|
||||||
|
#include "bootstrap.h"
|
||||||
|
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
#include "xtra.h"
|
#include "xtra.h"
|
||||||
@ -280,153 +281,6 @@ static void print_init_messages(ToxWindow *toxwin)
|
|||||||
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
|
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */
|
|
||||||
#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
|
||||||
#define MAXNODES 50
|
|
||||||
#define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7)
|
|
||||||
|
|
||||||
static struct toxNodes {
|
|
||||||
int lines;
|
|
||||||
char nodes[MAXNODES][NODELEN];
|
|
||||||
uint16_t ports[MAXNODES];
|
|
||||||
char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE];
|
|
||||||
} toxNodes;
|
|
||||||
|
|
||||||
static int load_nodelist(const char *filename)
|
|
||||||
{
|
|
||||||
if (!filename)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
FILE *fp = fopen(filename, "r");
|
|
||||||
|
|
||||||
if (fp == NULL)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
char line[MAX_NODE_LINE];
|
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
|
|
||||||
size_t line_len = strlen(line);
|
|
||||||
|
|
||||||
if (line_len >= MIN_NODE_LINE && line_len <= MAX_NODE_LINE) {
|
|
||||||
const char *name = strtok(line, " ");
|
|
||||||
const char *port_str = strtok(NULL, " ");
|
|
||||||
const char *key_ascii = strtok(NULL, " ");
|
|
||||||
|
|
||||||
if (name == NULL || port_str == NULL || key_ascii == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
long int port = strtol(port_str, NULL, 10);
|
|
||||||
|
|
||||||
if (port <= 0 || port > MAX_PORT_RANGE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
size_t key_len = strlen(key_ascii);
|
|
||||||
size_t name_len = strlen(name);
|
|
||||||
|
|
||||||
if (key_len < TOX_PUBLIC_KEY_SIZE * 2 || name_len >= NODELEN)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
|
|
||||||
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
|
|
||||||
toxNodes.ports[toxNodes.lines] = port;
|
|
||||||
|
|
||||||
/* remove possible trailing newline from key string */
|
|
||||||
char real_ascii_key[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
|
||||||
memcpy(real_ascii_key, key_ascii, TOX_PUBLIC_KEY_SIZE * 2);
|
|
||||||
key_len = TOX_PUBLIC_KEY_SIZE * 2;
|
|
||||||
real_ascii_key[key_len] = '\0';
|
|
||||||
|
|
||||||
if (hex_string_to_bin(real_ascii_key, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
toxNodes.lines++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
if (toxNodes.lines < 1)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bootstraps and adds as TCP relay.
|
|
||||||
* Returns 0 if both actions are successful.
|
|
||||||
* Returns -1 otherwise.
|
|
||||||
*/
|
|
||||||
int init_connection_helper(Tox *m, int line)
|
|
||||||
{
|
|
||||||
TOX_ERR_BOOTSTRAP err;
|
|
||||||
tox_bootstrap(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err);
|
|
||||||
|
|
||||||
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
|
||||||
fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
tox_add_tcp_relay(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err);
|
|
||||||
|
|
||||||
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
|
||||||
fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connects to a random DHT node listed in the DHTnodes file
|
|
||||||
*
|
|
||||||
* return codes:
|
|
||||||
* 0: success
|
|
||||||
* 1: failed to open node file
|
|
||||||
* 2: no line of sufficient length in node file
|
|
||||||
* 3: failed to resolve name to IP
|
|
||||||
* 4: nodelist file contains no acceptable line
|
|
||||||
*/
|
|
||||||
static bool srvlist_loaded = false;
|
|
||||||
|
|
||||||
#define NUM_INIT_NODES 5
|
|
||||||
|
|
||||||
int init_connection(Tox *m)
|
|
||||||
{
|
|
||||||
if (toxNodes.lines > 0) { /* already loaded nodelist */
|
|
||||||
init_connection_helper(m, rand() % toxNodes.lines);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* only once:
|
|
||||||
* - load the nodelist
|
|
||||||
* - connect to "everyone" inside
|
|
||||||
*/
|
|
||||||
if (!srvlist_loaded) {
|
|
||||||
srvlist_loaded = true;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
if (!arg_opts.nodes_path[0])
|
|
||||||
res = load_nodelist(PACKAGE_DATADIR "/DHTnodes");
|
|
||||||
else
|
|
||||||
res = load_nodelist(arg_opts.nodes_path);
|
|
||||||
|
|
||||||
if (res != 0)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res = 3;
|
|
||||||
int i;
|
|
||||||
int n = MIN(NUM_INIT_NODES, toxNodes.lines);
|
|
||||||
|
|
||||||
for (i = 0; i < n; ++i) {
|
|
||||||
if (init_connection_helper(m, rand() % toxNodes.lines) == 0)
|
|
||||||
res = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* empty nodelist file */
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void load_friendlist(Tox *m)
|
static void load_friendlist(Tox *m)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
@ -873,30 +727,7 @@ static Tox *load_toxic(char *data_path)
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TRY_BOOTSTRAP_INTERVAL 5
|
static void do_toxic(Tox *m)
|
||||||
static uint64_t last_bootstrap_time = 0;
|
|
||||||
|
|
||||||
static void do_bootstrap(Tox *m)
|
|
||||||
{
|
|
||||||
static int conn_err = 0;
|
|
||||||
|
|
||||||
if (!timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (tox_self_get_connection_status(m) != TOX_CONNECTION_NONE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (conn_err != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
last_bootstrap_time = get_unix_time();
|
|
||||||
conn_err = init_connection(m);
|
|
||||||
|
|
||||||
if (conn_err != 0)
|
|
||||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Auto-connect failed with error code %d", conn_err);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_toxic(Tox *m, ToxWindow *prompt)
|
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
update_unix_time();
|
update_unix_time();
|
||||||
@ -907,7 +738,7 @@ static void do_toxic(Tox *m, ToxWindow *prompt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tox_iterate(m);
|
tox_iterate(m);
|
||||||
do_bootstrap(m);
|
do_tox_connection(m);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1280,9 +1111,6 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
bool datafile_exists = file_exists(DATA_FILE);
|
bool datafile_exists = file_exists(DATA_FILE);
|
||||||
|
|
||||||
if (datafile_exists)
|
|
||||||
last_bootstrap_time = get_unix_time();
|
|
||||||
|
|
||||||
if (!datafile_exists && !arg_opts.unencrypt_data)
|
if (!datafile_exists && !arg_opts.unencrypt_data)
|
||||||
first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)");
|
first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)");
|
||||||
else if (arg_opts.encrypt_data)
|
else if (arg_opts.encrypt_data)
|
||||||
@ -1300,14 +1128,21 @@ int main(int argc, char **argv)
|
|||||||
if (settings_load(user_settings, p) == -1)
|
if (settings_load(user_settings, p) == -1)
|
||||||
queue_init_message("Failed to load user settings");
|
queue_init_message("Failed to load user settings");
|
||||||
|
|
||||||
|
int nodelist_ret = load_DHT_nodelist();
|
||||||
|
|
||||||
|
if (nodelist_ret != 0) {
|
||||||
|
queue_init_message("DHT nodelist failed to load (error %d). You can still connect manually with the /connect command.", nodelist_ret);
|
||||||
|
}
|
||||||
|
|
||||||
int nameserver_ret = name_lookup_init();
|
int nameserver_ret = name_lookup_init();
|
||||||
|
|
||||||
if (nameserver_ret == -1)
|
if (nameserver_ret == -1) {
|
||||||
queue_init_message("curl failed to initialize; name lookup service is disabled.");
|
queue_init_message("curl failed to initialize; name lookup service is disabled.");
|
||||||
else if (nameserver_ret == -2)
|
} else if (nameserver_ret == -2) {
|
||||||
queue_init_message("Name lookup server list could not be found.");
|
queue_init_message("Name lookup server list could not be found.");
|
||||||
else if (nameserver_ret == -3)
|
} else if (nameserver_ret == -3) {
|
||||||
queue_init_message("Name lookup server list does not contain any valid entries.");
|
queue_init_message("Name lookup server list does not contain any valid entries.");
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
if (init_xtra(DnD_callback) == -1)
|
if (init_xtra(DnD_callback) == -1)
|
||||||
@ -1379,7 +1214,7 @@ int main(int argc, char **argv)
|
|||||||
uint64_t last_save = (uint64_t) time(NULL);
|
uint64_t last_save = (uint64_t) time(NULL);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
do_toxic(m, prompt);
|
do_toxic(m);
|
||||||
|
|
||||||
uint64_t cur_time = get_unix_time();
|
uint64_t cur_time = get_unix_time();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user