changeset 15204:f814b2df9cce

[gaim-migrate @ 17993] Blocking on Google Talk. Our Privacy API sucks so bad that even with no prior support for blocking in Jabber, this has no interface changes. If someone wanted to implement the deprecated Jabber privacy lists API, though, that would be ok by me. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Thu, 14 Dec 2006 04:56:54 +0000
parents b8ae75fa8d67
children d83f71ca0cd7
files libgaim/protocols/jabber/disco.c libgaim/protocols/jabber/google.c libgaim/protocols/jabber/google.h libgaim/protocols/jabber/jabber.c libgaim/protocols/jabber/jabber.h libgaim/protocols/jabber/parser.c libgaim/protocols/jabber/roster.c libgaim/xmlnode.c libgaim/xmlnode.h
diffstat 9 files changed, 322 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/libgaim/protocols/jabber/disco.c	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/protocols/jabber/disco.c	Thu Dec 14 04:56:54 2006 +0000
@@ -27,7 +27,7 @@
 #include "iq.h"
 #include "disco.h"
 #include "jabber.h"
-
+#include "roster.h"
 
 struct _jabber_disco_info_cb_data {
 	gpointer data;
@@ -262,8 +262,14 @@
 		if (!strcmp("google:mail:notify", var)) {
 			js->server_caps |= JABBER_CAP_GMAIL_NOTIFY;
 			jabber_gmail_init(js);
+		} else if (!strcmp("google:roster", var)) {
+			js->server_caps |= JABBER_CAP_GOOGLE_ROSTER;
+			jabber_google_roster_init(js);
 		}
 	}
+
+	if (!js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
+		jabber_roster_request(js);
 }
 
 static void
--- a/libgaim/protocols/jabber/google.c	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/protocols/jabber/google.c	Thu Dec 14 04:56:54 2006 +0000
@@ -21,8 +21,12 @@
 
 #include "internal.h"
 #include "debug.h"
+#include "privacy.h"
+
+#include "buddy.h"
 #include "google.h"
 #include "jabber.h"
+#include "presence.h"
 #include "iq.h"
 
 static void 
@@ -167,3 +171,190 @@
 	jabber_iq_set_callback(iq, jabber_gmail_parse, NULL);
 	jabber_iq_send(iq);
 }
