changeset 27614:fa827f6f990f

Add support for receiving contact details from buddies. Also, show some contact details (e.g. phone number) in the tooltip and in 'Get Info' when available.
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Tue, 14 Jul 2009 21:08:56 +0000
parents 32d2caa6a777
children b909c4a7bfc8
files libpurple/protocols/yahoo/libymsg.c libpurple/protocols/yahoo/yahoo_aliases.c libpurple/protocols/yahoo/yahoo_aliases.h libpurple/protocols/yahoo/yahoo_friend.c libpurple/protocols/yahoo/yahoo_friend.h libpurple/protocols/yahoo/yahoo_packet.h
diffstat 6 files changed, 246 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/yahoo/libymsg.c	Tue Jul 14 19:55:40 2009 +0000
+++ b/libpurple/protocols/yahoo/libymsg.c	Tue Jul 14 21:08:56 2009 +0000
@@ -2898,6 +2898,9 @@
 	case YAHOO_SERVICE_AUDIBLE:
 		yahoo_process_audible(gc, pkt);
 		break;
+	case YAHOO_SERVICE_CONTACT_DETAILS:
+		yahoo_process_contact_details(gc, pkt);
+		break;
 	case YAHOO_SERVICE_FILETRANS_15:
 		yahoo_process_filetrans_15(gc, pkt);
 		break;
@@ -3539,7 +3542,7 @@
 	g_free(yd->pending_chat_goto);
 	g_strfreev(yd->profiles);
 
-	yahoo_personal_details_reset(&yd->ypd);
+	yahoo_personal_details_reset(&yd->ypd, TRUE);
 
 	g_free(yd->current_list15_grp);
 
@@ -3772,6 +3775,26 @@
 
 	if (presence != NULL)
 		purple_notify_user_info_add_pair(user_info, _("Presence"), presence);
+
+	if (full) {
+		YahooPersonalDetails *ypd = &f->ypd;
+		int i;
+		struct {
+			char *id;
+			char *text;
+			char *value;
+		} yfields[] = {
+			{"hp", N_("Home Phone Number"), ypd->phone.home},
+			{"wp", N_("Work Phone Number"), ypd->phone.work},
+			{"mo", N_("Mobile Phone Number"), ypd->phone.mobile},
+			{NULL, NULL, NULL}
+		};
+		for (i = 0; yfields[i].id; i++) {
+			if (!yfields[i].value || !*yfields[i].value)
+				continue;
+			purple_notify_user_info_add_pair(user_info, _(yfields[i].text), yfields[i].value);
+		}
+	}
 }
 
 static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data)
--- a/libpurple/protocols/yahoo/yahoo_aliases.c	Tue Jul 14 19:55:40 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Tue Jul 14 21:08:56 2009 +0000
@@ -53,9 +53,10 @@
 	gchar *who;
 };
 
