diff options
Diffstat (limited to 'src/nautilus-dropbox-hooks.c')
-rw-r--r-- | src/nautilus-dropbox-hooks.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/src/nautilus-dropbox-hooks.c b/src/nautilus-dropbox-hooks.c new file mode 100644 index 0000000..f37dbbb --- /dev/null +++ b/src/nautilus-dropbox-hooks.c @@ -0,0 +1,337 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * nautilus-dropbox-hooks.c + * Implements connection handling and C interface for the Dropbox hook socket. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nautilus-dropbox 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include <string.h> + +#include <glib.h> + +#include "g-util.h" +#include "async-io-coroutine.h" +#include "dropbox-client-util.h" +#include "nautilus-dropbox-hooks.h" + +typedef struct { + DropboxUpdateHook hook; + gpointer ud; +} HookData; + +static gboolean +try_to_connect(NautilusDropboxHookserv *hookserv); + +static gboolean +handle_hook_server_input(GIOChannel *chan, + GIOCondition cond, + NautilusDropboxHookserv *hookserv) { + /*debug_enter(); */ + + /* we have some sweet macros defined that allow us to write this + async event handler like a microthread yeahh, watch out for context */ + CRBEGIN(hookserv->hhsi.line); + while (1) { + hookserv->hhsi.command_args = + g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + hookserv->hhsi.numargs = 0; + + /* read the command name */ + { + gchar *line; + CRREADLINE(hookserv->hhsi.line, chan, line); + hookserv->hhsi.command_name = dropbox_client_util_desanitize(line); + g_free(line); + } + + /*debug("got a hook name: %s", hookserv->hhsi.command_name); */ + + /* now read each arg line (until a certain limit) until we receive "done" */ + while (1) { + gchar *line; + + /* if too many arguments, this connection seems malicious */ + if (hookserv->hhsi.numargs >= 20) { + CRHALT; + } + + CRREADLINE(hookserv->hhsi.line, chan, line); + + if (strcmp("done", line) == 0) { + g_free(line); + break; + } + else { + gboolean parse_result; + + parse_result = + dropbox_client_util_command_parse_arg(line, + hookserv->hhsi.command_args); + g_free(line); + + if (FALSE == parse_result) { + debug("bad parse"); + CRHALT; + } + } + + hookserv->hhsi.numargs += 1; + } + + { + HookData *hd; + hd = (HookData *) + g_hash_table_lookup(hookserv->dispatch_table, + hookserv->hhsi.command_name); + if (hd != NULL) { + (hd->hook)(hookserv->hhsi.command_args, hd->ud); + } + } + + g_free(hookserv->hhsi.command_name); + g_hash_table_unref(hookserv->hhsi.command_args); + hookserv->hhsi.command_name = NULL; + hookserv->hhsi.command_args = NULL; + } + CREND; +} + +static void +watch_killer(NautilusDropboxHookserv *hookserv) { + debug("hook client disconnected"); + + hookserv->connected = FALSE; + + g_hook_list_invoke(&(hookserv->ondisconnect_hooklist), FALSE); + + /* we basically just have to free the memory allocated in the + handle_hook_server_init ctx */ + if (hookserv->hhsi.command_name != NULL) { + g_free(hookserv->hhsi.command_name); + hookserv->hhsi.command_name = NULL; + } + + if (hookserv->hhsi.command_args != NULL) { + g_hash_table_unref(hookserv->hhsi.command_args); + hookserv->hhsi.command_args = NULL; + } + + g_io_channel_unref(hookserv->chan); + hookserv->chan = NULL; + hookserv->event_source = 0; + hookserv->socket = 0; + + /* lol we also have to start a new connection */ + try_to_connect(hookserv); +} + +static gboolean +try_to_connect(NautilusDropboxHookserv *hookserv) { + /* create socket */ + hookserv->socket = socket(PF_UNIX, SOCK_STREAM, 0); + + /* set native non-blocking, for connect timeout */ + { + unsigned int flags; + + if ((flags = fcntl(hookserv->socket, F_GETFL, 0)) < 0) { + goto FAIL_CLEANUP; + } + + if (fcntl(hookserv->socket, F_SETFL, flags | O_NONBLOCK) < 0) { + goto FAIL_CLEANUP; + } + } + + /* connect to server, might fail of course */ + { + struct sockaddr_un addr; + socklen_t addr_len; + + /* intialize address structure */ + addr.sun_family = AF_UNIX; + g_snprintf(addr.sun_path, + sizeof(addr.sun_path), + "%s/.dropbox/iface_socket", + g_get_home_dir()); + addr_len = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path); + + /* if there was an error we have to try again later */ + if (connect(hookserv->socket, (struct sockaddr *) &addr, addr_len) < 0) { + if (errno == EINPROGRESS) { + fd_set writers; + struct timeval tv = {1, 0}; + FD_ZERO(&writers); + FD_SET(hookserv->socket, &writers); + + /* if nothing was ready after 3 seconds, fail out homie */ + if (select(hookserv->socket+1, NULL, &writers, NULL, &tv) == 0) { + goto FAIL_CLEANUP; + } + + if (connect(hookserv->socket, (struct sockaddr *) &addr, addr_len) < 0) { + debug("couldn't connect to hook server after 1 second"); + goto FAIL_CLEANUP; + } + } + else { + goto FAIL_CLEANUP; + } + } + } + + /* lol sometimes i write funny codez */ + if (FALSE) { + FAIL_CLEANUP: + close(hookserv->socket); + g_timeout_add_seconds(1, (GSourceFunc) try_to_connect, hookserv); + return FALSE; + } + + /* great we connected!, let's create the channel and wait on it */ + hookserv->chan = g_io_channel_unix_new(hookserv->socket); + g_io_channel_set_line_term(hookserv->chan, "\n", -1); + g_io_channel_set_close_on_unref(hookserv->chan, TRUE); + + /*debug("create channel"); */ + + /* Set non-blocking ;) (again just in case) */ + { + GIOFlags flags; + GIOStatus iostat; + + flags = g_io_channel_get_flags(hookserv->chan); + iostat = g_io_channel_set_flags(hookserv->chan, flags | G_IO_FLAG_NONBLOCK, + NULL); + if (iostat == G_IO_STATUS_ERROR) { + g_io_channel_unref(hookserv->chan); + g_timeout_add_seconds(1, (GSourceFunc) try_to_connect, hookserv); + return FALSE; + } + } + + /*debug("set non blocking"); */ + + /* this is fun, async io watcher */ + hookserv->hhsi.line = 0; + hookserv->hhsi.command_args = NULL; + hookserv->hhsi.command_name = NULL; + hookserv->event_source = + g_io_add_watch_full(hookserv->chan, G_PRIORITY_DEFAULT, + G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) handle_hook_server_input, hookserv, + (GDestroyNotify) watch_killer); + + debug("hook client connected"); + hookserv->connected = TRUE; + g_hook_list_invoke(&(hookserv->onconnect_hooklist), FALSE); + + /*debug("added watch");*/ + return FALSE; +} + +/* should only be called in glib main loop */ +/* returns a gboolean because it is a GSourceFunc */ +gboolean nautilus_dropbox_hooks_force_reconnect(NautilusDropboxHookserv *hookserv) { + debug_enter(); + + if (hookserv->connected == FALSE) { + return FALSE; + } + + debug("forcing hook to reconnect"); + + g_assert(hookserv->event_source >= 0); + + if (hookserv->event_source > 0) { + g_source_remove(hookserv->event_source); + } + else if (hookserv->event_source == 0) { + debug("event source was zero!!!!!"); + } + + return FALSE; +} + +gboolean +nautilus_dropbox_hooks_is_connected(NautilusDropboxHookserv *hookserv) { + return hookserv->connected; +} + +void +nautilus_dropbox_hooks_setup(NautilusDropboxHookserv *hookserv) { + hookserv->dispatch_table = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + g_free, g_free); + hookserv->connected = FALSE; + + g_hook_list_init(&(hookserv->ondisconnect_hooklist), sizeof(GHook)); + g_hook_list_init(&(hookserv->onconnect_hooklist), sizeof(GHook)); +} + +void +nautilus_dropbox_hooks_add_on_disconnect_hook(NautilusDropboxHookserv *hookserv, + DropboxHookClientConnectHook dhcch, + gpointer ud) { + GHook *newhook; + + newhook = g_hook_alloc(&(hookserv->ondisconnect_hooklist)); + newhook->func = dhcch; + newhook->data = ud; + + g_hook_append(&(hookserv->ondisconnect_hooklist), newhook); +} + +void +nautilus_dropbox_hooks_add_on_connect_hook(NautilusDropboxHookserv *hookserv, + DropboxHookClientConnectHook dhcch, + gpointer ud) { + GHook *newhook; + + newhook = g_hook_alloc(&(hookserv->onconnect_hooklist)); + newhook->func = dhcch; + newhook->data = ud; + + g_hook_append(&(hookserv->onconnect_hooklist), newhook); +} + +void nautilus_dropbox_hooks_add(NautilusDropboxHookserv *ndhs, + const gchar *hook_name, + DropboxUpdateHook hook, gpointer ud) { + HookData *hd; + hd = g_new(HookData, 1); + hd->hook = hook; + hd->ud = ud; + g_hash_table_insert(ndhs->dispatch_table, g_strdup(hook_name), hd); +} + +void +nautilus_dropbox_hooks_start(NautilusDropboxHookserv *hookserv) { + try_to_connect(hookserv); +} |