static void infinoted_plugin_document_stream_deinitialize(gpointer plugin_info) { InfinotedPluginDocumentStream* plugin; plugin = (InfinotedPluginDocumentStream*)plugin_info; while(plugin->streams != NULL) { infinoted_plugin_document_stream_close_stream( (InfinotedPluginDocumentStreamStream*)plugin->streams->data ); } inf_signal_handlers_disconnect_by_func( G_OBJECT(infinoted_plugin_manager_get_directory(plugin->manager)), G_CALLBACK(infinoted_plugin_document_stream_node_removed_cb), plugin ); if(plugin->watch != NULL) { inf_io_remove_watch( infinoted_plugin_manager_get_io(plugin->manager), plugin->watch ); } if(plugin->socket != -1) { close(plugin->socket); } }
static gboolean infinoted_plugin_note_text_initialize(InfinotedPluginManager* manager, gpointer plugin_info, GError** error) { InfinotedPluginNoteText* plugin; gboolean result; plugin = (InfinotedPluginNoteText*)plugin_info; plugin->manager = manager; result = infd_directory_add_plugin( infinoted_plugin_manager_get_directory(manager), &INFINOTED_PLUGIN_NOTE_TEXT_PLUGIN ); if(result != TRUE) { g_set_error( error, g_quark_from_static_string("INFINOTED_PLUGIN_NOTE_TEXT_ERROR"), 0, _("There is a already a plugin which handles sessions of type \"%s\""), INFINOTED_PLUGIN_NOTE_TEXT_PLUGIN.note_type ); return FALSE; } plugin->plugin = &INFINOTED_PLUGIN_NOTE_TEXT_PLUGIN; return TRUE; }
static gboolean infinoted_plugin_document_stream_process_get_document( InfinotedPluginDocumentStreamStream* stream, const gchar** data, gsize* len) { guint16 user_len; const gchar* user_name; guint16 doc_len; const gchar* doc_name; /* get size of user name string */ if(*len < 2) return FALSE; user_len = *(guint16*)(*data); *data += 2; *len -= 2; /* get user name string */ if(*len < user_len) return FALSE; user_name = *data; *data += user_len; *len -= user_len; /* get size of document string */ if(*len < 2) return FALSE; doc_len = *(guint16*)(*data); *data += 2; *len -= 2; /* get document string */ if(*len < doc_len) return FALSE; doc_name = *data; *data += doc_len; *len -= doc_len; /* quit connection if we already have a buffer */ if(stream->buffer != NULL) { infinoted_plugin_document_stream_send_error( stream, "Stream is already open" ); } else { stream->username = g_strndup(user_name, user_len); stream->navigate_handle = infinoted_plugin_util_navigate_to( INF_BROWSER( infinoted_plugin_manager_get_directory(stream->plugin->manager) ), doc_name, doc_len, FALSE, infinoted_plugin_document_stream_navigate_func, stream ); } return TRUE; }
static void infinoted_plugin_autosave_stop(InfinotedPluginAutosaveSessionInfo* info) { InfIo* io; io = infd_directory_get_io( infinoted_plugin_manager_get_directory(info->plugin->manager) ); g_assert(info->timeout != NULL); inf_io_remove_timeout(io, info->timeout); info->timeout = NULL; }
static void infinoted_plugin_autosave_start(InfinotedPluginAutosaveSessionInfo* info) { InfIo* io; io = infd_directory_get_io( infinoted_plugin_manager_get_directory(info->plugin->manager) ); g_assert(info->timeout == NULL); info->timeout = inf_io_add_timeout( io, info->plugin->interval * 1000, infinoted_plugin_autosave_timeout_cb, info, NULL ); }
static void infinoted_plugin_note_text_deinitialize(gpointer plugin_info) { InfinotedPluginNoteText* plugin; plugin = (InfinotedPluginNoteText*)plugin_info; /* Note that this kills all sessions with that particular type. This is * typically not wanted when reloading a plugin in which case a plugin is * deinitialized and then re-initialized. */ /* TODO: To fix this, we should add a plugin API to reload its parameters * without unloading and reloading the whole plugin. */ if(plugin->plugin != NULL) { infd_directory_remove_plugin( infinoted_plugin_manager_get_directory(plugin->manager), plugin->plugin ); plugin->plugin = NULL; } }
static void infinoted_plugin_record_session_added(const InfBrowserIter* iter, InfSessionProxy* proxy, gpointer plugin_info, gpointer session_info) { InfinotedPluginRecord* plugin; InfinotedPluginRecordSessionInfo* info; InfSession* session; gchar* title; gchar* pos; InfAdoptedSessionRecord* record; plugin = (InfinotedPluginRecord*)plugin_info; info = (InfinotedPluginRecordSessionInfo*)session_info; g_object_get(G_OBJECT(proxy), "session", &session, NULL); g_assert(INF_ADOPTED_IS_SESSION(session)); title = inf_browser_get_path( INF_BROWSER(infinoted_plugin_manager_get_directory(plugin->manager)), iter ); for(pos = title + 1; *pos != '\0'; ++pos) if(*pos == '/') *pos = '_'; info->plugin = plugin; info->record = infinoted_plugin_record_start( plugin, INF_ADOPTED_SESSION(session), title + 1 ); g_object_set_data(G_OBJECT(session), "infinoted-record", info->record); g_object_unref(session); g_free(title); }
static void infinoted_plugin_linekeeper_text_erased_cb(InfTextBuffer* buffer, guint pos, InfTextChunk* chunk, InfUser* user, gpointer user_data) { InfinotedPluginLinekeeperSessionInfo* info; InfdDirectory* directory; info = (InfinotedPluginLinekeeperSessionInfo*)user_data; if(info->dispatch == NULL) { directory = infinoted_plugin_manager_get_directory(info->plugin->manager); info->dispatch = inf_io_add_dispatch( infd_directory_get_io(directory), infinoted_plugin_linekeeper_run_dispatch_func, info, NULL ); } }
static gboolean infinoted_plugin_document_stream_initialize(InfinotedPluginManager* manager, gpointer plugin_info, GError** error) { static const char ADDRESS_NAME[] = "org.infinote.infinoted"; struct sockaddr_un addr; InfinotedPluginDocumentStream* plugin; plugin = (InfinotedPluginDocumentStream*)plugin_info; plugin->manager = manager; plugin->socket = socket(AF_UNIX, SOCK_STREAM, 0); if(plugin->socket == -1) { infinoted_plugin_document_stream_make_system_error(errno, error); return FALSE; } /* TODO: Make the address configurable -- note that abstract paths * are a Linux extension. */ addr.sun_family = AF_UNIX; addr.sun_path[0] = '\0'; memcpy(&addr.sun_path[1], ADDRESS_NAME, sizeof(ADDRESS_NAME) - 1); memset( &addr.sun_path[1] + sizeof(ADDRESS_NAME) - 1, '\0', sizeof(addr.sun_path) - 1 - (sizeof(ADDRESS_NAME) - 1) ); if(!infinoted_plugin_document_stream_set_nonblock(plugin->socket, error)) return FALSE; if(bind(plugin->socket, (struct sockaddr*)&addr, sizeof(addr)) == -1) { infinoted_plugin_document_stream_make_system_error(errno, error); return FALSE; } if(listen(plugin->socket, 5) == -1) { infinoted_plugin_document_stream_make_system_error(errno, error); return FALSE; } plugin->watch = inf_io_add_watch( infinoted_plugin_manager_get_io(plugin->manager), &plugin->socket, INF_IO_INCOMING, infinoted_plugin_manager_socket_accept_func, plugin, NULL ); g_signal_connect( G_OBJECT(infinoted_plugin_manager_get_directory(plugin->manager)), "node-removed", G_CALLBACK(infinoted_plugin_document_stream_node_removed_cb), plugin ); return TRUE; }
static void infinoted_plugin_autosave_save(InfinotedPluginAutosaveSessionInfo* info) { InfdDirectory* directory; InfBrowserIter* iter; GError* error; gchar* path; InfSession* session; InfBuffer* buffer; gchar* root_directory; gchar* argv[4]; directory = infinoted_plugin_manager_get_directory(info->plugin->manager); iter = &info->iter; error = NULL; if(info->timeout != NULL) { inf_io_remove_timeout(infd_directory_get_io(directory), info->timeout); info->timeout = NULL; } g_object_get(G_OBJECT(info->proxy), "session", &session, NULL); buffer = inf_session_get_buffer(session); inf_signal_handlers_block_by_func( G_OBJECT(buffer), G_CALLBACK(infinoted_plugin_autosave_buffer_notify_modified_cb), info ); if(infd_directory_iter_save_session(directory, iter, &error) == FALSE) { path = inf_browser_get_path(INF_BROWSER(directory), iter); infinoted_log_warning( infinoted_plugin_manager_get_log(info->plugin->manager), _("Failed to auto-save session \"%s\": %s\n\n" "Will retry in %u seconds."), path, error->message, info->plugin->interval ); g_free(path); g_error_free(error); error = NULL; infinoted_plugin_autosave_start(info); } else { /* TODO: Remove this as soon as directory itself unsets modified flag * on session_write */ inf_buffer_set_modified(INF_BUFFER(buffer), FALSE); if(info->plugin->hook != NULL) { path = inf_browser_get_path(INF_BROWSER(directory), iter); g_object_get( G_OBJECT(infd_directory_get_storage(directory)), "root-directory", &root_directory, NULL ); argv[0] = info->plugin->hook; argv[1] = root_directory; argv[2] = path; argv[3] = NULL; if(!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error)) { infinoted_log_warning( infinoted_plugin_manager_get_log(info->plugin->manager), _("Could not execute autosave hook: \"%s\""), error->message ); g_error_free(error); error = NULL; } g_free(path); g_free(root_directory); } } inf_signal_handlers_unblock_by_func( G_OBJECT(buffer), G_CALLBACK(infinoted_plugin_autosave_buffer_notify_modified_cb), info ); g_object_unref(session); }
static void infinoted_plugin_certificate_auth_deinitialize(gpointer plugin_info) { InfinotedPluginCertificateAuth* plugin; InfRequest* remove_acl_account_request; InfCertificateCredentials* creds; guint i; plugin = (InfinotedPluginCertificateAuth*)plugin_info; /* Remove super user account. Note that this is not strictly necessary, * since the acocunt is transient and therefore is not written to disk, * so will not be re-created at the next server start. However, to be sure, * we explicitly remove the account at this point. */ if(plugin->super_id != 0) { remove_acl_account_request = inf_browser_remove_acl_account( INF_BROWSER(infinoted_plugin_manager_get_directory(plugin->manager)), plugin->super_id, infinoted_plugin_certificate_auth_remove_acl_account_cb, plugin ); /* This should be instantaneous: if we are not called back within the call * to inf_browser_remove_acl_account(), then we don't care about the * result, since we are being deinitialized. */ if(remove_acl_account_request != NULL) { inf_signal_handlers_disconnect_by_func( plugin->set_acl_request, G_CALLBACK(infinoted_plugin_certificate_auth_remove_acl_account_cb), plugin ); } } if(plugin->set_acl_request != NULL) { inf_signal_handlers_disconnect_by_func( plugin->set_acl_request, G_CALLBACK(infinoted_plugin_certificate_auth_set_acl_cb), plugin ); g_object_unref(plugin->set_acl_request); } creds = infinoted_plugin_manager_get_credentials(plugin->manager); if(creds != NULL) gnutls_certificate_free_cas(inf_certificate_credentials_get(creds)); infd_directory_set_certificate( infinoted_plugin_manager_get_directory(plugin->manager), NULL, NULL ); /* If we have a ca_key set, the certificate that belongs to the key had * its ownership transferred to the directory, so make sure not to free * it twice here. */ for(i = 0; i < plugin->n_cas; ++i) if(plugin->ca_key == NULL || i != plugin->ca_key_index) gnutls_x509_crt_deinit(plugin->cas[i]); g_free(plugin->cas); if(plugin->ca_key != NULL) gnutls_x509_privkey_deinit(plugin->ca_key); g_free(plugin->ca_list_file); g_free(plugin->ca_key_file); g_free(plugin->super_user); }
static gboolean infinoted_plugin_certificate_auth_initialize(InfinotedPluginManager* manager, gpointer plugin_info, GError** error) { InfinotedPluginCertificateAuth* plugin; InfCertificateCredentials* creds; GPtrArray* read_certs; int res; guint i; gnutls_x509_crt_t* sign_certs; InfCertificateChain* sign_chain; gnutls_x509_privkey_t super_key; InfCertUtilDescription desc; gnutls_x509_crt_t super_cert; InfAclAccountId super_id; gnutls_x509_crt_t chain[2]; gboolean written; InfdDirectory* directory; InfBrowserIter iter; InfAclSheetSet sheet_set; InfAclSheet sheet; InfRequest* request; plugin = (InfinotedPluginCertificateAuth*)plugin_info; plugin->manager = manager; creds = infinoted_plugin_manager_get_credentials(manager); if(creds == NULL) { g_set_error( error, infinoted_plugin_certificate_auth_error_quark(), INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CREDENTIALS, "%s", _("The certificate-auth plugin can only be used when TLS is enabled " "and a server certificate has been set.") ); return FALSE; } read_certs = inf_cert_util_read_certificate(plugin->ca_list_file, NULL, error); if(read_certs == NULL) return FALSE; if(read_certs->len == 0) { g_set_error( error, infinoted_plugin_certificate_auth_error_quark(), INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CAS, _("File \"%s\" does not contain any CA certificates"), plugin->ca_list_file ); g_ptr_array_free(read_certs, TRUE); return FALSE; } plugin->n_cas = read_certs->len; plugin->cas = (gnutls_x509_crt_t*)g_ptr_array_free(read_certs, FALSE); res = gnutls_certificate_set_x509_trust( inf_certificate_credentials_get(creds), plugin->cas, plugin->n_cas ); if(res < 0) { inf_gnutls_set_error(error, res); return FALSE; } if(plugin->ca_key_file != NULL) { plugin->ca_key = inf_cert_util_read_private_key(plugin->ca_key_file, error); if(plugin->ca_key == NULL) return FALSE; /* Walk through certificates and find the certificate that the key * belongs to. */ for(i = 0; i < plugin->n_cas; ++i) if(inf_cert_util_check_certificate_key(plugin->cas[i], plugin->ca_key)) break; if(i == plugin->n_cas) { gnutls_x509_privkey_deinit(plugin->ca_key); plugin->ca_key = NULL; g_set_error( error, infinoted_plugin_certificate_auth_error_quark(), INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CA_FOR_KEY, "%s", _("The given CA key does not match with any of the CA certificates") ); return FALSE; } plugin->ca_key_index = i; /* Set the signing certificate of the directory, so that it can handle * account creation requests. Note that this takes ownership of the * certificate, so we take special care in the cleanup code in * infinoted_plugin_certificate_auth_deinitialize(). */ sign_certs = g_malloc(sizeof(gnutls_x509_crt_t)); sign_certs[0] = plugin->cas[plugin->ca_key_index]; sign_chain = inf_certificate_chain_new(sign_certs, 1); infd_directory_set_certificate( infinoted_plugin_manager_get_directory(plugin->manager), plugin->ca_key, sign_chain ); inf_certificate_chain_unref(sign_chain); } if(plugin->super_user != NULL) { if(plugin->ca_key == NULL) { g_set_error( error, infinoted_plugin_certificate_auth_error_quark(), INFINOTED_PLUGIN_CERTIFICATE_AUTH_ERROR_NO_CA_KEY, "%s", _("Cannot generate a superuser certificate without CA key") ); return FALSE; } /* Create a private key and certificate for the super user. */ infinoted_log_info( infinoted_plugin_manager_get_log(plugin->manager), _("Creating 4096-bit RSA private key for the super user account...") ); super_key = inf_cert_util_create_private_key(GNUTLS_PK_RSA, 4096, error); if(super_key == NULL) return FALSE; desc.validity = 12 * 3600; /* 12 hours */ desc.dn_common_name = "Super User"; desc.san_dnsname = NULL; super_cert = inf_cert_util_create_signed_certificate( super_key, &desc, plugin->cas[plugin->ca_key_index], plugin->ca_key, error ); if(super_cert == NULL) { gnutls_x509_privkey_deinit(super_key); return FALSE; } super_id = infd_directory_create_acl_account( infinoted_plugin_manager_get_directory(plugin->manager), _("Super User"), TRUE, /* transient */ &super_cert, 1, error ); if(super_id == 0) { gnutls_x509_crt_deinit(super_cert); gnutls_x509_privkey_deinit(super_key); return FALSE; } plugin->super_id = super_id; chain[0] = super_cert; chain[1] = plugin->cas[plugin->ca_key_index]; written = inf_cert_util_write_certificate_with_key( super_key, chain, 2, plugin->super_user, error ); gnutls_x509_crt_deinit(super_cert); gnutls_x509_privkey_deinit(super_key); if(written == FALSE) return FALSE; inf_browser_get_root( INF_BROWSER(infinoted_plugin_manager_get_directory(plugin->manager)), &iter ); directory = infinoted_plugin_manager_get_directory(plugin->manager); sheet.account = super_id; sheet.mask = INF_ACL_MASK_ALL; infd_directory_get_support_mask(directory, &sheet.perms); sheet_set.n_sheets = 1; sheet_set.own_sheets = NULL; sheet_set.sheets = &sheet; request = inf_browser_set_acl( INF_BROWSER(directory), &iter, &sheet_set, infinoted_plugin_certificate_auth_set_acl_cb, plugin ); if(request != NULL) { plugin->set_acl_request = request; g_object_ref(plugin->set_acl_request); } } return TRUE; }
static void infinoted_plugin_dbus_main_invocation(gpointer user_data) { /* Main thread invocation handler */ InfinotedPluginDbusInvocation* invocation; const gchar* path; gsize len; InfinotedPluginUtilNavigateData* navigate; invocation = (InfinotedPluginDbusInvocation*)user_data; invocation->plugin->invocations = g_slist_prepend(invocation->plugin->invocations, invocation); g_atomic_int_inc(&invocation->ref_count); /* These commands take a path as the first parameter and do not * require that path to be explored. */ if(strcmp(invocation->method_name, "remove_node") == 0 || strcmp(invocation->method_name, "query_acl") == 0 || strcmp(invocation->method_name, "set_acl") == 0 || strcmp(invocation->method_name, "check_acl") == 0) { path = g_variant_get_string( g_variant_get_child_value(invocation->parameters, 0), &len ); navigate = infinoted_plugin_util_navigate_to( INF_BROWSER(infinoted_plugin_manager_get_directory(invocation->plugin->manager)), path, len, FALSE, infinoted_plugin_dbus_navigate_done, invocation ); if(navigate != NULL) invocation->navigate = navigate; } /* These commands take a path as the first parameter and DO require that * path to be explored. */ else if(strcmp(invocation->method_name, "explore_node") == 0 || strcmp(invocation->method_name, "add_node") == 0) { path = g_variant_get_string( g_variant_get_child_value(invocation->parameters, 0), &len ); navigate = infinoted_plugin_util_navigate_to( INF_BROWSER(infinoted_plugin_manager_get_directory(invocation->plugin->manager)), path, len, TRUE, infinoted_plugin_dbus_navigate_done, invocation ); if(navigate != NULL) invocation->navigate = navigate; } else { g_dbus_method_invocation_return_error_literal( invocation->invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Not implemented" ); infinoted_plugin_dbus_invocation_free(invocation->plugin, invocation); } }
static void infinoted_plugin_linekeeper_session_removed(const InfBrowserIter* iter, InfSessionProxy* proxy, gpointer plugin_info, gpointer session_info) { InfinotedPluginLinekeeperSessionInfo* info; InfdDirectory* directory; InfSession* session; InfUserTable* user_table; info = (InfinotedPluginLinekeeperSessionInfo*)session_info; g_object_get(G_OBJECT(info->proxy), "session", &session, NULL); user_table = inf_session_get_user_table(session); g_signal_handlers_disconnect_by_func( G_OBJECT(user_table), G_CALLBACK(infinoted_plugin_linekeeper_add_available_user_cb), info ); g_signal_handlers_disconnect_by_func( G_OBJECT(user_table), G_CALLBACK(infinoted_plugin_linekeeper_remove_available_user_cb), info ); if(info->dispatch != NULL) { directory = infinoted_plugin_manager_get_directory(info->plugin->manager); inf_io_remove_dispatch(infd_directory_get_io(directory), info->dispatch); info->dispatch = NULL; } if(info->user != NULL) { infinoted_plugin_linekeeper_remove_user(info); } if(info->buffer != NULL) { g_object_unref(info->buffer); info->buffer = NULL; } if(info->request != NULL) { inf_signal_handlers_disconnect_by_func( info->request, G_CALLBACK(infinoted_plugin_linekeeper_user_join_cb), info ); info->request = NULL; } g_assert(info->proxy != NULL); g_object_unref(info->proxy); g_object_unref(session); }
static gboolean infinoted_plugin_transformation_protection_check_request_cb(InfAdoptedSession* session, InfAdoptedRequest* request, InfAdoptedUser* user, gpointer user_data) { InfinotedPluginTransformationProtectionSessionInfo* info; guint vdiff; InfXmlConnection* connection; gchar* request_str; gchar* current_str; gchar* remote_id; gchar* path; info = (InfinotedPluginTransformationProtectionSessionInfo*)user_data; vdiff = inf_adopted_state_vector_vdiff( inf_adopted_request_get_vector(request), inf_adopted_algorithm_get_current( inf_adopted_session_get_algorithm(session) ) ); if(vdiff > info->plugin->max_vdiff) { connection = inf_user_get_connection(INF_USER(user)); /* Local requests do not need to be transformed, so always have a * zero vdiff. */ g_assert(connection != NULL); /* Kill the connection */ infd_session_proxy_unsubscribe( INFD_SESSION_PROXY(info->proxy), connection ); /* Write a log message */ path = inf_browser_get_path( INF_BROWSER( infinoted_plugin_manager_get_directory(info->plugin->manager) ), &info->iter ); request_str = inf_adopted_state_vector_to_string( inf_adopted_request_get_vector(request) ); current_str = inf_adopted_state_vector_to_string( inf_adopted_algorithm_get_current( inf_adopted_session_get_algorithm(session) ) ); g_object_get(G_OBJECT(connection), "remote-id", &remote_id, NULL); infinoted_log_warning( infinoted_plugin_manager_get_log(info->plugin->manager), _("In document \"%s\": Attempt to transform request \"%s\" to current state \"%s\" " "(vdiff=%u) by user \"%s\" (id=%u, conn=%s). Maximum allowed is %u; the " "connection has been unsubscribed."), path, request_str, current_str, vdiff, inf_user_get_name(INF_USER(user)), inf_user_get_id(INF_USER(user)), remote_id, info->plugin->max_vdiff ); g_free(path); g_free(request_str); g_free(current_str); g_free(remote_id); /* Prevent the request from being transformed */ return TRUE; } return FALSE; }