Index: channels/chan_gtalk.c
===================================================================
--- channels/chan_gtalk.c	(revision 84744)
+++ channels/chan_gtalk.c	(working copy)
@@ -184,6 +184,8 @@
 static int gtalk_call(struct ast_channel *ast, char *dest, int timeout);
 static int gtalk_hangup(struct ast_channel *ast);
 static int gtalk_answer(struct ast_channel *ast);
+static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action);
+static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p);
 static int gtalk_newcall(struct gtalk *client, ikspak *pak);
 static struct ast_frame *gtalk_read(struct ast_channel *ast);
 static int gtalk_write(struct ast_channel *ast, struct ast_frame *f);
@@ -299,6 +301,7 @@
 
 static int add_codec_to_answer(const struct gtalk_pvt *p, int codec, iks *dcodecs)
 {
+	int res = 0;
 	char *format = ast_getformatname(codec);
 
 	if (!strcasecmp("ulaw", format)) {
@@ -324,6 +327,7 @@
 		iks_insert_attrib(payload_eg711u, "bitrate","64000");
 		iks_insert_node(dcodecs, payload_pcmu);
 		iks_insert_node(dcodecs, payload_eg711u);
+		res ++;
 	}
 	if (!strcasecmp("alaw", format)) {
 		iks *payload_eg711a, *payload_pcma;
@@ -348,6 +352,7 @@
 		iks_insert_attrib(payload_eg711a, "bitrate","64000");
 		iks_insert_node(dcodecs, payload_pcma);
 		iks_insert_node(dcodecs, payload_eg711a);
+		res ++;
 	}
 	if (!strcasecmp("ilbc", format)) {
 		iks *payload_ilbc = iks_new("payload-type");
@@ -360,6 +365,7 @@
 		iks_insert_attrib(payload_ilbc, "clockrate","8000");
 		iks_insert_attrib(payload_ilbc, "bitrate","13300");
 		iks_insert_node(dcodecs, payload_ilbc);
+		res ++;
 	}
 	if (!strcasecmp("g723", format)) {
 		iks *payload_g723 = iks_new("payload-type");
@@ -372,6 +378,7 @@
 		iks_insert_attrib(payload_g723, "clockrate","8000");
 		iks_insert_attrib(payload_g723, "bitrate","6300");
 		iks_insert_node(dcodecs, payload_g723);
+		res ++;
 	}
 	if (!strcasecmp("speex", format)) {
 		iks *payload_speex = iks_new("payload-type");
@@ -384,9 +391,21 @@
 		iks_insert_attrib(payload_speex, "clockrate","8000");
 		iks_insert_attrib(payload_speex, "bitrate","11000");
 		iks_insert_node(dcodecs, payload_speex);
+		res++;
 	}
+	if (!strcasecmp("gsm", format)) {
+		iks *payload_gsm = iks_new("payload-type");
+		if(!payload_gsm) {
+			ast_log(LOG_WARNING,"Failed to allocate iks node");
+			return -1;
+		}
+		iks_insert_attrib(payload_gsm, "id", "103");
+		iks_insert_attrib(payload_gsm, "name", "gsm");
+		iks_insert_node(dcodecs, payload_gsm);
+		res++;
+	}
 	ast_rtp_lookup_code(p->rtp, 1, codec);
-	return 0;
+	return res;
 }
 
 static int gtalk_invite(struct gtalk_pvt *p, char *to, char *from, char *sid, int initiator)
@@ -396,8 +415,8 @@
 	int x;
 	int pref_codec = 0;
 	int alreadysent = 0;
+	int codecs_num = 0;
 
-
 	iq = iks_new("iq");
 	gtalk = iks_new("session");
 	dcodecs = iks_new("description");
@@ -428,14 +447,16 @@
 			continue;
 		if (alreadysent & pref_codec)
 			continue;
