From bf6d71e3810d1f0095035ffd51895ddcf239eaea Mon Sep 17 00:00:00 2001
From: Federico Cabiddu <federico.cabiddu@gmail.com>
Date: Tue, 9 Sep 2014 14:14:46 +0000
Subject: [PATCH] tm: new api function t_append_branches

---
 modules/tm/h_table.h           |    2 +
 modules/tm/t_append_branches.c |  188 ++++++++++++++++++++++++++++++++++++++++
 modules/tm/t_append_branches.h |   48 ++++++++++
 modules/tm/t_fwd.c             |   14 ++-
 modules/tm/t_fwd.h             |   10 ++
 modules/tm/tm_load.c           |    1 +
 modules/tm/tm_load.h           |    2 +
 7 files changed, 260 insertions(+), 5 deletions(-)
 create mode 100644 modules/tm/t_append_branches.c
 create mode 100644 modules/tm/t_append_branches.h

diff --git a/modules/tm/h_table.h b/modules/tm/h_table.h
index 93b84ea..50a711d 100644
--- a/modules/tm/h_table.h
+++ b/modules/tm/h_table.h
@@ -472,6 +472,8 @@ typedef struct cell
 	unsigned short on_reply;
 	 /* The route to take for each downstream branch separately */
 	unsigned short on_branch;
+	 /* branch route backup for late branch add (t_append_branch) */
+	unsigned short on_branch_delayed;
 
 	/* place holder for MD5checksum, MD5_LEN bytes are extra alloc'ed */
 	char md5[0];
