/* * 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_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); } }