+
+void jabber_google_roster_init(JabberStream *js)
+{
+	JabberIq *iq;
+	xmlnode *query;
+
+	iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:roster");
+	query = xmlnode_get_child(iq->node, "query");
+	
+	xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
+	xmlnode_set_attrib(query, "gr:ext", "2");
+
+	jabber_iq_send(iq);
+}
+
+void jabber_google_roster_outgoing(JabberStream *js, xmlnode *query, xmlnode *item)
+{
+	GaimAccount *account = gaim_connection_get_account(js->gc);
+	GSList *list = account->deny;
+	const char *jid = xmlnode_get_attrib(item, "jid");
+	char *jid_norm = g_strdup(jabber_normalize(account, jid));
+
+	while (list) {
+		if (!strcmp(jid_norm, (char*)list->data)) {
+			xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
+			xmlnode_set_attrib(item, "gr:t", "B");
+			xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
+			xmlnode_set_attrib(query, "gr:ext", "2");
+			return;
+		}
+		list = list->next;
+	}
+
+}
+
+void jabber_google_roster_incoming(JabberStream *js, xmlnode *item)
+{
+	GaimAccount *account = gaim_connection_get_account(js->gc);
+	GSList *list = account->deny;
+	const char *jid = xmlnode_get_attrib(item, "jid");
+	gboolean on_block_list = FALSE;
+
+	char *jid_norm = g_strdup(jabber_normalize(account, jid));
+
+	const char *grt = xmlnode_get_attrib_with_namespace(item, "t", "google:roster");
+	
+	while (list) {
+		if (!strcmp(jid_norm, (char*)list->data)) {
+			on_block_list = TRUE;
+			break;
+		}
+		list = list->next;
+	}
+	
+	if (!on_block_list && (grt && (*grt == 'B' || *grt == 'b'))) {
+		gaim_debug_info("jabber", "Blocking %s\n", jid_norm);
+		gaim_privacy_deny_add(account, jid_norm, TRUE);
+	} else if (on_block_list && (!grt || (*grt != 'B' && *grt != 'b' ))){
+		gaim_debug_info("jabber", "Unblocking %s\n", jid_norm);
+		gaim_privacy_deny_remove(account, jid_norm, TRUE);
+	}
+}
+
+void jabber_google_roster_add_deny(GaimConnection *gc, const char *who) 
+{
+	JabberStream *js;
+	GSList *buddies;
+	JabberIq *iq;
+	xmlnode *query;
+	xmlnode *item;
+	xmlnode *group;
+	GaimBuddy *b;
+	JabberBuddy *jb;
+
+	js = (JabberStream*)(gc->proto_data);
+	
+	if (!js || !js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
+		return;
+
+	jb = jabber_buddy_find(js, who, TRUE);
+
+	buddies = gaim_find_buddies(js->gc->account, who);
+	if(!buddies)
+		return;
+	
+	b = buddies->data;
+
+	iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
+	
+	query = xmlnode_get_child(iq->node, "query");
+	item = xmlnode_new_child(query, "item");
+
+	while(buddies) {
+		GaimGroup *g;
+
+		b = buddies->data;
+		g = gaim_buddy_get_group(b);
+
+		group = xmlnode_new_child(item, "group");
+		xmlnode_insert_data(group, g->name, -1);
+		
+		buddies = buddies->next;
+	}
+
+	iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
+
+	query = xmlnode_get_child(iq->node, "query");
+	item = xmlnode_new_child(query, "item");
+
+	xmlnode_set_attrib(item, "jid", who);
+	xmlnode_set_attrib(item, "name", b->alias ? b->alias : "");
+	xmlnode_set_attrib(item, "gr:t", "B");
+	xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
+	xmlnode_set_attrib(query, "gr:ext", "2");
+
+	jabber_iq_send(iq);
+
+	/* Synthesize a sign-off */
+	if (jb) {
+		JabberBuddyResource *jbr;
+		GList *l = jb->resources;
+		while (l) {
+			jbr = l->data;
+			printf("ASDFA %s\n", jbr->name);
+			jabber_buddy_remove_resource(jb, jbr->name);
+			l = l->next;
+		}
+	}
+	gaim_prpl_got_user_status(gaim_connection_get_account(gc), who, "offline", NULL);
+}
+
+void jabber_google_roster_rem_deny(GaimConnection *gc, const char *who)
+{
+	JabberStream *js;
+	GSList *buddies;
+	JabberIq *iq;
+	xmlnode *query;
+	xmlnode *item;
+	xmlnode *group;
+	GaimBuddy *b;
+
+	g_return_if_fail(gc != NULL);
+	g_return_if_fail(who != NULL);
+	
+	js = (JabberStream*)(gc->proto_data);
+	
+	if (!js || !js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
+		return;
+	
+	buddies = gaim_find_buddies(js->gc->account, who);
+	if(!buddies)
+		return;
+	
+	b = buddies->data;
+
+	iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
+	
+	query = xmlnode_get_child(iq->node, "query");
+	item = xmlnode_new_child(query, "item");
+
+	while(buddies) {
+		GaimGroup *g;
+
+		b = buddies->data;
+		g = gaim_buddy_get_group(b);
+
+		group = xmlnode_new_child(item, "group");
+		xmlnode_insert_data(group, g->name, -1);
+		
+		buddies = buddies->next;
+	}
+
+	iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster");
+
+	query = xmlnode_get_child(iq->node, "query");
+	item = xmlnode_new_child(query, "item");
+
+	xmlnode_set_attrib(item, "jid", who);
+	xmlnode_set_attrib(item, "name", b->alias ? b->alias : "");
+	xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
+	xmlnode_set_attrib(query, "gr:ext", "2");
+
+	jabber_iq_send(iq);
+
+	/* See if he's online */
+	jabber_presence_subscription_set(js, who, "probe");
+}
--- a/libgaim/protocols/jabber/google.h	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/protocols/jabber/google.h	Thu Dec 14 04:56:54 2006 +0000
@@ -29,4 +29,12 @@
 void jabber_gmail_init(JabberStream *js);
 void jabber_gmail_poke(JabberStream *js, xmlnode *node);
 
+void jabber_google_roster_init(JabberStream *js);
+void jabber_google_roster_outgoing(JabberStream *js, xmlnode *query, xmlnode *item);
+void jabber_google_roster_incoming(JabberStream *js, xmlnode *item);
+void jabber_google_roster_add_deny(GaimConnection *gc, const char *who);
+void jabber_google_roster_rem_deny(GaimConnection *gc, const char *who);
+
+
+
 #endif   /* _GAIM_GOOGLE_H_ */
--- a/libgaim/protocols/jabber/jabber.c	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/protocols/jabber/jabber.c	Thu Dec 14 04:56:54 2006 +0000
@@ -41,6 +41,7 @@
 #include "buddy.h"
 #include "chat.h"
 #include "disco.h"
+#include "google.h"
 #include "iq.h"
 #include "jutil.h"
 #include "message.h"
@@ -1041,7 +1042,6 @@
 			
 			break;
 		case JABBER_STREAM_CONNECTED:
-			jabber_roster_request(js);
 			gpresence = gaim_account_get_presence(js->gc->account);
 			status = gaim_presence_get_active_status(gpresence);
 			jabber_presence_send(js->gc->account, status);
@@ -1875,9 +1875,9 @@
 	jabber_roster_remove_buddy,		/* remove_buddy */
 	NULL,							/* remove_buddies */
 	NULL,							/* add_permit */
-	NULL,							/* add_deny */
+	jabber_google_roster_add_deny,				/* add_deny */
 	NULL,							/* rem_permit */
-	NULL,							/* rem_deny */
+	jabber_google_roster_rem_deny,				/* rem_deny */
 	NULL,							/* set_permit_deny */
 	jabber_chat_join,				/* join_chat */
 	NULL,							/* reject_chat */
--- a/libgaim/protocols/jabber/jabber.h	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/protocols/jabber/jabber.h	Thu Dec 14 04:56:54 2006 +0000
@@ -55,6 +55,7 @@
 	 * http://code.google.com/apis/talk/jep_extensions/extensions.html
 	 */
 	JABBER_CAP_GMAIL_NOTIFY   = 1 << 9,
+	JABBER_CAP_GOOGLE_ROSTER  = 1 << 10,
 
 	JABBER_CAP_RETRIEVED      = 1 << 31
 } JabberCapabilities;
--- a/libgaim/protocols/jabber/parser.c	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/protocols/jabber/parser.c	Thu Dec 14 04:56:54 2006 +0000
@@ -76,13 +76,21 @@
 			char *txt;
 			int attrib_len = attributes[i+4] - attributes[i+3];
 			char *attrib = g_malloc(attrib_len + 1);
+			char *attrib_ns = NULL;
+
+			if (attributes[i+2]) {
+				attrib_ns = g_strdup(attributes[i+2]);;
+			}
+
 			memcpy(attrib, attributes[i+3], attrib_len);
 			attrib[attrib_len] = '\0';
+
 			txt = attrib;
 			attrib = gaim_unescape_html(txt);
 			g_free(txt);
-			xmlnode_set_attrib(node, (const char*) attributes[i], attrib);
+			xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib);
 			g_free(attrib);