-void yahoo_personal_details_reset(YahooPersonalDetails *ypd)
+void yahoo_personal_details_reset(YahooPersonalDetails *ypd, gboolean all)
 {
-	g_free(ypd->id);
+	if (all)
+		g_free(ypd->id);
 	g_free(ypd->names.first);
 	g_free(ypd->names.last);
 	g_free(ypd->names.middle);
@@ -104,6 +105,7 @@
 		for(item = xmlnode_get_child(contacts, "ct"); item; item = xmlnode_get_next_twin(item)) {
 			/* Yahoo replies with two types of contact (ct) record, we are only interested in the alias ones */
 			if ((yid = xmlnode_get_attrib(item, "yi"))) {
+				YahooPersonalDetails *ypd = NULL;
 				/* Grab all the bits of information we can */
 				fn = xmlnode_get_attrib(item, "fn");
 				ln = xmlnode_get_attrib(item, "ln");
@@ -148,23 +150,29 @@
 						yahoo_update_alias(gc, yid, buddy_alias);
 						purple_debug_info("yahoo", "Sent updated alias '%s'\n", buddy_alias);
 					}
-				} else {
+				}
+				
+				if (f != NULL)
+					ypd = &f->ypd;
+				else {
 					/* May be the alias is for the account? */
 					const char *yidn = purple_normalize(account, yid);
 					if (purple_strequal(yidn, purple_connection_get_display_name(gc))) {
-						yahoo_personal_details_reset(&yd->ypd);
-
-						yd->ypd.id = g_strdup(id);
+						ypd = &yd->ypd;
+					}
+				}
 
-						yd->ypd.names.first = g_strdup(fn);
-						yd->ypd.names.middle = g_strdup(mn);
-						yd->ypd.names.last = g_strdup(ln);
-						yd->ypd.names.nick = g_strdup(nn);
+				if (ypd) {
+					yahoo_personal_details_reset(ypd, TRUE);
+					ypd->id = g_strdup(id);
+					ypd->names.first = g_strdup(fn);
+					ypd->names.middle = g_strdup(mn);
+					ypd->names.last = g_strdup(ln);
+					ypd->names.nick = g_strdup(nn);
 
-						yd->ypd.phone.work = g_strdup(wp);
-						yd->ypd.phone.home = g_strdup(hp);
-						yd->ypd.phone.mobile = g_strdup(mo);
-					}
+					ypd->phone.work = g_strdup(wp);
+					ypd->phone.home = g_strdup(hp);
+					ypd->phone.mobile = g_strdup(mo);
 				}
 
 				g_free(full_name);
@@ -388,6 +396,57 @@
  * User Info Update Functions
  **************************************************************************/
 
+#if 0
+/* This block of code can be used to send our contact details to
+ * everyone in the buddylist. But with the official messenger,
+ * doing this pops a conversation window at the receiver's end,
+ * which is stupid, and thus not really surprising. */
+
+struct yahoo_userinfo {
+	struct yahoo_data *yd;
+	char *xml;
+};
+
+static void
+yahoo_send_userinfo_to_user(struct yahoo_userinfo *yui, const char *who)
+{
+	struct yahoo_packet *pkt;
+	PurpleConnection *gc;
+
+	gc = yui->yd->gc;
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CONTACT_DETAILS, 0, 0);
+	yahoo_packet_hash(pkt, "siisis",
+			1, purple_connection_get_display_name(gc),
+			13, 1,    /* This creates a conversation window in the official client */
+			302, 5,
+			5, who,
+			303, 5,
+			280, yui->xml);
+	yahoo_packet_send_and_free(pkt, yui->yd);
+}
+
+static void
+yahoo_send_userinfo_foreach(gpointer key, gpointer value, gpointer data)
+{
+	const char *who = key;
+	YahooFriend *f = value;
+
+	if (f->status != YAHOO_STATUS_OFFLINE) {
+		yahoo_send_userinfo_to_user(data, who);
+	}
+}
+
+static void
+yahoo_sent_userinfo_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message)
+{
+	struct yahoo_userinfo *yui = user_data;
+	yahoo_fetch_aliases_cb(url_data, yui->yd->gc, url_text, len, error_message);
+	g_hash_table_foreach(yui->yd->friends, yahoo_send_userinfo_foreach, yui);
+	g_free(yui->xml);
+	g_free(yui);
+}
+#endif
+
 static void
 yahoo_set_userinfo_cb(PurpleConnection *gc, PurpleRequestFields *fields)
 {
@@ -418,6 +477,7 @@
 	}
 
 	content = xmlnode_to_formatted_str(node, &len);
+	xmlnode_free(node);
 	purple_url_parse(yd->jp ? YAHOOJP_USERINFO_URL : YAHOO_USERINFO_URL, &webaddress, NULL, &webpage, NULL, NULL);
 
 	request = g_strdup_printf("POST %s HTTP/1.1\r\n"
@@ -433,6 +493,30 @@
 				  len + 4,
 				  content);
 