-		add_codec_to_answer(p, pref_codec, dcodecs);
+		codecs_num = add_codec_to_answer(p, pref_codec, dcodecs);
 		alreadysent |= pref_codec;
 	}
 	
-	iks_insert_attrib(payload_telephone, "id", "106");
-	iks_insert_attrib(payload_telephone, "name", "telephone-event");
-	iks_insert_attrib(payload_telephone, "clockrate", "8000");
-	
+	if (codecs_num) {
+		/* only propose DTMF within an audio session */
+		iks_insert_attrib(payload_telephone, "id", "106");
+		iks_insert_attrib(payload_telephone, "name", "telephone-event");
+		iks_insert_attrib(payload_telephone, "clockrate", "8000");
+	}
 	iks_insert_attrib(transport,"xmlns","http://www.google.com/transport/p2p");
 	
 	iks_insert_attrib(iq, "type", "set");
@@ -603,13 +624,42 @@
 {
 	struct gtalk_pvt *tmp;
 	char *from;
+	iks *codec;
+	char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
+	int peernoncodeccapability;
+
 	ast_log(LOG_DEBUG, "The client is %s\n", client->name);
 	/* Make sure our new call doesn't exist yet */
 	for (tmp = client->p; tmp; tmp = tmp->next) {
 		if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid))
 			break;
 	}
+
+	/* codec points to the first <payload-type/> tag */
+	codec = iks_child(iks_child(iks_child(pak->x)));
+	while (codec) {
+		ast_rtp_set_m_type(tmp->rtp, atoi(iks_find_attrib(codec, "id")));
+		ast_rtp_set_rtpmap_type(tmp->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0);
+		codec = iks_next(codec);
+	}
 	
+	/* Now gather all of the codecs that we are asked for */
+	ast_rtp_get_current_formats(tmp->rtp, &tmp->peercapability, &peernoncodeccapability);
+	
+	/* at this point, we received an awser from the remote Gtalk client,
+	   which allows us to compare capabilities */
+	tmp->jointcapability = tmp->capability & tmp->peercapability;
+	if (!tmp->jointcapability) {
+		ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, tmp->capability),
+			ast_getformatname_multiple(s2, BUFSIZ, tmp->peercapability),
+			ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcapability));
+		/* close session if capabilities don't match */
+		ast_queue_hangup(tmp->owner);
+
+		return -1;
+
+	}	
+	
 	from = iks_find_attrib(pak->x, "to");
 	if(!from)
 		from = client->connection->jid->full;
@@ -911,7 +961,16 @@
 		ast_copy_string(tmp->us, us, sizeof(tmp->us));
 		tmp->initiator = 1;
 	}
+	/* clear codecs */
 	tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+	ast_rtp_pt_clear(tmp->rtp);
+
+	/* add user configured codec capabilites */
+	if (client->capability)
+		tmp->capability = client->capability;
+	else if (global_capability)
+		tmp->capability = global_capability;
+
 	tmp->parent = client;
 	if (!tmp->rtp) {
 		ast_log(LOG_WARNING, "Out of RTP sessions?\n");
@@ -955,7 +1014,6 @@
 
 	/* Select our native format based on codec preference until we receive
 	   something from another device to the contrary. */
-/*	ast_verbose("XXXXXXXXXXXXX\nXXX i->jointcapability = %X\nXXX i->capability = %X\nXXX global_capability %X\n XXXXXXXXXXXX\n",i->jointcapability,i->capability,global_capability); */
 	if (i->jointcapability)
 		what = i->jointcapability;
 	else if (i->capability)
@@ -1101,6 +1159,9 @@
 	int res;
 	iks *codec;
 	char *from = NULL;
+	char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ];
+	int peernoncodeccapability;
+
 	/* Make sure our new call doesn't exist yet */
 	from = iks_find_attrib(pak->x,"to");
 	if(!from)
@@ -1120,46 +1181,65 @@
 		ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n");
 		return -1;
 	}
+
 	chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user);
