<commit_before>/*
 * e-backend.c
 *
 * This library 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.
 *
 * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
 *
 */

/**
 * SECTION: e-backend
 * @include: libebackend/libebackend.h
 * @short_description: An abstract base class for backends
 *
 * An #EBackend is paired with an #ESource to facilitate performing
 * actions on the local or remote resource described by the #ESource.
 *
 * In other words, whereas a certain backend type knows how to talk to a
 * certain type of server or data store, the #ESource fills in configuration
 * details such as host name, user name, resource path, etc.
 *
 * All #EBackend instances are created by an #EBackendFactory.
 **/

#include <config.h>
#include <glib/gi18n-lib.h>

#include <gio/gio.h>

#include <libedataserver/libedataserver.h>

#include "e-backend.h"
#include "e-user-prompter.h"

#define E_BACKEND_GET_PRIVATE(obj) \
	(G_TYPE_INSTANCE_GET_PRIVATE \
	((obj), E_TYPE_BACKEND, EBackendPrivate))

#define G_IS_IO_ERROR(error, code) \
	(g_error_matches ((error), G_IO_ERROR, (code)))

#define G_IS_RESOLVER_ERROR(error, code) \
	(g_error_matches ((error), G_RESOLVER_ERROR, (code)))

struct _EBackendPrivate {
	GMutex property_lock;
	ESource *source;
	EUserPrompter *prompter;
	GMainContext *main_context;
	GSocketConnectable *connectable;
	gboolean online;

	GNetworkMonitor *network_monitor;
	gulong network_changed_handler_id;

	GSource *update_online_state;
	GMutex update_online_state_lock;

	GMutex network_monitor_cancellable_lock;
	GCancellable *network_monitor_cancellable;

	GMutex authenticate_cancellable_lock;
	GCancellable *authenticate_cancellable;
};

enum {
	PROP_0,
	PROP_CONNECTABLE,
	PROP_MAIN_CONTEXT,
	PROP_ONLINE,
	PROP_SOURCE,
	PROP_USER_PROMPTER
};

G_DEFINE_ABSTRACT_TYPE (EBackend, e_backend, G_TYPE_OBJECT)

static void
backend_network_monitor_can_reach_cb (GObject *source_object,
                                      GAsyncResult *result,
                                      gpointer user_data)
{
	EBackend *backend = E_BACKEND (user_data);
	gboolean host_is_reachable;
	GError *error = NULL;

	host_is_reachable = g_network_monitor_can_reach_finish (
		G_NETWORK_MONITOR (source_object), result, &error);

	/* Sanity check. */
	g_return_if_fail (
		(host_is_reachable && (error == NULL)) ||
		(!host_is_reachable && (error != NULL)));

	if (G_IS_IO_ERROR (error, G_IO_ERROR_CANCELLED) ||
	    host_is_reachable == e_backend_get_online (backend)) {
		g_clear_error (&error);
		g_object_unref (backend);
		return;
	}

	g_clear_error (&error);

	e_backend_set_online (backend, host_is_reachable);

	if (!host_is_reachable) {
		ESource *source;

		source = e_backend_get_source (backend);
		e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
	}

	g_object_unref (backend);
}

static gboolean
backend_update_online_state_timeout_cb (gpointer user_data)
{
	EBackend *backend;
	GSocketConnectable *connectable;
	GCancellable *cancellable;
	GSource *current_source;

	current_source = g_main_current_source ();
	if (current_source && g_source_is_destroyed (current_source))
		return FALSE;

	backend = E_BACKEND (user_data);
	connectable = e_backend_ref_connectable (backend);

	g_mutex_lock (&backend->priv->update_online_state_lock);
	g_source_unref (backend->priv->update_online_state);
	backend->priv->update_online_state = NULL;
	g_mutex_unlock (&backend->priv->update_online_state_lock);

	g_mutex_lock (&backend->priv->network_monitor_cancellable_lock);

	cancellable = backend->priv->network_monitor_cancellable;
	backend->priv->network_monitor_cancellable = NULL;

	if (cancellable != NULL) {
		g_cancellable_cancel (cancellable);
		g_object_unref (cancellable);
		cancellable = NULL;
	}

	if (!connectable) {
		gchar *host = NULL;
		guint16 port = 0;

		if (e_backend_get_destination_address (backend, &host, &port) && host)
			connectable = g_network_address_new (host, port);

		g_free (host);
	}

	if (connectable == NULL) {
		e_backend_set_online (backend, TRUE);
	} else {
		cancellable = g_cancellable_new ();

		g_network_monitor_can_reach_async (
			backend->priv->network_monitor,
			connectable, cancellable,
			backend_network_monitor_can_reach_cb,
			g_object_ref (backend));
	}

	backend->priv->network_monitor_cancellable = cancellable;

	g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);

	if (connectable != NULL)
		g_object_unref (connectable);

	return FALSE;
}

