diff libpurple/protocols/jabber/jabber.c @ 26432:0adf41b3de79

Add a resource selector when initiating media.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Sat, 04 Apr 2009 02:26:49 +0000
parents da651b0f40a1
children 28f1512b0f94 882748e27f9d 8399b545925c
line wrap: on
line diff
--- a/libpurple/protocols/jabber/jabber.c	Fri Apr 03 19:29:53 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Apr 04 02:26:49 2009 +0000
@@ -2612,6 +2612,39 @@
 	return TRUE;
 }
 
+#ifdef USE_VV
+typedef struct {
+	PurpleConnection *pc;
+	gchar *who;
+	PurpleMediaSessionType type;
+	
+} JabberMediaRequest;
+
+static void
+jabber_media_cancel_cb(JabberMediaRequest *request,
+		PurpleRequestFields *fields)
+{
+	g_free(request->who);
+	g_free(request);
+}
+
+static void
+jabber_media_ok_cb(JabberMediaRequest *request, PurpleRequestFields *fields)
+{
+	PurpleRequestField *field =
+			purple_request_fields_get_field(fields, "resource");
+	int selected_id = purple_request_field_choice_get_value(field);
+	GList *labels = purple_request_field_choice_get_labels(field);
+	gchar *who = g_strdup_printf("%s/%s", request->who,
+			(gchar*)g_list_nth_data(labels, selected_id));
+	jabber_initiate_media(request->pc, who, request->type);
+
+	g_free(who);
+	g_free(request->who);
+	g_free(request);
+}
+#endif
+
 gboolean
 jabber_initiate_media(PurpleConnection *gc, const char *who, 
 		      PurpleMediaSessionType type)
@@ -2619,6 +2652,8 @@
 #ifdef USE_VV
 	JabberStream *js = (JabberStream *) gc->proto_data;
 	JabberBuddy *jb;