+			g_free(attrib_ns);
 		}
 
 		js->current = node;
--- a/libgaim/protocols/jabber/roster.c	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/protocols/jabber/roster.c	Thu Dec 14 04:56:54 2006 +0000
@@ -24,6 +24,7 @@
 #include "util.h"
 
 #include "buddy.h"
+#include "google.h"
 #include "presence.h"
 #include "roster.h"
 #include "iq.h"
@@ -224,6 +225,8 @@
 				if (g_slist_find_custom(groups, group_name, (GCompareFunc)gaim_utf8_strcasecmp) == NULL)
 					groups = g_slist_append(groups, group_name);
 			}
+			if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
+				jabber_google_roster_incoming(js, item);
 			add_gaim_buddies_in_groups(js, jid, name, groups);
 		}
 	}
@@ -271,7 +274,12 @@
 
 	if(!grps)
 		g_slist_free(groups);
-
+	
+	if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER) {
+		jabber_google_roster_outgoing(js, query, item);
+		xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
+		xmlnode_set_attrib(query, "gr:ext", "2");
+	}
 	jabber_iq_send(iq);
 }
 
--- a/libgaim/xmlnode.c	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/xmlnode.c	Thu Dec 14 04:56:54 2006 +0000
@@ -142,6 +142,34 @@
 	}
 }
 