-	if (chan) {
-		ast_mutex_lock(&p->lock);
-		ast_copy_string(p->them, pak->from->full, sizeof(p->them));
-		if (iks_find_attrib(pak->query, "id")) {
-			ast_copy_string(p->sid, iks_find_attrib(pak->query, "id"),
-							sizeof(p->sid));
-		}
+	if (!chan) {
+		gtalk_free_pvt(client, p);
+		return -1;
+	}
 
-		codec = iks_child(iks_child(iks_child(pak->x)));
-		while (codec) {
-			ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id")));
-			ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio",
-						iks_find_attrib(codec, "name"), 0);
-			codec = iks_next(codec);
-		}
+	ast_mutex_lock(&p->lock);
+	ast_copy_string(p->them, pak->from->full, sizeof(p->them));
+	if (iks_find_attrib(pak->query, "id")) {
+		ast_copy_string(p->sid, iks_find_attrib(pak->query, "id"),
+				sizeof(p->sid));
+	}
+
+	/* codec points to the first <payload-type/> tag */	
+	codec = iks_child(iks_child(iks_child(pak->x)));
+	
+	while (codec) {
+		ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id")));
+		ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0);
+		codec = iks_next(codec);
+	}
+	
+	/* Now gather all of the codecs that we are asked for */
+	ast_rtp_get_current_formats(p->rtp, &p->peercapability, &peernoncodeccapability);
+	p->jointcapability = p->capability & p->peercapability;
+	ast_mutex_unlock(&p->lock);
 		
-		ast_mutex_unlock(&p->lock);
-		ast_setstate(chan, AST_STATE_RING);
-		res = ast_pbx_start(chan);
+	ast_setstate(chan, AST_STATE_RING);
+	if (!p->jointcapability) {
+		ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->capability),
+			ast_getformatname_multiple(s2, BUFSIZ, p->peercapability),
+			ast_getformatname_multiple(s3, BUFSIZ, p->jointcapability));
+			/* close session if capabilities don't match */
+		gtalk_action(client, p, "reject");
+		p->alreadygone = 1;
+		gtalk_hangup(chan);
+		return -1;
+	}	
 
-		switch (res) {
-		case AST_PBX_FAILED:
-			ast_log(LOG_WARNING, "Failed to start PBX :(\n");
-			gtalk_response(client, from, pak, "service-unavailable", NULL);
-			break;
-		case AST_PBX_CALL_LIMIT:
-			ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
-			gtalk_response(client, from, pak, "service-unavailable", NULL);
-			break;
-		case AST_PBX_SUCCESS:
-			gtalk_response(client, from, pak, NULL, NULL);
-			gtalk_invite_response(p, p->them, p->us,p->sid, 0);
-			gtalk_create_candidates(client, p, p->sid, p->them, p->us);
-			/* nothing to do */
-			break;
-		}
-	} else {
-		gtalk_free_pvt(client, p);
+	res = ast_pbx_start(chan);
+	
+	switch (res) {
+	case AST_PBX_FAILED:
+		ast_log(LOG_WARNING, "Failed to start PBX :(\n");
+		gtalk_response(client, from, pak, "service-unavailable", NULL);
+		break;
+	case AST_PBX_CALL_LIMIT:
+		ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
+		gtalk_response(client, from, pak, "service-unavailable", NULL);
+		break;
+	case AST_PBX_SUCCESS:
+		gtalk_response(client, from, pak, NULL, NULL);
+		gtalk_invite_response(p, p->them, p->us,p->sid, 0);
+		gtalk_create_candidates(client, p, p->sid, p->them, p->us);
+		/* nothing to do */
+		break;
 	}
+
 	return 1;
 }
 
@@ -1507,7 +1587,6 @@
 	}
 
 	ast_setstate(ast, AST_STATE_RING);
-	p->jointcapability = p->capability;
 	if (!p->ringrule) {
 		ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring));
 		p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p,