diff --git a/modules/tm/t_append_branches.c b/modules/tm/t_append_branches.c
new file mode 100644
index 0000000..2108bf2
--- /dev/null
+++ b/modules/tm/t_append_branches.c
@@ -0,0 +1,188 @@
+/*
+ * $Id$
+ *
+ * transaction maintenance functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    info@iptel.org
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * History:
+ * -------
+ *  2014-09-09  first release of t_append_branches
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../mod_fix.h"
+#include "../../route.h"
+#include "../../data_lump.h"
+#include "../../lib/kcore/kstats_wrapper.h"
+#include "../../dset.h"
+#include "../../script_cb.h"
+#include "../../parser/msg_parser.h"
+#include "../../parser/contact/parse_contact.h"
+#include "t_msgbuilder.h"
+#include "t_lookup.h"
+#include "t_fwd.h"
+#include "t_append_branches.h"
+
+int t_append_branches(void) {
+	struct cell *t = NULL;
+	struct sip_msg *orig_msg;
+	short outgoings;
+
+	int success_branch;
+
+	str current_uri;
+	str dst_uri, path, instance, ruid, location_ua;
+	struct socket_info* si;
+	int q, i, found;
+	flag_t backup_bflags = 0;
+	flag_t bflags = 0;
+	int new_branch, branch_ret, lowest_ret;
+	branch_bm_t	added_branches;
+
+	t = get_t();
+	if(t == NULL)
+	{
+		LM_ERR("cannot get transaction\n");
+		return -1;
+	}
+
+	LM_DBG("transaction %u:%u in status %d\n", t->hash_index, t->label, t->uas.status);
+
+	if ((t->uas.status >= 200 && t->uas.status<=399)
+			|| ((t->uas.status >= 600 && t->uas.status)
+				&& !(t->flags & (T_6xx | T_DISABLE_6xx))) ) {
+		LM_DBG("transaction %u:%u in status %d: cannot append new branch\n", t->hash_index, t->label, t->uas.status);
+		return -1;
+	}
+
+	/* set the lock on the transaction here */
+	LOCK_REPLIES(t);
+	outgoings = t->nr_of_outgoings;
+	orig_msg = t->uas.request;
+
+	LM_DBG("Call %.*s: %d (%d) outgoing branches\n",orig_msg->callid->body.len, orig_msg->callid->body.s,outgoings, nr_branches);
+
+	lowest_ret=E_UNSPEC;
+	added_branches=0;
+
+	/* it's a "late" branch so the on_branch variable has already been
+	reset by previous execution of t_forward_nonack: we use the saved
+	   value  */
+	if (t->on_branch_delayed) {
+		/* tell add_uac that it should run branch route actions */
+		set_branch_route(t->on_branch_delayed);
+	}
+
+	outgoings = t->nr_of_outgoings;
+
+	/* not really sure that the following is needed */
+
+	set_branch_iterator(nr_branches-1);
+	found = 0;
+	while((current_uri.s=next_branch( &current_uri.len, &q, &dst_uri, &path,
+										&bflags, &si, &ruid, &instance, &location_ua))) {
+		LM_DBG("Current uri %.*s\n",current_uri.len, current_uri.s);
+
+		for (i=0; i<nr_branches; i++) {
+			if (t->uac[i].ruid.len == ruid.len
+					&& !memcmp(t->uac[i].ruid.s, ruid.s, ruid.len)) {
+				LM_DBG("branch already added [%.*s]\n", ruid.len, ruid.s);
+				found = 1;
+				break;
+			}
+		}
+		if (found)
+			continue;
+
+		setbflagsval(0, bflags);
+		new_branch=add_uac( t, orig_msg, &current_uri,
+					(dst_uri.len) ? (&dst_uri) : &current_uri,
+					&path, 0, si, orig_msg->fwd_send_flags,
+					orig_msg->rcv.proto, (dst_uri.len)?-1:UAC_SKIP_BR_DST_F, &instance,
+					&ruid, &location_ua);
+		if (new_branch>=0)
+			added_branches |= 1<<new_branch;
+		else
+			lowest_ret=MIN_int(lowest_ret, new_branch);
+	}
+
+	clear_branches();
+
+	LM_DBG("Call %.*s: %d (%d) outgoing branches after clear_branches()\n",orig_msg->callid->body.len, orig_msg->callid->body.s,outgoings, nr_branches);
+	setbflagsval(0, backup_bflags);
+
+	/* update message flags, if changed in branch route */
+	t->uas.request->flags = orig_msg->flags;
+
+	if (added_branches==0) {
+		if(lowest_ret!=E_CFG)
+			LOG(L_ERR, "ERROR: t_append_branch: failure to add branches\n");
+		ser_error=lowest_ret;
+		UNLOCK_REPLIES(t);
+		return lowest_ret;
+	}
+
+	ser_error=0; /* clear branch adding errors */
+	/* send them out now */
+	success_branch=0;
+	/* since t_append_branch can only be called from REQUEST_ROUTE, always lock replies */
+
+	for (i=outgoings; i<t->nr_of_outgoings; i++) {
+		if (added_branches & (1<<i)) {
+			branch_ret=t_send_branch(t, i, orig_msg , 0, 0 /* replies are already locked */ );
+			if (branch_ret>=0){ /* some kind of success */
+				if (branch_ret==i) { /* success */
+					success_branch++;
+					if (unlikely(has_tran_tmcbs(t, TMCB_REQUEST_OUT)))
+						run_trans_callbacks_with_buf( TMCB_REQUEST_OUT, &t->uac[nr_branches].request,
+								orig_msg, 0, -orig_msg->REQ_METHOD);
+				}
+				else /* new branch added */
+					added_branches |= 1<<branch_ret;
+			}
+		}
+	}
+	if (success_branch<=0) {
+		/* return always E_SEND for now
+		 * (the real reason could be: denied by onsend routes, blacklisted,
+		 *  send failed or any of the errors listed before + dns failed
+		 *  when attempting dns failover) */
+		ser_error=E_SEND;
+		/* else return the last error (?) */
+		/* the caller should take care and delete the transaction */
+		UNLOCK_REPLIES(t);
+		return -1;
+	}
+
+	ser_error=0; /* clear branch send errors, we have overall success */
+	set_kr(REQ_FWDED);
+	UNLOCK_REPLIES(t);
+	return 1;
+}
diff --git a/modules/tm/t_append_branches.h b/modules/tm/t_append_branches.h
new file mode 100644
index 0000000..31e9b16
--- /dev/null
+++ b/modules/tm/t_append_branches.h
@@ -0,0 +1,45 @@
+/*
+ * $Id$
+ *
+ * transaction maintenance functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    info@iptel.org
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * History:
+ * -------
+ *  2014-09-09  first release of t_append_branches
+ */
+
+#ifndef _T_APPEND_BRANCHES_H
+#define _T_APPEND_BRANCHES_H
+
+#include "defs.h"
+
+#include "../../proxy.h"
+#include "h_table.h"
+
+int t_append_branches(void);
+typedef int (*t_append_branches_f)(void);
+
+#endif
diff --git a/modules/tm/t_fwd.c b/modules/tm/t_fwd.c
index 50a31cd..3903679 100644
--- a/modules/tm/t_fwd.c
+++ b/modules/tm/t_fwd.c
@@ -150,12 +150,12 @@ unsigned int get_on_branch(void)
 	return goto_on_branch;
 }
 