static void
backend_update_online_state (EBackend *backend)
{
	GMainContext *main_context;
	GSource *timeout_source;

	g_mutex_lock (&backend->priv->update_online_state_lock);

	/* Reference the backend before destroying any already scheduled GSource,
	   in case the backend's last reference is held by that GSource. */
	g_object_ref (backend);

	if (backend->priv->update_online_state) {
		g_source_destroy (backend->priv->update_online_state);<commit_msg>Bug 756530 - Deadlock in e_backend_set_online()<commit_after>/*
 * e-backend.c
 *
 * This library 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.
 *
 * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
 *
 */

/**
 * SECTION: e-backend
 * @include: libebackend/libebackend.h
 * @short_description: An abstract base class for backends
 *
 * An #EBackend is paired with an #ESource to facilitate performing
 * actions on the local or remote resource described by the #ESource.
 *
 * In other words, whereas a certain backend type knows how to talk to a
 * certain type of server or data store, the #ESource fills in configuration
 * details such as host name, user name, resource path, etc.
 *
 * All #EBackend instances are created by an #EBackendFactory.
 **/

#include <config.h>
#include <glib/gi18n-lib.h>

#include <gio/gio.h>

#include <libedataserver/libedataserver.h>

#include "e-backend.h"
#include "e-user-prompter.h"

#define E_BACKEND_GET_PRIVATE(obj) \
	(G_TYPE_INSTANCE_GET_PRIVATE \
	((obj), E_TYPE_BACKEND, EBackendPrivate))

#define G_IS_IO_ERROR(error, code) \
	(g_error_matches ((error), G_IO_ERROR, (code)))

#define G_IS_RESOLVER_ERROR(error, code) \
	(g_error_matches ((error), G_RESOLVER_ERROR, (code)))

struct _EBackendPrivate {
	GMutex property_lock;
	ESource *source;
	EUserPrompter *prompter;
	GMainContext *main_context;
	GSocketConnectable *connectable;
	gboolean online;

	GNetworkMonitor *network_monitor;
	gulong network_changed_handler_id;

	GSource *update_online_state;
	GMutex update_online_state_lock;

	GMutex network_monitor_cancellable_lock;
	GCancellable *network_monitor_cancellable;

	GMutex authenticate_cancellable_lock;
	GCancellable *authenticate_cancellable;
};

enum {
	PROP_0,
	PROP_CONNECTABLE,
	PROP_MAIN_CONTEXT,
	PROP_ONLINE,
	PROP_SOURCE,
	PROP_USER_PROMPTER
};

G_DEFINE_ABSTRACT_TYPE (EBackend, e_backend, G_TYPE_OBJECT)

static void
backend_network_monitor_can_reach_cb (GObject *source_object,
                                      GAsyncResult *result,
                                      gpointer user_data)
{
	EBackend *backend = E_BACKEND (user_data);
	gboolean host_is_reachable;
	GError *error = NULL;

	host_is_reachable = g_network_monitor_can_reach_finish (
		G_NETWORK_MONITOR (source_object), result, &error);

	/* Sanity check. */
	g_return_if_fail (
		(host_is_reachable && (error == NULL)) ||
		(!host_is_reachable && (error != NULL)));

	if (G_IS_IO_ERROR (error, G_IO_ERROR_CANCELLED) ||
	    host_is_reachable == e_backend_get_online (backend)) {
		g_clear_error (&error);
		g_object_unref (backend);
		return;
	}

	g_clear_error (&error);

	e_backend_set_online (backend, host_is_reachable);

	if (!host_is_reachable) {
		ESource *source;

		source = e_backend_get_source (backend);
		e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
	}

	g_object_unref (backend);
}

static gboolean
backend_update_online_state_timeout_cb (gpointer user_data)
{
	EBackend *backend;
	GSocketConnectable *connectable;
	GCancellable *cancellable;
	GSource *current_source;

	current_source = g_main_current_source ();
	if (current_source && g_source_is_destroyed (current_source))
		return FALSE;

	backend = E_BACKEND (user_data);
	connectable = e_backend_ref_connectable (backend);

	g_mutex_lock (&backend->priv->update_online_state_lock);
	g_source_unref (backend->priv->update_online_state);
	backend->priv->update_online_state = NULL;
	g_mutex_unlock (&backend->priv->update_online_state_lock);

	g_mutex_lock (&backend->priv->network_monitor_cancellable_lock);

	cancellable = backend->priv->network_monitor_cancellable;
	backend->priv->network_monitor_cancellable = NULL;

	if (cancellable != NULL) {
		g_cancellable_cancel (cancellable);
		g_object_unref (cancellable);
		cancellable = NULL;
	}

	if (!connectable) {
		gchar *host = NULL;
		guint16 port = 0;

		if (e_backend_get_destination_address (backend, &host, &port) && host)
			connectable = g_network_address_new (host, port);

		g_free (host);
	}

	if (connectable == NULL) {
		backend->priv->network_monitor_cancellable = cancellable;
		g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);

		e_backend_set_online (backend, TRUE);
	} else {
		cancellable = g_cancellable_new ();

		g_network_monitor_can_reach_async (
			backend->priv->network_monitor,
			connectable, cancellable,
			backend_network_monitor_can_reach_cb,
			g_object_ref (backend));

		backend->priv->network_monitor_cancellable = cancellable;
		g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);
	}

	if (connectable != NULL)
		g_object_unref (connectable);

	return FALSE;
}

static void
backend_update_online_state (EBackend *backend)
{
	GMainContext *main_context;
	GSource *timeout_source;

	g_mutex_lock (&backend->priv->update_online_state_lock);

	/* Reference the backend before destroying any already scheduled GSource,
	   in case the backend's last reference is held by that GSource. */
	g_object_ref (backend);

	if (backend->priv->update_online_state) {
		g_source_destroy (backend->priv->update_online_state);
{'fraction_non_alphanumeric': 0.09399835571389421, 'fraction_numerical': 0.001644286105782406, 'mean_word_length': 5.109375, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4857124', 'n_tokens_mistral': 3778, 'n_tokens_neox': 3646, 'n_words': 879}