From: Drew Fisher Date: Sat, 12 Apr 2014 08:35:20 +0000 (-0700) Subject: WIP on ribbon, a Qt libpurple client X-Git-Url: http://git.zarvox.org/dispatcher.js?a=commitdiff_plain;h=932fcb3c1a4703ff3d158c54a5251688eac17051;p=imoo.git WIP on ribbon, a Qt libpurple client --- diff --git a/ribbon/main.cpp b/ribbon/main.cpp new file mode 100644 index 0000000..ae507c1 --- /dev/null +++ b/ribbon/main.cpp @@ -0,0 +1,38 @@ +//#include +#include +#include + +#include +#include + +#include + +#include "ribbonmanager.h" + + +int main(int argc, char** argv) +{ + QApplication* app = new QApplication(argc, argv); + //GMainLoop *loop = g_main_loop_new(NULL, FALSE); + + /* libpurple's built-in DNS resolution forks processes to perform + * blocking lookups without blocking the main process. It does not + * handle SIGCHLD itself. This is rather unsettling. + */ + signal(SIGCHLD, SIG_IGN); + + QPushButton* b = new QPushButton("Sign in"); + b->resize(100,50); + b->show(); + + RibbonManager* purple = new RibbonManager(); + purple->init_libpurple(); + + qDebug() << "libpurple initialized, version" << purple->get_version(); + + QObject::connect(b, SIGNAL(clicked()), + purple, SLOT(sign_in_user())); + + return app->exec(); +} + diff --git a/ribbon/ribbon.pro b/ribbon/ribbon.pro new file mode 100644 index 0000000..03e93f3 --- /dev/null +++ b/ribbon/ribbon.pro @@ -0,0 +1,19 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Wed Apr 9 18:55:15 2014 +###################################################################### + +TEMPLATE = app +TARGET = ribbon +DEPENDPATH += . +INCLUDEPATH += . + +unix { + CONFIG += link_pkgconfig + PKGCONFIG += glib-2.0 purple +} + +# Input +HEADERS += ribbonmanager.h + +SOURCES += ribbonmanager.cpp \ + main.cpp diff --git a/ribbon/ribbonmanager.cpp b/ribbon/ribbonmanager.cpp new file mode 100644 index 0000000..304fa03 --- /dev/null +++ b/ribbon/ribbonmanager.cpp @@ -0,0 +1,257 @@ +#include "ribbonmanager.h" + +#include +#include +#include +#include + +/* Eventloop and glib IO stuff copied verbatim from nullclient.c */ +#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) + +typedef struct _PurpleGLibIOClosure { + PurpleInputFunction function; + guint result; + gpointer data; +} PurpleGLibIOClosure; + +static void purple_glib_io_destroy(gpointer data) +{ + g_free(data); +} + +static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ + PurpleGLibIOClosure *closure = (PurpleGLibIOClosure*)data; + PurpleInputCondition purple_cond = (PurpleInputCondition)0; + + if (condition & PURPLE_GLIB_READ_COND) + purple_cond = (PurpleInputCondition)(purple_cond | PURPLE_INPUT_READ); + if (condition & PURPLE_GLIB_WRITE_COND) + purple_cond = (PurpleInputCondition)(purple_cond | PURPLE_INPUT_WRITE); + + closure->function(closure->data, g_io_channel_unix_get_fd(source), + purple_cond); + + return TRUE; +} + +static guint glib_input_add(gint fd, PurpleInputCondition condition, + PurpleInputFunction function, gpointer data) +{ + PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1); + GIOChannel *channel; + GIOCondition cond = (GIOCondition)0; + + closure->function = function; + closure->data = data; + + if (condition & PURPLE_INPUT_READ) + cond = (GIOCondition)(cond | PURPLE_GLIB_READ_COND); + if (condition & PURPLE_INPUT_WRITE) + cond = (GIOCondition)(cond | PURPLE_GLIB_WRITE_COND); + + channel = g_io_channel_unix_new(fd); + closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, + cond, purple_glib_io_invoke, closure, purple_glib_io_destroy); + + g_io_channel_unref(channel); + return closure->result; +} + +static PurpleEventLoopUiOps glib_eventloops = +{ + g_timeout_add, /* guint timeout_add(guint interval, GSourceFunc function, gpointer data) */ + g_source_remove, /* gboolean timeout_remove(guint handle); */ + glib_input_add, /* guint input_add(int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer user_data) */ + g_source_remove, /* gboolean input_remove(guint handle) */ + NULL, /* int input_get_error(int fd, int* error) */ + g_timeout_add_seconds, /* guint timeout_add_seconds(guint interval, GSourceFunc function, gpointer data) */ + + /* padding */ + //NULL, + NULL, + NULL, + NULL +}; +/* End verbatim copied code */ + +/* noops for now, add features later */ +static PurpleConversationUiOps ribbon_conversation_callbacks = { + NULL, /* create_conversation */ + NULL, /* destroy_conversation */ + NULL, /* write_chat */ + NULL, /* write_im */ + NULL, /* write_conv */ + NULL, /* chat_add_users */ + NULL, /* chat_rename_user */ + NULL, /* chat_remove_users */ + NULL, /* chat_update_user */ + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL +}; + +static void connect_progress(PurpleConnection *gc, const char* text, size_t step, size_t step_count) +{ + Q_UNUSED(gc); + qDebug() << "Connection progress:" << text << step << "of" << step_count; +} + +static void connection_notice(PurpleConnection *gc, const char* text) +{ + Q_UNUSED(gc); + qDebug() << "Connection notice:" << text; +} + +static void connection_disconnected(PurpleConnection *gc, const char* text) +{ + Q_UNUSED(gc); + qDebug() << "Connection disconnected:" << text; +} + +static void network_connected(void) +{ + printf("Network connection established\n"); +} + +static void network_disconnected(void) +{ + printf("This machine has been disconnected from the internet\n"); +} + +static void report_disconnect_reason(PurpleConnection *gc, PurpleConnectionError reason, const char *text) +{ + PurpleAccount *account = purple_connection_get_account(gc); + printf("Connection disconnected: \"%s\" (%s)\n >Error: %d\n >Reason: %s\n", purple_account_get_username(account), purple_account_get_protocol_id(account), reason, text); +} + +static PurpleConnectionUiOps connection_uiops = +{ + connect_progress, /* connect_progress */ + NULL, /* connected */ + NULL, /* disconnected */ + connection_notice, /* notice */ + connection_disconnected, /* report_disconnect */ + network_connected, /* network_connected */ + network_disconnected, /* network_disconnected */ + report_disconnect_reason, /* report_disconnect_reason */ + NULL, + NULL, + NULL +}; + +static void ribbon_core_init(void) { + printf("core_init called\n"); + printf("setting connection callbacks\n"); + purple_connections_set_ui_ops(&connection_uiops); +} + +static PurpleCoreUiOps ribbon_core_callbacks = { + NULL, /* ui_prefs_init - called just after preferences subsystem is initialized */ + NULL, /* debug_ui_init - called just after the debug system is inited, but before other components init */ + ribbon_core_init, /* ui_init - called after all of libpurple has been initialized */ + NULL, /* quit - called after most of libpurple has been uninitialized */ + NULL, /* get_ui_info - called by purple_core_get_ui_info, should return a + GHashTable* with string key/value pairs containing info about the UI, like: + name, version, website, dev_website */ + /* padding */ + //NULL, + NULL, + NULL, + NULL +}; + +static void signed_on(PurpleConnection *gc) +{ + PurpleAccount *account = purple_connection_get_account(gc); + printf("Account connected: \"%s\" (%s)\n", purple_account_get_username(account), purple_account_get_protocol_id(account)); +} + +static void received_im_msg(PurpleAccount *account, char *sender, char *message, + PurpleConversation *conv, PurpleMessageFlags flags) +{ + Q_UNUSED(flags); + if (conv==NULL) { + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); + } + printf("(%s) %s (%s): %s\n", purple_utf8_strftime("%H:%M:%S", NULL), sender, purple_conversation_get_name(conv), message); +} + +void connect_to_signals(void) +{ + static int handle; + purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, + PURPLE_CALLBACK(signed_on), NULL); + purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", &handle, + PURPLE_CALLBACK(received_im_msg), NULL); +} + +RibbonManager::RibbonManager(QObject* parent) : QObject(parent) +{ +} + +RibbonManager::~RibbonManager() +{ +} + +void RibbonManager::init_libpurple(void) +{ + /* optional: + purple_util_set_user_dir(CUSTOM_USER_DIRECTORY); + */ + + /* Enable debugging messages */ + purple_debug_set_enabled(FALSE); + + /* configure purple core callbacks */ + purple_core_set_ui_ops(&ribbon_core_callbacks); + /* configure purple eventloop */ + purple_eventloop_set_ui_ops(&glib_eventloops); + /* Init purple core */ + if (!purple_core_init(UI_ID)) { + /* Initializing the core failed. Terminate. */ + fprintf(stderr, + "libpurple initialization failed. Dumping core.\n" + "Please report this!\n"); + abort(); + } + + /* Create and load the buddylist */ + purple_set_blist(purple_blist_new()); + purple_blist_load(); + + /* Hopefully this is an okay time for this? */ + connect_to_signals(); +} + +const char* RibbonManager::get_version(void) +{ + return purple_core_get_version(); +} + +void RibbonManager::sign_in_user(void) +{ + QFile file("account.txt"); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "Couldn't load account information from account.txt!"; + return; + } + QByteArray protocol = file.readLine().trimmed(); + QByteArray username = file.readLine().trimmed(); + QByteArray password = file.readLine().trimmed(); + // Allocate and enable an account. + // PurpleAccount *account = purple_account_new("YOUR_IM_ACCOUNTS_USERNAME_HERE", "prpl-IM_NETWORK_HERE"); //this could be prpl-aim, prpl-yahoo, prpl-msn, prpl-icq, etc. + PurpleAccount *account = purple_account_new(username.data(), protocol.data()); + // purple_account_set_password(account, "YOUR_IM_ACCOUNTS_PASSWORD_HERE"); + purple_account_set_password(account, password.data()); + purple_accounts_add(account); + purple_account_set_enabled(account, UI_ID, TRUE); +} diff --git a/ribbon/ribbonmanager.h b/ribbon/ribbonmanager.h new file mode 100644 index 0000000..a9d15f9 --- /dev/null +++ b/ribbon/ribbonmanager.h @@ -0,0 +1,22 @@ +#ifndef RIBBONMANAGER_H +#define RIBBONMANAGER_H + +#include + +#define UI_ID "ribbon" + +class RibbonManager : public QObject { + Q_OBJECT +public: + RibbonManager(QObject* parent = 0); + ~RibbonManager(); + void init_libpurple(void); + //void connect_to_signals(void); + const char* get_version(void); +public slots: + void sign_in_user(void); +private: + +}; + +#endif /* RIBBONMANAGER_H */