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);
  }
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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
  );
}
Exemplo n.º 6
0
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;
  }
}
Exemplo n.º 7
0
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;
}
Exemplo n.º 10
0
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);
  }
}
Exemplo n.º 14
0
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;
}