From 9af30563e29bc250d2ef75d06e593774811e4699 Mon Sep 17 00:00:00 2001
From: Anthony Minessale <anthm@freeswitch.org>
Date: Wed, 1 Sep 2010 14:22:29 -0500
Subject: [PATCH] make mod_dingaling compat with google's new free phonecalls
 thing

---
 libs/libdingaling/src/libdingaling.c | 170 ++++++++++++++++++++++++---
 libs/libdingaling/src/libdingaling.h |  12 +-
 2 files changed, 161 insertions(+), 21 deletions(-)

diff --git a/libs/libdingaling/src/libdingaling.c b/libs/libdingaling/src/libdingaling.c
index 353dd139ab..ef6e7447a1 100644
--- a/libs/libdingaling/src/libdingaling.c
+++ b/libs/libdingaling/src/libdingaling.c
@@ -163,6 +163,7 @@ struct ldl_session {
 	apr_hash_t *variables;
 	apr_time_t created;
 	void *private_data;
+	ldl_user_flag_t flags;
 };
 
 static int on_disco_default(void *user_data, ikspak *pak);
@@ -346,6 +347,7 @@ ldl_status ldl_session_create(ldl_session_t **session_p, ldl_handle_t *handle, c
 	session->created = apr_time_now();
 	session->state = LDL_STATE_NEW;
 	session->variables = apr_hash_make(session->pool);
+	session->flags = flags;
 	*session_p = session;
 
 
@@ -387,7 +389,24 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
 		
 		if (type) {
 			
-			if (!strcasecmp(type, "initiate") || !strcasecmp(type, "accept")) {
+			if (!strcasecmp(type, "redirect")) {
+				apr_hash_t *hash = session->handle->sessions;
+				char *p = to;
+				if ((p = strchr(to, ':'))) {
+					p++;
+				} else {
+					p = to;
+				}
+						
+
+				apr_hash_set(hash, session->them, APR_HASH_KEY_STRING, NULL);
+				apr_hash_set(hash, session->id, APR_HASH_KEY_STRING, NULL);
+				session->them = apr_pstrdup(session->pool, p);
+				apr_hash_set(handle->sessions, session->them, APR_HASH_KEY_STRING, session);
+				apr_hash_set(handle->sessions, session->id, APR_HASH_KEY_STRING, session);
+
+				dl_signal = LDL_SIGNAL_REDIRECT;
+			} else if (!strcasecmp(type, "initiate") || !strcasecmp(type, "accept")) {
 
 				dl_signal = LDL_SIGNAL_INITIATE;
 
@@ -499,6 +518,12 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
 						if ((key = iks_find_attrib(tag, "port"))) {
 							session->candidates[index].port = (uint16_t)atoi(key);
 						}
+						
+						if (!session->candidates[index].type) {
+							session->candidates[index].type = apr_pstrdup(session->pool, "stun");
+						}
+
+
 						if (globals.debug) {
 							globals.logger(DL_LOG_DEBUG, 
 									"New Candidate %d\n"
@@ -947,6 +972,18 @@ static void cancel_retry(ldl_handle_t *handle, char *id)
 	apr_thread_mutex_unlock(handle->lock);
 }
 
+static iks* working_find(iks *tag, const char *name) 
+{
+	while(tag) {
+		if (!strcasecmp(iks_name(tag), name)) {
+			return tag;
+		}
+		tag = iks_next_tag(tag);
+	}
+	
+	return NULL;
+}
+
 static int on_commands(void *user_data, ikspak *pak)
 {
 	ldl_handle_t *handle = user_data;
@@ -956,8 +993,22 @@ static int on_commands(void *user_data, ikspak *pak)
 	char *type = iks_find_attrib(pak->x, "type");
 	uint8_t is_result = strcasecmp(type, "result") ? 0 : 1;
 	uint8_t is_error = strcasecmp(type, "error") ? 0 : 1;
+	iks *xml, *xsession, *xerror = NULL, *xredir = NULL;
+
+	xml = iks_child (pak->x);
+
+	if (is_error) {
+		if ((xerror = working_find(xml, "error"))) {
+			char *code = iks_find_attrib(xerror, "code");
+			if (code && !strcmp(code, "302") && 
+				((xredir = iks_find(xerror, "ses:redirect")) || (xredir = iks_find(xerror, "redirect")))) {
+				is_result = 0;
+				is_error = 0;
+				cancel_retry(handle, iqid);
+			}
+		}
+	}
 
-	iks *xml;
 
 	if (is_result) {
 		iks *tag = iks_child (pak->x);
@@ -989,9 +1040,12 @@ static int on_commands(void *user_data, ikspak *pak)
 		}
 	}
 
+	
+
 	if ((is_result || is_error) && iqid && from) {
 
 		cancel_retry(handle, iqid);
+
 		if (is_result) {
 			if (handle->response_callback) {
 				handle->response_callback(handle, iqid); 
@@ -999,30 +1053,37 @@ static int on_commands(void *user_data, ikspak *pak)
 			return IKS_FILTER_EAT;
 		} else if (is_error) {
 			return IKS_FILTER_EAT;
+
 		}
 	}
 	
-	xml = iks_child (pak->x);
-	while (xml) {
-		char *name = iks_name_nons(xml);
-		if (!strcasecmp(name, "session")) {
-			char *id = iks_find_attrib(xml, "id");
-			//printf("SESSION type=%s name=%s id=%s\n", type, name, id);
-			if (parse_session_code(handle, id, from, to, xml, strcasecmp(type, "error") ? NULL : type) == LDL_STATUS_SUCCESS) {
-				iks *reply;
-				if ((reply = iks_make_iq(IKS_TYPE_RESULT, NULL))) {
-					iks_insert_attrib(reply, "to", from);
-					iks_insert_attrib(reply, "from", to);
-					iks_insert_attrib(reply, "id", iqid);
-					apr_queue_push(handle->queue, reply);
-					reply = NULL;
-				}
+	
+	if ((xsession = working_find(xml, "ses:session")) || (xsession = working_find(xml, "session"))) {
+		char *id;
+
+		id = iks_find_attrib(xsession, "id");
+
+		if (xredir) {
+			to = iks_cdata(iks_child(xredir));
+			type = "redirect";
+		}
+
+		if (strcasecmp(type, "error") && strcasecmp(type, "redirect")) {
+			type = NULL;
+		}
+
+		if (parse_session_code(handle, id, from, to, xsession, type) == LDL_STATUS_SUCCESS) {
+			iks *reply;
+			if ((reply = iks_make_iq(IKS_TYPE_RESULT, NULL))) {
+				iks_insert_attrib(reply, "to", from);
+				iks_insert_attrib(reply, "from", to);
+				iks_insert_attrib(reply, "id", iqid);
+				apr_queue_push(handle->queue, reply);
+				reply = NULL;
 			}
 		}
-		xml = iks_next_tag(xml);
 	}
 
-
 	return IKS_FILTER_EAT;
 }
 
@@ -1922,6 +1983,69 @@ unsigned int ldl_session_terminate(ldl_session_t *session)
 }
 
 
+
+unsigned int ldl_session_transport(ldl_session_t *session,
+									ldl_candidate_t *candidates,
+									unsigned int clen)
+
+{
+	iks *iq, *sess, *tag;
+	unsigned int x, id = 0;
+
+
+	for (x = 0; x < clen; x++) {
+		char buf[512];
+		iq = NULL;
+		sess = NULL;
+		id = 0;
+		
+		new_session_iq(session, &iq, &sess, &id, "transport-info");
+		//tag = iks_insert(sess, "transport");
+		//iks_insert_attrib(tag, "xmlns", "http://www.google.com/transport/p2p");
+		tag = sess;
+
+		if (0) add_elements(session, tag);
+		tag = iks_insert(tag, "transport");
+		iks_insert_attrib(tag, "xmlns", "http://www.google.com/transport/p2p");
+
+		tag = iks_insert(tag, "candidate");
+
+		if (candidates[x].name) {
+			iks_insert_attrib(tag, "name", candidates[x].name);
+		}
+		if (candidates[x].address) {
+			iks_insert_attrib(tag, "address", candidates[x].address);
+		}
+		if (candidates[x].port) {
+			snprintf(buf, sizeof(buf), "%u", candidates[x].port);
+			iks_insert_attrib(tag, "port", buf);
+		}
+		if (candidates[x].username) {
+			iks_insert_attrib(tag, "username", candidates[x].username);
+		}
+		if (candidates[x].password) {
+			iks_insert_attrib(tag, "password", candidates[x].password);
+		}
+		if (candidates[x].pref) {
+			snprintf(buf, sizeof(buf), "%0.1f", candidates[x].pref);
+			iks_insert_attrib(tag, "preference", buf);
+		}
+		if (candidates[x].protocol) {
+			iks_insert_attrib(tag, "protocol", candidates[x].protocol);
+		}
+		if (candidates[x].type) {
+			iks_insert_attrib(tag, "type", candidates[x].type);
+		}
+
+		iks_insert_attrib(tag, "network", "0");
+		iks_insert_attrib(tag, "generation", "0");
+		schedule_packet(session->handle, id, iq, LDL_RETRY);
+	}
+
+
+	return id;
+}
+
 unsigned int ldl_session_candidates(ldl_session_t *session,
 									ldl_candidate_t *candidates,
 									unsigned int clen)
@@ -1981,6 +2105,8 @@ unsigned int ldl_session_candidates(ldl_session_t *session,
 	return id;
 }
 
+
+
 char *ldl_handle_probe(ldl_handle_t *handle, char *id, char *from, char *buf, unsigned int len)
 {
 	iks *pres, *msg;
@@ -2344,6 +2470,12 @@ int ldl_handle_running(ldl_handle_t *handle)
 	return ldl_test_flag(handle, LDL_FLAG_RUNNING) ? 1 : 0;
 }
 
+
+int ldl_session_gateway(ldl_session_t *session)
+{
+	return ldl_test_flag(session, LDL_FLAG_GATEWAY) ? 1 : 0;
+}
+
 int ldl_handle_connected(ldl_handle_t *handle)
 {
 	return ldl_test_flag(handle, LDL_FLAG_CONNECTED) ? 1 : 0;
diff --git a/libs/libdingaling/src/libdingaling.h b/libs/libdingaling/src/libdingaling.h
index 07a71b3987..d78643810c 100644
--- a/libs/libdingaling/src/libdingaling.h
+++ b/libs/libdingaling/src/libdingaling.h
@@ -131,7 +131,8 @@ typedef enum {
 	LDL_FLAG_SASL_PLAIN = (1 << 11),
 	LDL_FLAG_SASL_MD5 = (1 << 12),
 	LDL_FLAG_COMPONENT = (1 << 13),
-	LDL_FLAG_OUTBOUND = (1 << 14)
+	LDL_FLAG_OUTBOUND = (1 << 14),
+	LDL_FLAG_GATEWAY = (1 << 15)
 } ldl_user_flag_t;
 
 typedef enum {
@@ -152,7 +153,8 @@ typedef enum {
 	LDL_SIGNAL_LOGIN_FAILURE,
 	LDL_SIGNAL_CONNECTED,
 	LDL_SIGNAL_TRANSPORT_ACCEPT,
-	LDL_SIGNAL_REJECT
+	LDL_SIGNAL_REJECT,
+	LDL_SIGNAL_REDIRECT
 } ldl_signal_t;
 
 typedef enum {
@@ -494,6 +496,10 @@ unsigned int ldl_session_candidates(ldl_session_t *session,
 								  ldl_candidate_t *candidates,
 								  unsigned int clen);
 
+unsigned int ldl_session_transport(ldl_session_t *session,
+								  ldl_candidate_t *candidates,
+								  unsigned int clen);
+
 /*!
   \brief Initiate or Accept a new session and provide transport options
   \param session the session to initiate or accept
@@ -586,6 +592,8 @@ ldl_status ldl_handle_init(ldl_handle_t **handle,
 */
 void ldl_handle_run(ldl_handle_t *handle);
 
+int ldl_session_gateway(ldl_session_t *handle);
+
 /*!
   \brief Stop a libDingaLing handle
   \param handle the Dingaling handle to stop