mutriz

git clone git://archive.git.mtrnord.blog/Nordgedanken/mutriz.git
Log | Files | Refs

commit 5a40a7084809148b757dd0f8054f29ea4f72d340
Author: MTRNord <mtrnord1@gmail.com>
Date:   Fri,  6 Apr 2018 15:34:03 +0200

initial commit

Diffstat:
A.gitignore | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.idea/Mutriz.iml | 3+++
A.idea/codeStyles/Project.xml | 30++++++++++++++++++++++++++++++
A.idea/misc.xml | 5+++++
A.idea/modules.xml | 9+++++++++
A.idea/vcs.xml | 7+++++++
ACMakeLists.txt | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmake/Boost.cmake | 31+++++++++++++++++++++++++++++++
Acmake/MatrixStructs.cmake | 36++++++++++++++++++++++++++++++++++++
Acmake/Mtxclient.cmake | 39+++++++++++++++++++++++++++++++++++++++
Acmake/Olm.cmake | 24++++++++++++++++++++++++
Amain.cpp | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
12 files changed, 623 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,98 @@ +# Created by .ignore support plugin (hsz.mobi) +### CMake template +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ +cmake-build-debug-visualstudio/ +cmake-build-release/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### C++ template +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +.third-party/ diff --git a/.idea/Mutriz.iml b/.idea/Mutriz.iml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module classpath="CMake" type="CPP_MODULE" version="4" /> +\ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ +<component name="ProjectCodeStyleConfiguration"> + <code_scheme name="Project" version="173"> + <Objective-C-extensions> + <file> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" /> + </file> + <class> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" /> + <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" /> + </class> + <extensions> + <pair source="cpp" header="h" fileNamingConvention="NONE" /> + <pair source="c" header="h" fileNamingConvention="NONE" /> + </extensions> + </Objective-C-extensions> + </code_scheme> +</component> +\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" /> +</project> +\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/Mutriz.iml" filepath="$PROJECT_DIR$/.idea/Mutriz.iml" /> + </modules> + </component> +</project> +\ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$/.third-party/mtxclient" vcs="Git" /> + </component> +</project> +\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -0,0 +1,102 @@ +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +project(Mutriz LANGUAGES CXX C) + +set(CMAKE_CXX_STANDARD 14) + +set(EXTERNAL_PROJECT_DEPS "") +# Dependencies on ExternalProjects +set(MUTRIZ_DEPS "") + +# Libraries linked to matrix_client +set(MUTRIZ_LIBS "") + + +file(DOWNLOAD + "https://github.com/nlohmann/json/releases/download/v3.1.2/json.hpp" + ${PROJECT_SOURCE_DIR}/json.hpp + EXPECTED_HASH SHA256=fbdfec4b4cf63b3b565d09f87e6c3c183bdd45c5be1864d3fcb338f6f02c1733) + +file(DOWNLOAD + "https://github.com/mpark/variant/releases/download/v1.3.0/variant.hpp" + ${PROJECT_SOURCE_DIR}/variant.hpp + EXPECTED_MD5 "be0ce322cdd408e1b347b9f1d59ea67a") + +# +# OpenSSL +# +find_package(OpenSSL REQUIRED) +include_directories(${OPENSSL_INCLUDE_DIR}) +set(MUTRIZ_LIBS ${MUTRIZ_LIBS} ${OPENSSL_LIBRARIES}) + +# +# mtxclient +# +find_library(MTXCLIENT_LIBRARY + NAMES matrix_client + PATHS ${MTXCLIENT_ROOT} + ${MTXCLIENT_ROOT}/lib + ${MTXCLIENT_ROOT}/lib/static) + +if(NOT MTXCLIENT_LIBRARY) + include(Mtxclient) + set(EXTERNAL_PROJECT_DEPS ${EXTERNAL_PROJECT_DEPS} Mtxclient) +endif() + +include_directories(SYSTEM ${MTXCLIENT_INCLUDE_DIR}) +set(MUTRIZ_LIBS ${MUTRIZ_LIBS} ${MTXCLIENT_LIBRARY}) + +# +# Boost 1.66 +# +# If we can't find an already installed version we will +# download it and build it from source. +# +set(Boost_USE_STATIC_LIBS OFF) +set(Boost_USE_MULTITHREADED ON) +find_package(Boost 1.66) +find_package(Boost 1.66 COMPONENTS system random thread iostreams) + +if(NOT Boost_FOUND) + include(Boost) + set(MUTRIZ_DEPS ${MUTRIZ_DEPS} Boost) +endif() + +include_directories(${Boost_INCLUDE_DIRS}) +set(MUTRIZ_LIBS ${MUTRIZ_LIBS} ${Boost_LIBRARIES}) + +# +# matrix-structs +# +find_library(MATRIX_STRUCTS_LIBRARY + NAMES matrix_structs + PATHS ${MATRIX_STRUCTS_ROOT} + ${MATRIX_STRUCTS_ROOT}/lib + ${MATRIX_STRUCTS_ROOT}/lib/static) + +if(NOT MATRIX_STRUCTS_LIBRARY) + include(MatrixStructs) +endif() +set(MUTRIZ_DEPS ${MUTRIZ_DEPS} MatrixStructs) +include_directories(${MATRIX_STRUCTS_INCLUDE_DIRS}) +set(MUTRIZ_LIBS ${MUTRIZ_LIBS} matrix_structs) + +# +# libolm +# +include(Olm) +set(MTXCLIENT_DEPS ${MTXCLIENT_DEPS} Olm) +set(MTXCLIENT_LIBS ${MTXCLIENT_LIBS} olm) + + +add_executable(Mutriz main.cpp) + +if(EXTERNAL_PROJECT_DEPS) + add_dependencies(Mutriz ${EXTERNAL_PROJECT_DEPS}) +endif() + +add_dependencies(Mutriz ${MUTRIZ_DEPS}) +target_link_libraries(Mutriz ${MUTRIZ_LIBS}) +target_include_directories(Mutriz SYSTEM PRIVATE $ENV{INCLUDE}) +\ No newline at end of file diff --git a/cmake/Boost.cmake b/cmake/Boost.cmake @@ -0,0 +1,31 @@ +include(ExternalProject) + +# +# Download & install Boost from source. +# + +set(THIRD_PARTY_ROOT ${CMAKE_SOURCE_DIR}/.third-party/mtxclient/.third-party) +set(BUNDLED_BOOST_ROOT ${THIRD_PARTY_ROOT}/boost_1_66_0) + +ExternalProject_Add( + Boost + + URL https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.bz2 + URL_HASH SHA256=5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9 + DOWNLOAD_DIR ${THIRD_PARTY_ROOT}/downloads + DOWNLOAD_NO_PROGRESS 0 + + BUILD_IN_SOURCE 1 + SOURCE_DIR ${BUNDLED_BOOST_ROOT} + CONFIGURE_COMMAND ${BUNDLED_BOOST_ROOT}/bootstrap.sh + --with-libraries=random,thread,system,iostreams + --prefix=${BUNDLED_BOOST_ROOT} + BUILD_COMMAND ${BUNDLED_BOOST_ROOT}/b2 -d0 variant=release link=static threading=multi + INSTALL_COMMAND ${BUNDLED_BOOST_ROOT}/b2 -d0 install +) + +set(Boost_INCLUDE_DIRS ${BUNDLED_BOOST_ROOT}) +set(Boost_LIBRARIES boost_random boost_system boost_thread boost_iostreams) + +include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) +link_directories(${Boost_INCLUDE_DIRS}/stage/lib) diff --git a/cmake/MatrixStructs.cmake b/cmake/MatrixStructs.cmake @@ -0,0 +1,35 @@ + include(ExternalProject) + + # + # Build matrix-structs. + # + + set(THIRD_PARTY_ROOT ${CMAKE_SOURCE_DIR}/.third-party/mtxclient/.third-party) + set(MATRIX_STRUCTS_ROOT ${THIRD_PARTY_ROOT}/matrix_structs) + set(MATRIX_STRUCTS_INCLUDE_DIRS ${MATRIX_STRUCTS_ROOT}/include) + set(MATRIX_STRUCTS_INCLUDE_DIRS ${MATRIX_STRUCTS_INCLUDE_DIRS} ${MATRIX_STRUCTS_ROOT}/include/mtx) + set(MATRIX_STRUCTS_LIBRARY matrix_structs) + + link_directories(${MATRIX_STRUCTS_ROOT}) + + set(WINDOWS_FLAGS "") + + if(MSVC) + set(WINDOWS_FLAGS "-DCMAKE_GENERATOR_PLATFORM=x64") + endif() + + ExternalProject_Add( + MatrixStructs + + GIT_REPOSITORY https://github.com/mujx/matrix-structs + GIT_TAG 93fcce2d83d728cc0db31b0b9eed196409de0df1 + + BUILD_IN_SOURCE 1 + SOURCE_DIR ${MATRIX_STRUCTS_ROOT} + CONFIGURE_COMMAND ${CMAKE_COMMAND} + -DCMAKE_BUILD_TYPE=Release ${MATRIX_STRUCTS_ROOT} + ${WINDOWS_FLAGS} + BUILD_COMMAND ${CMAKE_COMMAND} --build ${MATRIX_STRUCTS_ROOT} --config Release + INSTALL_COMMAND "" + ) + link_directories(${MATRIX_STRUCTS_ROOT}/Release) +\ No newline at end of file diff --git a/cmake/Mtxclient.cmake b/cmake/Mtxclient.cmake @@ -0,0 +1,38 @@ +include(ExternalProject) + +# +# Build mtxclient. +# + +set(THIRD_PARTY_ROOT ${CMAKE_SOURCE_DIR}/.third-party) +set(MTXCLIENT_ROOT ${THIRD_PARTY_ROOT}/mtxclient) +set(MTXCLIENT_INCLUDE_DIR ${MTXCLIENT_ROOT}/src) +set(MTXCLIENT_LIBRARY matrix_client) + +link_directories(${MTXCLIENT_ROOT}) + +set(WINDOWS_FLAGS "") + +if(MSVC) + set(WINDOWS_FLAGS "-DCMAKE_GENERATOR_PLATFORM=x64") +endif() + +ExternalProject_Add( + Mtxclient + + GIT_REPOSITORY https://github.com/mujx/mtxclient + GIT_TAG 6ecda9ed78244b1d2aa71eb043fd51cb49a9c75d + + BUILD_IN_SOURCE 1 + SOURCE_DIR ${MTXCLIENT_ROOT} + CONFIGURE_COMMAND ${CMAKE_COMMAND} + -H. + -DBUILD_LIB_TESTS=OFF + -DBUILD_LIB_EXAMPLES=OFF + -DCMAKE_BUILD_TYPE=Release ${MTXCLIENT_ROOT} + ${WINDOWS_FLAGS} + BUILD_COMMAND ${CMAKE_COMMAND} --build ${MTXCLIENT_ROOT} --config Release + INSTALL_COMMAND "" +) + +link_directories(${MTXCLIENT_ROOT}/Release) +\ No newline at end of file diff --git a/cmake/Olm.cmake b/cmake/Olm.cmake @@ -0,0 +1,24 @@ + include(ExternalProject) + + # + # Build & install olm. + # + + set(THIRD_PARTY_ROOT ${CMAKE_SOURCE_DIR}/.third-party/mtxclient/.third-party) + set(OLM_ROOT ${THIRD_PARTY_ROOT}/olm) + + ExternalProject_Add( + Olm + + GIT_REPOSITORY https://git.matrix.org/git/olm.git + GIT_TAG 3f5b9dd6d72540a66da90b382a2eda088af63da0 + + BUILD_IN_SOURCE 1 + SOURCE_DIR ${OLM_ROOT} + CONFIGURE_COMMAND "" + BUILD_COMMAND make static + INSTALL_COMMAND "" + ) + + include_directories(SYSTEM ${OLM_ROOT}/include ${OLM_ROOT}/include/olm) + link_directories(${OLM_ROOT}/build) diff --git a/main.cpp b/main.cpp @@ -0,0 +1,237 @@ +#include <boost/beast.hpp> +#include <iostream> +#ifdef WIN32 + #include <windows.h> +#else + #include <cstdlib> +#endif +#include <variant.hpp> + +#include "client.hpp" +#include "errors.hpp" +#include "mtx.hpp" + +// +// Simple usage example of the /login & /sync endpoints which +// will print the stream of messages from all rooms as received by the client. +// + +using namespace std; +using namespace mtx::client; +using namespace mtx::events; + +using TimelineEvent = mtx::events::collections::TimelineEvents; + +void +print_errors(RequestErr err) +{ + if (err->status_code != boost::beast::http::status::unknown) + cout << err->status_code << "\n"; + if (!err->matrix_error.error.empty()) + cout << err->matrix_error.error << "\n"; + if (err->error_code) + cout << err->error_code.message() << "\n"; +} + +// Check if the given event has a textual representation. +bool +is_room_message(const TimelineEvent &event) +{ + return mpark::holds_alternative<mtx::events::RoomEvent<msg::Audio>>(event) || + mpark::holds_alternative<mtx::events::RoomEvent<msg::Emote>>(event) || + mpark::holds_alternative<mtx::events::RoomEvent<msg::File>>(event) || + mpark::holds_alternative<mtx::events::RoomEvent<msg::Image>>(event) || + mpark::holds_alternative<mtx::events::RoomEvent<msg::Notice>>(event) || + mpark::holds_alternative<mtx::events::RoomEvent<msg::Text>>(event) || + mpark::holds_alternative<mtx::events::RoomEvent<msg::Video>>(event); +} + +// Retrieves the fallback body value from the event. +std::string +get_body(const TimelineEvent &event) +{ + if (mpark::holds_alternative<RoomEvent<msg::Audio>>(event)) + return mpark::get<RoomEvent<msg::Audio>>(event).content.body; + else if (mpark::holds_alternative<RoomEvent<msg::Emote>>(event)) + return mpark::get<RoomEvent<msg::Emote>>(event).content.body; + else if (mpark::holds_alternative<RoomEvent<msg::File>>(event)) + return mpark::get<RoomEvent<msg::File>>(event).content.body; + else if (mpark::holds_alternative<RoomEvent<msg::Image>>(event)) + return mpark::get<RoomEvent<msg::Image>>(event).content.body; + else if (mpark::holds_alternative<RoomEvent<msg::Notice>>(event)) + return mpark::get<RoomEvent<msg::Notice>>(event).content.body; + else if (mpark::holds_alternative<RoomEvent<msg::Text>>(event)) + return mpark::get<RoomEvent<msg::Text>>(event).content.body; + else if (mpark::holds_alternative<RoomEvent<msg::Video>>(event)) + return mpark::get<RoomEvent<msg::Video>>(event).content.body; + + return ""; +} + +// Retrieves the sender of the event. +std::string +get_sender(const TimelineEvent &event) +{ + return mpark::visit([](auto e) { return e.sender; }, event); +} + +// Simple print of the message contents. +void +print_message(const TimelineEvent &event) +{ + if (is_room_message(event)) + cout << get_sender(event) << ": " << get_body(event) << "\n"; +} + +// Callback to executed after a /sync request completes. +void +sync_handler(shared_ptr<Client> client, const mtx::responses::Sync &res, RequestErr err) +{ + if (err) { + cout << "sync error:\n"; + print_errors(err); + + client->sync( + "", + client->next_batch_token(), + false, + 30000, + std::bind(&sync_handler, client, std::placeholders::_1, std::placeholders::_2)); + + return; + } + + for (const auto room : res.rooms.join) { + for (const auto msg : room.second.timeline.events) + print_message(msg); + } + + client->set_next_batch_token(res.next_batch); + + client->sync( + "", + client->next_batch_token(), + false, + 30000, + std::bind(&sync_handler, client, std::placeholders::_1, std::placeholders::_2)); +} + +// Callback to executed after the first (initial) /sync request completes. +void +initial_sync_handler(shared_ptr<Client> client, const nlohmann::json &res, RequestErr err) +{ + if (err) { + cout << "error during initial sync:\n"; + print_errors(err); + + if (err->status_code != boost::beast::http::status::ok) { + cout << "retrying initial sync ...\n"; + client->sync("", + "", + false, + 0, + std::bind(&initial_sync_handler, + client, + std::placeholders::_1, + std::placeholders::_2)); + } + + return; + } + + client->set_next_batch_token(res.at("next_batch")); + + client->sync( + "", + client->next_batch_token(), + false, + 30000, + std::bind(&sync_handler, client, std::placeholders::_1, std::placeholders::_2)); +} + +#ifdef WIN32 + string getpassword(const char *prompt, bool show_asterisk=true) + { + const char BACKSPACE=8; + const char RETURN=13; + + string password; + unsigned char ch=0; + + cout <<prompt<<endl; + + DWORD con_mode; + DWORD dwRead; + + HANDLE hIn=GetStdHandle(STD_INPUT_HANDLE); + + GetConsoleMode( hIn, &con_mode ); + SetConsoleMode( hIn, con_mode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT) ); + + while(ReadConsoleA( hIn, &ch, 1, &dwRead, NULL) && ch !=RETURN) + { + if(ch==BACKSPACE) + { + if(password.length()!=0) + { + if(show_asterisk) + cout <<"\b \b"; + password.resize(password.length()-1); + } + } + else + { + password+=ch; + if(show_asterisk) + cout <<'*'; + } + } + cout <<endl; + return password; + } +#else + string getpassword(const char *prompt, bool show_asterisk=true) + { + return getpass("Password: "); + } +#endif + +int +main() +{ + std::string username, server, password; + + cout << "Username: "; + std::getline(std::cin, username); + + cout << "HomeServer: "; + std::getline(std::cin, server); + + password = getpassword("Password: "); + + auto client = std::make_shared<Client>(server); + + client->login( + username, password, [client](const mtx::responses::Login &res, RequestErr err) { + if (err) { + cout << "There was an error during login: " << err->matrix_error.error + << "\n"; + return; + } + + cout << "Logged in as: " << res.user_id.to_string() << "\n"; + client->set_access_token(res.access_token); + + client->sync( + "", + "", + false, + 0, + std::bind( + &initial_sync_handler, client, std::placeholders::_1, std::placeholders::_2)); + }); + + client->close(); + + return 0; +} +\ No newline at end of file