summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/daemon/main.c23
-rw-r--r--src/daemon/meson.build2
-rw-r--r--src/daemon/net.c19
-rw-r--r--src/daemon/purple.c254
-rw-r--r--src/daemon/purple_private.h56
-rw-r--r--src/daemon/settings.c168
-rw-r--r--src/daemon/settings_private.h37
7 files changed, 543 insertions, 16 deletions
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 302a9ce..94e59fd 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -21,9 +21,17 @@
#include <unistd.h>
#include <time.h>
+#include <stdlib.h>
#include <stdio.h>
#include "settings.h"
#include "net.h"
+#include "purple.h"
+
+static struct _state {
+ int nd;
+} _progstate;
+
+void cleanup(void);
/* the logic is a placeholder right now */
int main(int argc, char **argv)
@@ -33,8 +41,12 @@ int main(int argc, char **argv)
printf("Usurpation daemon version %s starting\n", version);
+ atexit(cleanup);
settings_init();
- net_init(setting_port()); /* TODO: get port from settings. */
+ _progstate.nd = net_init(setting_port());
+ 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
@@ -48,4 +60,13 @@ int main(int argc, char **argv)
/* noop */
}
+
+ return 0;
+}
+
+void cleanup(void)
+{
+ purple_close();
+ net_close(_progstate.nd);
+ settings_cleanup();
}
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/net.c b/src/daemon/net.c
index 3b4f022..9180344 100644
--- a/src/daemon/net.c
+++ b/src/daemon/net.c
@@ -94,9 +94,9 @@ int net_init(const unsigned short int port)
state[i].lastreply.tv_sec -= 5;
state[i].nd = i + 1;
state[i].port = port;
- state[i].data = malloc(dgsize);
- memset(state[i].data, 0, dgsize);
- state[i].bufsize = dgsize;
+ state[i].data = malloc(MTU);
+ memset(state[i].data, 0, MTU);
+ state[i].bufsize = MTU;
state[i].status = NONEWDATA;
pthread_mutex_init(&state[i].datamutex, NULL);
@@ -136,16 +136,25 @@ int net_close(int nd)
return ret;
}
-int net_getlastdata(int nd, char * const data)
+int net_getlastdata(int nd, char ** const data)
{
int ret;
if (nd > count || nd < 1 || state[--nd].available) {
ret = ERROR;
} else if (!(ret = state[nd].status)) {
- memcpy(data, state[nd].data, dgsize);
+ if (!data) {
+ ret = ERROR;
+ goto out;
+ } else if (!(*data)) {
+ *data = malloc(MTU);
+ }
+
+ memset(*data, 0, MTU);
+ memcpy(*data, state[nd].data, MTU);
}
+out:
return ret;
}
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);
+ }
+}
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 bac1c3b..5314fef 100644
--- a/src/daemon/settings.c
+++ b/src/daemon/settings.c
@@ -19,6 +19,16 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#define _GNU_SOURCE
+
+#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"
@@ -30,7 +40,16 @@ void settings_init(void)
{
unset_flag(flag_daemonize);
settings.port = 6996;
- settings.verbosity = USURP_VERBOSITY;
+ settings.verboselevel = USURP_VERBOSITY;
+ settings.progname = strdup(program_invocation_short_name);
+ settings.im_user = strdup("user");
+ settings.im_password = strdup("password");
+ settings.im_proto = strdup("prpl-irc");
+#define macro2str(a) _macro2str(a)
+#define _macro2str(a) #a
+ setting_readconf("/" macro2str(SYSCONFDIR) "/usurpation.conf");
+#undef macro2str
+#undef _macro2str
}
int setting_detach(void)
@@ -40,7 +59,7 @@ int setting_detach(void)
int setting_verbose(void)
{
- return settings.verbosity;
+ return settings.verboselevel;
}
unsigned short int setting_port(void)
@@ -48,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;
@@ -59,12 +98,131 @@ 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;
}
+
+/* TODO: semi-stub! */
+static int setting_readconf(const char * const path)
+{
+ struct stat filestat;
+ char *buf;
+ char *line;
+ char *entry;
+ char *value;
+ char *line_state;
+ char *token_state;
+ int fd;
+
+
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ if (USURP_VERBOSITY >= 2) {
+ perror("error while opening the configuration file");
+ return 1;
+ }
+ }
+
+ fstat(fd, &filestat);
+ buf = malloc(filestat.st_size+1);
+ buf[filestat.st_size] = 0;
+ if (read(fd, buf, filestat.st_size) != filestat.st_size) {
+ if (USURP_VERBOSITY >= 2) {
+ perror("partial read of configuration file detected");
+ free(buf);
+ return 1;
+ }
+ }
+
+ line = strtok_r(buf, "\n", &line_state);
+
+ while (line) {
+ if (line[0] != '#') {
+ entry = strtok_r(line, "=", &token_state);
+ value = strtok_r(NULL, "=", &token_state);
+ setting_handle_config_entry(entry, value);
+ }
+
+ line = strtok_r(NULL, "\n", &line_state);
+ }
+
+ free(buf);
+
+ return 0;
+}
+
+static int setting_handle_config_entry(const char * const entry, const char * const value)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(ent_table)/sizeof(*ent_table); ++i) {
+ if (!(strcmp(entry, ent_table[i].name))) {
+ ent_table[i].set(value);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void set_daemonize(const void * const arg)
+{
+ const char * const a = arg;
+
+ atoi(a) ? set_flag(flag_daemonize) : unset_flag(flag_daemonize);
+}
+
+static void set_port(const void * const arg)
+{
+ const char * const a = arg;
+
+ settings.port = strtoul(a, NULL, 0);
+}
+
+static void set_verbosity(const void * const arg)
+{
+ const char * const a = arg;
+
+ settings.verboselevel = strtol(a, NULL, 0);
+}
+
+static void set_progname(const void * const arg)
+{
+ const char * const a = arg;
+ free(settings.progname);
+ settings.progname = strdup(a);
+}
+
+static void set_im_user(const void * const arg)
+{
+ const char * const a = arg;
+ free(settings.im_user);
+ settings.im_user = strdup(a);
+}
+
+static void set_im_password(const void * const arg)
+{
+ const char * const a = arg;
+ free(settings.im_password);
+ settings.im_password = strdup(a);
+}
+
+static void set_im_proto(const void * const arg)
+{
+ const char * const a = arg;
+ free(settings.im_proto);
+ settings.im_proto = strdup(a);
+}
+
+void settings_cleanup(void)
+{
+ free(settings.progname);
+ free(settings.im_user);
+ free(settings.im_password);
+ free(settings.im_proto);
+}
diff --git a/src/daemon/settings_private.h b/src/daemon/settings_private.h
index 7839cb9..69ed25a 100644
--- a/src/daemon/settings_private.h
+++ b/src/daemon/settings_private.h
@@ -23,7 +23,7 @@
#define USURPATION_SETTINGS_PRIVATE_H
#ifndef USURP_VERBOSITY
- #define USURP_VERBOSITY 1
+ #define USURP_VERBOSITY ERR
#endif
static const unsigned int flag_daemonize = 1 << 0; /* 1st bit */
@@ -31,11 +31,38 @@ static const unsigned int flag_daemonize = 1 << 0; /* 1st bit */
static struct settings {
unsigned int flags;
unsigned short int port;
- int verbosity;
+ enum verbosity verboselevel;
+ char *progname;
+ char *im_user;
+ char *im_password;
+ char *im_proto;
} settings;
-int test_flag(unsigned int flag);
-void set_flag(unsigned int flag);
-void unset_flag(unsigned int flag);
+struct entry_description {
+ const char * const name;
+ void (*set)(const void * const);
+};
+
+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);
+
+static void set_daemonize(const void * const arg);
+static void set_port(const void * const arg);
+static void set_verbosity(const void * const arg);
+static void set_progname(const void * const arg);
+static void set_im_user(const void * const arg);
+static void set_im_password(const void * const arg);
+static void set_im_proto(const void * const arg);
+
+struct entry_description ent_table[] = {{ "daemonize", set_daemonize },
+ { "port", set_port },
+ { "verbosity", set_verbosity },
+ { "progname", set_progname },
+ { "user", set_im_user},
+ { "password", set_im_password },
+ { "proto", set_im_proto }};
#endif /* USURPATION_SETTINGS_PRIVATE_H */