summaryrefslogtreecommitdiffstats
path: root/src/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon')
-rw-r--r--src/daemon/main.c4
-rw-r--r--src/daemon/meson.build2
-rw-r--r--src/daemon/purple.c211
-rw-r--r--src/daemon/purple_private.h56
-rw-r--r--src/daemon/settings.c34
-rw-r--r--src/daemon/settings_private.h11
6 files changed, 310 insertions, 8 deletions
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 302a9ce..41e798b 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -24,6 +24,7 @@
#include <stdio.h>
#include "settings.h"
#include "net.h"
+#include "purple.h"
/* the logic is a placeholder right now */
int main(int argc, char **argv)
@@ -35,6 +36,9 @@ int main(int argc, char **argv)
settings_init();
net_init(setting_port()); /* TODO: get port from settings. */
+ if (purple_init() && setting_verbose()) {
+ fprintf(stderr, "libpurple initialization failed\n");
+ }
/* by default and if running by as a system service, the init system
* needs to keep control of the process and thus only detach if
diff --git a/src/daemon/meson.build b/src/daemon/meson.build
index 44fa299..aa0aa75 100644
--- a/src/daemon/meson.build
+++ b/src/daemon/meson.build
@@ -2,7 +2,9 @@ d_filenames = [
'main.c',
'settings.c',
'net.c',
+ 'purple.c',
'settings_private.h',
+ 'purple_private.h',
]
d_conf_filenames = [
diff --git a/src/daemon/purple.c b/src/daemon/purple.c
new file mode 100644
index 0000000..6b73774
--- /dev/null
+++ b/src/daemon/purple.c
@@ -0,0 +1,211 @@
+/*
+ * 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_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);
+ pthread_cleanup_push(free, progname);
+ pthread_cleanup_push(free, user);
+ pthread_cleanup_push(free, password);
+ pthread_cleanup_push(free, proto);
+
+ progname = setting_progname();
+ user = setting_im_user();
+ password = setting_im_password();
+ proto = setting_im_proto();
+ loop = g_main_loop_new(NULL, FALSE);
+
+ /* 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(setting_progname());
+ purple_prefs_load();
+
+ state.account = purple_account_new(setting_im_user(), setting_im_proto());
+ purple_account_set_password(state.account, setting_im_password());
+ purple_account_set_enabled(state.account, setting_progname(), TRUE);
+
+ g_main_loop_run(loop);
+
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+
+ return 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);
+ }
+}
diff --git a/src/daemon/purple_private.h b/src/daemon/purple_private.h
new file mode 100644
index 0000000..ff50139
--- /dev/null
+++ b/src/daemon/purple_private.h
@@ -0,0 +1,56 @@
+/*
+ * Usurpation – server daemon IM module, private header
+ *
+ * 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
+ */
+
+#ifndef USURPATION_PURPLE_PRIVATE_H
+#define USURPATION_PURPLE_PRIVATE_H
+
+#include <pthread.h>
+
+static void iface_init(void);
+
+static struct state {
+ pthread_mutex_t mutex;
+ pthread_t purple;
+ PurpleAccount *account;
+} state = {PTHREAD_MUTEX_INITIALIZER, 0, 0};
+
+struct glib_io {
+ PurpleInputFunction func;
+ guint result;
+ void *data;
+};
+
+static PurpleCoreUiOps core_uiops = {
+ NULL,
+ NULL,
+ iface_init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static gboolean purple_glib_io(GIOChannel *source, GIOCondition cond, gpointer data);
+static guint glib_input_add(gint fd, PurpleInputCondition pio_cond, PurpleInputFunction function, gpointer data);
+static void *purple_spawn(void *disregard);
+static void pthread_mutex_unlock_thunk(void *arg);
+
+#endif /* USURPATION_PURPLE_PRIVATE_H */
diff --git a/src/daemon/settings.c b/src/daemon/settings.c
index e5941cc..2b07b98 100644
--- a/src/daemon/settings.c
+++ b/src/daemon/settings.c
@@ -20,13 +20,15 @@
*/
#define _GNU_SOURCE
-#include <stdio.h>
+
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
+#include <errno.h>
#include "settings.h"
#include "settings_private.h"
@@ -39,6 +41,10 @@ void settings_init(void)
unset_flag(flag_daemonize);
settings.port = 6996;
settings.verboselevel = USURP_VERBOSITY;
+ settings.progname = program_invocation_short_name;
+ settings.im_user = "user";
+ settings.im_password = "password";
+ settings.im_proto = "prpl-irc";
#define macro2str(a) _macro2str(a)
#define _macro2str(a) #a
setting_readconf("/" macro2str(SYSCONFDIR) "/usurpation.conf");
@@ -61,8 +67,28 @@ unsigned short int setting_port(void)
return settings.port;
}
+char *setting_progname(void)
+{
+ return strdup(settings.progname);
+}
+
+char *setting_im_user(void)
+{
+ return strdup(settings.im_user);
+}
+
+char *setting_im_password(void)
+{
+ return strdup(settings.im_password);
+}
+
+char *setting_im_proto(void)
+{
+ return strdup(settings.im_proto);
+}
+
/* could be a one-liner, but let's make the logic more obvious */
-int test_flag(unsigned int flag)
+static int test_flag(unsigned int flag)
{
int ret;
@@ -72,12 +98,12 @@ int test_flag(unsigned int flag)
return ret;
}
-void set_flag(unsigned int flag)
+static void set_flag(unsigned int flag)
{
settings.flags |= flag;
}
-void unset_flag(unsigned int flag)
+static void unset_flag(unsigned int flag)
{
settings.flags &= ~flag;
}
diff --git a/src/daemon/settings_private.h b/src/daemon/settings_private.h
index 2d97bdd..0b0680e 100644
--- a/src/daemon/settings_private.h
+++ b/src/daemon/settings_private.h
@@ -32,6 +32,10 @@ static struct settings {
unsigned int flags;
unsigned short int port;
enum verbosity verboselevel;
+ char *progname;
+ char *im_user;
+ char *im_password;
+ char *im_proto;
} settings;
struct entry_description {
@@ -39,10 +43,9 @@ struct entry_description {
void (*set)(const void * const);
};
-
-int test_flag(unsigned int flag);
-void set_flag(unsigned int flag);
-void unset_flag(unsigned int flag);
+static int test_flag(unsigned int flag);
+static void set_flag(unsigned int flag);
+static void unset_flag(unsigned int flag);
static int setting_readconf(const char * const path);
static int setting_handle_config_entry(const char * const entry, const char * const value);