From 604082466e48a026400eeb328a4f417c2101ad77 Mon Sep 17 00:00:00 2001 From: Jaidyn Ann Date: Fri, 3 Sep 2021 03:43:28 -0500 Subject: [PATCH] (matrix) Init Matrix protocol Just the scaffolding of a Matrix protocol using mtxclient: Room-joins and (unencrypted) messages can be received, but everything's read-only as yet. More to come! --- data/icons/protocols/Matrix.png | Bin 0 -> 2128 bytes protocols/matrix/Makefile | 142 +++++++++++++ protocols/matrix/Matrix.h | 12 ++ protocols/matrix/MatrixApp.cpp | 299 ++++++++++++++++++++++++++++ protocols/matrix/MatrixApp.h | 58 ++++++ protocols/matrix/MatrixMain.cpp | 50 +++++ protocols/matrix/MatrixMessages.h | 23 +++ protocols/matrix/MatrixProtocol.cpp | 271 +++++++++++++++++++++++++ protocols/matrix/MatrixProtocol.h | 75 +++++++ protocols/matrix/matrix.rdef | 16 ++ 10 files changed, 946 insertions(+) create mode 100644 data/icons/protocols/Matrix.png create mode 100644 protocols/matrix/Makefile create mode 100644 protocols/matrix/Matrix.h create mode 100644 protocols/matrix/MatrixApp.cpp create mode 100644 protocols/matrix/MatrixApp.h create mode 100644 protocols/matrix/MatrixMain.cpp create mode 100644 protocols/matrix/MatrixMessages.h create mode 100644 protocols/matrix/MatrixProtocol.cpp create mode 100644 protocols/matrix/MatrixProtocol.h create mode 100644 protocols/matrix/matrix.rdef diff --git a/data/icons/protocols/Matrix.png b/data/icons/protocols/Matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..48793de8112cdcbc9d29623de1c034ed83c115f1 GIT binary patch literal 2128 zcmb`J`#;l-0>{5ILz|fU<>(@tyR9SEjz?~h<~q5AEIgT8lCU9{&1$*LogG4L9rxTe zB1ywjl1r3JlB^<1%;FJc^mP7!bIvd4^?H9kpV#|8_+;Q+?Ds>}p#T8v$DOu4^J{zl z9VzgyCW@7t0)Ut@-pRx6mx_srwY9bV{P|NP5{Zk8Lm&_d35oXhb`FO_p-`AiW`2Ht zK|w)IP7a^X-`m>*fk03wba!`m-@bh_Gc(`6f8W{JnVXyY@#DwWuV1I8raC%0s;a7r zi;EcyMqXYXi^XbeY<&9k>B7Q-Kp+?!8{=}hckbL_v)OfZbvzzVC=~Yh_uJUmsH&<; zNlEGI>Uw&578MmuPftI2^5p8(t0zvJP*haJVzEI%LAP(;o}HZ?92`7<{(MMCNN{j) zXlSUPpWn*LN^Wkhp`jrd46dlC*xK5u-riPLR#sP6&(6-SudhcS z5O6p=Ha2#7dAYW>R#H+jEiG+eV8Gbecw%DW)2B~vZf;6SN;Ne#bUHmND@#U3=KlTr z-QC?98X5!w;oG-wot>R#W@ek4n=URcFVk-Ffd3>O|7o39vT{&oSa0VP@0;W_V)Jk^Yb_yE-5KVO-*febyZ$oUSD6INF@6D z`bI=VWM*cDhlgLhcriISxv8lsK0ZD@J>9;KXbk|6O`NT@NBqoc`FDzfooYXSbtQ7f z#5uSC<7kC)zI zO>7y?^3ka4C5zGL7W{-bGTE|J+KMh6dRV*8v9vPvX&DZt(8F!K%e^q>%>%OG2FQDT z`ePnEz*$qNWqu@sNh{E^A=wva6?kw}kG;_7XzmZmbIE52Vp~0LRK7HSAUig5itFM= zn)!JvRFBAPyT=5SjUGb{Z2)nr|Hj)?xK-R?@v#F^pjb&8;6Lk;u;Soh5e+jwJQm6@ z6=zi|gjRmwM`@Az%H6F4QB_B?2T9uJns6bYEBH@mnzldnpJgppvtdY8s<2f$lbk-mpoDOt!mn zCR>c!o3YE1AEKW1ci#pT`_o3kk?CTG4>{u)e^(kyDtX1n>sy-u#D79=Cv?u{m4AGd z``-1MrWedbIpmSL2W#wd6)4=JYf`d&y{jH{S*Cv0K@0M(;@O-T$t-1kRe^vfwW&*} zu02c)PD&b0T&wxMV$hvFRm-4Bf#Mmm7Uo?ean6hs4?5L#XzBT4k`7^VgVSIQo2vIa z`2utlEJNwbx<#=c7Fzyz8Q7t%BH^V{E>pFT)Ldz&f~sYF7y!2Cg?4)^_jvimwX6je zQYY&D_{L*Bi(?^J0rIOwa1VPQ-5Rb&1yvwaux5@ZM@M2$h(hn(G9F(V=C${@CR!^S z+Cv`~Wtaq11}(%q6Yo9T&R+k3c~%g~_LY8wSACN29OR!lnk&L>g4mBESPF`ZF!d5*=tb15vAjA)g?7r|DLeCd{z2*E1seoRz zCNO0CrCHOf8{p9^m*pDED(f`XyS%8D6Ce~F@SA`NYs`AKZ6vp0AUN>9*~B?{1RruYu=dX@$I!^Mc%u&8Nq_aH;N=*$}!sOo8Aw4RtoeZ0zoCM5P*({PS zIpMrlgj_*|phTwjUlhZ~X47bI`^Cr#11rMkn?S8DNv9(5Zj3~&jwUto{{;jzbGp3^ zDKZt*_`_bD88i$t09CrfIAV1#tBc4F|MB7s-ni<)W=x|UPTz-78k#e-bq>SAU726F z>DX=*5ZK8jOvC4LtS{_}nb@ + +# Specify the source files to use. Full paths or paths relative to the +# Makefile can be included. All files, regardless of directory, will have +# their object files created in the common object directory. Note that this +# means this Makefile will not work correctly if two source files with the +# same name (source.c or source.cpp) are included from different directories. +# Also note that spaces in folder names do not work well with this Makefile. +SRCS = \ + protocols/matrix/MatrixApp.cpp \ + protocols/matrix/MatrixMain.cpp \ + protocols/matrix/MatrixProtocol.cpp \ + +# Specify the resource definition files to use. Full or relative paths can be +# used. +RDEFS = \ + data/icons/protocols/IRC.rdef \ + protocols/matrix/matrix.rdef \ + +# Specify the resource files to use. Full or relative paths can be used. +# Both RDEFS and RSRCS can be utilized in the same Makefile. +RSRCS = + +# End Pe/Eddie support. +# @<-src@ +#%} + +# Specify libraries to link against. +# There are two acceptable forms of library specifications: +# - if your library follows the naming pattern of libXXX.so or libXXX.a, +# you can simply specify XXX for the library. (e.g. the entry for +# "libtracker.so" would be "tracker") +# +# - for GCC-independent linking of standard C++ libraries, you can use +# $(STDCPPLIBS) instead of the raw "stdc++[.r4] [supc++]" library names. +# +# - if your library does not follow the standard library naming scheme, +# you need to specify the path to the library and it's name. +# (e.g. for mylib.a, specify "mylib.a" or "path/mylib.a") +LIBS = be bnetapi localestub matrix_client network $(STDCPPLIBS) + + +# Specify additional paths to directories following the standard libXXX.so +# or libXXX.a naming scheme. You can specify full paths or paths relative +# to the Makefile. The paths included are not parsed recursively, so +# include all of the paths where libraries must be found. Directories where +# source files were specified are automatically included. +LIBPATHS = + +# Additional paths to look for system headers. These use the form +# "#include
". Directories that contain the files in SRCS are +# NOT auto-included here. +SYSTEM_INCLUDE_PATHS = application/ libs/ + +# Additional paths paths to look for local headers. These use the form +# #include "header". Directories that contain the files in SRCS are +# automatically included. +LOCAL_INCLUDE_PATHS = + +# Specify the level of optimization that you want. Specify either NONE (O0), +# SOME (O1), FULL (O3), or leave blank (for the default optimization level). +OPTIMIZE := + +# Specify the codes for languages you are going to support in this +# application. The default "en" one must be provided too. "make catkeys" +# will recreate only the "locales/en.catkeys" file. Use it as a template +# for creating catkeys for other languages. All localization files must be +# placed in the "locales" subdirectory. +LOCALES = + +# Specify all the preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) to +# use. For example, setting DEFINES to "DEBUG=1" will cause the compiler +# option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" would pass +# "-DDEBUG" on the compiler's command line. +DEFINES = + +# Specify the warning level. Either NONE (suppress all warnings), +# ALL (enable all warnings), or leave blank (enable default warnings). +WARNINGS = + +# With image symbols, stack crawls in the debugger are meaningful. +# If set to "TRUE", symbols will be created. +SYMBOLS := + +# Includes debug information, which allows the binary to be debugged easily. +# If set to "TRUE", debug info will be created. +DEBUGGER := + +# Specify any additional compiler flags to be used. +COMPILER_FLAGS = -std=c++17 + +# Specify any additional linker flags to be used. +LINKER_FLAGS = + +# Specify the version of this binary. Example: +# -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL" +# This may also be specified in a resource. +APP_VERSION := + +# (Only used when "TYPE" is "DRIVER"). Specify the desired driver install +# location in the /dev hierarchy. Example: +# DRIVER_PATH = video/usb +# will instruct the "driverinstall" rule to place a symlink to your driver's +# binary in ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will +# appear at /dev/video/usb when loaded. The default is "misc". +DRIVER_PATH = + +## Include the Makefile-Engine +DEVEL_DIRECTORY := /boot/system/develop/ +include $(DEVEL_DIRECTORY)/etc/makefile-engine + +include Makefile.common +include protocols/Makefile.common + +CATKEYS_DIR = locales/matrix diff --git a/protocols/matrix/Matrix.h b/protocols/matrix/Matrix.h new file mode 100644 index 0000000..429fe97 --- /dev/null +++ b/protocols/matrix/Matrix.h @@ -0,0 +1,12 @@ +/* + * Copyright 2021, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#ifndef _MATRIX_H +#define _MATRIX_H + +#define MATRIX_SIGNATURE "application/x-vnd.chat-o-matic.matrix" +#define MATRIX_ADDON "matrix" + +#endif // _MATRIX_H diff --git a/protocols/matrix/MatrixApp.cpp b/protocols/matrix/MatrixApp.cpp new file mode 100644 index 0000000..17210cf --- /dev/null +++ b/protocols/matrix/MatrixApp.cpp @@ -0,0 +1,299 @@ +/* + * Copyright 2021, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include "MatrixApp.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "Matrix.h" +#include "MatrixMessages.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "MatrixApp" + + +std::shared_ptr client = nullptr; +MatrixApp* m_app = NULL; + + +int +main(int arc, char** argv) +{ + MatrixApp app; + app.Run(); + return 0; +} + + +MatrixApp::MatrixApp() + : + BApplication(MATRIX_SIGNATURE), + fUser(NULL), + fPassword(NULL), + fInitStatus(B_NOT_INITIALIZED), + fProtoThread(-1) +{ + new BMessageRunner(this, new BMessage(CHECK_APP), 10000000, -1); +} + + +void +MatrixApp::MessageReceived(BMessage* msg) +{ + switch (msg->what) { + case MATRIX_REGISTER_ACCOUNT: + { + int64 thread_id; + if (msg->FindInt64("thread_id", &thread_id) != B_OK) + break; + + fProtoThread = thread_id; + fUser = msg->FindString("username"); + fSession = msg->GetString("session", "Chat-O-Matic [Haiku]"); + fServer = msg->FindString("server"); + fPassword = msg->FindString("password"); + + app_info info; + GetAppInfo(&info); + BMessage registerApp(MATRIX_ACCOUNT_REGISTERED); + registerApp.AddInt64("team_id", info.team); + SendMessage(registerApp); + + Connect(); + break; + } + case CHECK_APP: + { + BRoster roster; + if (roster.IsRunning(APP_SIGNATURE) == false) + Quit(); + break; + } + default: + BApplication::MessageReceived(msg); + } +} + + +void +MatrixApp::ImMessage(BMessage* msg) +{ + int32 im_what = msg->GetInt32("im_what", -1); + switch (im_what) { + case IM_SET_OWN_STATUS: + { + } + default: { + std::cout << "Unhandled message for Matrix:\n"; + msg->PrintToStream(); + } + } +} + + +void +MatrixApp::Connect() +{ + client = std::make_shared(fServer.String()); + client->set_device_id(fSession.String()); + client->login(fUser.String(), fPassword.String(), + [this](const mtx::responses::Login &res, mtx::http::RequestErr err) + { + if (err) { + SendError("Error occured during login, please try again.", err, + true); + fInitStatus = B_ERROR; + } + else { + client->set_access_token(res.access_token); + fInitStatus = B_OK; + StartLoop(); + } + }); +} + + +void +MatrixApp::StartLoop() +{ + BMessage ready(IM_MESSAGE); + ready.AddInt32("im_what", IM_PROTOCOL_READY); + SendMessage(ready); + + BString mxId("@"); + mxId << fUser << ":" << fServer; + BMessage init(IM_MESSAGE); + init.AddInt32("im_what", IM_OWN_CONTACT_INFO); + init.AddString("user_id", mxId); + SendMessage(init); + + BMessage status(IM_MESSAGE); + status.AddInt32("im_what", IM_OWN_STATUS_SET); + status.AddInt32("status", (int32)STATUS_ONLINE); + SendMessage(status); + + mtx::http::SyncOpts opts; + opts.timeout = 0; + client->sync(opts, &initial_sync_handler); + client->close(); +} + + +void +MatrixApp::SendMessage(BMessage msg) +{ + if (fProtoThread <= 0) + return; + + ssize_t size = msg.FlattenedSize(); + char buffer[size]; + + send_data(fProtoThread, size, NULL, 0); + msg.Flatten(buffer, size); + send_data(fProtoThread, 0, buffer, size); +} + + +void +MatrixApp::SendError(const char* message, mtx::http::RequestErr err, + bool fatal) +{ + print_error(err); + + BString detail; + if (!err->matrix_error.error.empty()) + detail << err->matrix_error.error.c_str(); + + BMessage error(IM_ERROR); + error.AddString("error", message); + error.AddString("detail", detail); + SendMessage(error); + + if (fatal == true) { + BMessage disable(IM_MESSAGE); + disable.AddInt32("im_what", IM_PROTOCOL_DISABLE); + SendMessage(disable); + } +} + + +void +print_error(mtx::http::RequestErr err, const char* message) +{ + if (message != NULL) + std::cerr << message << " ― "; + std::cerr << err->status_code << " : " << err->error_code; + if (!err->matrix_error.error.empty()) + std::cerr << " : " << err->matrix_error.error; + std::cerr << std::endl; +} + + +void +initial_sync_handler(const mtx::responses::Sync &res, mtx::http::RequestErr err) +{ + mtx::http::SyncOpts opts; + + if (err) { + print_error(err, "Error occured during initial sync. Retrying…"); + if ((int)err->status_code != 200) { + opts.timeout = 0; + client->sync(opts, &initial_sync_handler); + } + return; + } + + room_sync(res.rooms); + + opts.since = res.next_batch; + client->set_next_batch_token(res.next_batch); + client->sync(opts, &sync_handler); +} + + +// Callback to executed after a /sync request completes. +void +sync_handler(const mtx::responses::Sync &res, mtx::http::RequestErr err) +{ + mtx::http::SyncOpts opts; + + if (err) { + print_error(err, "Error occured during sync. Retrying…"); + opts.since = client->next_batch_token(); + client->sync(opts, &sync_handler); + return; + } + + room_sync(res.rooms); + + opts.since = res.next_batch; + client->set_next_batch_token(res.next_batch); + client->sync(opts, &sync_handler); +} + + +void +room_sync(mtx::responses::Rooms rooms) +{ + MatrixApp* app = (MatrixApp*)be_app; + + std::map joined = rooms.join; + for (std::map::iterator iter + = joined.begin(); + iter != joined.end(); + ++iter) + { + const char* chat_id = iter->first.c_str(); + mtx::responses::JoinedRoom room = iter->second; + + if (app->fRoomList.HasString(BString(chat_id)) == false) { + BMessage joinedMsg(IM_MESSAGE); + joinedMsg.AddInt32("im_what", IM_ROOM_JOINED); + joinedMsg.AddString("chat_id", chat_id); + ((MatrixApp*)be_app)->SendMessage(joinedMsg); + + app->fRoomList.Add(BString(chat_id)); + } + + for (mtx::events::collections::TimelineEvents &ev : room.timeline.events) + if (auto event = std::get_if>(&ev); + event != nullptr) + { + BMessage msg(IM_MESSAGE); + msg.AddInt32("im_what", IM_MESSAGE_RECEIVED); + msg.AddString("body", event->content.body.c_str()); + msg.AddString("chat_id", chat_id); + msg.AddString("user_id", event->sender.c_str()); + app->SendMessage(msg); + } + } + + std::map left = rooms.leave; + for (std::map::iterator iter + = left.begin(); + iter != left.end(); + ++iter) + { + const char* chat_id = iter->first.c_str(); + if (app->fRoomList.HasString(BString(chat_id)) == true) + app->fRoomList.Remove(BString(chat_id)); + + BMessage leftMsg(IM_MESSAGE); + leftMsg.AddInt32("im_what", IM_ROOM_LEFT); + leftMsg.AddString("chat_id", ((std::string)iter->first).c_str()); + ((MatrixApp*)be_app)->SendMessage(leftMsg); + } +} diff --git a/protocols/matrix/MatrixApp.h b/protocols/matrix/MatrixApp.h new file mode 100644 index 0000000..48ad365 --- /dev/null +++ b/protocols/matrix/MatrixApp.h @@ -0,0 +1,58 @@ +/* + * Copyright 2021, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ +#ifndef _MATRIX_APP_H +#define _MATRIX_APP_H + +#include +#include +#include + +#include +#include +#include + + +const uint32 CHECK_APP = 'Paca'; + + +class MatrixApp : public BApplication { +public: + MatrixApp(); + + virtual void MessageReceived(BMessage* msg); + void ImMessage(BMessage* msg); + + void Connect(); + void StartLoop(); + + void SendMessage(BMessage msg); + void SendError(const char* message, + mtx::http::RequestErr err, bool fatal = false); + + BMessage* fSettings; + status_t fInitStatus; + BStringList fRoomList; + +private: + // Settings + BString fUser; + BString fPassword; + BString fServer; + BString fSession; + + thread_id fProtoThread; +}; + + +void print_error(mtx::http::RequestErr err, const char* message = NULL); + +void login_handler(const mtx::responses::Login &res, mtx::http::RequestErr err); + +void initial_sync_handler(const mtx::responses::Sync &res, mtx::http::RequestErr err); +void sync_handler(const mtx::responses::Sync &res, mtx::http::RequestErr err); + +void room_sync(mtx::responses::Rooms rooms); + +#endif // _MATRIX_APP_H diff --git a/protocols/matrix/MatrixMain.cpp b/protocols/matrix/MatrixMain.cpp new file mode 100644 index 0000000..415cad1 --- /dev/null +++ b/protocols/matrix/MatrixMain.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2021, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include "MatrixProtocol.h" + + +extern "C" _EXPORT ChatProtocol* protocol_at(int32 i); +extern "C" _EXPORT int32 protocol_count(); +extern "C" _EXPORT const char* signature(); +extern "C" _EXPORT const char* friendly_signature(); +extern "C" _EXPORT uint32 version(); + + +ChatProtocol* +protocol_at(int32 i) +{ + if (i == 0) + return (ChatProtocol*)new MatrixProtocol(); + return NULL; +} + + +int32 +protocol_count() +{ + return 1; +} + + +const char* +signature() +{ + return "matrix"; +} + + +const char* +friendly_signature() +{ + return "Matrix"; +} + + +uint32 +version() +{ + return APP_VERSION_1_ALPHA_1; +} diff --git a/protocols/matrix/MatrixMessages.h b/protocols/matrix/MatrixMessages.h new file mode 100644 index 0000000..ceb4027 --- /dev/null +++ b/protocols/matrix/MatrixMessages.h @@ -0,0 +1,23 @@ +/* + * Copyright 2021, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ +#ifndef _MATRIX_MESSAGES_H +#define _MATRIX_MESSAGES_H + +enum matrix_message { + /* + * Messages between the Matrix add-on and server + */ + + /*! Register account & thread with app →Server + Requires: Account template, int64 thread_id */ + MATRIX_REGISTER_ACCOUNT = 'MXra', + + + /*! Inform the protocol of app team id →Protocol + Requires: int64 team_id */ + MATRIX_ACCOUNT_REGISTERED = 'MXar' +}; + +#endif // _MATRIX_MESSAGES_H diff --git a/protocols/matrix/MatrixProtocol.cpp b/protocols/matrix/MatrixProtocol.cpp new file mode 100644 index 0000000..eb88d4f --- /dev/null +++ b/protocols/matrix/MatrixProtocol.cpp @@ -0,0 +1,271 @@ +/* + * Copyright 2021, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include "MatrixProtocol.h" + +#include +#include +#include + +#include + +#include +#include + +#include "Matrix.h" +#include "MatrixMessages.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "MatrixProtocol" + + +status_t +connect_thread(void* data) +{ + MatrixProtocol* protocol = (MatrixProtocol*)data; + while (true) { + BMessage* msg = new BMessage(receive_message()); + switch (msg->what) { + case MATRIX_ACCOUNT_REGISTERED: + protocol->RegisterApp((team_id)msg->GetInt64("team_id", -1)); + break; + default: + protocol->SendMessage(msg); + } + } +} + + +BMessage +receive_message() +{ + thread_id sender; + int32 size = receive_data(&sender, NULL, 0); + char buffer[size]; + receive_data(&sender, buffer, size); + BMessage temp; + temp.Unflatten(buffer); + return temp; +} + + +MatrixProtocol::MatrixProtocol() + : + fAppMessenger(NULL), + fAppTeam(-1) +{ +} + + +MatrixProtocol::~MatrixProtocol() +{ + Shutdown(); +} + + +status_t +MatrixProtocol::Init(ChatProtocolMessengerInterface* interface) +{ + fMessenger = interface; + return B_OK; +} + + +status_t +MatrixProtocol::Shutdown() +{ + _SendMatrixMessage(new BMessage(B_QUIT_REQUESTED)); + kill_thread(fRecvThread); + return B_OK; +} + + +status_t +MatrixProtocol::UpdateSettings(BMessage* settings) +{ + fRecvThread = spawn_thread(connect_thread, "moon_w_blackjack_and_hookers", + B_NORMAL_PRIORITY, (void*)this); + + if (fRecvThread < B_OK) + return B_ERROR; + + settings->AddInt64("thread_id", fRecvThread); + fSettings = new BMessage(*settings); + return B_OK; +} + + +status_t +MatrixProtocol::Process(BMessage* msg) +{ + int32 im_what = msg->GetInt32("im_what", -1); + + switch (im_what) { + case IM_SET_OWN_STATUS: + { + int32 status = msg->GetInt32("status", -1); + switch (status) { + case STATUS_ONLINE: + resume_thread(fRecvThread); + if (fAppMessenger == NULL || fAppMessenger->IsValid() == false) + _StartApp(); + break; + case STATUS_OFFLINE: + { + _SendMatrixMessage(new BMessage(B_QUIT_REQUESTED)); + kill_thread(fRecvThread); + + delete fAppMessenger; + fAppMessenger = NULL; + fAppTeam = -1; + break; + } + default: + _SendMatrixMessage(msg); + } + } + default: + _SendMatrixMessage(msg); + } + return B_OK; +} + + +BMessage +MatrixProtocol::SettingsTemplate(const char* name) +{ + BMessage settings; + if (strcmp(name, "account") == 0) + settings = _AccountTemplate(); + else if (strcmp(name, "join_room") == 0 || strcmp(name, "create_room") == 0) + settings = _RoomTemplate(); + else if (strcmp(name, "roster") == 0) + settings = _RosterTemplate(); + return settings; +} + + +BObjectList +MatrixProtocol::Commands() +{ + return BObjectList(); +} + + +BBitmap* +MatrixProtocol::Icon() const +{ + return NULL; +// return ReadNodeIcon(fAddOnPath.Path(), B_LARGE_ICON, true); +} + + +void +MatrixProtocol::SendMessage(BMessage* msg) +{ + msg->AddString("protocol", Signature()); + fMessenger->SendMessage(msg); +} + + +void +MatrixProtocol::RegisterApp(team_id team) +{ + if (team < 0) + return; + fAppTeam = team; + fAppMessenger = new BMessenger(NULL, team); +} + + +void +MatrixProtocol::_SendMatrixMessage(BMessage* msg) +{ + msg->AddString("protocol", MATRIX_ADDON); + if (fAppMessenger != NULL && fAppMessenger->IsValid()) + fAppMessenger->SendMessage(msg); +} + + +void +MatrixProtocol::_StartApp() +{ + BMessage* start = new BMessage(*fSettings); + start->what = MATRIX_REGISTER_ACCOUNT; + + BRoster roster; + if (roster.Launch(MATRIX_SIGNATURE, start) == B_OK) + snooze(100000); +} + + +BMessage +MatrixProtocol::_AccountTemplate() +{ + BMessage settings; + + BMessage server; + server.AddString("name", "server"); + server.AddString("description", B_TRANSLATE("Homeserver:")); + server.AddString("default", "matrix.org"); + server.AddString("error", B_TRANSLATE("Please enter a valid server address.")); + server.AddInt32("type", B_STRING_TYPE); + settings.AddMessage("setting", &server); + + BMessage user; + user.AddString("name", "username"); + user.AddString("description", B_TRANSLATE("Username:")); + user.AddString("error", B_TRANSLATE("You need a username in order to connect!")); + user.AddInt32("type", B_STRING_TYPE); + settings.AddMessage("setting", &user); + + BMessage password; + password.AddString("name", "password"); password.AddString("description", B_TRANSLATE("Password:")); + password.AddString("error", B_TRANSLATE("Without a password, how will love survive?")); + password.AddInt32("type", B_STRING_TYPE); + settings.AddMessage("setting", &password); + + BMessage session; + session.AddString("name", "session"); + session.AddString("description", B_TRANSLATE("Session name:")); + session.AddInt32("type", B_STRING_TYPE); + session.AddString("default", "Chat-O-Matic [Haiku]"); + settings.AddMessage("setting", &session); + + return settings; +} + + +BMessage +MatrixProtocol::_RoomTemplate() +{ + BMessage settings; + + BMessage id; + id.AddString("name", "chat_id"); + id.AddString("description", B_TRANSLATE("Channel:")); + id.AddString("error", B_TRANSLATE("Please enter a channel― skipping it doesn't make sense!")); + id.AddInt32("type", B_STRING_TYPE); + settings.AddMessage("setting", &id); + + return settings; +} + + +BMessage +MatrixProtocol::_RosterTemplate() +{ + BMessage settings; + + BMessage nick; + nick.AddString("name", "user_id"); + nick.AddString("description", B_TRANSLATE("User ID:")); + nick.AddString("error", B_TRANSLATE("How can someone be your friend if you don't know their ID?")); + nick.AddInt32("type", B_STRING_TYPE); + settings.AddMessage("setting", &nick); + + return settings; +} diff --git a/protocols/matrix/MatrixProtocol.h b/protocols/matrix/MatrixProtocol.h new file mode 100644 index 0000000..eca91ce --- /dev/null +++ b/protocols/matrix/MatrixProtocol.h @@ -0,0 +1,75 @@ +/* + * Copyright 2021, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ +#ifndef _MATRIX_PROTOCOL_H +#define _MATRIX_PROTOCOL_H + +#include + +#include + +#include "Matrix.h" + + +status_t connect_thread(void* data); +BMessage receive_message(); + + +class MatrixProtocol : public ChatProtocol { +public: + MatrixProtocol(); + ~MatrixProtocol(); + + // ChatProtocol inheritance + virtual status_t Init(ChatProtocolMessengerInterface* interface); + virtual status_t Shutdown(); + + virtual status_t UpdateSettings(BMessage* settings); + + virtual status_t Process(BMessage* msg); + + virtual BMessage SettingsTemplate(const char* name); + virtual BObjectList + Commands(); + + virtual const char* Signature() const { return MATRIX_ADDON; } + virtual const char* FriendlySignature() const { return "Matrix"; } + + virtual BBitmap* Icon() const; + + virtual void SetAddOnPath(BPath path) { fAddOnPath = path; } + virtual BPath AddOnPath() { return fAddOnPath; } + + virtual const char* GetName() { return fName; } + virtual void SetName(const char* name) { fName = name; } + + virtual ChatProtocolMessengerInterface* + MessengerInterface() const { return fMessenger; } + + // Matrix + void SendMessage(BMessage* msg); + void RegisterApp(team_id team); + +private: + void _SendMatrixMessage(BMessage* msg); + void _StartApp(); + + // GUI templates + BMessage _AccountTemplate(); + BMessage _RoomTemplate(); + BMessage _RosterTemplate(); + + // Settings + BMessage* fSettings; + + BPath fAddOnPath; + BString fName; + ChatProtocolMessengerInterface* fMessenger; + + team_id fAppTeam; + thread_id fRecvThread; + BMessenger* fAppMessenger; +}; + +#endif // _MATRIX_PROTOCOL_H diff --git a/protocols/matrix/matrix.rdef b/protocols/matrix/matrix.rdef new file mode 100644 index 0000000..0792ac2 --- /dev/null +++ b/protocols/matrix/matrix.rdef @@ -0,0 +1,16 @@ +#include "Matrix.h" + +resource app_signature MATRIX_SIGNATURE; +resource app_flags B_BACKGROUND_APP | B_MULTIPLE_LAUNCH; + +resource app_version { + major = 0, + middle = 0, + minor = 1, + + variety = B_APPV_ALPHA, + internal = 0, + + short_info = "Chat-O-Matic Matrix add-on", + long_info = "©2021 Jaidyn Levesque" +};