static void
inf_test_mass_join_explore_finished_cb(InfRequest* request,
                                       const InfRequestResult* result,
                                       const GError* error,
                                       gpointer user_data)
{
    InfTestMassJoiner* joiner;
    InfBrowser* browser;
    InfBrowserIter iter;
    const char* name;
    gboolean document_exists;

    joiner = (InfTestMassJoiner*)user_data;
    browser = INF_BROWSER(joiner->browser);
    inf_browser_get_root(browser, &iter);
    if(inf_browser_get_child(browser, &iter) == FALSE)
    {
        fprintf(
            stderr,
            "Joiner %s: Document %s does not exist\n",
            joiner->username,
            joiner->document
        );

        inf_xml_connection_close(infc_browser_get_connection(joiner->browser));
    }

    document_exists = FALSE;

    do
    {
        name = inf_browser_get_node_name(browser, &iter);
        if(strcmp(name, joiner->document) == 0)
        {
            document_exists = TRUE;

            inf_browser_subscribe(
                browser,
                &iter,
                inf_test_mass_join_subscribe_finished_cb,
                joiner
            );

            break;
        }
    } while(inf_browser_get_next(browser, &iter) == TRUE);

    if(!document_exists)
    {
        fprintf(
            stderr,
            "Joiner %s: Document %s does not exist\n",
            joiner->username,
            joiner->document
        );

        inf_xml_connection_close(infc_browser_get_connection(joiner->browser));
    }
}
static void
remove_chat (InfcBrowser                    *browser,
             GeditCollaborationWindowHelper *helper)
{
	GtkWidget *chat;
	GeditPanel *panel;
	InfXmlConnection *connection;
	InfcSessionProxy *proxy;

	proxy = infc_browser_get_chat_session (browser);

	if (proxy != NULL)
	{
		InfSession *session;

		session = infc_session_proxy_get_session (proxy);
		inf_session_close (session);
	}

	connection = infc_browser_get_connection (browser);

	chat = GTK_WIDGET (g_object_get_data (G_OBJECT (connection),
	                                      CHAT_DATA_KEY));

	if (chat != NULL)
	{
		panel = gedit_window_get_bottom_panel (helper->priv->window);
		gedit_panel_remove_item (panel, chat);

		g_object_set_data (G_OBJECT (connection), CHAT_DATA_KEY, NULL);
	}
}
static void
inf_test_mass_join_user_join_finished_cb(InfRequest* request,
        const InfRequestResult* result,
        const GError* error,
        gpointer user_data)
{
    InfTestMassJoiner* joiner;
    joiner = (InfTestMassJoiner*)user_data;

    if(error == NULL)
    {
        fprintf(stdout, "Joiner %s: User joined!\n", joiner->username);
    }
    else
    {
        fprintf(
            stderr,
            "Joiner %s: User join failed: %s\n",
            joiner->username,
            error->message
        );

        inf_xml_connection_close(infc_browser_get_connection(joiner->browser));
    }
}
static void
inf_test_mass_join_subscribe_finished_cb(InfRequest* request,
        const InfRequestResult* result,
        const GError* error,
        gpointer user_data)
{
    InfTestMassJoiner* joiner;
    const InfBrowserIter* iter;
    InfSession* session;

    joiner = (InfTestMassJoiner*)user_data;
    inf_request_result_get_subscribe_session(result, NULL, &iter, NULL);

    joiner->session = INFC_SESSION_PROXY(
                          inf_browser_get_session(
                              INF_BROWSER(joiner->browser),
                              iter
                          )
                      );

    g_assert(joiner->session != NULL);

    g_object_get(G_OBJECT(joiner->session), "session", &session, NULL);
    switch(inf_session_get_status(session))
    {
    case INF_SESSION_PRESYNC:
    case INF_SESSION_SYNCHRONIZING:
        g_signal_connect_after(
            G_OBJECT(session),
            "synchronization-failed",
            G_CALLBACK(inf_test_mass_join_session_synchronization_failed_cb),
            joiner
        );

        g_signal_connect_after(
            G_OBJECT(session),
            "synchronization-complete",
            G_CALLBACK(inf_test_mass_join_session_synchronization_complete_cb),
            joiner
        );

        break;
    case INF_SESSION_RUNNING:
        inf_test_mass_join_join_user(joiner);
        break;
    case INF_SESSION_CLOSED:
        fprintf(
            stderr,
            "Joiner %s: Session closed after subscription\n",
            joiner->username
        );

        inf_xml_connection_close(infc_browser_get_connection(joiner->browser));
        break;
    }

    g_object_unref(session);
}
void Gobby::BrowserContextCommands::on_disconnect(InfcBrowser* browser)
{
	InfXmlConnection* connection = infc_browser_get_connection(browser);
	InfXmlConnectionStatus status;
	g_object_get(G_OBJECT(connection), "status", &status, NULL);

	if(status != INF_XML_CONNECTION_CLOSED &&
	   status != INF_XML_CONNECTION_CLOSING)
	{
		inf_xml_connection_close(connection);
	}
}
void Gobby::AuthCommands::browser_error_callback(InfcBrowser* browser,
                                                 GError* error)
{
	// The Browser already displays errors inline, but we want
	// auth-related error messages to show up in the status bar.

	InfXmlConnection* connection = infc_browser_get_connection(browser);
	g_assert(INF_IS_XMPP_CONNECTION(connection));

	InfXmppConnection* xmpp = INF_XMPP_CONNECTION(connection);
	RetryMap::iterator iter = m_retries.find(xmpp);
	if(iter == m_retries.end())
		iter = insert_retry_info(xmpp);
	Glib::ustring& last_password(iter->second.last_password);
	Glib::ustring old_password;

	old_password.swap(last_password);

	if(error->domain ==
	     g_quark_from_static_string("INF_XMPP_CONNECTION_AUTH_ERROR"))
	{
		// Authentication failed for some reason, maybe because the
		// server aborted authentication. If we were querying a
		// password then close the dialog now.
		delete iter->second.password_dialog;
		iter->second.password_dialog = NULL;

		const GError* sasl_error =
			inf_xmpp_connection_get_sasl_error(xmpp);
		if(sasl_error != NULL &&
		   sasl_error->domain ==
		     inf_authentication_detail_error_quark())
		{
			handle_error_detail(xmpp, sasl_error,
			                    old_password,
			                    last_password);
		}
		else if(sasl_error != NULL)
		{
			show_error(sasl_error, m_statusbar, connection);
		}
		else
		{
			show_error(error, m_statusbar, connection);
		}
	}
	else if(error->domain == inf_gsasl_error_quark())
	{
		show_error(error, m_statusbar, connection);
	}
}
void
on_action_bookmark_edit (GtkAction                      *action,
                         GeditCollaborationWindowHelper *helper)
{
	GtkTreeIter iter;
	InfcBrowser *browser;
	GeditCollaborationBookmark *bookmark;
	InfXmlConnection *connection;
	GtkTreeIter selected;

	if (!inf_gtk_browser_view_get_selected (INF_GTK_BROWSER_VIEW (helper->priv->browser_view),
	                                        &selected))
	{
		return;
	}

	gtk_tree_model_sort_convert_iter_to_child_iter (
		GTK_TREE_MODEL_SORT (
			gtk_tree_view_get_model (
				GTK_TREE_VIEW (helper->priv->browser_view)
			)
		),
		&iter,
		&selected
	);

	gtk_tree_model_get (GTK_TREE_MODEL (helper->priv->browser_store),
	                    &iter,
	                    INF_GTK_BROWSER_MODEL_COL_BROWSER,
	                    &browser,
	                    -1);

	if (browser == NULL)
	{
		return;
	}

	connection = infc_browser_get_connection (browser);
	bookmark = g_object_get_data (G_OBJECT (connection),
	                              BOOKMARK_DATA_KEY);

	create_bookmark_dialog (helper, bookmark);

	g_object_unref (browser);
}
static void
inf_test_mass_join_session_synchronization_failed_cb(InfSession* session,
        InfXmlConnection* connection,
        const GError* error,
        gpointer user_data)
{
    InfTestMassJoiner* joiner;
    joiner = (InfTestMassJoiner*)user_data;

    fprintf(
        stderr,
        "Joiner %s: Session synchronization failed: %s\n",
        joiner->username,
        error->message
    );

    inf_xml_connection_close(infc_browser_get_connection(joiner->browser));
}
void
on_action_session_disconnect (GtkAction                      *action,
                              GeditCollaborationWindowHelper *helper)
{
	GtkTreeIter iter;
	GtkTreeIter selected;
	InfcBrowser *browser;
	InfXmlConnection *connection;

	if (!inf_gtk_browser_view_get_selected (INF_GTK_BROWSER_VIEW (helper->priv->browser_view),
	                                        &selected))
	{
		return;
	}

	gtk_tree_model_sort_convert_iter_to_child_iter (
		GTK_TREE_MODEL_SORT (
			gtk_tree_view_get_model (
				GTK_TREE_VIEW (helper->priv->browser_view)
			)
		),
		&iter,
		&selected
	);

	gtk_tree_model_get (GTK_TREE_MODEL (helper->priv->browser_store),
	                    &iter,
	                    INF_GTK_BROWSER_MODEL_COL_BROWSER,
	                    &browser,
	                    -1);

	if (browser == NULL)
	{
		return;
	}

	connection = infc_browser_get_connection (browser);
	inf_xml_connection_close (connection);

	inf_gtk_browser_store_clear_connection_error (helper->priv->browser_store,
	                                              connection);

	g_object_unref (browser);
}
static gchar *
get_chat_name (GeditCollaborationWindowHelper *helper,
               InfXmlConnection               *connection)
{
	GtkTreeIter iter;
	GtkTreeModel *model = GTK_TREE_MODEL (helper->priv->browser_store);

	if (!gtk_tree_model_get_iter_first (model, &iter))
	{
		return NULL;
	}

	do
	{
		gchar *name;
		InfcBrowser *browser;

		gtk_tree_model_get (model,
		                    &iter,
		                    INF_GTK_BROWSER_MODEL_COL_BROWSER,
		                    &browser,
		                    INF_GTK_BROWSER_MODEL_COL_NAME,
		                    &name,
		                    -1);

		if (browser != NULL &&
		    infc_browser_get_connection (browser) == connection)
		{
			g_object_unref (browser);
			return name;
		}

		g_object_unref (browser);
		g_free (name);
	} while (gtk_tree_model_iter_next (model, &iter));

	return NULL;
}
static void
on_browser_activate (InfGtkBrowserView              *view,
                     GtkTreeIter                    *iter,
                     GeditCollaborationWindowHelper *helper)
{
	InfcBrowser *browser;
	InfcBrowserIter *browser_iter;
	InfDiscovery *discovery;
	GeditCollaborationUser *user;
	GtkTreeIter selected;

	gtk_tree_model_sort_convert_iter_to_child_iter (
		GTK_TREE_MODEL_SORT (inf_gtk_browser_view_get_model (view)),
		&selected,
		iter
	);

	gtk_tree_model_get (GTK_TREE_MODEL (helper->priv->browser_store),
	                    &selected,
	                    INF_GTK_BROWSER_MODEL_COL_BROWSER,
	                    &browser,
	                    INF_GTK_BROWSER_MODEL_COL_DISCOVERY,
	                    &discovery,
	                    -1);

	if (browser == NULL)
	{
		if (discovery)
		{
			g_object_unref (discovery);
		}

		return;
	}

	gtk_tree_model_get (GTK_TREE_MODEL (helper->priv->browser_store),
	                    &selected,
	                    INF_GTK_BROWSER_MODEL_COL_NODE,
	                    &browser_iter,
	                    -1);

	if (browser_iter == NULL ||
	    infc_browser_iter_is_subdirectory (browser, browser_iter))
	{
		g_object_unref (browser);

		if (browser_iter)
		{
			infc_browser_iter_free (browser_iter);
		}

		if (discovery)
		{
			g_object_unref (discovery);
		}

		return;
	}

	if (discovery)
	{
		user = gedit_collaboration_user_get_default ();
	}
	else
	{
		GeditCollaborationBookmark *bookmark;

		bookmark = g_object_get_data (G_OBJECT (infc_browser_get_connection (browser)),
		                              BOOKMARK_DATA_KEY);

		user = gedit_collaboration_bookmark_get_user (bookmark);
	}

	gedit_collaboration_manager_subscribe (helper->priv->manager,
	                                       user,
	                                       browser,
	                                       browser_iter);

	if (discovery)
	{
		g_object_unref (discovery);
	}

	if (browser_iter)
	{
		infc_browser_iter_free (browser_iter);
	}
}
void
on_action_item_delete (GtkAction                      *action,
                       GeditCollaborationWindowHelper *helper)
{
	GtkTreeIter iter;
	GtkTreeIter selected;
	InfcBrowser *browser;
	InfcBrowserIter *browser_iter;
	InfcBrowserIter parent;

	if (!inf_gtk_browser_view_get_selected (INF_GTK_BROWSER_VIEW (helper->priv->browser_view),
	                                        &selected))
	{
		return;
	}

	gtk_tree_model_sort_convert_iter_to_child_iter (
		GTK_TREE_MODEL_SORT (
			gtk_tree_view_get_model (
				GTK_TREE_VIEW (helper->priv->browser_view)
			)
		),
		&iter,
		&selected
	);

	gtk_tree_model_get (GTK_TREE_MODEL (helper->priv->browser_store),
	                    &iter,
	                    INF_GTK_BROWSER_MODEL_COL_BROWSER,
	                    &browser,
	                    INF_GTK_BROWSER_MODEL_COL_NODE,
	                    &browser_iter,
	                    -1);

	parent = *browser_iter;

	if (!infc_browser_iter_get_parent (browser, &parent))
	{
		/* Toplevel bookmark */
		InfXmlConnection *connection = infc_browser_get_connection (browser);
		GeditCollaborationBookmarks *bookmarks;
		GeditCollaborationBookmark *bookmark =
			g_object_get_data (G_OBJECT (connection),
			                   BOOKMARK_DATA_KEY);

		/* Close connection first */
		if (infc_browser_get_status (browser) != INFC_BROWSER_DISCONNECTED)
		{
			inf_xml_connection_close (connection);
		}

		inf_gtk_browser_store_remove_connection (helper->priv->browser_store,
		                                         connection);

		bookmarks = gedit_collaboration_bookmarks_get_default ();
		gedit_collaboration_bookmarks_remove (bookmarks, bookmark);
	}
	else
	{
		/* Remove the iter itself */
		infc_browser_remove_node (browser, browser_iter);
	}

	g_object_unref (browser);

	if (browser_iter)
	{
		infc_browser_iter_free (browser_iter);
	}
}
static void
inf_test_mass_join_connect(InfTestMassJoin* massjoin,
                           const char* hostname,
                           guint port,
                           const char* document,
                           const char* username)
{
    InfIpAddress* addr;
    InfTcpConnection* tcp;
    InfXmppConnection* xmpp;
    InfTestMassJoiner* joiner;
    InfXmlConnection* xml;
    GError* error;

    addr = inf_ip_address_new_from_string(hostname);
    tcp = inf_tcp_connection_new(massjoin->io, addr, port);
    xmpp = inf_xmpp_connection_new(
               tcp,
               INF_XMPP_CONNECTION_CLIENT,
               g_get_host_name(),
               hostname,
               INF_XMPP_CONNECTION_SECURITY_BOTH_PREFER_TLS,
               NULL,
               NULL,
               NULL
           );

    joiner = g_slice_new(InfTestMassJoiner);
    joiner->communication_manager = inf_communication_manager_new();
    joiner->browser = infc_browser_new(
                          massjoin->io,
                          joiner->communication_manager,
                          INF_XML_CONNECTION(xmpp)
                      );
    joiner->session = NULL;
    joiner->document = g_strdup(document);
    joiner->username = g_strdup(username);

    g_object_unref(xmpp);
    g_object_unref(tcp);
    inf_ip_address_free(addr);

    massjoin->joiners = g_slist_prepend(massjoin->joiners, joiner);
    infc_browser_add_plugin(joiner->browser, &INF_TEST_MASS_JOIN_TEXT_PLUGIN);

    g_signal_connect(
        G_OBJECT(joiner->browser),
        "notify::status",
        G_CALLBACK(inf_test_mass_join_browser_notify_status_cb),
        massjoin
    );

    error = NULL;
    xml = infc_browser_get_connection(joiner->browser);
    if(inf_xml_connection_open(xml, &error) == FALSE)
    {
        fprintf(
            stderr,
            "Joiner %s: Failed to connect to %s: %s\n",
            joiner->username,
            hostname,
            error->message
        );

        g_error_free(error);
        massjoin->joiners = g_slist_remove(massjoin->joiners, joiner);

        if(massjoin->joiners == NULL)
            inf_standalone_io_loop_quit(INF_STANDALONE_IO(massjoin->io));
    }
}
void Gobby::BrowserContextCommands::on_account_created(
	gnutls_x509_privkey_t key,
	InfCertificateChain* certificate,
	const InfAclAccount* account)
{
	InfBrowser* browser;
	g_object_get(G_OBJECT(m_dialog->gobj()), "browser", &browser, NULL);
	const InfAclAccount* own_account =
		inf_browser_get_acl_local_account(browser);
	const InfAclAccountId default_id =
		inf_acl_account_id_from_string("default");
	const bool is_default_account = (own_account->id == default_id);
	g_object_unref(browser);

	gnutls_x509_crt_t cert =
		inf_certificate_chain_get_own_certificate(certificate);
	gchar* name = inf_cert_util_get_dn_by_oid(
		cert, GNUTLS_OID_X520_COMMON_NAME, 0);
	const std::string cn = name;
	g_free(name);

	std::string host;
	if(INFC_IS_BROWSER(browser))
	{
		InfXmlConnection* xml =
			infc_browser_get_connection(INFC_BROWSER(browser));
		if(INF_IS_XMPP_CONNECTION(xml))
		{
			gchar* hostname;
			g_object_get(G_OBJECT(xml), "remote-hostname",
			             &hostname, NULL);
			host = hostname;
			g_free(hostname);
		}
	}

	if(host.empty())
		host = "local";

	gnutls_x509_crt_t* certs = inf_certificate_chain_get_raw(certificate);
	const unsigned int n_certs =
		inf_certificate_chain_get_n_certificates(certificate);

	std::auto_ptr<Gtk::MessageDialog> dlg;

	try
	{
		const std::string filename = make_certificate_filename(cn, host);

		GError* error = NULL;
		inf_cert_util_write_certificate_with_key(
			key, certs, n_certs, filename.c_str(), &error);

		if(error != NULL)
		{
			const std::string message = error->message;
			g_error_free(error);
			throw std::runtime_error(message);
		}

		if(is_default_account &&
		   (m_cert_manager.get_private_key() == NULL ||
		    m_cert_manager.get_certificates() == NULL))
		{
			m_preferences.security.key_file = filename;
			m_preferences.security.certificate_file = filename;

			dlg.reset(new Gtk::MessageDialog(
				m_parent, _("Account successfully created"),
				false, Gtk::MESSAGE_INFO,
				Gtk::BUTTONS_CLOSE));
			dlg->set_secondary_text(
				_("When re-connecting to the server, the "
				  "new account will be used."));
		}
		else
		{
			// TODO: Gobby should support multiple certificates
			dlg.reset(new Gtk::MessageDialog(
				m_parent, _("Account successfully created"),
				false, Gtk::MESSAGE_INFO,
				Gtk::BUTTONS_CLOSE));
			dlg->set_secondary_text(Glib::ustring::compose(
				_("The certificate has been stored at %1.\n\n"
				  "To login to this account, set the "
				  "certificate in Gobby's preferences "
				  "and re-connect to the server."),
				filename));
		}
	}
	catch(const std::exception& ex)
	{
		// This is actually a bit unfortunate, because the
		// account was actually created and we have a valid
		// certificate for it, but we cannot keep it...
		dlg.reset(new Gtk::MessageDialog(
			m_parent, _("Failed to create account"),
			false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE));
		dlg->set_secondary_text(Glib::ustring::compose(
			_("Could not save the certificate for the "
			  "account: %1"), ex.what()));
	}

	m_dialog = dlg;
	m_dialog->signal_response().connect(
		sigc::mem_fun(
			*this,
			&BrowserContextCommands::
				on_account_created_response));
	m_dialog->present();
}