+
+void
+xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns)
+{
+	xmlnode *attr_node, *sibling = NULL;
+
+	g_return_if_fail(node != NULL);
+	g_return_if_fail(attr != NULL);
+
+	for(attr_node = node->child; attr_node; attr_node = attr_node->next)
+	{
+		if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
+		   !strcmp(attr_node->name, attr) &&
+		   !strcmp(attr_node->xmlns, xmlns)) {
+			if(node->child == attr_node) {
+				node->child = attr_node->next;
+			} else if (node->lastchild == attr_node) {
+				node->lastchild = sibling;
+			} else {
+				sibling->next = attr_node->next;
+			}
+			xmlnode_free(attr_node);
+			return;
+		}
+		sibling = attr_node;
+	}
+}
+
 void
 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value)
 {
@@ -160,6 +188,25 @@
 	xmlnode_insert_child(node, attrib_node);
 }
 
+void
+xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value)
+{
+	xmlnode *attrib_node;
+
+	g_return_if_fail(node != NULL);
+	g_return_if_fail(attr != NULL);
+	g_return_if_fail(value != NULL);
+
+	xmlnode_remove_attrib_with_namespace(node, attr, xmlns);
+
+	attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB);
+
+	attrib_node->data = g_strdup(value);
+	attrib_node->xmlns = g_strdup(xmlns);
+
+	xmlnode_insert_child(node, attrib_node);	
+}
+
 const char *
 xmlnode_get_attrib(xmlnode *node, const char *attr)
 {
@@ -176,6 +223,23 @@
 	return NULL;
 }
 
+const char *
+xmlnode_get_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns)
+{
+	xmlnode *x;
+
+	g_return_val_if_fail(node != NULL, NULL);
+
+	for(x = node->child; x; x = x->next) {
+		if(x->type == XMLNODE_TYPE_ATTRIB && 
+		   !strcmp(attr, x->name) && !strcmp(x->xmlns, xmlns)) {
+			return x->data;
+		}
+	}
+
+	return NULL;	
+}
+
 
 void xmlnode_set_namespace(xmlnode *node, const char *xmlns)
 {
--- a/libgaim/xmlnode.h	Thu Dec 14 04:17:02 2006 +0000
+++ b/libgaim/xmlnode.h	Thu Dec 14 04:56:54 2006 +0000
@@ -143,6 +143,16 @@
 void xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value);
 
 /**
+ * Sets a namespaced attribute for a node
+ *
+ * @param node  The node to set an attribute for.
+ * @param attr  The name of the attribute to set
+ * @param xmlns The namespace of the attribute to ste
+ * @param value The value of the attribute
+ */
+void xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value);
+
+/**
  * Gets an attribute from a node.
  *
  * @param node The node to get an attribute from.
@@ -153,6 +163,17 @@
 const char *xmlnode_get_attrib(xmlnode *node, const char *attr);
 
 /**
+ * Gets a namespaced attribute from a node
+ *
+ * @param node  The node to get an attribute from.
+ * @param attr  The attribute to get
+ * @param xmlns The namespace of the attribute to get
+ *
+ * @return The value of the attribute/
+ */
+const char *xmlnode_get_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns);
+
+/**
  * Removes an attribute from a node.
  *
  * @param node The node to remove an attribute from.
@@ -161,6 +182,15 @@
 void xmlnode_remove_attrib(xmlnode *node, const char *attr);
 
 /**
+ * Removes a namespaced attribute from a node
+ *
+ * @param node  The node to remove an attribute from
+ * @param attr  The attribute to remove
+ * @param xmlns The namespace of the attribute to remove
+ */
+void xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns);
+
+/**
  * Sets the namespace of a node
  *
  * @param node The node to qualify