summaryrefslogtreecommitdiffstats
path: root/src/daemon/purple.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/purple.c')
-rw-r--r--src/daemon/purple.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/src/daemon/purple.c b/src/daemon/purple.c
new file mode 100644
index 0000000..882ea61
--- /dev/null
+++ b/src/daemon/purple.c
@@ -0,0 +1,254 @@
+/*
+ * Usurpation – server daemon main logic
+ *
+ * Copyright (C) 2019 Gediminas Jakutis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1
+ * of the License.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <libpurple/purple.h>
+#include <glib.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include "purple_private.h"
+#include "settings.h"
+
+static PurpleConversationUiOps conv_uiops =
+{
+ 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, /* send_confirm */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static PurpleEventLoopUiOps eventloops =
+{
+ g_timeout_add,
+ g_source_remove,
+ glib_input_add,
+ g_source_remove,
+ NULL,
+ g_timeout_add_seconds,
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL
+};
+
+static gboolean purple_glib_io(GIOChannel *source, GIOCondition cond, gpointer data)
+{
+ struct glib_io *bloc = data;
+ PurpleInputCondition pio_cond = 0;
+
+ if (cond & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
+ pio_cond |= PURPLE_INPUT_READ;
+ }
+
+ if (cond & (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ pio_cond |= PURPLE_INPUT_WRITE;
+ }
+
+ bloc->func(bloc->data, g_io_channel_unix_get_fd(source), pio_cond);
+
+ return 1;
+}
+
+static guint glib_input_add(gint fd, PurpleInputCondition pio_cond, PurpleInputFunction func, gpointer data)
+{
+ struct glib_io *bloc;
+ GIOChannel *channel;
+ GIOCondition cond = 0;
+
+ bloc = g_new0(struct glib_io, 1);
+ bloc->func = func;
+ bloc->data = data;
+
+ if (pio_cond & PURPLE_INPUT_READ) {
+ cond |= G_IO_IN | G_IO_HUP | G_IO_ERR;
+ }
+
+ if (pio_cond & PURPLE_INPUT_WRITE) {
+ cond |= G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+ }
+
+ channel = g_io_channel_unix_new(fd);
+ bloc->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, purple_glib_io, bloc, g_free);
+ g_io_channel_unref(channel);
+
+ return bloc->result;
+}
+
+/* TODO: semi-stub */
+static void iface_init(void)
+{
+ purple_conversations_set_ui_ops(&conv_uiops);
+}
+
+static void pthread_mutex_unlock_thunk(void *arg)
+{
+ pthread_mutex_t *mutex = arg;
+
+ (void) pthread_mutex_unlock(mutex);
+}
+
+static void purple_account_destroy_thunk(void *arg)
+{
+ PurpleAccount *account = arg;
+
+ purple_account_destroy(account);
+}
+
+static void purple_core_destroy(void *arg)
+{
+ (void) arg;
+
+ purple_timeout_add(0, purple_core_quit_cb, NULL);
+}
+
+static void g_main_loop_unref_thunk(void *arg)
+{
+ GMainLoop *loop = arg;
+
+ g_main_loop_unref(loop);
+}
+
+static void g_main_loop_quit_thunk(void *arg)
+{
+ GMainLoop *loop = arg;
+
+ g_main_loop_quit(loop);
+}
+
+
+static void *purple_spawn(void *disregard)
+{
+ GMainLoop *loop;
+ char *progname = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *proto = NULL;
+
+ (void) disregard;
+
+ pthread_cleanup_push(pthread_mutex_unlock_thunk, &state.mutex);
+
+ progname = setting_progname();
+ pthread_cleanup_push(free, progname);
+
+ user = setting_im_user();
+ pthread_cleanup_push(free, user);
+
+ password = setting_im_password();
+ pthread_cleanup_push(free, password);
+
+ proto = setting_im_proto();
+ pthread_cleanup_push(free, proto);
+
+ loop = g_main_loop_new(NULL, FALSE);
+ pthread_cleanup_push(g_main_loop_unref_thunk, loop);
+
+ /* avoid an unholy army of libpurple's DNS resolver process zombies */
+ signal(SIGCHLD, SIG_IGN);
+
+ /* do not actually save anything about the account through libpurple */
+ purple_util_set_user_dir("/dev/full");
+
+ /*
+ * TODO:
+ * we do allow verbosity here yet
+ * use this once we do:
+ * purple_debug_set_enabled(setting_verbose());
+ */
+ purple_debug_set_enabled(0);
+
+ purple_core_set_ui_ops(&core_uiops);
+ purple_eventloop_set_ui_ops(&eventloops);
+ purple_core_init(progname);
+ purple_prefs_load();
+
+ pthread_cleanup_push(purple_core_destroy, NULL);
+
+ state.account = purple_account_new(user, proto);
+ purple_account_set_password(state.account, password);
+ purple_account_set_enabled(state.account, progname, TRUE);
+
+ pthread_cleanup_push(purple_account_destroy_thunk, state.account);
+
+ g_main_loop_run(loop);
+ pthread_cleanup_push(g_main_loop_quit_thunk, loop);
+
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+
+ pthread_exit(NULL);
+}
+
+/*
+ * The following functions are exported and thus, can be wildly called by other
+ * modules and thus, are guarded by a mutex to make kind of a singleton, as it
+ * is NOT safe to access libpurple from more than one context / thread while
+ * using the same configuration (read: confdir).
+ */
+
+int purple_init(void)
+{
+ int ret = 0;
+
+ /* aquire the """singleton""" mutex */
+ if (pthread_mutex_trylock(&state.mutex)) {
+ /* TODO: use proper error numbers */
+ ret = 1;
+ } else {
+ if ((ret = pthread_create(&state.purple, NULL, purple_spawn, NULL))) {
+ /* thread creation failed, release the mutex */
+ pthread_mutex_unlock(&state.mutex);
+ }
+ }
+
+ return ret;
+}
+
+void purple_close(void)
+{
+
+ if (pthread_mutex_trylock(&state.mutex) == EBUSY) {
+ pthread_cancel(state.purple);
+ pthread_join(state.purple, NULL);
+ }
+}