From 588b32b9c392d6bf95d474353e6e6bc27ba0fa5f Mon Sep 17 00:00:00 2001 From: Jaidyn Ann Date: Sun, 27 Jun 2021 16:46:38 -0500 Subject: [PATCH] =?UTF-8?q?(purple)=20Main=20loop,=20server=E2=86=92add-on?= =?UTF-8?q?=E2=86=92app=20communication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now the purple add-on's starting to come together with a clear structure: * Add-on sends IM_MESSAGES etc to the server for processing * Server sends all (reply/etc) messages to add-on, which sends to app It's worth noting that on the add-on's side, no looper or handler is used for receiving messages, it's all through sending serialized BMessages to the add-on's connect_thread buffer. PurpleAccounts are now reliably associated with Cardie's account names and the thread ID of their respective connect_thread. The GLib main-loop is gone over regularly thanks to a message runner. Now, the add-on can log into/create accounts, connect to them, and send the IM_PROTOCOL_READY notification to Cardie as appropriate. --- protocols/purple/Purple.h | 24 ++++++ protocols/purple/PurpleApp.cpp | 126 ++++++++++++++++++++++++---- protocols/purple/PurpleApp.h | 21 ++++- protocols/purple/PurpleMessages.h | 11 ++- protocols/purple/PurpleProtocol.cpp | 69 ++++++++++----- protocols/purple/PurpleProtocol.h | 5 +- protocols/purple/purple.rdef | 3 + 7 files changed, 217 insertions(+), 42 deletions(-) create mode 100644 protocols/purple/Purple.h diff --git a/protocols/purple/Purple.h b/protocols/purple/Purple.h new file mode 100644 index 0000000..d34963e --- /dev/null +++ b/protocols/purple/Purple.h @@ -0,0 +1,24 @@ +/* + * Copyright 2021, Jaidyn Levesque + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _PURPLE_H +#define _PURPLE_H + +#define PURPLE_SIGNATURE "application/x-vnd.cardie.purple" + +#endif // _PURPLE_H diff --git a/protocols/purple/PurpleApp.cpp b/protocols/purple/PurpleApp.cpp index acdffc0..e1a0be7 100644 --- a/protocols/purple/PurpleApp.cpp +++ b/protocols/purple/PurpleApp.cpp @@ -25,6 +25,13 @@ #include #include +#include + +#include + +#include "Purple.h" +#include "PurpleMessages.h" + int main(int arc, char** argv) @@ -37,30 +44,32 @@ main(int arc, char** argv) PurpleApp::PurpleApp() : - BApplication("application/x-vnd.cardie.purple") + BApplication(PURPLE_SIGNATURE), + fGloop(g_main_loop_new(NULL, false)) { if (init_libpurple() != B_OK) std::cerr << "libpurple initialization failed. Please report!\n"; _GetProtocolsInfo(); + fGRunner = new BMessageRunner(this, new BMessage(G_MAIN_LOOP), 100000, -1); } void PurpleApp::MessageReceived(BMessage* msg) { - int64 thread_id; - switch (msg->what) { case PURPLE_REQUEST_PROTOCOL_COUNT: { + int64 thread_id; if (msg->FindInt64("thread_id", &thread_id) != B_OK) return; send_data(thread_id, fProtocols.CountItems(), NULL, 0); break; } case PURPLE_REQUEST_PROTOCOL_INFO: { + int64 thread_id; if (msg->FindInt64("thread_id", &thread_id) != B_OK) return; int32 index = msg->FindInt32("index", 0); ProtocolInfo* info = fProtocols.ItemAt(index); @@ -68,14 +77,8 @@ PurpleApp::MessageReceived(BMessage* msg) BMessage protocolInfo = info->settingsTemplate; protocolInfo.AddString("name", info->name); protocolInfo.AddString("id", info->id); + SendMessage(thread_id, protocolInfo); - // Send message to requester - ssize_t size = protocolInfo.FlattenedSize(); - char buffer[size]; - - send_data(thread_id, size, NULL, 0); - protocolInfo.Flatten(buffer, size); - send_data(thread_id, 0, buffer, size); break; } case PURPLE_LOAD_ACCOUNT: @@ -83,12 +86,51 @@ PurpleApp::MessageReceived(BMessage* msg) _ParseCardieSettings(msg); break; } + case PURPLE_REGISTER_THREAD: + { + msg->PrintToStream(); + BString accName = msg->FindString("account_name"); + BString username = fAccounts.ValueFor(accName); + int64 thread; + if (username.IsEmpty() == true + || msg->FindInt64("thread_id", &thread) != B_OK) + break; + fAccountThreads.AddItem(username, thread); + break; + } + case G_MAIN_LOOP: + g_main_context_iteration(g_main_loop_get_context(fGloop), false); + break; default: BApplication::MessageReceived(msg); } } +void +PurpleApp::SendMessage(thread_id thread, BMessage msg) +{ + ssize_t size = msg.FlattenedSize(); + char buffer[size]; + + send_data(thread, size, NULL, 0); + msg.Flatten(buffer, size); + send_data(thread, 0, buffer, size); +} + + +void +PurpleApp::SendMessage(PurpleAccount* account, BMessage msg) +{ + const char* username = purple_account_get_username(account); + thread_id thread = fAccountThreads.ValueFor(BString(username)); + if (thread > 0) + SendMessage(thread, msg); + else + std::cerr << "Failed to send message: " << msg.what << std::endl; +} + + void PurpleApp::_GetProtocolsInfo() { @@ -217,7 +259,7 @@ PurpleApp::_ParseCardieSettings(BMessage* settings) { PurplePlugin* plugin = _PluginFromMessage(settings); PurplePluginProtocolInfo* info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - const char* protoId = settings->FindString("signature"); + const char* protoId = settings->FindString("protocol"); if (plugin == NULL || info == NULL) return; @@ -245,10 +287,11 @@ PurpleApp::_ParseCardieSettings(BMessage* settings) PurpleAccount* account = purple_accounts_find(username.String(), protoId); if (account == NULL) { account = purple_account_new(username.String(), protoId); - purple_account_set_password(account, password.String()); purple_accounts_add(account); } + purple_account_set_password(account, password.String()); + // Set all protocol settings GList* prefIter = info->protocol_options; for (int i = 0; prefIter != NULL; prefIter = prefIter->next) @@ -295,15 +338,24 @@ PurpleApp::_ParseCardieSettings(BMessage* settings) value.String()); } } - fAccounts.AddItem(settings->FindString("account_name"), username); + purple_account_set_enabled(account, PURPLE_UI_ID, true); } PurplePlugin* PurpleApp::_PluginFromMessage(BMessage* msg) { - return purple_plugins_find_with_id(msg->FindString("signature")); + return purple_plugins_find_with_id(msg->FindString("protocol")); +} + + +PurpleAccount* +PurpleApp::_AccountFromMessage(BMessage* msg) +{ + BString protocol = msg->FindString("protocol"); + BString username = fAccounts.ValueFor(msg->FindString("account_name")); + return purple_accounts_find(username.String(), protocol.String()); } @@ -324,14 +376,56 @@ static PurpleEventLoopUiOps _glib_eventloops = status_t init_libpurple() { - purple_eventloop_set_ui_ops(&_glib_eventloops); + init_ui_ops(); - if (!purple_core_init("cardie")) + if (!purple_core_init(PURPLE_UI_ID)) return B_ERROR; + + purple_set_blist(purple_blist_new()); + purple_blist_load(); + + init_signals(); return B_OK; } +void +init_ui_ops() +{ + purple_eventloop_set_ui_ops(&_glib_eventloops); +} + + +void +init_signals() +{ + int handle; + purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, + PURPLE_CALLBACK(signal_signed_on), NULL); + purple_signal_connect(purple_connections_get_handle(), "connection-error", &handle, + PURPLE_CALLBACK(signal_connection_error), NULL); +} + + +static void +signal_signed_on(PurpleConnection* gc) +{ + BMessage readyMsg(IM_MESSAGE); + readyMsg.AddInt32("im_what", IM_PROTOCOL_READY); + + PurpleApp* app = (PurpleApp*)be_app; + app->SendMessage(purple_connection_get_account(gc), readyMsg); +} + + +static void +signal_connection_error(PurpleConnection* gc, PurpleConnectionError err, + const gchar* desc) +{ + std::cout << "Connection failed: " << (const char*)desc << std::endl; +} + + static guint _purple_glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function, gpointer data) { diff --git a/protocols/purple/PurpleApp.h b/protocols/purple/PurpleApp.h index f46c833..0962d95 100644 --- a/protocols/purple/PurpleApp.h +++ b/protocols/purple/PurpleApp.h @@ -29,14 +29,16 @@ #include -#include "PurpleMessages.h" +typedef KeyMap Accounts; // Cardie username → Purple username +typedef KeyMap AccountThreads; // Purple username → Thread -typedef KeyMap Accounts; +const uint32 G_MAIN_LOOP = 'GLml'; #define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) #define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) +#define PURPLE_UI_ID "cardie" typedef struct _PurpleGLibIOClosure { @@ -59,6 +61,8 @@ class PurpleApp : public BApplication { public: PurpleApp(); virtual void MessageReceived(BMessage* msg); + void SendMessage(thread_id thread, BMessage msg); + void SendMessage(PurpleAccount* account, BMessage msg); private: void _GetProtocolsInfo(); @@ -68,18 +72,29 @@ private: void _ParseCardieSettings(BMessage* settings); PurplePlugin* _PluginFromMessage(BMessage* msg); + PurpleAccount* _AccountFromMessage(BMessage* msg); Accounts fAccounts; + AccountThreads fAccountThreads; BObjectList fProtocols; + + GMainLoop* fGloop; + BMessageRunner* fGRunner; }; status_t init_libpurple(); +void init_ui_ops(); +void init_signals(); + +// Connection signals +static void signal_signed_on(PurpleConnection* gc); +static void signal_connection_error(PurpleConnection* gc, + PurpleConnectionError err, const gchar* desc); static guint _purple_glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function, gpointer data); static gboolean _purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data); - #endif // _PURPLE_APP_H diff --git a/protocols/purple/PurpleMessages.h b/protocols/purple/PurpleMessages.h index 65cdb61..b1a02a6 100644 --- a/protocols/purple/PurpleMessages.h +++ b/protocols/purple/PurpleMessages.h @@ -44,8 +44,15 @@ enum purple_message { * Just the account's settings message from Cardie's end. * It's the server's job to tie the Cardie account name * to the PurpleAccount. */ - PURPLE_LOAD_ACCOUNT = 'PAla' + PURPLE_LOAD_ACCOUNT = 'PAla', + + /*! Associate account with thread →Server + * Makes the server associate the given account with + * the given thread. All subsequent Server→Add-On + * messages related to the account will be sent to this + * thread. + * Requires: String account_name, int64 thread_id */ + PURPLE_REGISTER_THREAD = 'PArl' }; - #endif // _PURPLE_MESSAGES_H diff --git a/protocols/purple/PurpleProtocol.cpp b/protocols/purple/PurpleProtocol.cpp index 50f54b1..99138a6 100644 --- a/protocols/purple/PurpleProtocol.cpp +++ b/protocols/purple/PurpleProtocol.cpp @@ -24,6 +24,7 @@ #include +#include "Purple.h" #include "PurpleMessages.h" @@ -40,18 +41,11 @@ protocol_at(int32 i) msg->AddInt32("index", i); msgr->SendMessage(msg); - thread_id sender; + BMessage protoInfo = receive_message(); + BString name = protoInfo.FindString("name"); + BString id = protoInfo.FindString("id"); - int32 size = receive_data(&sender, NULL, 0); - char buffer[size]; - receive_data(&sender, buffer, size); - BMessage temp; - temp.Unflatten(buffer); - - BString name = temp.FindString("name"); - BString id = temp.FindString("id"); - - return (ChatProtocol*)new PurpleProtocol(name, id, temp); + return (ChatProtocol*)new PurpleProtocol(name, id, protoInfo); } @@ -93,7 +87,7 @@ ensure_app_messenger() { if (kAppMessenger == NULL || kAppMessenger->IsValid() == false) { ensure_app(); - kAppMessenger = new BMessenger("application/x-vnd.cardie.purple"); + kAppMessenger = new BMessenger(PURPLE_SIGNATURE); } return kAppMessenger; } @@ -103,7 +97,7 @@ void ensure_app() { BRoster roster; - if (roster.IsRunning("application/x-vnd.cardie.purple") == true) + if (roster.IsRunning(PURPLE_SIGNATURE) == true) return; app_info aInfo; @@ -115,12 +109,32 @@ ensure_app() entry_ref protoRef; BEntry(protoPath.Path()).GetRef(&protoRef); roster.Launch(&protoRef); + snooze(100000); } status_t connect_thread(void* data) { + PurpleProtocol* protocol = (PurpleProtocol*)data; + + while (true) { + BMessage msg = receive_message(); + protocol->SendMessage(new BMessage(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; } @@ -159,16 +173,21 @@ status_t PurpleProtocol::UpdateSettings(BMessage* msg) { ensure_app(); - fPrplMessenger = new BMessenger("application/x-vnd.cardie.purple"); + fPrplMessenger = new BMessenger(PURPLE_SIGNATURE); msg->what = PURPLE_LOAD_ACCOUNT; _SendPrplMessage(msg); -// thread_id thread = spawn_thread(connect_thread, "connect_thread", -// B_NORMAL_PRIORITY, (void*)this); -// if (thread < B_OK) -// return B_ERROR; + thread_id thread = spawn_thread(connect_thread, "bird_superiority", + B_NORMAL_PRIORITY, (void*)this); -// resume_thread(thread); + if (thread < B_OK) + return B_ERROR; + + BMessage* account = new BMessage(PURPLE_REGISTER_THREAD); + account->AddInt64("thread_id", thread); + _SendPrplMessage(account); + + resume_thread(thread); return B_OK; } @@ -270,11 +289,21 @@ PurpleProtocol::MessengerInterface() const } +void +PurpleProtocol::SendMessage(BMessage* msg) +{ + if (!msg) + return; + msg->AddString("protocol", fSignature); + fMessenger->SendMessage(msg); +} + + void PurpleProtocol::_SendPrplMessage(BMessage* msg) { msg->AddString("account_name", fName); - msg->AddString("signature", fSignature); + msg->AddString("protocol", fSignature); if (fPrplMessenger->IsValid()) fPrplMessenger->SendMessage(msg); } diff --git a/protocols/purple/PurpleProtocol.h b/protocols/purple/PurpleProtocol.h index 28074f9..7b55299 100644 --- a/protocols/purple/PurpleProtocol.h +++ b/protocols/purple/PurpleProtocol.h @@ -37,7 +37,7 @@ BMessenger* ensure_app_messenger(); void ensure_app(); status_t connect_thread(void* data); - +BMessage receive_message(); class PurpleProtocol : public ChatProtocol { public: @@ -74,8 +74,11 @@ public: virtual ChatProtocolMessengerInterface* MessengerInterface() const; + void SendMessage(BMessage* msg); + private: void _SendPrplMessage(BMessage* msg); + ChatProtocolMessengerInterface* fMessenger; BMessenger* fPrplMessenger; thread_id fServerThread; diff --git a/protocols/purple/purple.rdef b/protocols/purple/purple.rdef index b4d40a5..af6ad21 100644 --- a/protocols/purple/purple.rdef +++ b/protocols/purple/purple.rdef @@ -1,3 +1,6 @@ +#include "Purple.h" + +resource app_signature PURPLE_SIGNATURE; resource app_version { major = 0,