From e355b074ac00e8b05af89f854b0e46d80a5de00a Mon Sep 17 00:00:00 2001 From: Gediminas Jakutis Date: Fri, 17 May 2019 13:56:05 +0300 Subject: daemon: add a skeleton libpurple interface. This loads and initialized libpurple, but does not actually do anything. This should be enough to warrant closing Ticket #18. Signed-off-by: Gediminas Jakutis --- src/daemon/main.c | 4 + src/daemon/meson.build | 2 + src/daemon/purple.c | 211 ++++++++++++++++++++++++++++++++++++++++++ src/daemon/purple_private.h | 56 +++++++++++ src/daemon/settings.c | 28 ++++++ src/daemon/settings_private.h | 4 + 6 files changed, 305 insertions(+) create mode 100644 src/daemon/purple.c create mode 100644 src/daemon/purple_private.h (limited to 'src/daemon') 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 #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 +#include +#include +#include +#include +#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 + +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 8decf08..b5c9c0c 100644 --- a/src/daemon/settings.c +++ b/src/daemon/settings.c @@ -19,6 +19,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define _GNU_SOURCE + +#include +#include #include "settings.h" #include "settings_private.h" @@ -31,6 +35,10 @@ void settings_init(void) unset_flag(flag_daemonize); settings.port = 6996; set_flag(flag_verbose); + settings.progname = program_invocation_short_name; + settings.im_user = "user"; + settings.im_password = "password"; + settings.im_proto = "prpl-irc"; } int setting_detach(void) @@ -48,6 +56,26 @@ 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) { diff --git a/src/daemon/settings_private.h b/src/daemon/settings_private.h index 336c151..388509a 100644 --- a/src/daemon/settings_private.h +++ b/src/daemon/settings_private.h @@ -28,6 +28,10 @@ static const unsigned int flag_verbose = 1 << 1; static struct settings { unsigned int flags; unsigned short int port; + char *progname; + char *im_user; + char *im_password; + char *im_proto; } settings; int test_flag(unsigned int flag); -- cgit v1.2.3