diff options
Diffstat (limited to 'src/daemon')
-rw-r--r-- | src/daemon/main.c | 4 | ||||
-rw-r--r-- | src/daemon/meson.build | 2 | ||||
-rw-r--r-- | src/daemon/purple.c | 211 | ||||
-rw-r--r-- | src/daemon/purple_private.h | 56 | ||||
-rw-r--r-- | src/daemon/settings.c | 34 | ||||
-rw-r--r-- | src/daemon/settings_private.h | 11 |
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); |