+#if 0
+	{
+		/* This is if we wanted to send our contact details to everyone
+		 * in the buddylist. But this cannot be done now, because in the
+		 * official messenger, doing this pops a conversation window at
+		 * the receiver's end, which is stupid, and thus not really
+		 * surprising. */
+		struct yahoo_userinfo *ui = g_new(struct yahoo_userinfo, 1);
+		node = xmlnode_new("contact");
+
+		for (i = 0; yfields[i]; i++) {
+			const char *v = purple_request_fields_get_string(fields, yfields[i]);
+			if (v) {
+				xmlnode *nd = xmlnode_new_child(node, yfields[i]);
+				xmlnode_insert_data(nd, v, -1);
+			}
+		}
+
+		ui->yd = yd;
+		ui->xml = xmlnode_to_str(node, NULL);
+		xmlnode_free(node);
+	}
+#endif
+
 	url_data = purple_util_fetch_url_request_len_with_account(account, webaddress, FALSE,
 			YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
 			yahoo_fetch_aliases_cb, gc);
@@ -443,7 +527,6 @@
 	g_free(webpage);
 	g_free(content);
 	g_free(request);
-	xmlnode_free(node);
 }
 
 void yahoo_set_userinfo(PurpleConnection *gc)
@@ -484,3 +567,118 @@
 			purple_connection_get_account(gc), NULL, NULL, gc);
 }
 
+static gboolean
+parse_contact_details(struct yahoo_data *yd, const char *who, const char *xml)
+{
+	xmlnode *node, *nd;
+	YahooFriend *f;
+	char *yid;
+
+	node = xmlnode_from_str(xml, -1);
+	if (!node) {
+		purple_debug_info("yahoo", "Received malformed XML for contact details from '%s':\n%s\n",
+				who, xml);
+		return FALSE;
+	}
+
+	nd = xmlnode_get_child(node, "yi");
+	if (!nd || !(yid = xmlnode_get_data(nd))) {
+		xmlnode_free(node);
+		return FALSE;
+	}
+
+	if (!purple_strequal(yid, who)) {
+		/* The user may not want to set the contact details about folks in the buddylist
+		   to what some random dude might have sent. So it would be good if we popped
+		   up a prompt requiring the user to confirm the details before we set them.
+		   However, someone could send details about hundreds of users at the same time,
+		   which would make things really bad. So for now, until we have a better way of
+		   dealing with this, ignore this details. */
+		purple_debug_info("yahoo", "Ignoring contact details sent by %s about %s\n",
+				who, yid);
+		g_free(yid);
+		xmlnode_free(node);
+		return FALSE;
+	}
+
+	f = yahoo_friend_find(yd->gc, yid);
+	if (!f) {
+		g_free(yid);
+		xmlnode_free(node);
+		return FALSE;
+	} else {
+		int i;
+		YahooPersonalDetails *ypd = &f->ypd;
+		char *alias = NULL;
+		struct {
+			char *id;
+			char **field;
+		} details[] = {
+			{"fn", &ypd->names.first},
+			{"mn", &ypd->names.middle},
+			{"ln", &ypd->names.last},
+			{"nn", &ypd->names.nick},
+			{"wp", &ypd->phone.work},
+			{"hp", &ypd->phone.home},
+			{"mo", &ypd->phone.mobile},
+			{NULL, NULL}
+		};
+
+		yahoo_personal_details_reset(ypd, FALSE);
+
+		for (i = 0; details[i].id; i++) {
+			nd = xmlnode_get_child(node, details[i].id);
+			*details[i].field = nd ? xmlnode_get_data(nd) : NULL;
+		}
+
+		if (ypd->names.nick)
+			alias = ypd->names.nick;
+		else if (ypd->names.first || ypd->names.last) {
+			alias = g_strstrip(g_strdup_printf("%s %s",
+						ypd->names.first ? ypd->names.first : "",
+						ypd->names.last ? ypd->names.last : ""));
+		}
+
+		if (alias) {
+			serv_got_alias(yd->gc, yid, alias);
+			if (alias != ypd->names.nick)
+				g_free(alias);
+		}
+	}
+
+	xmlnode_free(node);
+	g_free(yid);
+	return TRUE;
+}
+
+/* I don't think this happens for MSN buddies. -- sad */
+void yahoo_process_contact_details(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	GSList *l = pkt->hash;
+	const char *who = NULL, *xml = NULL;
+	struct yahoo_data *yd = purple_connection_get_protocol_data(gc);
+
+	for (; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		switch (pair->key) {
+			case 4:
+				who = pair->value;	/* This is the person who sent us the details.
+									   But not necessarily about himself. */
+				break;
+			case 5:
+				break;
+			case 13:
+				/* This is '1' if 'who' is sending the contact details about herself,
+				   '0' if 'who' is sending the contact details she has about buddies
+				   in her list. However, in all cases, the xml in key 280 always seems
+				   to contain the yid of the person, so we may as well ignore this field
+				   and look into the xml instead to see who the information is about. */
+				break;
+			case 280:
+				xml = pair->value;
+				parse_contact_details(yd, who, xml);
+				break;
+		}
+	}
+}
+
--- a/libpurple/protocols/yahoo/yahoo_aliases.h	Tue Jul 14 19:55:40 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.h	Tue Jul 14 21:08:56 2009 +0000
@@ -36,5 +36,5 @@
 void yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias);
 void yahoo_fetch_aliases(PurpleConnection *gc);
 void yahoo_set_userinfo(PurpleConnection *gc);
