commit 5a40a7084809148b757dd0f8054f29ea4f72d340
Author: MTRNord <mtrnord1@gmail.com>
Date: Fri, 6 Apr 2018 15:34:03 +0200
initial commit
Diffstat:
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