+	JabberBuddyResource *jbr = NULL;
+	char *resource;
 
 	if (!js) {
 		purple_debug_error("jabber",
@@ -2626,23 +2661,135 @@
 		return FALSE;
 	}
 
-	jb = jabber_buddy_find(js, who, FALSE);
-
-	if (!jb) {
-		purple_debug_error("jabber", "Could not find buddy\n");
-		return FALSE;
+
+	if((resource = jabber_get_resource(who)) != NULL) {
+		/* they've specified a resource, no need to ask or
+		 * default or anything, just do it */
+
+		jb = jabber_buddy_find(js, who, FALSE);
+		jbr = jabber_buddy_find_resource(jb, resource);
+		g_free(resource);
+
+		if (type & PURPLE_MEDIA_AUDIO &&
+				!jabber_resource_has_capability(jbr,
+				JINGLE_APP_RTP_SUPPORT_AUDIO) &&
+				jabber_resource_has_capability(jbr,
+				GOOGLE_VOICE_CAP))
+			return jabber_google_session_initiate(
+					gc->proto_data, who, type);
+		else
+			return jingle_rtp_initiate_media(
+					gc->proto_data, who, type);
 	}
 
-	if (type & PURPLE_MEDIA_AUDIO &&
-			!jabber_buddy_has_capability(jb,
-			JINGLE_APP_RTP_SUPPORT_AUDIO) &&
-			jabber_buddy_has_capability(jb, GOOGLE_VOICE_CAP))
-		return jabber_google_session_initiate(gc->proto_data, who, type);
-	else
-		return jingle_rtp_initiate_media(gc->proto_data, who, type);
-#else
+	jb = jabber_buddy_find(js, who, FALSE);
+
+	if(!jb || !jb->resources) {
+		/* no resources online, we're trying to initiate with someone
+		 * whose presence we're not subscribed to, or
+		 * someone who is offline.  Let's inform the user */
+		char *msg;
+
+		if(!jb) {
+			msg = g_strdup_printf(_("Unable to initiate media with %s, invalid JID"), who);
+		} else if(jb->subscription & JABBER_SUB_TO) {
+			msg = g_strdup_printf(_("Unable to initiate media with %s, user is not online"), who);
+		} else {
+			msg = g_strdup_printf(_("Unable to initiate media with %s, not subscribed to user presence"), who);
+		}
+
+		purple_notify_error(js->gc, _("Media Initiation Failed"),
+				_("Media Initiation Failed"), msg);
+		g_free(msg);
+		return FALSE;
+	} else if(!jb->resources->next) {
+		/* only 1 resource online (probably our most common case)
+		 * so no need to ask who to initiate with */
+		gchar *name;
+		gboolean result;
+		jbr = jb->resources->data;
+		name = g_strdup_printf("%s/%s", who, jbr->name);
+		result = jabber_initiate_media(gc, name, type);
+		g_free(name);
+		return result;
+	} else {
+		/* we've got multiple resources,
+		 * we need to pick one to initiate with */
+		GList *l;
+		char *msg;
+		PurpleRequestFields *fields;
+		PurpleRequestField *field = purple_request_field_choice_new(
+				"resource", _("Resource"), 0);
+		PurpleRequestFieldGroup *group;
+		JabberMediaRequest *request;
+
+		for(l = jb->resources; l; l = l->next)
+		{
+			JabberBuddyResource *ljbr = l->data;
+			PurpleMediaCaps caps;
+			gchar *name;
+			name = g_strdup_printf("%s/%s", who, ljbr->name);
+			caps = jabber_get_media_caps(gc, name);
+			g_free(name);
+
+			if ((type & PURPLE_MEDIA_AUDIO) &&
+					(type & PURPLE_MEDIA_VIDEO)) {
+				if (caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) {
+					jbr = ljbr;
+					purple_request_field_choice_add(
+							field, jbr->name);
+				}
+			} else if (type & (PURPLE_MEDIA_AUDIO) &&
+					(caps & PURPLE_MEDIA_CAPS_AUDIO)) {
+				jbr = ljbr;
+				purple_request_field_choice_add(
+						field, jbr->name);
+			}else if (type & (PURPLE_MEDIA_VIDEO) &&
+					(caps & PURPLE_MEDIA_CAPS_VIDEO)) {
+				jbr = ljbr;
+				purple_request_field_choice_add(
+						field, jbr->name);
+			}
+		}
+
+		if (jbr == NULL) {
+			purple_debug_error("jabber",
+					"No resources available\n");
+			return FALSE;
+		}
+
+		if (g_list_length(purple_request_field_choice_get_labels(
+				field)) <= 1) {
+			gchar *name;
+			gboolean result;
+			purple_request_field_destroy(field);
+			name = g_strdup_printf("%s/%s", who, jbr->name);
+			result = jabber_initiate_media(gc, name, type);
+			g_free(name);
+			return result;
+		}
+
+		msg = g_strdup_printf(_("Please select the resource of %s to which you would like to start a media session with."), who);
+		fields = purple_request_fields_new();
+		group =	purple_request_field_group_new(NULL);
+		request = g_new0(JabberMediaRequest, 1);
+		request->pc = gc;
+		request->who = g_strdup(who);
+		request->type = type;
+
+		purple_request_field_group_add_field(group, field);
+		purple_request_fields_add_group(fields, group);
+		purple_request_fields(gc, _("Select a Resource"), msg, NULL,
+				fields,	_("Initiate Media"),
+				G_CALLBACK(jabber_media_ok_cb), _("Cancel"),
+				G_CALLBACK(jabber_media_cancel_cb),
+				gc->account, who, NULL, request);
+
+		g_free(msg);
+		return TRUE;
+	}
+#endif
 	return FALSE;
-#endif
 }
 
 PurpleMediaCaps jabber_get_media_caps(PurpleConnection *gc, const char *who)
@@ -2650,7 +2797,9 @@
 #ifdef USE_VV
 	JabberStream *js = (JabberStream *) gc->proto_data;
 	JabberBuddy *jb;
+	JabberBuddyResource *jbr;
 	PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE;
+	gchar *resource;
 
 	if (!js) {
 		purple_debug_info("jabber",
@@ -2658,35 +2807,75 @@
 		return FALSE;
 	}
 
-	jb = jabber_buddy_find(js, who, FALSE);
-
-	if (!jb) {
-		purple_debug_error("jabber", "Could not find buddy\n");
-		return FALSE;
+	if ((resource = jabber_get_resource(who)) != NULL) {
+		/* they've specified a resource, no need to ask or
+		 * default or anything, just do it */
+
+		jb = jabber_buddy_find(js, who, FALSE);
+		jbr = jabber_buddy_find_resource(jb, resource);
+		g_free(resource);
+
+		if (!jbr) {
+			purple_debug_error("jabber", "jabber_get_media_caps:"
+					" Can't find resource %s\n", who);
+			return caps;
+		}
+
+		if (jabber_resource_has_capability(jbr,
+				JINGLE_APP_RTP_SUPPORT_AUDIO))
+			caps |= PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION |
+					PURPLE_MEDIA_CAPS_AUDIO;
+		if (jabber_resource_has_capability(jbr,
+				JINGLE_APP_RTP_SUPPORT_VIDEO))
+			caps |= PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION |
+					PURPLE_MEDIA_CAPS_VIDEO;
+		if (caps & PURPLE_MEDIA_CAPS_AUDIO && caps &
+				PURPLE_MEDIA_CAPS_VIDEO)
+			caps |= PURPLE_MEDIA_CAPS_AUDIO_VIDEO;
+		if (caps != PURPLE_MEDIA_CAPS_NONE) {
+			if (!jabber_resource_has_capability(jbr,
+					JINGLE_TRANSPORT_ICEUDP) &&
+					!jabber_resource_has_capability(jbr,
+					JINGLE_TRANSPORT_RAWUDP)) {
+				purple_debug_info("jingle-rtp", "Buddy doesn't "
+						"support the same transport types\n");
+				caps = PURPLE_MEDIA_CAPS_NONE;
+			} else
+				caps |= PURPLE_MEDIA_CAPS_MODIFY_SESSION |
+						PURPLE_MEDIA_CAPS_CHANGE_DIRECTION;
+		}
+		if (jabber_resource_has_capability(jbr, GOOGLE_VOICE_CAP))
+			caps |= PURPLE_MEDIA_CAPS_AUDIO;
+		return caps;
 	}
 
-	if (jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_AUDIO))
-		caps |= PURPLE_MEDIA_CAPS_AUDIO |
-				PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION;
-	if (jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_VIDEO))
-		caps |= PURPLE_MEDIA_CAPS_VIDEO |
-				PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION;
-	if (caps & PURPLE_MEDIA_CAPS_AUDIO && caps & PURPLE_MEDIA_CAPS_VIDEO)
-		caps |= PURPLE_MEDIA_CAPS_AUDIO_VIDEO;
-	if (caps != PURPLE_MEDIA_CAPS_NONE) {
-		if (!jabber_buddy_has_capability(jb,
-				JINGLE_TRANSPORT_ICEUDP) &&
-				!jabber_buddy_has_capability(jb,
-				JINGLE_TRANSPORT_RAWUDP)) {
-			purple_debug_info("jingle-rtp", "Buddy doesn't "
-					"support the same transport types\n");
-			caps = PURPLE_MEDIA_CAPS_NONE;
-		} else
-			caps |= PURPLE_MEDIA_CAPS_MODIFY_SESSION |
-					PURPLE_MEDIA_CAPS_CHANGE_DIRECTION;
+	jb = jabber_buddy_find(js, who, FALSE);
+
+	if(!jb || !jb->resources) {
+		/* no resources online, we're trying to get caps for someone
+		 * whose presence we're not subscribed to, or
+		 * someone who is offline. */
+		return caps;
+	} else if(!jb->resources->next) {
+		/* only 1 resource online (probably our most common case) */
+		gchar *name;
+		jbr = jb->resources->data;
+		name = g_strdup_printf("%s/%s", who, jbr->name);
+		caps = jabber_get_media_caps(gc, name);
+		g_free(name);
+	} else {
+		/* we've got multiple resources, combine their caps */
+		GList *l;
+
+		for(l = jb->resources; l; l = l->next)
+		{
+			gchar *name;
+			jbr = l->data;
+			name = g_strdup_printf("%s/%s", who, jbr->name);
+			caps |= jabber_get_media_caps(gc, name);
+			g_free(name);
+		}
 	}
-	if (jabber_buddy_has_capability(jb, GOOGLE_VOICE_CAP))
-		caps |= PURPLE_MEDIA_CAPS_AUDIO;
 
 	return caps;
 #else