-void yahoo_personal_details_reset(YahooPersonalDetails *ypd);
-
+void yahoo_personal_details_reset(YahooPersonalDetails *ypd, gboolean all);
+void yahoo_process_contact_details(PurpleConnection *gc, struct yahoo_packet *pkt);
--- a/libpurple/protocols/yahoo/yahoo_friend.c	Tue Jul 14 19:55:40 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_friend.c	Tue Jul 14 21:08:56 2009 +0000
@@ -27,6 +27,7 @@
 #include "debug.h"
 
 #include "yahoo_friend.h"
+#include "yahoo_aliases.h"
 
 static YahooFriend *yahoo_friend_new(void)
 {
@@ -124,13 +125,13 @@
 
 void yahoo_friend_set_alias_id(YahooFriend *f, const char *alias_id)
 {
-	g_free(f->alias_id);
-	f->alias_id = g_strdup(alias_id);
+	g_free(f->ypd.id);
+	f->ypd.id = g_strdup(alias_id);
 }
 
 const char *yahoo_friend_get_alias_id(YahooFriend *f)
 {
-	return f->alias_id;
+	return f->ypd.id;
 }
 
 void yahoo_friend_free(gpointer p)
@@ -139,7 +140,7 @@
 	g_free(f->msg);
 	g_free(f->game);
 	g_free(f->ip);
-	g_free(f->alias_id);
+	yahoo_personal_details_reset(&f->ypd, TRUE);
 	g_free(f);
 }
 
--- a/libpurple/protocols/yahoo/yahoo_friend.h	Tue Jul 14 19:55:40 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_friend.h	Tue Jul 14 21:08:56 2009 +0000
@@ -56,7 +56,7 @@
 	YahooPresenceVisibility presence;
 	int protocol; /* 1=LCS, 2=MSN*/
 	long int version_id;
-	gchar *alias_id;		/* XXX: stick in a YahooPersonalDetails instead? */
+	YahooPersonalDetails ypd;
 	YahooP2PStatus p2p_status;
 	gboolean p2p_packet_sent;	/* 0:not sent, 1=sent */
 	gint session_id;	/* session id of friend */
--- a/libpurple/protocols/yahoo/yahoo_packet.h	Tue Jul 14 19:55:40 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.h	Tue Jul 14 21:08:56 2009 +0000
@@ -98,6 +98,7 @@
 	YAHOO_SERVICE_AVATAR_UPDATE = 0xc7,
 	YAHOO_SERVICE_VERIFY_ID_EXISTS = 0xc8,
 	YAHOO_SERVICE_AUDIBLE = 0xd0,
+	YAHOO_SERVICE_CONTACT_DETAILS = 0xd3,
 	/* YAHOO_SERVICE_CHAT_SESSION = 0xd4,?? Reports start of chat session, gets an id from server */
 	YAHOO_SERVICE_AUTH_REQ_15 = 0xd6,
 	YAHOO_SERVICE_FILETRANS_15 = 0xdc,