libpurple/protocol.c

branch
soc.2013.gobjectification.plugins
changeset 36543
a8c3fecee2d3
parent 36537
ca8057929836
child 36544
1bf8b6ef5aea
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocol.c	Sat Aug 17 23:27:20 2013 +0530
@@ -0,0 +1,756 @@
+/*
+ * purple
+ *
+ * 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 program 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 program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ *
+ */
+#include "internal.h"
+#include "accountopt.h"
+#include "conversation.h"
+#include "debug.h"
+#include "network.h"
+#include "notify.h"
+#include "protocol.h"
+#include "request.h"
+#include "util.h"
+
+static GHashTable *protocols = NULL;
+
+/**************************************************************************/
+/** @name Attention Type API                                              */
+/**************************************************************************/
+
+struct _PurpleAttentionType
+{
+	const char *name;                  /**< Shown in GUI elements */
+	const char *incoming_description;  /**< Shown when sent */
+	const char *outgoing_description;  /**< Shown when receied */
+	const char *icon_name;             /**< Icon to display (optional) */
+	const char *unlocalized_name;      /**< Unlocalized name for UIs needing it */
+};
+
+
+PurpleAttentionType *
+purple_attention_type_new(const char *ulname, const char *name,
+						const char *inc_desc, const char *out_desc)
+{
+	PurpleAttentionType *attn = g_new0(PurpleAttentionType, 1);
+
+	purple_attention_type_set_name(attn, name);
+	purple_attention_type_set_incoming_desc(attn, inc_desc);
+	purple_attention_type_set_outgoing_desc(attn, out_desc);
+	purple_attention_type_set_unlocalized_name(attn, ulname);
+
+	return attn;
+}
+
+
+void
+purple_attention_type_set_name(PurpleAttentionType *type, const char *name)
+{
+	g_return_if_fail(type != NULL);
+
+	type->name = name;
+}
+
+void
+purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc)
+{
+	g_return_if_fail(type != NULL);
+
+	type->incoming_description = desc;
+}
+
+void
+purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc)
+{
+	g_return_if_fail(type != NULL);
+
+	type->outgoing_description = desc;
+}
+
+void
+purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name)
+{
+	g_return_if_fail(type != NULL);
+
+	type->icon_name = name;
+}
+
+void
+purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname)
+{
+	g_return_if_fail(type != NULL);
+
+	type->unlocalized_name = ulname;
+}
+
+const char *
+purple_attention_type_get_name(const PurpleAttentionType *type)
+{
+	g_return_val_if_fail(type != NULL, NULL);
+
+	return type->name;
+}
+
+const char *
+purple_attention_type_get_incoming_desc(const PurpleAttentionType *type)
+{
+	g_return_val_if_fail(type != NULL, NULL);
+
+	return type->incoming_description;
+}
+
+const char *
+purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type)
+{
+	g_return_val_if_fail(type != NULL, NULL);
+
+	return type->outgoing_description;
+}
+
+const char *
+purple_attention_type_get_icon_name(const PurpleAttentionType *type)
+{
+	g_return_val_if_fail(type != NULL, NULL);
+
+	if(type->icon_name == NULL || *(type->icon_name) == '\0')
+		return NULL;
+
+	return type->icon_name;
+}
+
+const char *
+purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type)
+{
+	g_return_val_if_fail(type != NULL, NULL);
+
+	return type->unlocalized_name;
+}
+
+/**************************************************************************/
+/** @name Protocol Plugin API  */
+/**************************************************************************/
+void
+purple_prpl_got_account_idle(PurpleAccount *account, gboolean idle,
+						   time_t idle_time)
+{
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(purple_account_is_connected(account));
+
+	purple_presence_set_idle(purple_account_get_presence(account),
+						   idle, idle_time);
+}
+
+void
+purple_prpl_got_account_login_time(PurpleAccount *account, time_t login_time)
+{
+	PurplePresence *presence;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(purple_account_is_connected(account));
+
+	if (login_time == 0)
+		login_time = time(NULL);
+
+	presence = purple_account_get_presence(account);
+
+	purple_presence_set_login_time(presence, login_time);
+}
+
+void
+purple_prpl_got_account_status(PurpleAccount *account, const char *status_id, ...)
+{
+	PurplePresence *presence;
+	PurpleStatus *status;
+	va_list args;
+
+	g_return_if_fail(account   != NULL);
+	g_return_if_fail(status_id != NULL);
+	g_return_if_fail(purple_account_is_connected(account));
+
+	presence = purple_account_get_presence(account);
+	status   = purple_presence_get_status(presence, status_id);
+
+	g_return_if_fail(status != NULL);
+
+	va_start(args, status_id);
+	purple_status_set_active_with_attrs(status, TRUE, args);
+	va_end(args);
+}
+
+void
+purple_prpl_got_account_actions(PurpleAccount *account)
+{
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(purple_account_is_connected(account));
+
+	purple_signal_emit(purple_accounts_get_handle(), "account-actions-changed",
+	                   account);
+}
+
+void
+purple_prpl_got_user_idle(PurpleAccount *account, const char *name,
+		gboolean idle, time_t idle_time)
+{
+	PurplePresence *presence;
+	GSList *list;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(name    != NULL);
+	g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
+
+	if ((list = purple_blist_find_buddies(account, name)) == NULL)
+		return;
+
+	while (list) {
+		presence = purple_buddy_get_presence(list->data);
+		list = g_slist_delete_link(list, list);
+		purple_presence_set_idle(presence, idle, idle_time);
+	}
+}
+
+void
+purple_prpl_got_user_login_time(PurpleAccount *account, const char *name,
+		time_t login_time)
+{
+	GSList *list;
+	PurplePresence *presence;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(name    != NULL);
+
+	if ((list = purple_blist_find_buddies(account, name)) == NULL)
+		return;
+
+	if (login_time == 0)
+		login_time = time(NULL);
+
+	while (list) {
+		PurpleBuddy *buddy = list->data;
+		presence = purple_buddy_get_presence(buddy);
+		list = g_slist_delete_link(list, list);
+
+		if (purple_presence_get_login_time(presence) != login_time)
+		{
+			purple_presence_set_login_time(presence, login_time);
+
+			purple_signal_emit(purple_blist_get_handle(), "buddy-got-login-time", buddy);
+		}
+	}
+}
+
+void
+purple_prpl_got_user_status(PurpleAccount *account, const char *name,
+		const char *status_id, ...)
+{
+	GSList *list, *l;
+	PurpleBuddy *buddy;
+	PurplePresence *presence;
+	PurpleStatus *status;
+	PurpleStatus *old_status;
+	va_list args;
+
+	g_return_if_fail(account   != NULL);
+	g_return_if_fail(name      != NULL);
+	g_return_if_fail(status_id != NULL);
+	g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
+
+	if((list = purple_blist_find_buddies(account, name)) == NULL)
+		return;
+
+	for(l = list; l != NULL; l = l->next) {
+		buddy = l->data;
+
+		presence = purple_buddy_get_presence(buddy);
+		status   = purple_presence_get_status(presence, status_id);
+
+		if(NULL == status)
+			/*
+			 * TODO: This should never happen, right?  We should call
+			 *       g_warning() or something.
+			 */
+			continue;
+
+		old_status = purple_presence_get_active_status(presence);
+
+		va_start(args, status_id);
+		purple_status_set_active_with_attrs(status, TRUE, args);
+		va_end(args);
+
+		purple_buddy_update_status(buddy, old_status);
+	}
+
+	g_slist_free(list);
+
+	/* The buddy is no longer online, they are therefore by definition not
+	 * still typing to us. */
+	if (!purple_status_is_online(status)) {
+		serv_got_typing_stopped(purple_account_get_connection(account), name);
+		purple_prpl_got_media_caps(account, name);
+	}
+}
+
+void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *name,
+					const char *status_id)
+{
+	GSList *list, *l;
+	PurpleBuddy *buddy;
+	PurplePresence *presence;
+	PurpleStatus *status;
+
+	g_return_if_fail(account   != NULL);
+	g_return_if_fail(name      != NULL);
+	g_return_if_fail(status_id != NULL);
+	g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
+
+	if((list = purple_blist_find_buddies(account, name)) == NULL)
+		return;
+
+	for(l = list; l != NULL; l = l->next) {
+		buddy = l->data;
+
+		presence = purple_buddy_get_presence(buddy);
+		status   = purple_presence_get_status(presence, status_id);
+
+		if(NULL == status)
+			continue;
+
+		if (purple_status_is_active(status)) {
+			purple_status_set_active(status, FALSE);
+			purple_buddy_update_status(buddy, status);
+		}
+	}
+
+	g_slist_free(list);
+}
+
+static void
+do_prpl_change_account_status(PurpleAccount *account,
+								PurpleStatus *old_status, PurpleStatus *new_status)
+{
+	PurplePluginProtocolInfo *prpl_info;
+
+	if (purple_status_is_online(new_status) &&
+		purple_account_is_disconnected(account) &&
+		purple_network_is_available())
+	{
+		purple_account_connect(account);
+		return;
+	}
+
+	if (!purple_status_is_online(new_status))
+	{
+		if (!purple_account_is_disconnected(account))
+			purple_account_disconnect(account);
+		/* Clear out the unsaved password if we switch to offline status */
+		if (!purple_account_get_remember_password(account))
+			purple_account_set_password(account, NULL, NULL, NULL);
+
+		return;
+	}
+
+	if (purple_account_is_connecting(account))
+		/*
+		 * We don't need to call the set_status PRPL function because
+		 * the PRPL will take care of setting its status during the
+		 * connection process.
+		 */
+		return;
+
+	prpl_info = purple_find_protocol_info(purple_account_get_protocol_id(account));
+
+	if (prpl_info == NULL)
+		return;
+
+	if (!purple_account_is_disconnected(account) && prpl_info->set_status != NULL)
+	{
+		prpl_info->set_status(account, new_status);
+	}
+}
+
+void
+purple_prpl_change_account_status(PurpleAccount *account,
+								PurpleStatus *old_status, PurpleStatus *new_status)
+{
+	g_return_if_fail(account    != NULL);
+	g_return_if_fail(new_status != NULL);
+	g_return_if_fail(!purple_status_is_exclusive(new_status) || old_status != NULL);
+
+	do_prpl_change_account_status(account, old_status, new_status);
+
+	purple_signal_emit(purple_accounts_get_handle(), "account-status-changed",
+					account, old_status, new_status);
+}
+
+GList *
+purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence)
+{
+	GList *statuses = NULL;
+	GList *l;
+	PurpleStatus *status;
+
+	g_return_val_if_fail(account  != NULL, NULL);
+	g_return_val_if_fail(presence != NULL, NULL);
+
+	for (l = purple_account_get_status_types(account); l != NULL; l = l->next)
+	{
+		status = purple_status_new((PurpleStatusType *)l->data, presence);
+		statuses = g_list_prepend(statuses, status);
+	}
+
+	statuses = g_list_reverse(statuses);
+
+	return statuses;
+}
+
+static void
+purple_prpl_attention(PurpleConversation *conv, const char *who,
+	guint type, PurpleMessageFlags flags, time_t mtime)
+{
+	PurpleAccount *account = purple_conversation_get_account(conv);
+	purple_signal_emit(purple_conversations_get_handle(),
+		flags == PURPLE_MESSAGE_SEND ? "sent-attention" : "got-attention",
+		account, who, conv, type);
+}
+
+void
+purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code)
+{
+	PurpleAttentionType *attn;
+	PurpleMessageFlags flags;
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleIMConversation *im;
+	gboolean (*send_attention)(PurpleConnection *, const char *, guint);
+	PurpleBuddy *buddy;
+	const char *alias;
+	gchar *description;
+	time_t mtime;
+
+	g_return_if_fail(gc != NULL);
+	g_return_if_fail(who != NULL);
+
+	prpl_info = purple_find_protocol_info(purple_account_get_protocol_id(purple_connection_get_account(gc)));
+	send_attention = prpl_info->send_attention;
+	g_return_if_fail(send_attention != NULL);
+
+	mtime = time(NULL);
+
+	attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code);
+
+	if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL)
+		alias = purple_buddy_get_contact_alias(buddy);
+	else
+		alias = who;
+
+	if (attn && purple_attention_type_get_outgoing_desc(attn)) {
+		description = g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias);
+	} else {
+		description = g_strdup_printf(_("Requesting %s's attention..."), alias);
+	}
+
+	flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM;
+
+	purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n",
+			description, who);
+
+	if (!send_attention(gc, who, type_code))
+		return;
+
+	im = purple_im_conversation_new(purple_connection_get_account(gc), who);
+	purple_conversation_write_message(PURPLE_CONVERSATION(im), NULL, description, flags, mtime);
+	purple_prpl_attention(PURPLE_CONVERSATION(im), who, type_code, PURPLE_MESSAGE_SEND, time(NULL));
+
+	g_free(description);
+}
+
+static void
+got_attention(PurpleConnection *gc, int id, const char *who, guint type_code)
+{
+	PurpleMessageFlags flags;
+	PurpleAttentionType *attn;
+	PurpleBuddy *buddy;
+	const char *alias;
+	gchar *description;
+	time_t mtime;
+
+	mtime = time(NULL);
+
+	attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code);
+
+	/* PURPLE_MESSAGE_NOTIFY is for attention messages. */
+	flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV;
+
+	/* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display
+	 * it next to the attention command. And if it is null, display a generic icon. */
+
+	if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL)
+		alias = purple_buddy_get_contact_alias(buddy);
+	else
+		alias = who;
+
+	if (attn && purple_attention_type_get_incoming_desc(attn)) {
+		description = g_strdup_printf(purple_attention_type_get_incoming_desc(attn), alias);
+	} else {
+		description = g_strdup_printf(_("%s has requested your attention!"), alias);
+	}
+
+	purple_debug_info("server", "got_attention: got '%s' from %s\n",
+			description, who);
+
+	if (id == -1)
+		serv_got_im(gc, who, description, flags, mtime);
+	else
+		serv_got_chat_in(gc, id, who, flags, description, mtime);
+
+	/* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */
+
+	g_free(description);
+}
+
+void
+purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code)
+{
+	PurpleConversation *conv = NULL;
+	PurpleAccount *account = purple_connection_get_account(gc);
+
+	got_attention(gc, -1, who, type_code);
+	conv =
+		purple_conversations_find_with_account(who, account);
+	if (conv)
+		purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_RECV,
+			time(NULL));
+}
+
+void
+purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code)
+{
+	got_attention(gc, id, who, type_code);
+}
+
+gboolean
+purple_prpl_initiate_media(PurpleAccount *account,
+			   const char *who,
+			   PurpleMediaSessionType type)
+{
+#ifdef USE_VV
+	PurpleConnection *gc = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+
+	if (account)
+		gc = purple_account_get_connection(account);
+	if (gc)
+		prpl_info = purple_connection_get_protocol_info(gc);
+
+	if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, initiate_media)) {
+		/* should check that the protocol supports this media type here? */
+		return prpl_info->initiate_media(account, who, type);
+	} else
+#endif
+	return FALSE;
+}
+
+PurpleMediaCaps
+purple_prpl_get_media_caps(PurpleAccount *account, const char *who)
+{
+#ifdef USE_VV
+	PurpleConnection *gc = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+
+	if (account)
+		gc = purple_account_get_connection(account);
+	if (gc)
+		prpl_info = purple_connection_get_protocol_info(gc);
+
+	if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info,
+			get_media_caps)) {
+		return prpl_info->get_media_caps(account, who);
+	}
+#endif
+	return PURPLE_MEDIA_CAPS_NONE;
+}
+
+void
+purple_prpl_got_media_caps(PurpleAccount *account, const char *name)
+{
+#ifdef USE_VV
+	GSList *list;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(name    != NULL);
+
+	if ((list = purple_blist_find_buddies(account, name)) == NULL)
+		return;
+
+	while (list) {
+		PurpleBuddy *buddy = list->data;
+		PurpleMediaCaps oldcaps = purple_buddy_get_media_caps(buddy);
+		PurpleMediaCaps newcaps = 0;
+		const gchar *bname = purple_buddy_get_name(buddy);
+		list = g_slist_delete_link(list, list);
+
+
+		newcaps = purple_prpl_get_media_caps(account, bname);
+		purple_buddy_set_media_caps(buddy, newcaps);
+
+		if (oldcaps == newcaps)
+			continue;
+
+		purple_signal_emit(purple_blist_get_handle(),
+				"buddy-caps-changed", buddy,
+				newcaps, oldcaps);
+	}
+#endif
+}
+
+PurpleProtocolAction *
+purple_protocol_action_new(const char* label,
+		PurpleProtocolActionCallback callback)
+{
+	PurpleProtocolAction *action;
+
+	g_return_val_if_fail(label != NULL && callback != NULL, NULL);
+
+	action = g_new0(PurpleProtocolAction, 1);
+
+	action->label    = g_strdup(label);
+	action->callback = callback;
+
+	return action;
+}
+
+void
+purple_protocol_action_free(PurpleProtocolAction *action)
+{
+	g_return_if_fail(action != NULL);
+
+	g_free(action->label);
+	g_free(action);
+}
+
+/**************************************************************************
+ * Protocols API
+ **************************************************************************/
+static void
+purple_protocol_destroy(PurplePluginProtocolInfo *prpl_info)
+{
+	GList *accounts, *l;
+
+	accounts = purple_accounts_get_all_active();
+	for (l = accounts; l != NULL; l = l->next) {
+		PurpleAccount *account = PURPLE_ACCOUNT(l->data);
+		if (purple_account_is_disconnected(account))
+			continue;
+
+		if (purple_strequal(prpl_info->id, purple_account_get_protocol_id(account)))
+			purple_account_disconnect(account);
+	}
+
+	g_list_free(accounts);
+
+	while (prpl_info->user_splits) {
+		PurpleAccountUserSplit *split = prpl_info->user_splits->data;
+		purple_account_user_split_destroy(split);
+		prpl_info->user_splits = g_list_delete_link(prpl_info->user_splits,
+				prpl_info->user_splits);
+	}
+
+	while (prpl_info->protocol_options) {
+		PurpleAccountOption *option = prpl_info->protocol_options->data;
+		purple_account_option_destroy(option);
+		prpl_info->protocol_options =
+				g_list_delete_link(prpl_info->protocol_options,
+				prpl_info->protocol_options);
+	}
+
+	purple_request_close_with_handle(prpl_info);
+	purple_notify_close_with_handle(prpl_info);
+
+	purple_signals_disconnect_by_handle(prpl_info);
+	purple_signals_unregister_by_instance(prpl_info);
+
+	purple_prefs_disconnect_by_handle(prpl_info);
+}
+
+PurplePluginProtocolInfo *
+purple_find_protocol_info(const char *id)
+{
+	return g_hash_table_lookup(protocols, id);
+}
+
+gboolean
+purple_protocols_add(PurplePluginProtocolInfo *prpl_info)
+{
+	if (purple_find_protocol_info(prpl_info->id))
+		return FALSE;
+
+	g_hash_table_insert(protocols, g_strdup(prpl_info->id), prpl_info);
+	return TRUE;
+}
+
+gboolean purple_protocols_remove(PurplePluginProtocolInfo *prpl_info)
+{
+	if (purple_find_protocol_info(prpl_info->id) == NULL)
+		return FALSE;
+
+	g_hash_table_remove(protocols, prpl_info->id);
+	purple_protocol_destroy(prpl_info);
+
+	return TRUE;
+}
+
+GList *
+purple_protocols_get_all(void)
+{
+	GList *ret = NULL;
+	PurplePluginProtocolInfo *prpl_info;
+	GHashTableIter iter;
+
+	g_hash_table_iter_init(&iter, protocols);
+	while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prpl_info))
+		ret = g_list_append(ret, prpl_info);
+
+	return ret;
+}
+
+/**************************************************************************
+ * Protocols Subsystem API
+ **************************************************************************/
+void
+purple_protocols_init(void)
+{
+	protocols = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+			(GDestroyNotify)purple_protocol_destroy);
+}
+
+void *
+purple_protocols_get_handle(void)
+{
+	static int handle;
+
+	return &handle;
+}
+
+void
+purple_protocols_uninit(void) 
+{
+	g_hash_table_destroy(protocols);
+}
+

mercurial