pidgin/pidgin

Add some new methods to purple tags
default tip
18 hours ago, Gary Kramlich
b45add2a840c
Add some new methods to purple tags

* purple_tags_exists is a simplier version of purple_tags_lookup.
* purple_tags_contains makes it easier to find multiple matching tags.

Testing Done:
Ran the unit tests under valgrind and had the turtles check in on things too.

Reviewed at https://reviews.imfreedom.org/r/3143/
/*
* Purple - Internet Messaging Library
* Copyright (C) Pidgin Developers <devel@pidgin.im>
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This library 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 2 of the License, or (at your option)
* any later version.
*
* 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this library; if not, see <https://www.gnu.org/licenses/>.
*/
#include <glib/gi18n-lib.h>
#include "connection.h"
#include "debug.h"
#include "notify.h"
#include "prefs.h"
#include "proxy.h"
#include "purpleaccount.h"
#include "purpleenums.h"
#include "purpleprivate.h"
#include "purpleprotocolserver.h"
#include "request.h"
#include "signals.h"
#include "util.h"
/**
* PurpleConnection:
*
* Represents an active connection on an account.
*/
typedef struct {
GObject gparent;
gchar *id;
GCancellable *cancellable;
PurpleProtocol *protocol; /* The protocol. */
PurpleConnectionFlags flags; /* Connection flags. */
PurpleConnectionState state; /* The connection state. */
PurpleAccount *account; /* The account being connected to. */
char *password; /* The password used. */
char *display_name; /* How you appear to other people. */
/* Wants to Die state. This is set when the user chooses to log out, or
* when the protocol is disconnected and should not be automatically
* reconnected (incorrect password, etc.). Protocols should rely on
* purple_connection_error() to set this for them rather than
* setting it themselves.
* See purple_connection_error_is_fatal()
*/
gboolean wants_to_die;
/* The connection error and its description if an error occurred. */
PurpleConnectionErrorInfo *error_info;
guint disconnect_timeout; /* Timer used for nasty stack tricks. */
} PurpleConnectionPrivate;
enum {
PROP_0,
PROP_ID,
PROP_CANCELLABLE,
PROP_PROTOCOL,
PROP_FLAGS,
PROP_STATE,
PROP_ACCOUNT,
PROP_PASSWORD,
PROP_DISPLAY_NAME,
N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };
static GList *connections = NULL;
static GList *connections_connected = NULL;
static int connections_handle;
G_DEFINE_TYPE_WITH_PRIVATE(PurpleConnection, purple_connection, G_TYPE_OBJECT)
/**************************************************************************
* Connection API
**************************************************************************/
/*
* d:)->-<
*
* d:O-\-<
*
* d:D-/-<
*
* d8D->-< DANCE!
*/
void
purple_connection_set_state(PurpleConnection *connection,
PurpleConnectionState state)
{
PurpleConnectionPrivate *priv = NULL;
g_return_if_fail(PURPLE_IS_CONNECTION(connection));
priv = purple_connection_get_instance_private(connection);
if(priv->state == state) {
return;
}
priv->state = state;
if(priv->state == PURPLE_CONNECTION_STATE_CONNECTED) {
PurplePresence *presence;
GDateTime *timestamp = NULL;
gboolean emit_online = FALSE;
gpointer handle = NULL;
presence = purple_account_get_presence(priv->account);
/* Set the time the account came online */
timestamp = g_date_time_new_now_utc();
purple_presence_set_login_time(presence, timestamp);
g_date_time_unref(timestamp);
handle = purple_connections_get_handle();
purple_signal_emit(handle, "signed-on", connection);
purple_signal_emit_return_1(handle, "autojoin", connection);
/* check if connections_connected is NULL, if so we need to emit the
* online signal.
*/
if(connections_connected == NULL) {
emit_online = TRUE;
}
connections_connected = g_list_append(connections_connected,
connection);
if(emit_online) {
purple_signal_emit(handle, "online");
}
}
g_object_notify_by_pspec(G_OBJECT(connection), properties[PROP_STATE]);
}
void
purple_connection_set_flags(PurpleConnection *connection,
PurpleConnectionFlags flags)
{
PurpleConnectionPrivate *priv = NULL;
g_return_if_fail(PURPLE_IS_CONNECTION(connection));
priv = purple_connection_get_instance_private(connection);
priv->flags = flags;
g_object_notify_by_pspec(G_OBJECT(connection), properties[PROP_FLAGS]);
}
void
purple_connection_set_display_name(PurpleConnection *connection,
const gchar *name)
{
PurpleConnectionPrivate *priv = NULL;
g_return_if_fail(PURPLE_IS_CONNECTION(connection));
priv = purple_connection_get_instance_private(connection);
g_clear_pointer(&priv->display_name, g_free);
priv->display_name = g_strdup(name);
g_object_notify_by_pspec(G_OBJECT(connection),
properties[PROP_DISPLAY_NAME]);
}
PurpleConnectionState
purple_connection_get_state(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection),
PURPLE_CONNECTION_STATE_DISCONNECTED);
priv = purple_connection_get_instance_private(connection);
return priv->state;
}
PurpleConnectionFlags
purple_connection_get_flags(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), 0);
priv = purple_connection_get_instance_private(connection);
return priv->flags;
}
PurpleAccount *
purple_connection_get_account(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
priv = purple_connection_get_instance_private(connection);
return priv->account;
}
const gchar *
purple_connection_get_id(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
priv = purple_connection_get_instance_private(connection);
return priv->id;
}
PurpleProtocol *
purple_connection_get_protocol(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
priv = purple_connection_get_instance_private(connection);
return priv->protocol;
}
const char *
purple_connection_get_password(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
priv = purple_connection_get_instance_private(connection);
return priv->password;
}
void
purple_connection_set_password(PurpleConnection *connection,
const char *password)
{
PurpleConnectionPrivate *priv = NULL;
g_return_if_fail(PURPLE_IS_CONNECTION(connection));
priv = purple_connection_get_instance_private(connection);
purple_str_wipe(priv->password);
priv->password = g_strdup(password);
g_object_notify_by_pspec(G_OBJECT(connection), properties[PROP_PASSWORD]);
}
const char *
purple_connection_get_display_name(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
priv = purple_connection_get_instance_private(connection);
return priv->display_name;
}
gboolean
_purple_connection_wants_to_die(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), FALSE);
priv = purple_connection_get_instance_private(connection);
return priv->wants_to_die;
}
static gboolean
purple_connection_disconnect_cb(gpointer data) {
PurpleAccount *account = data;
PurpleConnection *connection;
connection = purple_account_get_connection(account);
if(PURPLE_IS_CONNECTION(connection)) {
PurpleConnectionPrivate *priv = NULL;
priv = purple_connection_get_instance_private(connection);
priv->disconnect_timeout = 0;
if(priv->state != PURPLE_CONNECTION_STATE_DISCONNECTED) {
/* If the connection is not disconnected, disconnect it. */
purple_account_disconnect(account);
} else {
/* Otherwise assume the connection was already disconnected or in
* the process of being disconnected and we just need to finish our
* cleanup.
*/
GError *error = NULL;
if(!purple_connection_disconnect(connection, &error)) {
const char *message = "unknown error";
if(error != NULL) {
message = error->message;
}
purple_debug_warning("connections",
"failed to disconnect %p : %s",
connection, message);
}
g_clear_error(&error);
purple_account_set_connection(account, NULL);
}
}
return G_SOURCE_REMOVE;
}
void
purple_connection_error(PurpleConnection *connection,
PurpleConnectionError reason,
const char *description)
{
PurpleConnectionPrivate *priv = NULL;
g_return_if_fail(PURPLE_IS_CONNECTION(connection));
priv = purple_connection_get_instance_private(connection);
/* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
* being the last member of the PurpleConnectionError enum in
* connection.h; if other reasons are added after it, this check should
* be updated.
*/
if(reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
purple_debug_error("connection",
"purple_connection_error: reason %u isn't a "
"valid reason\n", reason);
reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
}
if(description == NULL) {
purple_debug_error("connection", "purple_connection_error called with NULL description\n");
description = _("Unknown error");
}
/* If we've already got one error, we don't need any more */
if(priv->error_info != NULL) {
return;
}
priv->wants_to_die = purple_connection_error_is_fatal(reason);
purple_debug_info("connection",
"Connection error on %p (reason: %u description: %s)\n",
connection, reason, description);
priv->error_info = purple_connection_error_info_new(reason, description);
purple_signal_emit(purple_connections_get_handle(), "connection-error",
connection, reason, description);
priv->disconnect_timeout = g_timeout_add(0, purple_connection_disconnect_cb,
priv->account);
}
PurpleConnectionErrorInfo *
purple_connection_get_error_info(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
priv = purple_connection_get_instance_private(connection);
return priv->error_info;
}
void
purple_connection_g_error(PurpleConnection *connection, const GError *error) {
PurpleConnectionError reason;
if(g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
/* Not a connection error. Ignore. */
return;
}
if(error->domain == G_TLS_ERROR) {
switch (error->code) {
case G_TLS_ERROR_UNAVAILABLE:
reason = PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT;
break;
case G_TLS_ERROR_NOT_TLS:
case G_TLS_ERROR_HANDSHAKE:
reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
break;
case G_TLS_ERROR_BAD_CERTIFICATE:
case G_TLS_ERROR_CERTIFICATE_REQUIRED:
reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
break;
case G_TLS_ERROR_EOF:
case G_TLS_ERROR_MISC:
default:
reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
}
} else if (error->domain == G_IO_ERROR) {
reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
} else if (error->domain == PURPLE_CONNECTION_ERROR) {
reason = error->code;
} else {
reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
}
purple_connection_error(connection, reason, error->message);
}
void
purple_connection_take_error(PurpleConnection *connection, GError *error) {
purple_connection_g_error(connection, error);
g_error_free(error);
}
gboolean
purple_connection_error_is_fatal(PurpleConnectionError reason) {
switch (reason) {
case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
case PURPLE_CONNECTION_ERROR_CUSTOM_TEMPORARY:
return FALSE;
case PURPLE_CONNECTION_ERROR_INVALID_USERNAME:
case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT:
case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS:
case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED:
case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED:
case PURPLE_CONNECTION_ERROR_CERT_EXPIRED:
case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED:
case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH:
case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH:
case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED:
case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR:
case PURPLE_CONNECTION_ERROR_CUSTOM_FATAL:
case PURPLE_CONNECTION_ERROR_OTHER_ERROR:
return TRUE;
default:
g_return_val_if_reached(TRUE);
}
}
/**************************************************************************
* Helpers
**************************************************************************/
static void
purple_connection_set_id(PurpleConnection *connection, const gchar *id) {
PurpleConnectionPrivate *priv = NULL;
priv = purple_connection_get_instance_private(connection);
g_free(priv->id);
priv->id = g_strdup(id);
g_object_notify_by_pspec(G_OBJECT(connection), properties[PROP_ID]);
}
static void
purple_connection_set_account(PurpleConnection *connection,
PurpleAccount *account)
{
PurpleConnectionPrivate *priv = NULL;
priv = purple_connection_get_instance_private(connection);
if(g_set_object(&priv->account, account)) {
g_object_notify_by_pspec(G_OBJECT(connection),
properties[PROP_ACCOUNT]);
}
}
/**************************************************************************
* GObject Implementation
**************************************************************************/
static void
purple_connection_set_property(GObject *obj, guint param_id,
const GValue *value, GParamSpec *pspec)
{
PurpleConnection *connection = PURPLE_CONNECTION(obj);
PurpleConnectionPrivate *priv = NULL;
priv = purple_connection_get_instance_private(connection);
switch (param_id) {
case PROP_ID:
purple_connection_set_id(connection, g_value_get_string(value));
break;
case PROP_PROTOCOL:
priv->protocol = g_value_get_object(value);
break;
case PROP_FLAGS:
purple_connection_set_flags(connection, g_value_get_flags(value));
break;
case PROP_STATE:
purple_connection_set_state(connection, g_value_get_enum(value));
break;
case PROP_ACCOUNT:
purple_connection_set_account(connection,
g_value_get_object(value));
break;
case PROP_PASSWORD:
purple_connection_set_password(connection,
g_value_get_string(value));
break;
case PROP_DISPLAY_NAME:
purple_connection_set_display_name(connection,
g_value_get_string(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
purple_connection_get_property(GObject *obj, guint param_id, GValue *value,
GParamSpec *pspec)
{
PurpleConnection *connection = PURPLE_CONNECTION(obj);
switch (param_id) {
case PROP_ID:
g_value_set_string(value, purple_connection_get_id(connection));
break;
case PROP_CANCELLABLE:
g_value_set_object(value,
purple_connection_get_cancellable(connection));
break;
case PROP_PROTOCOL:
g_value_set_object(value,
purple_connection_get_protocol(connection));
break;
case PROP_FLAGS:
g_value_set_flags(value, purple_connection_get_flags(connection));
break;
case PROP_STATE:
g_value_set_enum(value, purple_connection_get_state(connection));
break;
case PROP_ACCOUNT:
g_value_set_object(value,
purple_connection_get_account(connection));
break;
case PROP_PASSWORD:
g_value_set_string(value,
purple_connection_get_password(connection));
break;
case PROP_DISPLAY_NAME:
g_value_set_string(value,
purple_connection_get_display_name(connection));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
purple_connection_init(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
priv = purple_connection_get_instance_private(connection);
priv->cancellable = g_cancellable_new();
purple_connection_set_state(connection, PURPLE_CONNECTION_STATE_CONNECTING);
connections = g_list_append(connections, connection);
}
static void
purple_connection_constructed(GObject *object) {
PurpleConnection *connection = PURPLE_CONNECTION(object);
PurpleConnectionPrivate *priv = NULL;
G_OBJECT_CLASS(purple_connection_parent_class)->constructed(object);
priv = purple_connection_get_instance_private(connection);
if(priv->id == NULL) {
gchar *uuid = g_uuid_string_random();
purple_connection_set_id(connection, uuid);
g_free(uuid);
}
purple_signal_emit(purple_connections_get_handle(), "signing-on",
connection);
}
static void
purple_connection_dispose(GObject *obj) {
PurpleConnection *connection = PURPLE_CONNECTION(obj);
PurpleConnectionPrivate *priv = NULL;
priv = purple_connection_get_instance_private(connection);
g_clear_object(&priv->account);
g_clear_handle_id(&priv->disconnect_timeout, g_source_remove);
G_OBJECT_CLASS(purple_connection_parent_class)->dispose(obj);
}
static void
purple_connection_finalize(GObject *object) {
PurpleConnection *connection = PURPLE_CONNECTION(object);
PurpleConnectionPrivate *priv = NULL;
priv = purple_connection_get_instance_private(connection);
g_clear_pointer(&priv->error_info, purple_connection_error_info_free);
purple_str_wipe(priv->password);
g_free(priv->display_name);
g_free(priv->id);
g_clear_object(&priv->cancellable);
G_OBJECT_CLASS(purple_connection_parent_class)->finalize(object);
}
static void
purple_connection_class_init(PurpleConnectionClass *klass) {
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
obj_class->get_property = purple_connection_get_property;
obj_class->set_property = purple_connection_set_property;
obj_class->dispose = purple_connection_dispose;
obj_class->finalize = purple_connection_finalize;
obj_class->constructed = purple_connection_constructed;
/**
* PurpleConnection:id:
*
* The unique identifier for the connection.
*
* Since: 3.0
*/
properties[PROP_ID] = g_param_spec_string(
"id", "id",
"The identifier of the connection",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* PurpleConnection:cancellable:
*
* A [class@Gio.Cancellable] to be used with the connection.
*
* This can be passed function that require a cancellable for the
* connection.
*
* Since: 3.0
*/
properties[PROP_CANCELLABLE] = g_param_spec_object(
"cancellable", "cancellable",
"A cancellable for the connection.",
G_TYPE_CANCELLABLE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* PurpleConnection:protocol:
*
* The protocol that this connection is for.
*
* Since: 3.0
*/
properties[PROP_PROTOCOL] = g_param_spec_object(
"protocol", "Protocol",
"The protocol that the connection is using.",
PURPLE_TYPE_PROTOCOL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* PurpleConnection:flags:
*
* The flags for this connection.
*
* Since: 3.0
*/
properties[PROP_FLAGS] = g_param_spec_flags(
"flags", "Connection flags",
"The flags of the connection.",
PURPLE_TYPE_CONNECTION_FLAGS, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/**
* PurpleConnection:state:
*
* The state of the connection.
*
* Since: 3.0
*/
properties[PROP_STATE] = g_param_spec_enum(
"state", "Connection state",
"The current state of the connection.",
PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_STATE_DISCONNECTED,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/**
* PurpleConnection:account:
*
* The account this connection belongs to.
*
* Since: 3.0
*/
properties[PROP_ACCOUNT] = g_param_spec_object(
"account", "Account",
"The account using the connection.", PURPLE_TYPE_ACCOUNT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* PurpleConnection:password:
*
* The password for this connection.
*
* This is only stored for reconnections and may go away in the future.
*
* Since: 3.0
*/
properties[PROP_PASSWORD] = g_param_spec_string(
"password", "Password",
"The password used for connection.", NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/**
* PurpleConnection:display-name:
*
* The display name for the account.
*
* Since: 3.0
*/
properties[PROP_DISPLAY_NAME] = g_param_spec_string(
"display-name", "Display name",
"Your name that appears to other people.", NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}
gboolean
purple_connection_connect(PurpleConnection *connection, GError **error) {
PurpleConnectionClass *klass = NULL;
PurpleConnectionPrivate *priv = NULL;
PurpleContactInfo *info = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), FALSE);
priv = purple_connection_get_instance_private(connection);
info = PURPLE_CONTACT_INFO(priv->account);
if(!purple_account_is_disconnected(priv->account)) {
g_set_error(error, PURPLE_CONNECTION_ERROR, 0,
"account %s is not disconnected",
purple_contact_info_get_username(info));
return TRUE;
}
if(((priv->password == NULL) || (*priv->password == '\0')) &&
!(purple_protocol_get_options(priv->protocol) & OPT_PROTO_NO_PASSWORD) &&
!(purple_protocol_get_options(priv->protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
{
g_set_error(error, PURPLE_CONNECTION_ERROR, 0,
"Cannot connect to account %s without a password.",
purple_contact_info_get_username(info));
return FALSE;
}
purple_debug_info("connection", "Connecting. connection = %p",
connection);
klass = PURPLE_CONNECTION_GET_CLASS(connection);
if(klass != NULL && klass->connect != NULL) {
return klass->connect(connection, error);
}
g_set_error(error, PURPLE_CONNECTION_ERROR, 0,
"The connection for %s did not implement the connect method",
purple_contact_info_get_username(info));
return FALSE;
}
gboolean
purple_connection_disconnect(PurpleConnection *connection, GError **error) {
PurpleConnectionClass *klass = NULL;
PurpleConnectionPrivate *priv = NULL;
gboolean ret = TRUE;
gpointer handle = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), FALSE);
/* We don't check if the connection's state is connected as everything
* should be idempotent when doing cleanup.
*/
priv = purple_connection_get_instance_private(connection);
handle = purple_connections_get_handle();
purple_debug_info("connection", "Disconnecting connection %p", connection);
purple_connection_set_state(connection,
PURPLE_CONNECTION_STATE_DISCONNECTING);
purple_signal_emit(handle, "signing-off", connection);
/* Dispatch to the connection's disconnect method. */
klass = PURPLE_CONNECTION_GET_CLASS(connection);
if(klass != NULL && klass->disconnect != NULL) {
ret = klass->disconnect(connection, error);
}
/* Do the rest of our cleanup. */
connections = g_list_remove(connections, connection);
purple_connection_set_state(connection,
PURPLE_CONNECTION_STATE_DISCONNECTED);
purple_signal_emit(handle, "signed-off", connection);
purple_account_request_close_with_account(priv->account);
purple_request_close_with_handle(connection);
purple_notify_close_with_handle(connection);
connections_connected = g_list_remove(connections_connected, connection);
if(connections_connected == NULL) {
purple_signal_emit(handle, "offline");
}
purple_debug_info("connection", "Destroying connection %p", connection);
return ret;
}
GCancellable *
purple_connection_get_cancellable(PurpleConnection *connection) {
PurpleConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), NULL);
priv = purple_connection_get_instance_private(connection);
return priv->cancellable;
}
/**************************************************************************
* Connections API
**************************************************************************/
void
purple_connections_disconnect_all(void) {
GList *l;
while((l = purple_connections_get_all()) != NULL) {
PurpleConnection *connection = l->data;
PurpleConnectionPrivate *priv = NULL;
priv = purple_connection_get_instance_private(connection);
priv->wants_to_die = TRUE;
purple_account_disconnect(priv->account);
}
}
GList *
purple_connections_get_all(void) {
return connections;
}
gboolean
purple_connections_is_online(void) {
return (connections_connected != NULL);
}
void
purple_connections_init(void) {
void *handle = purple_connections_get_handle();
purple_signal_register(handle, "online", purple_marshal_VOID, G_TYPE_NONE,
0);
purple_signal_register(handle, "offline", purple_marshal_VOID, G_TYPE_NONE,
0);
purple_signal_register(handle, "signing-on",
purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
PURPLE_TYPE_CONNECTION);
purple_signal_register(handle, "signed-on",
purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
PURPLE_TYPE_CONNECTION);
purple_signal_register(handle, "signing-off",
purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
PURPLE_TYPE_CONNECTION);
purple_signal_register(handle, "signed-off",
purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
PURPLE_TYPE_CONNECTION);
purple_signal_register(handle, "connection-error",
purple_marshal_VOID__POINTER_INT_POINTER,
G_TYPE_NONE, 3, PURPLE_TYPE_CONNECTION,
PURPLE_TYPE_CONNECTION_ERROR, G_TYPE_STRING);
purple_signal_register(handle, "autojoin",
purple_marshal_BOOLEAN__POINTER, G_TYPE_NONE, 1,
PURPLE_TYPE_CONNECTION);
}
void
purple_connections_uninit(void) {
purple_signals_unregister_by_instance(purple_connections_get_handle());
}
void *
purple_connections_get_handle(void) {
return &connections_handle;
}