+void set_branch_route( unsigned int on_branch)
+{
+	branch_route = on_branch;
+}
 
 
-/* prepare_new_uac flags */
-#define UAC_DNS_FAILOVER_F 1 /**< new branch due to dns failover */
-#define UAC_SKIP_BR_DST_F  2 /**< don't set next hop as dst_uri for
-							   branch_route */
 
 
 /** prepares a new branch "buffer".
@@ -797,7 +797,7 @@ int add_blind_uac( /*struct cell *t*/ )
  *                    @see prepare_new_uac().
  *  @returns branch id (>=0) or error (<0)
 */
-static int add_uac( struct cell *t, struct sip_msg *request, str *uri,
+int add_uac( struct cell *t, struct sip_msg *request, str *uri,
 					str* next_hop, str* path, struct proxy_l *proxy,
 					struct socket_info* fsocket, snd_flags_t snd_flags,
 					int proto, int flags, str *instance, str *ruid,
@@ -1693,6 +1693,10 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 	if (t->on_branch) {
 		/* tell add_uac that it should run branch route actions */
 		branch_route = t->on_branch;
+		/* save the branch route so that it
+		 * can be used for adding branches later
+		 */
+		t->on_branch_delayed = t->on_branch;
 		/* reset the flag before running the actions (so that it
 		 * could be set again in branch_route if needed
 		 */
diff --git a/modules/tm/t_fwd.h b/modules/tm/t_fwd.h
index ea592a5..00d41fc 100644
--- a/modules/tm/t_fwd.h
+++ b/modules/tm/t_fwd.h
@@ -52,6 +52,7 @@ typedef int (*taddblind_f)( /*struct cell *t */ void);
 typedef int (*treplicate_uri_f)(struct sip_msg* p_msg , str *suri );
 
 void t_on_branch(unsigned int go_to);
+void set_branch_route(unsigned int on_branch);
 unsigned int get_on_branch(void);
 int t_replicate_uri(struct sip_msg *p_msg, str *suri);
 int t_replicate(struct sip_msg *p_msg, struct proxy_l * proxy, int proto);
@@ -65,6 +66,15 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, struct
 int add_uac(struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 				str* path, struct proxy_l *proxy, int proto );
 */
+
+/* prepare_new_uac flags */
+#define UAC_DNS_FAILOVER_F 1 /**< new branch due to dns failover */
+#define UAC_SKIP_BR_DST_F  2 /**< don't set next hop as dst_uri for
+							   branch_route */
+int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
+			str* path, struct proxy_l *proxy, struct socket_info* fsocket,
+			snd_flags_t snd_flags, int proto, int flags, str *instance, str *ruid,
+			str *location_ua);
 #ifdef USE_DNS_FAILOVER
 int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg, 
 									struct ua_client* old_uac,
diff --git a/modules/tm/tm_load.c b/modules/tm/tm_load.c
index 607bac2..e95d618 100644
--- a/modules/tm/tm_load.c
+++ b/modules/tm/tm_load.c
@@ -147,6 +147,7 @@ int load_tm( struct tm_binds *tmb)
 #ifdef WITH_TM_CTX
 	tmb->tm_ctx_get = tm_ctx_get;
 #endif
+	tmb->t_append_branches = t_append_branches;
 	return 1;
 }
 
diff --git a/modules/tm/tm_load.h b/modules/tm/tm_load.h
index f95dd3d..06905c2 100644
--- a/modules/tm/tm_load.h
+++ b/modules/tm/tm_load.h
@@ -49,6 +49,7 @@
 #include "callid.h"
 #include "t_cancel.h"
 #include "t_suspend.h"
+#include "t_append_branches.h"
 #include "t_stats.h"
 
 /* export not usable from scripts */
@@ -133,6 +134,7 @@ struct tm_binds {
 #else
 	void* reserved5;
 #endif
+	t_append_branches_f	t_append_branches;
 };
 
 typedef struct tm_binds tm_api_t;
-- 
1.7.1

