pidgin/pidgin

Stop automatically adding/remove conversations from the conversation manager

This was done to mimic the old conversations API and shouldn't be needed going
forward. However, since we can't create conversations right now, we're probably
missing some edge cases here.

Also make sure to keep a reference around on the conversation while removing to avoid use after frees.

Testing Done:
Trained with the turtles.

Reviewed at https://reviews.imfreedom.org/r/3049/
/*
* 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 "purpleaccount.h"
#include "accounts.h"
#include "core.h"
#include "debug.h"
#include "network.h"
#include "notify.h"
#include "prefs.h"
#include "purpleaccountpresence.h"
#include "purpleaddcontactrequest.h"
#include "purpleconversationmanager.h"
#include "purplecredentialmanager.h"
#include "purplenotification.h"
#include "purplenotificationmanager.h"
#include "purpleprivate.h"
#include "purpleprotocolclient.h"
#include "purpleprotocolmanager.h"
#include "purpleprotocolserver.h"
#include "request.h"
#include "request/purplerequestfieldbool.h"
#include "request/purplerequestfieldstring.h"
#include "server.h"
#include "signals.h"
#include "util.h"
typedef struct {
GSList *names;
guint ref_count;
} PurpleAccountSettingFreezeQueue;
G_LOCK_DEFINE_STATIC(setting_notify_lock);
struct _PurpleAccount {
PurpleContactInfo parent;
gboolean require_password;
char *user_info;
char *buddy_icon_path;
gboolean enabled;
gboolean remember_pass;
char *protocol_id;
PurpleConnection *gc;
gboolean disconnecting;
GHashTable *settings;
PurpleAccountSettingFreezeQueue *freeze_queue;
PurpleProxyInfo *proxy_info;
GList *status_types;
PurplePresence *presence;
PurpleConnectionErrorInfo *error;
PurpleNotification *error_notification;
} PurpleAccountPrivate;
typedef struct {
char *ui;
GValue value;
} PurpleAccountSetting;
enum {
PROP_0,
PROP_REQUIRE_PASSWORD,
PROP_ENABLED,
PROP_CONNECTION,
PROP_PROTOCOL_ID,
PROP_USER_INFO,
PROP_BUDDY_ICON_PATH,
PROP_REMEMBER_PASSWORD,
PROP_PROXY_INFO,
PROP_ERROR,
PROP_CONNECTED,
N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };
enum {
SIG_SETTING_CHANGED,
SIG_CONNECTED,
SIG_DISCONNECTED,
N_SIGNALS,
};
static guint signals[N_SIGNALS] = {0, };
G_DEFINE_FINAL_TYPE(PurpleAccount, purple_account, PURPLE_TYPE_CONTACT_INFO);
/******************************************************************************
* Helpers
*****************************************************************************/
static void
purple_account_free_notify_settings(PurpleAccount *account) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
if(account->freeze_queue != NULL) {
g_slist_free_full(account->freeze_queue->names, g_free);
g_slice_free(PurpleAccountSettingFreezeQueue, account->freeze_queue);
account->freeze_queue = NULL;
}
}
static void
purple_account_setting_changed_emit(PurpleAccount *account, const char *name) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(name != NULL);
G_LOCK(setting_notify_lock);
if(account->freeze_queue != NULL) {
GSList *names = account->freeze_queue->names;
if(g_slist_find_custom(names, name, (GCompareFunc)g_strcmp0) == NULL) {
names = g_slist_prepend(names, g_strdup(name));
account->freeze_queue->names = names;
}
} else {
g_signal_emit(account, signals[SIG_SETTING_CHANGED],
g_quark_from_string(name), name);
}
G_UNLOCK(setting_notify_lock);
}
static void
purple_account_real_connect(PurpleAccount *account, const char *password) {
PurpleConnection *connection = NULL;
PurpleProtocol *protocol = NULL;
GError *error = NULL;
protocol = purple_account_get_protocol(account);
connection = purple_protocol_create_connection(protocol, account, password,
&error);
if(error != NULL) {
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
purple_debug_warning("failed to create connection for %s: %s",
purple_contact_info_get_username(info),
error->message);
g_clear_error(&error);
return;
}
g_return_if_fail(PURPLE_IS_CONNECTION(connection));
purple_account_set_connection(account, connection);
if(!purple_connection_connect(connection, &error)) {
const char *message = "unknown error";
if(error != NULL && error->message != NULL) {
message = error->message;
}
purple_connection_error(connection,
PURPLE_CONNECTION_ERROR_OTHER_ERROR,
message);
g_clear_error(&error);
}
/* Finally remove our reference to the connection. */
g_object_unref(connection);
}
static void
request_password_write_cb(GObject *obj, GAsyncResult *res, gpointer data) {
PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
PurpleAccount *account = PURPLE_ACCOUNT(data);
GError *error = NULL;
gchar *password = NULL;
/* We stash the password on the account to get it to this call back... It's
* kind of gross but shouldn't be a big deal because any plugin has access
* to the credential store, so it's not really a security leak.
*/
password = (gchar *)g_object_get_data(G_OBJECT(account), "_tmp_password");
g_object_set_data(G_OBJECT(account), "_tmp_password", NULL);
if(!purple_credential_manager_write_password_finish(manager, res, &error))
{
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
const gchar *name = purple_contact_info_get_name_for_display(info);
/* we can't error an account without a connection, so we just drop a
* debug message for now and continue to connect the account.
*/
purple_debug_info("account",
"failed to save password for account \"%s\": %s",
name,
error != NULL ? error->message : "unknown error");
g_clear_error(&error);
}
purple_account_real_connect(account, password);
g_free(password);
}
static void
request_password_ok_cb(PurpleAccount *account, PurpleRequestPage *page) {
const char *entry;
gboolean remember;
entry = purple_request_page_get_string(page, "password");
remember = purple_request_page_get_bool(page, "remember");
if(purple_strempty(entry)) {
purple_notify_error(account, NULL,
_("Password is required to sign on."), NULL,
purple_request_cpar_from_account(account));
return;
}
purple_account_set_remember_password(account, remember);
if(remember) {
PurpleCredentialManager *manager = NULL;
manager = purple_credential_manager_get_default();
/* The requests field can be invalidated by the time we write the
* password and we want to use it in the write callback, so we need to
* duplicate it for that callback.
*/
g_object_set_data(G_OBJECT(account), "_tmp_password", g_strdup(entry));
purple_credential_manager_write_password_async(manager, account, entry,
NULL,
request_password_write_cb,
account);
} else {
purple_account_real_connect(account, entry);
}
}
static void
request_password_cancel_cb(PurpleAccount *account,
G_GNUC_UNUSED PurpleRequestPage *page)
{
/* Disable the account as the user has cancelled connecting */
purple_account_set_enabled(account, FALSE);
}
static void
purple_account_connect_got_password_cb(GObject *obj, GAsyncResult *res,
gpointer data)
{
PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
PurpleAccount *account = PURPLE_ACCOUNT(data);
PurpleProtocol *protocol = NULL;
PurpleProtocolOptions options;
GError *error = NULL;
gchar *password = NULL;
gboolean require_password = TRUE;
password = purple_credential_manager_read_password_finish(manager, res,
&error);
if(error != NULL) {
purple_debug_warning("account", "failed to read password %s",
error->message);
g_error_free(error);
}
protocol = purple_account_get_protocol(account);
options = purple_protocol_get_options(protocol);
if(options & OPT_PROTO_PASSWORD_OPTIONAL) {
require_password = purple_account_get_require_password(account);
} else if(options & OPT_PROTO_NO_PASSWORD) {
require_password = FALSE;
}
if((password == NULL || *password == '\0') && require_password) {
purple_account_request_password(account,
G_CALLBACK(request_password_ok_cb),
G_CALLBACK(request_password_cancel_cb),
account);
} else {
purple_account_real_connect(account, password);
}
g_free(password);
}
static void
change_password_cb(PurpleAccount *account, PurpleRequestPage *page) {
const char *orig_pass, *new_pass_1, *new_pass_2;
orig_pass = purple_request_page_get_string(page, "password");
new_pass_1 = purple_request_page_get_string(page, "new_password_1");
new_pass_2 = purple_request_page_get_string(page, "new_password_2");
if(g_utf8_collate(new_pass_1, new_pass_2)) {
purple_notify_error(account, NULL,
_("New passwords do not match."), NULL,
purple_request_cpar_from_account(account));
return;
}
if((purple_request_page_is_field_required(page, "password") &&
purple_strempty(orig_pass)) ||
(purple_request_page_is_field_required(page, "new_password_1") &&
purple_strempty(new_pass_1)) ||
(purple_request_page_is_field_required(page, "new_password_2") &&
purple_strempty(new_pass_2)))
{
purple_notify_error(account, NULL,
_("Fill out all fields completely."), NULL,
purple_request_cpar_from_account(account));
return;
}
purple_account_change_password(account, orig_pass, new_pass_1);
}
static gboolean
no_password_cb(gpointer data) {
PurpleAccount *account = data;
purple_account_real_connect(account, NULL);
return G_SOURCE_REMOVE;
}
static void
set_user_info_cb(PurpleAccount *account, const char *user_info) {
PurpleProtocol *protocol = NULL;
purple_account_set_user_info(account, user_info);
protocol = purple_account_get_protocol(account);
if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, set_info)) {
PurpleConnection *connection = purple_account_get_connection(account);
purple_protocol_server_set_info(PURPLE_PROTOCOL_SERVER(protocol),
connection, user_info);
}
}
static void
delete_setting(void *data) {
PurpleAccountSetting *setting = (PurpleAccountSetting *)data;
g_free(setting->ui);
g_value_unset(&setting->value);
g_free(setting);
}
static PurpleConnectionState
purple_account_get_state(PurpleAccount *account) {
PurpleConnection *gc;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account),
PURPLE_CONNECTION_STATE_DISCONNECTED);
gc = purple_account_get_connection(account);
if(!gc) {
return PURPLE_CONNECTION_STATE_DISCONNECTED;
}
return purple_connection_get_state(gc);
}
static void
purple_account_changed_cb(GObject *obj, GParamSpec *pspec,
G_GNUC_UNUSED gpointer data)
{
const char *name = NULL;
purple_accounts_schedule_save();
name = g_param_spec_get_name(pspec);
if(purple_strequal(name, "username")) {
/* if the username changes, we should re-write the buddy list to disk
* with the new name.
*/
purple_blist_save_account(purple_blist_get_default(),
PURPLE_ACCOUNT(obj));
}
}
static void
purple_account_connection_state_cb(GObject *obj,
G_GNUC_UNUSED GParamSpec *pspec,
gpointer data)
{
PurpleAccount *account = data;
PurpleConnection *connection = PURPLE_CONNECTION(obj);
PurpleConnectionState state = PURPLE_CONNECTION_STATE_DISCONNECTED;
state = purple_connection_get_state(connection);
if(state == PURPLE_CONNECTION_STATE_CONNECTED) {
g_signal_emit(account, signals[SIG_CONNECTED], 0);
} else if(state == PURPLE_CONNECTION_STATE_DISCONNECTED) {
g_signal_emit(account, signals[SIG_DISCONNECTED], 0);
}
}
static void
purple_account_can_connect_cb(GObject *source, GAsyncResult *result,
gpointer data)
{
PurpleAccount *account = data;
PurpleProtocol *protocol = PURPLE_PROTOCOL(source);
PurpleProtocolOptions options;
GError *error = NULL;
gboolean can_connect = FALSE;
gboolean require_password = TRUE;
can_connect = purple_protocol_can_connect_finish(protocol, result, &error);
if(!can_connect || error != NULL) {
PurpleConnectionErrorInfo *info = NULL;
const char *error_message = _("unknown error");
if(error != NULL) {
error_message = error->message;
}
info = purple_connection_error_info_new(PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
error_message);
purple_account_set_error(account, info);
g_clear_error(&error);
return;
}
options = purple_protocol_get_options(protocol);
if(options & OPT_PROTO_PASSWORD_OPTIONAL) {
require_password = purple_account_get_require_password(account);
} else if(options & OPT_PROTO_NO_PASSWORD) {
require_password = FALSE;
}
if(require_password) {
PurpleCredentialManager *manager = NULL;
manager = purple_credential_manager_get_default();
purple_credential_manager_read_password_async(manager, account, NULL,
purple_account_connect_got_password_cb,
account);
} else {
g_timeout_add_seconds(0, no_password_cb, account);
}
}
/******************************************************************************
* XmlNode Helpers
*****************************************************************************/
static PurpleXmlNode *
proxy_settings_to_xmlnode(PurpleProxyInfo *proxy_info) {
PurpleXmlNode *node, *child;
PurpleProxyType proxy_type;
const char *value;
int int_value;
char buf[21];
proxy_type = purple_proxy_info_get_proxy_type(proxy_info);
node = purple_xmlnode_new("proxy");
child = purple_xmlnode_new_child(node, "type");
purple_xmlnode_insert_data(child,
(proxy_type == PURPLE_PROXY_TYPE_USE_GLOBAL ? "global" :
proxy_type == PURPLE_PROXY_TYPE_NONE ? "none" :
proxy_type == PURPLE_PROXY_TYPE_HTTP ? "http" :
proxy_type == PURPLE_PROXY_TYPE_SOCKS4 ? "socks4" :
proxy_type == PURPLE_PROXY_TYPE_SOCKS5 ? "socks5" :
proxy_type == PURPLE_PROXY_TYPE_TOR ? "tor" :
proxy_type == PURPLE_PROXY_TYPE_USE_ENVVAR ? "envvar" : "unknown"), -1);
if((value = purple_proxy_info_get_hostname(proxy_info)) != NULL) {
child = purple_xmlnode_new_child(node, "host");
purple_xmlnode_insert_data(child, value, -1);
}
if((int_value = purple_proxy_info_get_port(proxy_info)) != 0) {
g_snprintf(buf, sizeof(buf), "%d", int_value);
child = purple_xmlnode_new_child(node, "port");
purple_xmlnode_insert_data(child, buf, -1);
}
if((value = purple_proxy_info_get_username(proxy_info)) != NULL) {
child = purple_xmlnode_new_child(node, "username");
purple_xmlnode_insert_data(child, value, -1);
}
if((value = purple_proxy_info_get_password(proxy_info)) != NULL) {
child = purple_xmlnode_new_child(node, "password");
purple_xmlnode_insert_data(child, value, -1);
}
return node;
}
static PurpleXmlNode *
current_error_to_xmlnode(PurpleConnectionErrorInfo *err) {
PurpleXmlNode *node, *child;
char type_str[3];
node = purple_xmlnode_new("current_error");
if(err == NULL) {
return node;
}
/* It doesn't make sense to have transient errors persist across a
* restart.
*/
if(!purple_connection_error_is_fatal (err->type)) {
return node;
}
child = purple_xmlnode_new_child(node, "type");
g_snprintf(type_str, sizeof(type_str), "%u", err->type);
purple_xmlnode_insert_data(child, type_str, -1);
child = purple_xmlnode_new_child(node, "description");
if(err->description) {
char *utf8ized = purple_utf8_try_convert(err->description);
if(utf8ized == NULL) {
utf8ized = g_utf8_make_valid(err->description, -1);
}
purple_xmlnode_insert_data(child, utf8ized, -1);
g_free(utf8ized);
}
return node;
}
static void
setting_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
{
const char *name;
PurpleAccountSetting *setting;
PurpleXmlNode *node, *child;
char buf[21];
name = (const char *)key;
setting = (PurpleAccountSetting *)value;
node = (PurpleXmlNode *)user_data;
child = purple_xmlnode_new_child(node, "setting");
purple_xmlnode_set_attrib(child, "name", name);
if(G_VALUE_HOLDS_INT(&setting->value)) {
purple_xmlnode_set_attrib(child, "type", "int");
g_snprintf(buf, sizeof(buf), "%d", g_value_get_int(&setting->value));
purple_xmlnode_insert_data(child, buf, -1);
} else if(G_VALUE_HOLDS_STRING(&setting->value) &&
g_value_get_string(&setting->value) != NULL)
{
purple_xmlnode_set_attrib(child, "type", "string");
purple_xmlnode_insert_data(child, g_value_get_string(&setting->value),
-1);
} else if (G_VALUE_HOLDS_BOOLEAN(&setting->value)) {
purple_xmlnode_set_attrib(child, "type", "bool");
g_snprintf(buf, sizeof(buf), "%d",
g_value_get_boolean(&setting->value));
purple_xmlnode_insert_data(child, buf, -1);
}
}
PurpleXmlNode *
_purple_account_to_xmlnode(PurpleAccount *account) {
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
PurpleXmlNode *node, *child;
gchar *data = NULL;
const char *tmp;
PurpleProxyInfo *proxy_info;
node = purple_xmlnode_new("account");
tmp = purple_contact_info_get_id(info);
if(tmp != NULL) {
child = purple_xmlnode_new_child(node, "id");
purple_xmlnode_insert_data(child, tmp, -1);
}
child = purple_xmlnode_new_child(node, "protocol");
purple_xmlnode_insert_data(child, purple_account_get_protocol_id(account),
-1);
child = purple_xmlnode_new_child(node, "name");
purple_xmlnode_insert_data(child, purple_contact_info_get_username(info),
-1);
child = purple_xmlnode_new_child(node, "require_password");
data = g_strdup_printf("%d", account->require_password);
purple_xmlnode_insert_data(child, data, -1);
g_clear_pointer(&data, g_free);
child = purple_xmlnode_new_child(node, "enabled");
data = g_strdup_printf("%d", account->enabled);
purple_xmlnode_insert_data(child, data, -1);
g_clear_pointer(&data, g_free);
tmp = purple_contact_info_get_alias(info);
if(tmp != NULL) {
child = purple_xmlnode_new_child(node, "alias");
purple_xmlnode_insert_data(child, tmp, -1);
}
if((tmp = purple_account_get_user_info(account)) != NULL) {
/* TODO: Do we need to call purple_str_strip_char(tmp, '\r') here? */
child = purple_xmlnode_new_child(node, "user-info");
purple_xmlnode_insert_data(child, tmp, -1);
}
if(g_hash_table_size(account->settings) > 0) {
child = purple_xmlnode_new_child(node, "settings");
g_hash_table_foreach(account->settings, setting_to_xmlnode, child);
}
if((proxy_info = purple_account_get_proxy_info(account)) != NULL) {
child = proxy_settings_to_xmlnode(proxy_info);
purple_xmlnode_insert_child(node, child);
}
child = current_error_to_xmlnode(account->error);
purple_xmlnode_insert_child(node, child);
return node;
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
static void
purple_account_set_property(GObject *obj, guint param_id, const GValue *value,
GParamSpec *pspec)
{
PurpleAccount *account = PURPLE_ACCOUNT(obj);
switch(param_id) {
case PROP_REQUIRE_PASSWORD:
purple_account_set_require_password(account,
g_value_get_boolean(value));
break;
case PROP_ENABLED:
purple_account_set_enabled(account, g_value_get_boolean(value));
break;
case PROP_CONNECTION:
purple_account_set_connection(account, g_value_get_object(value));
break;
case PROP_PROTOCOL_ID:
purple_account_set_protocol_id(account, g_value_get_string(value));
break;
case PROP_USER_INFO:
purple_account_set_user_info(account, g_value_get_string(value));
break;
case PROP_BUDDY_ICON_PATH:
purple_account_set_buddy_icon_path(account,
g_value_get_string(value));
break;
case PROP_REMEMBER_PASSWORD:
purple_account_set_remember_password(account,
g_value_get_boolean(value));
break;
case PROP_PROXY_INFO:
purple_account_set_proxy_info(account, g_value_get_object(value));
break;
case PROP_ERROR:
purple_account_set_error(account, g_value_get_boxed(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
purple_account_get_property(GObject *obj, guint param_id, GValue *value,
GParamSpec *pspec)
{
PurpleAccount *account = PURPLE_ACCOUNT(obj);
switch(param_id) {
case PROP_REQUIRE_PASSWORD:
g_value_set_boolean(value,
purple_account_get_require_password(account));
break;
case PROP_ENABLED:
g_value_set_boolean(value, purple_account_get_enabled(account));
break;
case PROP_CONNECTION:
g_value_set_object(value, purple_account_get_connection(account));
break;
case PROP_PROTOCOL_ID:
g_value_set_string(value, purple_account_get_protocol_id(account));
break;
case PROP_USER_INFO:
g_value_set_string(value, purple_account_get_user_info(account));
break;
case PROP_BUDDY_ICON_PATH:
g_value_set_string(value,
purple_account_get_buddy_icon_path(account));
break;
case PROP_REMEMBER_PASSWORD:
g_value_set_boolean(value,
purple_account_get_remember_password(account));
break;
case PROP_PROXY_INFO:
g_value_set_object(value, purple_account_get_proxy_info(account));
break;
case PROP_ERROR:
g_value_set_boxed(value, purple_account_get_error(account));
break;
case PROP_CONNECTED:
g_value_set_boolean(value, purple_account_is_connected(account));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
purple_account_init(PurpleAccount *account) {
account->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
delete_setting);
}
static void
purple_account_constructed(GObject *object) {
PurpleAccount *account = PURPLE_ACCOUNT(object);
gchar *username, *protocol_id;
const char *id = NULL;
PurpleProtocol *protocol = NULL;
PurpleProtocolManager *manager = NULL;
PurpleStatusType *status_type;
G_OBJECT_CLASS(purple_account_parent_class)->constructed(object);
/* If we didn't get an id, checksum the protocol id and the username. */
id = purple_contact_info_get_id(PURPLE_CONTACT_INFO(object));
if(purple_strempty(id)) {
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
GChecksum *checksum = NULL;
const char *username = NULL;
checksum = g_checksum_new(G_CHECKSUM_SHA256);
username = purple_contact_info_get_username(info);
g_checksum_update(checksum, (const guchar *)account->protocol_id, -1);
g_checksum_update(checksum, (const guchar *)username, -1);
purple_contact_info_set_id(info, g_checksum_get_string(checksum));
g_checksum_free(checksum);
}
g_object_get(object,
"username", &username,
"protocol-id", &protocol_id,
NULL);
manager = purple_protocol_manager_get_default();
protocol = purple_protocol_manager_find(manager, protocol_id);
if(protocol == NULL) {
g_free(username);
g_free(protocol_id);
return;
}
purple_account_set_status_types(account,
purple_protocol_get_status_types(protocol, account));
account->presence = PURPLE_PRESENCE(purple_account_presence_new(account));
status_type = purple_account_get_status_type_with_primitive(account,
PURPLE_STATUS_AVAILABLE);
if(status_type != NULL) {
purple_presence_set_status_active(account->presence,
purple_status_type_get_id(status_type),
TRUE);
} else {
purple_presence_set_status_active(account->presence,
"offline",
TRUE);
}
g_free(username);
g_free(protocol_id);
/* Connect to our own notify signal so we can update accounts.xml. */
g_signal_connect(object, "notify",
G_CALLBACK(purple_account_changed_cb), NULL);
}
static void
purple_account_dispose(GObject *object) {
PurpleAccount *account = PURPLE_ACCOUNT(object);
if(!purple_account_is_disconnected(account)) {
purple_account_disconnect(account);
}
g_clear_object(&account->gc);
g_clear_object(&account->presence);
G_OBJECT_CLASS(purple_account_parent_class)->dispose(object);
}
static void
purple_account_finalize(GObject *object) {
PurpleAccount *account = PURPLE_ACCOUNT(object);
purple_debug_info("account", "Destroying account %p", account);
purple_account_free_notify_settings(account);
purple_account_set_status_types(account, NULL);
g_clear_object(&account->proxy_info);
g_clear_pointer(&account->error, purple_connection_error_info_free);
g_clear_object(&account->error_notification);
g_free(account->user_info);
g_free(account->buddy_icon_path);
g_free(account->protocol_id);
g_hash_table_destroy(account->settings);
G_OBJECT_CLASS(purple_account_parent_class)->finalize(object);
}
static void
purple_account_class_init(PurpleAccountClass *klass) {
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
obj_class->constructed = purple_account_constructed;
obj_class->dispose = purple_account_dispose;
obj_class->finalize = purple_account_finalize;
obj_class->get_property = purple_account_get_property;
obj_class->set_property = purple_account_set_property;
/**
* PurpleAccount:require-password:
*
* Whether or not this account should require a password. This is only used
* if the [class@Purple.Protocol] that this account is for allows optional
* passwords.
*
* Since: 3.0
*/
properties[PROP_REQUIRE_PASSWORD] = g_param_spec_boolean(
"require-password", "require-password",
"Whether or not to require a password for this account.",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:user-info:
*
* The user information or profile for the account.
*
* Since: 3.0
*/
properties[PROP_USER_INFO] = g_param_spec_string(
"user-info", "user-info",
"Detailed user information for the account.",
NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:buddy-icon-path:
*
* The path to the file to use as the avatar for this account.
*
* Since: 3.0
*/
properties[PROP_BUDDY_ICON_PATH] = g_param_spec_string(
"buddy-icon-path", "buddy-icon-path",
"Path to the buddyicon for the account.",
NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:enabled:
*
* Whether or not this account should track the user's global status.
*
* Since: 3.0
*/
properties[PROP_ENABLED] = g_param_spec_boolean(
"enabled", "enabled",
"Whether the account is enabled or not.",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:remember-password:
*
* Whether or not the password for this account should be stored in the
* configured [class@CredentialProvider].
*
* Since: 3.0
*/
properties[PROP_REMEMBER_PASSWORD] = g_param_spec_boolean(
"remember-password", "remember-password",
"Whether to remember and store the password for this account.",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:connection:
*
* The [class@Connection] object for this account. This will be %NULL when
* the account is offline.
*
* Since: 3.0
*/
properties[PROP_CONNECTION] = g_param_spec_object(
"connection", "connection",
"The connection for the account.",
PURPLE_TYPE_CONNECTION,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:protocol-id:
*
* The identifier of the protocol that this account is using.
*
* Since: 3.0
*/
properties[PROP_PROTOCOL_ID] = g_param_spec_string(
"protocol-id", "protocol-id",
"ID of the protocol that is responsible for the account.",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:proxy-info:
*
* The [class@ProxyInfo] for this account.
*
* Since: 3.0
*/
properties[PROP_PROXY_INFO] = g_param_spec_object(
"proxy-info", "proxy-info",
"The PurpleProxyInfo for this account.",
PURPLE_TYPE_PROXY_INFO,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:error:
*
* The [type@GLib.Error] of the account. This is set when an account enters
* an error state and is automatically cleared when a connection attempt is
* made.
*
* Setting this will not disconnect an account, but this will be set when
* there is a connection failure.
*
* Since: 3.0
*/
properties[PROP_ERROR] = g_param_spec_boxed(
"error", "error",
"The connection error info of the account",
PURPLE_TYPE_CONNECTION_ERROR_INFO,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PurpleAccount:connected:
*
* Whether or not the account is connected.
*
* Since: 3.0
*/
properties[PROP_CONNECTED] = g_param_spec_boolean(
"connected", "connected",
"Whether or not the account is connected.",
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
/**
* PurpleAccount::setting-changed:
* @account: The account whose setting changed.
* @name: The name of the setting that changed.
*
* The ::setting-changed signal is emitted whenever an account setting is
* changed.
*
* This signal supports details, so you can be notified when a single
* setting changes. For example, say there's a setting named `foo`,
* connecting to `setting-changed::foo` will only be called when the `foo`
* setting is changed.
*
* Since: 3.0
*/
signals[SIG_SETTING_CHANGED] = g_signal_new_class_handler(
"setting-changed",
G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
NULL,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
G_TYPE_STRING);
/**
* PurpleAccount::connected:
* @account: The account instance.
*
* This is emitted when the [property@Account:connection]'s
* [property@Connection:state] has changed to
* %PURPLE_CONNECTION_STATE_CONNECTED.
*
* Since: 3.0
*/
signals[SIG_CONNECTED] = g_signal_new_class_handler(
"connected",
G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_LAST,
NULL,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
/**
* PurpleAccount::disconnected:
* @account: The account instance.
*
* This is emitted when the [property@Account:connection]'s
* [property@Connection:state] has changed to
* %PURPLE_CONNECTION_STATE_DISCONNECTED.
*
* Since: 3.0
*/
signals[SIG_DISCONNECTED] = g_signal_new_class_handler(
"disconnected",
G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_LAST,
NULL,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
}
/******************************************************************************
* Private API
*****************************************************************************/
/* This is a temporary method that the deserializer can call to set the
* enabled property without bringing the account online.
*/
void
purple_account_set_enabled_plain(PurpleAccount *account, gboolean enabled) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
account->enabled = enabled;
}
/******************************************************************************
* Public API
*****************************************************************************/
PurpleAccount *
purple_account_new(const gchar *username, const gchar *protocol_id) {
g_return_val_if_fail(username != NULL, NULL);
g_return_val_if_fail(protocol_id != NULL, NULL);
return g_object_new(
PURPLE_TYPE_ACCOUNT,
"username", username,
"protocol-id", protocol_id,
"enabled", FALSE,
NULL);
}
void
purple_account_connect(PurpleAccount *account)
{
PurpleProtocol *protocol = NULL;
const char *username = NULL;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
purple_account_set_error(account, NULL);
username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
if(!purple_account_get_enabled(account)) {
purple_debug_info("account",
"Account %s not enabled, not connecting.\n",
username);
return;
}
protocol = purple_account_get_protocol(account);
if(protocol == NULL) {
gchar *message;
message = g_strdup_printf(_("Missing protocol for %s"), username);
purple_notify_error(account, _("Connection Error"), message,
NULL, purple_request_cpar_from_account(account));
g_free(message);
return;
}
purple_protocol_can_connect_async(protocol, account, NULL,
purple_account_can_connect_cb, account);
}
void
purple_account_disconnect(PurpleAccount *account)
{
GError *error = NULL;
const char *username;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(!purple_account_is_disconnecting(account));
g_return_if_fail(!purple_account_is_disconnected(account));
username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
purple_debug_info("account", "Disconnecting account %s (%p)\n",
username ? username : "(null)", account);
account->disconnecting = TRUE;
if(!purple_connection_disconnect(account->gc, &error)) {
g_warning("error while disconnecting account %s (%s): %s",
username,
purple_account_get_protocol_id(account),
(error != NULL) ? error->message : "unknown error");
g_clear_error(&error);
}
purple_account_set_connection(account, NULL);
account->disconnecting = FALSE;
}
gboolean
purple_account_is_disconnecting(PurpleAccount *account)
{
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), TRUE);
return account->disconnecting;
}
void
purple_account_request_close_with_account(PurpleAccount *account) {
PurpleNotificationManager *manager = NULL;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
manager = purple_notification_manager_get_default();
purple_notification_manager_remove_with_account(manager, account, FALSE);
}
void
purple_account_request_password(PurpleAccount *account, GCallback ok_cb,
GCallback cancel_cb, void *user_data)
{
gchar *primary;
const gchar *username;
PurpleRequestGroup *group;
PurpleRequestField *field;
PurpleRequestPage *page;
/* Close any previous password request windows */
purple_request_close_with_handle(account);
username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
primary = g_strdup_printf(_("Enter password for %s (%s)"), username,
purple_account_get_protocol_name(account));
page = purple_request_page_new();
group = purple_request_group_new(NULL);
purple_request_page_add_group(page, group);
field = purple_request_field_string_new("password", _("Enter Password"),
NULL, FALSE);
purple_request_field_string_set_masked(PURPLE_REQUEST_FIELD_STRING(field),
TRUE);
purple_request_field_set_required(field, TRUE);
purple_request_group_add_field(group, field);
field = purple_request_field_bool_new("remember", _("Save password"),
FALSE);
purple_request_group_add_field(group, field);
purple_request_fields(account, NULL, primary, NULL, page, _("OK"), ok_cb,
_("Cancel"), cancel_cb,
purple_request_cpar_from_account(account),
user_data);
g_free(primary);
}
void
purple_account_request_change_password(PurpleAccount *account)
{
PurpleRequestPage *page;
PurpleRequestGroup *group;
PurpleRequestField *field;
PurpleConnection *gc;
PurpleProtocol *protocol = NULL;
char primary[256];
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(purple_account_is_connected(account));
gc = purple_account_get_connection(account);
if(gc != NULL) {
protocol = purple_connection_get_protocol(gc);
}
page = purple_request_page_new();
group = purple_request_group_new(NULL);
purple_request_page_add_group(page, group);
field = purple_request_field_string_new("password", _("Original password"),
NULL, FALSE);
purple_request_field_string_set_masked(PURPLE_REQUEST_FIELD_STRING(field),
TRUE);
if(!protocol || !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
{
purple_request_field_set_required(field, TRUE);
}
purple_request_group_add_field(group, field);
field = purple_request_field_string_new("new_password_1",
_("New password"), NULL, FALSE);
purple_request_field_string_set_masked(PURPLE_REQUEST_FIELD_STRING(field),
TRUE);
if(!protocol || !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
{
purple_request_field_set_required(field, TRUE);
}
purple_request_group_add_field(group, field);
field = purple_request_field_string_new("new_password_2",
_("New password (again)"), NULL,
FALSE);
purple_request_field_string_set_masked(PURPLE_REQUEST_FIELD_STRING(field),
TRUE);
if(!protocol || !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
{
purple_request_field_set_required(field, TRUE);
}
purple_request_group_add_field(group, field);
g_snprintf(primary, sizeof(primary), _("Change password for %s"),
purple_contact_info_get_username(PURPLE_CONTACT_INFO(account)));
/* I'm sticking this somewhere in the code: bologna */
purple_request_fields(purple_account_get_connection(account), NULL,
primary,
_("Please enter your current password and your new "
"password."),
page, _("OK"), G_CALLBACK(change_password_cb),
_("Cancel"), NULL,
purple_request_cpar_from_account(account), account);
}
void
purple_account_request_change_user_info(PurpleAccount *account) {
PurpleConnection *gc;
char primary[256];
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(purple_account_is_connected(account));
gc = purple_account_get_connection(account);
g_snprintf(primary, sizeof(primary),
_("Change user information for %s"),
purple_contact_info_get_username(PURPLE_CONTACT_INFO(account)));
purple_request_input(gc, _("Set User Info"), primary, NULL,
purple_account_get_user_info(account),
TRUE, FALSE, ((gc != NULL) &&
(purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_HTML) ? "html" : NULL),
_("Save"), G_CALLBACK(set_user_info_cb),
_("Cancel"), NULL,
purple_request_cpar_from_account(account),
account);
}
void
purple_account_set_user_info(PurpleAccount *account, const char *user_info) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_free(account->user_info);
account->user_info = g_strdup(user_info);
g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_USER_INFO]);
purple_accounts_schedule_save();
}
void
purple_account_set_buddy_icon_path(PurpleAccount *account, const char *path) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_free(account->buddy_icon_path);
account->buddy_icon_path = g_strdup(path);
g_object_notify_by_pspec(G_OBJECT(account),
properties[PROP_BUDDY_ICON_PATH]);
purple_accounts_schedule_save();
}
void
purple_account_set_protocol_id(PurpleAccount *account, const char *protocol_id)
{
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(protocol_id != NULL);
g_free(account->protocol_id);
account->protocol_id = g_strdup(protocol_id);
g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_PROTOCOL_ID]);
purple_accounts_schedule_save();
}
void
purple_account_set_connection(PurpleAccount *account, PurpleConnection *gc) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
/* If we got the same pointer, bail. */
if(account->gc == gc) {
return;
}
/* Remove our old signal handler. */
if(PURPLE_IS_CONNECTION(account->gc)) {
g_signal_handlers_disconnect_by_func(account->gc,
purple_account_connection_state_cb,
account);
}
if(g_set_object(&account->gc, gc)) {
GObject *obj = G_OBJECT(account);
if(PURPLE_IS_CONNECTION(account->gc)) {
g_signal_connect(account->gc, "notify::state",
G_CALLBACK(purple_account_connection_state_cb),
account);
}
g_object_freeze_notify(obj);
g_object_notify_by_pspec(obj, properties[PROP_CONNECTION]);
g_object_notify_by_pspec(obj, properties[PROP_CONNECTED]);
g_object_thaw_notify(obj);
} else {
/* If the set didn't work, restore our old signal. */
if(PURPLE_IS_CONNECTION(account->gc)) {
g_signal_connect(account->gc, "notify::state",
G_CALLBACK(purple_account_connection_state_cb),
account);
}
}
}
void
purple_account_set_remember_password(PurpleAccount *account, gboolean value) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
account->remember_pass = value;
g_object_notify_by_pspec(G_OBJECT(account),
properties[PROP_REMEMBER_PASSWORD]);
purple_accounts_schedule_save();
}
void
purple_account_set_enabled(PurpleAccount *account, gboolean value) {
PurpleConnection *gc;
gboolean was_enabled = FALSE;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
was_enabled = account->enabled;
account->enabled = value;
if(was_enabled != value) {
g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_ENABLED]);
}
gc = purple_account_get_connection(account);
if(gc != NULL && _purple_connection_wants_to_die(gc)) {
return;
}
if(value && purple_presence_is_online(account->presence)) {
purple_account_connect(account);
} else if (!value && !purple_account_is_disconnected(account)) {
purple_account_disconnect(account);
}
purple_accounts_schedule_save();
}
void
purple_account_set_proxy_info(PurpleAccount *account, PurpleProxyInfo *info) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
if(g_set_object(&account->proxy_info, info)) {
g_object_notify_by_pspec(G_OBJECT(account),
properties[PROP_PROXY_INFO]);
purple_accounts_schedule_save();
}
}
void
purple_account_set_status_types(PurpleAccount *account, GList *status_types) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
/* Out with the old... */
g_list_free_full(account->status_types,
(GDestroyNotify)purple_status_type_destroy);
/* In with the new... */
account->status_types = status_types;
}
void
purple_account_set_status(PurpleAccount *account, const char *status_id,
gboolean active, ...)
{
GHashTable *attrs;
va_list args;
va_start(args, active);
attrs = purple_attrs_from_vargs(args);
purple_account_set_status_attrs(account, status_id, active, attrs);
g_hash_table_destroy(attrs);
va_end(args);
}
void
purple_account_set_status_attrs(PurpleAccount *account, const char *status_id,
gboolean active, GHashTable *attrs)
{
PurpleStatus *status;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(status_id != NULL);
status = purple_account_get_status(account, status_id);
if(status == NULL) {
PurpleContactInfo *info = PURPLE_CONTACT_INFO(account);
purple_debug_error("account",
"Invalid status ID '%s' for account %s (%s)\n",
status_id,
purple_contact_info_get_username(info),
purple_account_get_protocol_id(account));
return;
}
if(active || purple_status_is_independent(status)) {
purple_status_set_active_with_attributes(status, active, attrs);
}
/*
* Our current statuses are saved to accounts.xml (so that when we
* reconnect, we go back to the previous status).
*/
purple_accounts_schedule_save();
}
void
purple_account_set_int(PurpleAccount *account, const char *name, int value) {
PurpleAccountSetting *setting;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(name != NULL);
setting = g_new0(PurpleAccountSetting, 1);
g_value_init(&setting->value, G_TYPE_INT);
g_value_set_int(&setting->value, value);
g_hash_table_insert(account->settings, g_strdup(name), setting);
purple_account_setting_changed_emit(account, name);
purple_accounts_schedule_save();
}
void
purple_account_set_string(PurpleAccount *account, const char *name,
const char *value)
{
PurpleAccountSetting *setting;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(name != NULL);
setting = g_new0(PurpleAccountSetting, 1);
g_value_init(&setting->value, G_TYPE_STRING);
g_value_set_string(&setting->value, value);
g_hash_table_insert(account->settings, g_strdup(name), setting);
purple_account_setting_changed_emit(account, name);
purple_accounts_schedule_save();
}
void
purple_account_set_bool(PurpleAccount *account, const char *name,
gboolean value)
{
PurpleAccountSetting *setting;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(name != NULL);
setting = g_new0(PurpleAccountSetting, 1);
g_value_init(&setting->value, G_TYPE_BOOLEAN);
g_value_set_boolean(&setting->value, value);
g_hash_table_insert(account->settings, g_strdup(name), setting);
purple_account_setting_changed_emit(account, name);
purple_accounts_schedule_save();
}
gboolean
purple_account_is_connected(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
return (purple_account_get_state(account) == PURPLE_CONNECTION_STATE_CONNECTED);
}
gboolean
purple_account_is_connecting(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
return (purple_account_get_state(account) == PURPLE_CONNECTION_STATE_CONNECTING);
}
gboolean
purple_account_is_disconnected(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
return (purple_account_get_state(account) == PURPLE_CONNECTION_STATE_DISCONNECTED);
}
const char *
purple_account_get_user_info(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return account->user_info;
}
const char *
purple_account_get_buddy_icon_path(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return account->buddy_icon_path;
}
const char *
purple_account_get_protocol_id(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return account->protocol_id;
}
PurpleProtocol *
purple_account_get_protocol(PurpleAccount *account) {
PurpleProtocolManager *manager = NULL;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
manager = purple_protocol_manager_get_default();
return purple_protocol_manager_find(manager, account->protocol_id);
}
const char *
purple_account_get_protocol_name(PurpleAccount *account) {
PurpleProtocol *p;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
p = purple_account_get_protocol(account);
return (p && purple_protocol_get_name(p) ?
_(purple_protocol_get_name(p)) : _("Unknown"));
}
PurpleConnection *
purple_account_get_connection(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return account->gc;
}
gboolean
purple_account_get_remember_password(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
return account->remember_pass;
}
gboolean
purple_account_get_enabled(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
return account->enabled;
}
PurpleProxyInfo *
purple_account_get_proxy_info(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return account->proxy_info;
}
PurpleStatus *
purple_account_get_active_status(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return purple_presence_get_active_status(account->presence);
}
PurpleStatus *
purple_account_get_status(PurpleAccount *account, const char *status_id) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
g_return_val_if_fail(status_id != NULL, NULL);
return purple_presence_get_status(account->presence, status_id);
}
PurpleStatusType *
purple_account_get_status_type(PurpleAccount *account, const char *id) {
GList *l;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
g_return_val_if_fail(id != NULL, NULL);
for (l = purple_account_get_status_types(account); l != NULL; l = l->next)
{
PurpleStatusType *status_type = (PurpleStatusType *)l->data;
if(purple_strequal(purple_status_type_get_id(status_type), id)) {
return status_type;
}
}
return NULL;
}
PurpleStatusType *
purple_account_get_status_type_with_primitive(PurpleAccount *account,
PurpleStatusPrimitive primitive)
{
GList *l;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
for(l = purple_account_get_status_types(account); l != NULL; l = l->next) {
PurpleStatusType *status_type = (PurpleStatusType *)l->data;
if(purple_status_type_get_primitive(status_type) == primitive) {
return status_type;
}
}
return NULL;
}
PurplePresence *
purple_account_get_presence(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return account->presence;
}
gboolean
purple_account_is_status_active(PurpleAccount *account,
const char *status_id)
{
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
g_return_val_if_fail(status_id != NULL, FALSE);
return purple_presence_is_status_active(account->presence, status_id);
}
GList *
purple_account_get_status_types(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return account->status_types;
}
int
purple_account_get_int(PurpleAccount *account, const char *name,
int default_value)
{
PurpleAccountSetting *setting;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(name != NULL, default_value);
setting = g_hash_table_lookup(account->settings, name);
if(setting == NULL) {
return default_value;
}
g_return_val_if_fail(G_VALUE_HOLDS_INT(&setting->value), default_value);
return g_value_get_int(&setting->value);
}
const char *
purple_account_get_string(PurpleAccount *account, const char *name,
const char *default_value)
{
PurpleAccountSetting *setting;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(name != NULL, default_value);
setting = g_hash_table_lookup(account->settings, name);
if(setting == NULL) {
return default_value;
}
g_return_val_if_fail(G_VALUE_HOLDS_STRING(&setting->value), default_value);
return g_value_get_string(&setting->value);
}
gboolean
purple_account_get_bool(PurpleAccount *account, const char *name,
gboolean default_value)
{
PurpleAccountSetting *setting;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), default_value);
g_return_val_if_fail(name != NULL, default_value);
setting = g_hash_table_lookup(account->settings, name);
if(setting == NULL) {
return default_value;
}
g_return_val_if_fail(G_VALUE_HOLDS_BOOLEAN(&setting->value), default_value);
return g_value_get_boolean(&setting->value);
}
void
purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy,
const char *message)
{
PurpleProtocol *protocol = NULL;
PurpleConnection *gc;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
g_return_if_fail(PURPLE_IS_BUDDY(buddy));
gc = purple_account_get_connection(account);
if(gc != NULL) {
protocol = purple_connection_get_protocol(gc);
}
if(PURPLE_IS_PROTOCOL_SERVER(protocol)) {
PurpleGroup *group = purple_buddy_get_group(buddy);
purple_protocol_server_add_buddy(PURPLE_PROTOCOL_SERVER(protocol), gc,
buddy, group, message);
}
}
void
purple_account_add_buddies(PurpleAccount *account, GList *buddies,
const char *message)
{
PurpleProtocol *protocol = NULL;
PurpleConnection *gc = purple_account_get_connection(account);
if(gc != NULL) {
protocol = purple_connection_get_protocol(gc);
}
if(PURPLE_IS_PROTOCOL_SERVER(protocol)) {
PurpleProtocolServer *server = PURPLE_PROTOCOL_SERVER(protocol);
for(GList *b = buddies; b != NULL; b = b->next) {
PurpleBuddy *buddy = b->data;
purple_protocol_server_add_buddy(server, gc, buddy,
purple_buddy_get_group(buddy),
message);
}
}
}
void
purple_account_remove_buddy(PurpleAccount *account, PurpleBuddy *buddy,
PurpleGroup *group)
{
PurpleProtocol *protocol = NULL;
PurpleConnection *gc = purple_account_get_connection(account);
if(gc != NULL) {
protocol = purple_connection_get_protocol(gc);
}
if(PURPLE_IS_PROTOCOL_SERVER(protocol)) {
purple_protocol_server_remove_buddy(PURPLE_PROTOCOL_SERVER(protocol),
gc, buddy, group);
}
}
void
purple_account_remove_buddies(PurpleAccount *account, GList *buddies,
GList *groups)
{
PurpleProtocol *protocol = NULL;
PurpleConnection *gc = purple_account_get_connection(account);
if(gc != NULL) {
protocol = purple_connection_get_protocol(gc);
}
if(PURPLE_IS_PROTOCOL_SERVER(protocol)) {
purple_protocol_server_remove_buddies(PURPLE_PROTOCOL_SERVER(protocol),
gc, buddies, groups);
}
}
void
purple_account_remove_group(PurpleAccount *account, PurpleGroup *group) {
PurpleProtocol *protocol = NULL;
PurpleConnection *gc = purple_account_get_connection(account);
if(gc != NULL) {
protocol = purple_connection_get_protocol(gc);
}
if(PURPLE_IS_PROTOCOL_SERVER(protocol)) {
purple_protocol_server_remove_group(PURPLE_PROTOCOL_SERVER(protocol),
gc, group);
}
}
void
purple_account_change_password(PurpleAccount *account, const char *orig_pw,
const char *new_pw)
{
PurpleCredentialManager *manager = NULL;
PurpleProtocol *protocol = NULL;
PurpleConnection *gc = purple_account_get_connection(account);
/* just going to fire and forget this for now as not many protocols even
* implement the change password stuff.
*/
manager = purple_credential_manager_get_default();
purple_credential_manager_write_password_async(manager, account, new_pw,
NULL, NULL, NULL);
if(gc != NULL) {
protocol = purple_connection_get_protocol(gc);
}
if(PURPLE_IS_PROTOCOL_SERVER(protocol)) {
purple_protocol_server_change_passwd(PURPLE_PROTOCOL_SERVER(protocol),
gc, orig_pw, new_pw);
}
}
gboolean
purple_account_supports_offline_message(PurpleAccount *account,
PurpleBuddy *buddy)
{
PurpleConnection *gc;
PurpleProtocol *protocol = NULL;
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), FALSE);
gc = purple_account_get_connection(account);
if(gc == NULL) {
return FALSE;
}
protocol = purple_connection_get_protocol(gc);
if(!protocol) {
return FALSE;
}
return purple_protocol_client_offline_message(PURPLE_PROTOCOL_CLIENT(protocol),
buddy);
}
const PurpleConnectionErrorInfo *
purple_account_get_error(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return account->error;
}
void
purple_account_set_error(PurpleAccount *account,
PurpleConnectionErrorInfo *info)
{
PurpleNotificationManager *manager = NULL;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
if(info == account->error) {
return;
}
g_clear_pointer(&account->error,
purple_connection_error_info_free);
account->error = info;
manager = purple_notification_manager_get_default();
if(PURPLE_IS_NOTIFICATION(account->error_notification)) {
purple_notification_manager_remove(manager,
account->error_notification);
g_clear_object(&account->error_notification);
}
if(info != NULL) {
account->error_notification =
purple_notification_new_from_connection_error(account, info);
purple_notification_manager_add(manager, account->error_notification);
}
g_object_notify_by_pspec(G_OBJECT(account), properties[PROP_ERROR]);
purple_accounts_schedule_save();
}
void
purple_account_set_require_password(PurpleAccount *account,
gboolean require_password)
{
gboolean old = FALSE;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
old = account->require_password;
account->require_password = require_password;
if(old != require_password) {
g_object_notify_by_pspec(G_OBJECT(account),
properties[PROP_REQUIRE_PASSWORD]);
}
}
gboolean
purple_account_get_require_password(PurpleAccount *account) {
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
return account->require_password;
}
void
purple_account_freeze_notify_settings(PurpleAccount *account) {
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
G_LOCK(setting_notify_lock);
if(account->freeze_queue == NULL) {
account->freeze_queue = g_slice_new0(PurpleAccountSettingFreezeQueue);
}
account->freeze_queue->ref_count++;
G_UNLOCK(setting_notify_lock);
}
void
purple_account_thaw_notify_settings(PurpleAccount *account) {
GSList *names = NULL;
g_return_if_fail(PURPLE_IS_ACCOUNT(account));
G_LOCK(setting_notify_lock);
if(G_UNLIKELY(account->freeze_queue->ref_count == 0)) {
G_UNLOCK(setting_notify_lock);
g_critical("purple_account_settings_thaw_notify called for account %s "
"(%s) when not frozen",
purple_contact_info_get_username(PURPLE_CONTACT_INFO(account)),
purple_account_get_protocol_id(account));
return;
}
account->freeze_queue->ref_count--;
if(account->freeze_queue->ref_count > 0) {
G_UNLOCK(setting_notify_lock);
return;
}
/* This was the last ref, so fire off the signals. */
names = account->freeze_queue->names;
while(names != NULL) {
char *name = names->data;
g_signal_emit(account, signals[SIG_SETTING_CHANGED],
g_quark_from_string(name), name);
names = g_slist_delete_link(names, names);
g_free(name);
}
account->freeze_queue->names = names;
purple_account_free_notify_settings(account);
G_UNLOCK(setting_notify_lock);
}