diff --git a/external/toxcore/CMakeLists.txt b/external/toxcore/CMakeLists.txt index 7974ddc..938a426 100644 --- a/external/toxcore/CMakeLists.txt +++ b/external/toxcore/CMakeLists.txt @@ -181,6 +181,7 @@ if (NOT TARGET toxcore) ) target_include_directories(toxcore PRIVATE "${TOX_DIR}toxcore") + target_include_directories(toxcore INTERFACE "${TOX_DIR}") #toxencryptsave target_include_directories(toxcore PUBLIC "${toxcore_BINARY_DIR}/include/") target_compile_definitions(toxcore PUBLIC USE_IPV6=1) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b6ffced..fd773c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,10 +2,23 @@ cmake_minimum_required(VERSION 3.9 FATAL_ERROR) add_executable(totato ./main.cpp + ./tox_client.hpp + ./tox_client.cpp + ./tox_private_impl.hpp + ./auto_dirty.hpp + ./auto_dirty.cpp ) target_compile_features(totato PUBLIC cxx_std_17) target_link_libraries(totato PUBLIC - toxcore + solanaceae_util + solanaceae_contact + solanaceae_message3 + + solanaceae_plugin + + solanaceae_toxcore + solanaceae_tox_contacts + solanaceae_tox_messages ) diff --git a/src/auto_dirty.cpp b/src/auto_dirty.cpp new file mode 100644 index 0000000..614fafe --- /dev/null +++ b/src/auto_dirty.cpp @@ -0,0 +1,62 @@ +#include "./auto_dirty.hpp" + +#include "./tox_client.hpp" + +// TODO: add more events + +void AutoDirty::subscribe(void) { + _tc.subscribe(this, Tox_Event::TOX_EVENT_SELF_CONNECTION_STATUS); + _tc.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_CONNECTION_STATUS); + _tc.subscribe(this, Tox_Event::TOX_EVENT_FRIEND_REQUEST); + _tc.subscribe(this, Tox_Event::TOX_EVENT_GROUP_INVITE); + _tc.subscribe(this, Tox_Event::TOX_EVENT_GROUP_SELF_JOIN); + _tc.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_JOIN); + _tc.subscribe(this, Tox_Event::TOX_EVENT_GROUP_PEER_EXIT); + _tc.subscribe(this, Tox_Event::TOX_EVENT_CONFERENCE_INVITE); +} + +AutoDirty::AutoDirty(ToxClient& tc) : _tc(tc) { + subscribe(); +} + +bool AutoDirty::onToxEvent(const Tox_Event_Self_Connection_Status*) { + _tc.setDirty(); + return false; +} + +bool AutoDirty::onToxEvent(const Tox_Event_Friend_Connection_Status*) { + _tc.setDirty(); + return false; +} + +bool AutoDirty::onToxEvent(const Tox_Event_Friend_Request*) { + _tc.setDirty(); + return false; +} + +bool AutoDirty::onToxEvent(const Tox_Event_Group_Invite*) { + _tc.setDirty(); + return false; +} + +bool AutoDirty::onToxEvent(const Tox_Event_Group_Self_Join*) { + _tc.setDirty(); + return false; +} + +bool AutoDirty::onToxEvent(const Tox_Event_Group_Peer_Join*) { + _tc.setDirty(); + return false; +} + +bool AutoDirty::onToxEvent(const Tox_Event_Group_Peer_Exit*) { + _tc.setDirty(); + return false; +} + +bool AutoDirty::onToxEvent(const Tox_Event_Conference_Invite*) { + _tc.setDirty(); + return false; +} + + diff --git a/src/auto_dirty.hpp b/src/auto_dirty.hpp new file mode 100644 index 0000000..27409c0 --- /dev/null +++ b/src/auto_dirty.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +// fwd +class ToxClient; + +// sets ToxClient dirty on some events +class AutoDirty : public ToxEventI { + ToxClient& _tc; + + void subscribe(void); // private + + public: + AutoDirty(ToxClient& tc); + + protected: // tox events + bool onToxEvent(const Tox_Event_Self_Connection_Status* e) override; + bool onToxEvent(const Tox_Event_Friend_Connection_Status* e) override; + bool onToxEvent(const Tox_Event_Friend_Request* e) override; + bool onToxEvent(const Tox_Event_Group_Invite* e) override; + bool onToxEvent(const Tox_Event_Group_Self_Join* e) override; + bool onToxEvent(const Tox_Event_Group_Peer_Join* e) override; + bool onToxEvent(const Tox_Event_Group_Peer_Exit* e) override; + bool onToxEvent(const Tox_Event_Conference_Invite* e) override; +}; + diff --git a/src/main.cpp b/src/main.cpp index b4775c9..a2f1d10 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,18 @@ +#include +#include +#include +#include +#include +#include +#include "./tox_private_impl.hpp" + +#include +#include +#include + +#include "./tox_client.hpp" +#include "./auto_dirty.hpp" + #include #include #include @@ -20,7 +35,6 @@ void sigint_handler(int signo) { } #endif - int main(int argc, char** argv) { #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) struct sigaction sigint_action; @@ -31,17 +45,65 @@ int main(int argc, char** argv) { #elif defined (_WIN32) signal(SIGINT, sigint_handler); #endif + auto last_time = std::chrono::steady_clock::now(); // TODO: parse arg // totato -p - //auto last_time = std::chrono::steady_clock::now(); + // HACK: config + std::string tox_profile_path {"totato.tox"}; + + SimpleConfigModel conf; + Contact3Registry cr; + RegistryMessageModel rmm{cr}; + MessageTimeSort mts{rmm}; + + PluginManager pm; + + ToxEventLogger tel{std::cout}; + ToxClient tc{tox_profile_path, ""}; + ToxPrivateImpl tpi{tc.getTox()}; + AutoDirty ad{tc}; + ToxContactModel2 tcm{cr, tc, tc}; + ToxMessageManager tmm{rmm, cr, tcm, tc, tc}; + ToxTransferManager ttm{rmm, cr, tcm, tc, tc}; + + { // setup plugin instances + g_provideInstance("ConfigModelI", "host", &conf); + g_provideInstance("Contact3Registry", "host", &cr); + g_provideInstance("RegistryMessageModel", "host", &rmm); + + //g_provideInstance("ToxI", "host", &tc); + //g_provideInstance("ToxPrivateI", "host", &tpi); + //g_provideInstance("ToxEventProviderI", "host", &tc); + //g_provideInstance("ToxContactModel2", "host", &tcm); + + // TODO: pm? + } + + for (const auto& ppath : {"../../solanaceae_ecosystem/build/bin/libplugin_transfer_auto_accept.so"}) { + if (!pm.add(ppath)) { + std::cerr << "TOTATO error: loading plugin '" << ppath << "' failed!\n"; + // thow? + assert(false && "failed to load plugin"); + } + } + + conf.dump(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); // at startup, just to be safe while (!quit) { //auto new_time = std::chrono::steady_clock::now(); + quit = !tc.iterate(); + tcm.iterate(/*time_delta*/0.02f); + ttm.iterate(); + + mts.iterate(); + + pm.tick(/*time_delta*/0.02f); + //std::this_thread::sleep_for( // time left to get to 60fps //std::chrono::duration(0.0166f) // 60fps frame duration //- std::chrono::duration(std::chrono::steady_clock::now() - new_time) // time used for rendering diff --git a/src/tox_client.cpp b/src/tox_client.cpp new file mode 100644 index 0000000..e015a1d --- /dev/null +++ b/src/tox_client.cpp @@ -0,0 +1,181 @@ +#include "./tox_client.hpp" + +// meh, change this +#include +#include +#include + +#include + +#include +#include +#include +#include + +static void eee(std::string& mod) { + for (char& c : mod) { + c ^= 0x59; + } +} + +ToxClient::ToxClient(std::string_view save_path, std::string_view save_password) : + _tox_profile_path(save_path), _tox_profile_password(save_password) +{ + TOX_ERR_OPTIONS_NEW err_opt_new; + Tox_Options* options = tox_options_new(&err_opt_new); + assert(err_opt_new == TOX_ERR_OPTIONS_NEW::TOX_ERR_OPTIONS_NEW_OK); + + std::vector profile_data{}; + if (!_tox_profile_path.empty()) { + std::ifstream ifile{_tox_profile_path, std::ios::binary}; + + if (ifile.is_open()) { + std::cout << "TOX loading save " << _tox_profile_path << "\n"; + // fill savedata + while (ifile.good()) { + auto ch = ifile.get(); + if (ch == EOF) { + break; + } else { + profile_data.push_back(ch); + } + } + + if (profile_data.empty()) { + std::cerr << "empty tox save\n"; + } else { + // set options + if (!save_password.empty()) { + std::vector encrypted_copy(profile_data.begin(), profile_data.end()); + //profile_data.clear(); + profile_data.resize(encrypted_copy.size() - TOX_PASS_ENCRYPTION_EXTRA_LENGTH); + if (!tox_pass_decrypt( + encrypted_copy.data(), encrypted_copy.size(), + reinterpret_cast(save_password.data()), save_password.size(), + profile_data.data(), + nullptr // TODO: error checking + )) { + throw std::runtime_error("FAILED to decrypt save file!!!!"); + } + eee(_tox_profile_password); + } + tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE); + tox_options_set_savedata_data(options, profile_data.data(), profile_data.size()); + } + + ifile.close(); // do i need this? + } + } + + TOX_ERR_NEW err_new; + _tox = tox_new(options, &err_new); + tox_options_free(options); + if (err_new != TOX_ERR_NEW_OK) { + std::cerr << "tox_new failed with error code " << err_new << "\n"; + throw std::runtime_error{"tox failed"}; + } + + // no callbacks, use events + tox_events_init(_tox); + + // dht bootstrap + { + struct DHT_node { + const char *ip; + uint16_t port; + const char key_hex[TOX_PUBLIC_KEY_SIZE*2 + 1]; // 1 for null terminator + unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; + }; + + DHT_node nodes[] = + { + // TODO: more/diff nodes + // you can change or add your own bs and tcprelays here, ideally closer to you + + //{"tox.plastiras.org", 33445, "8E8B63299B3D520FB377FE5100E65E3322F7AE5B20A0ACED2981769FC5B43725", {}}, // LU tha14 + {"104.244.74.69", 443, "8E8B63299B3D520FB377FE5100E65E3322F7AE5B20A0ACED2981769FC5B43725", {}}, // LU tha14 + {"104.244.74.69", 33445, "8E8B63299B3D520FB377FE5100E65E3322F7AE5B20A0ACED2981769FC5B43725", {}}, // LU tha14 + + //{"tox2.plastiras.org", 33445, "B6626D386BE7E3ACA107B46F48A5C4D522D29281750D44A0CBA6A2721E79C951", {}}, // DE tha14 + + //{"tox4.plastiras.org", 33445, "836D1DA2BE12FE0E669334E437BE3FB02806F1528C2B2782113E0910C7711409", {}}, // MD tha14 + {"37.221.66.161", 443, "836D1DA2BE12FE0E669334E437BE3FB02806F1528C2B2782113E0910C7711409", {}}, // MD tha14 + {"37.221.66.161", 33445, "836D1DA2BE12FE0E669334E437BE3FB02806F1528C2B2782113E0910C7711409", {}}, // MD tha14 + }; + + for (size_t i = 0; i < sizeof(nodes)/sizeof(DHT_node); i ++) { + sodium_hex2bin( + nodes[i].key_bin, sizeof(nodes[i].key_bin), + nodes[i].key_hex, sizeof(nodes[i].key_hex)-1, + NULL, NULL, NULL + ); + tox_bootstrap(_tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, NULL); + // TODO: use extra tcp option to avoid error msgs + // ... this is hardcore + tox_add_tcp_relay(_tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, NULL); + } + } +} + +ToxClient::~ToxClient(void) { + tox_kill(_tox); +} + +bool ToxClient::iterate(void) { + Tox_Err_Events_Iterate err_e_it = TOX_ERR_EVENTS_ITERATE_OK; + auto* events = tox_events_iterate(_tox, false, &err_e_it); + if (err_e_it == TOX_ERR_EVENTS_ITERATE_OK && events != nullptr) { + _subscriber_raw(events); + + // forward events to event handlers + dispatchEvents(events); + } + + tox_events_free(events); + + if (_tox_profile_dirty) { + saveToxProfile(); + } + + return true; +} + +void ToxClient::subscribeRaw(std::function fn) { + _subscriber_raw = fn; +} + +void ToxClient::saveToxProfile(void) { + if (_tox_profile_path.empty()) { + return; + } + std::cout << "TOX saving\n"; + + std::vector data{}; + data.resize(tox_get_savedata_size(_tox)); + tox_get_savedata(_tox, data.data()); + + if (!_tox_profile_password.empty()) { + std::vector unencrypted_copy(data.begin(), data.end()); + //profile_data.clear(); + data.resize(unencrypted_copy.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH); + eee(_tox_profile_password); + if (!tox_pass_encrypt( + unencrypted_copy.data(), unencrypted_copy.size(), + reinterpret_cast(_tox_profile_password.data()), _tox_profile_password.size(), + data.data(), + nullptr // TODO: error checking + )) { + eee(_tox_profile_password); + throw std::runtime_error("FAILED to encrypt save file!!!!"); + } + eee(_tox_profile_password); + } + std::ofstream ofile{_tox_profile_path, std::ios::binary}; + // TODO: improve + for (const auto& ch : data) { + ofile.put(ch); + } + + _tox_profile_dirty = false; +} + diff --git a/src/tox_client.hpp b/src/tox_client.hpp new file mode 100644 index 0000000..208c95b --- /dev/null +++ b/src/tox_client.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +struct ToxEventI; + +class ToxClient : public ToxDefaultImpl, public ToxEventProviderBase { + private: + bool _should_stop {false}; + + std::function _subscriber_raw {[](const auto*) {}}; // only 1? + + std::string _self_name; + + std::string _tox_profile_path; + std::string _tox_profile_password; + bool _tox_profile_dirty {true}; // set in callbacks + + public: + //ToxClient(/*const CommandLine& cl*/); + ToxClient(std::string_view save_path, std::string_view save_password); + ~ToxClient(void); + + public: // tox stuff + Tox* getTox(void) { return _tox; } + + void setDirty(void) { _tox_profile_dirty = true; } + + // returns false when we shoul stop the program + bool iterate(void); + void stop(void); // let it know it should exit + + void setToxProfilePath(const std::string& new_path) { _tox_profile_path = new_path; } + void setSelfName(std::string_view new_name) { _self_name = new_name; toxSelfSetName(new_name); } + + public: // raw events + void subscribeRaw(std::function fn); + + private: + void saveToxProfile(void); +}; + + diff --git a/src/tox_private_impl.hpp b/src/tox_private_impl.hpp new file mode 100644 index 0000000..fe2605d --- /dev/null +++ b/src/tox_private_impl.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +struct ToxPrivateImpl : public ToxPrivateI { + Tox* _tox = nullptr; + + ToxPrivateImpl(Tox* tox) : _tox(tox) {} + virtual ~ToxPrivateImpl(void) {} + + uint16_t toxDHTGetNumCloselist(void) override { + return tox_dht_get_num_closelist(_tox); + } + + uint16_t toxDHTGetNumCloselistAnnounceCapable(void) override { + return tox_dht_get_num_closelist_announce_capable(_tox); + } +};