From d71d8ed99525f982cb3eaf988f8e8d78fd5bf527 Mon Sep 17 00:00:00 2001
From: Richard Mudgett <rmudgett@digium.com>
Date: Wed, 9 May 2012 02:35:29 +0000
Subject: [PATCH] Keep answered FollowMe calls until call accepted or last step
 times out.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@365856 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 UPGRADE.txt                  |  5 ++++
 apps/app_followme.c          | 54 +++++++++++++++++++++++++++++++-----
 configs/followme.conf.sample | 13 +++++----
 3 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/UPGRADE.txt b/UPGRADE.txt
index adff551695..e5bbb6ae17 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -79,6 +79,11 @@ app_meetme:
   - The 'c' option (announce user count) will now work even if the 'q' (quiet)
     option is enabled.
 
+app_followme:
+ - Answered outgoing calls no longer get cut off when the next step is started.
+   You now have until the last step times out to decide if you want to accept
+   the call or not before being disconnected.
+
 SIP
 ===
  - A new option "tonezone" for setting default tonezone for the channel driver
diff --git a/apps/app_followme.c b/apps/app_followme.c
index 4f98113add..04439e4bf3 100644
--- a/apps/app_followme.c
+++ b/apps/app_followme.c
@@ -190,6 +190,8 @@ struct findme_user {
 	char dialarg[256];
 	/*! Collected digits to accept/decline the call. */
 	char yn[MAX_YN_STRING];
+	/*! TRUE if the outgoing call is answered. */
+	unsigned int answered:1;
 	/*! TRUE if connected line information is available. */
 	unsigned int pending_connected_update:1;
 	AST_LIST_ENTRY(findme_user) entry;
@@ -550,12 +552,14 @@ static void clear_caller(struct findme_user *tmpuser)
 	tmpuser->ochan = NULL;
 }
 
-static void clear_calling_tree(struct findme_user_listptr *findme_user_list) 
+static void clear_unanswered_calls(struct findme_user_listptr *findme_user_list) 
 {
 	struct findme_user *tmpuser;
 
 	AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
-		clear_caller(tmpuser);
+		if (!tmpuser->answered) {
+			clear_caller(tmpuser);
+		}
 	}
 }
 
@@ -704,8 +708,8 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
 		totalwait -= tmpto;
 		wtd = to;
 		if (totalwait <= 0) {
-			ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
-			clear_calling_tree(findme_user_list);
+			ast_verb(3, "We've hit our timeout for this step. Dropping unanswered calls and starting the next step.\n");
+			clear_unanswered_calls(findme_user_list);
 			return NULL;
 		}
 		if (winner) {
@@ -743,6 +747,7 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
 							break;
 						}
 						ast_verb(3, "%s answered %s\n", ast_channel_name(winner), ast_channel_name(caller));
+						tmpuser->answered = 1;
 						/* If call has been answered, then the eventual hangup is likely to be normal hangup */ 
 						ast_channel_hangupcause_set(winner, AST_CAUSE_NORMAL_CLEARING);
 						ast_channel_hangupcause_set(caller, AST_CAUSE_NORMAL_CLEARING);
@@ -956,6 +961,19 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
 
 		ast_debug(2, "Number(s) %s timeout %ld\n", nm->number, nm->timeout);
 
+		/*
+		 * Put all active outgoing channels into autoservice.
+		 *
+		 * This needs to be done because ast_exists_extension() may put
+		 * the caller into autoservice.
+		 */
+		AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
+			if (tmpuser->ochan) {
+				ast_autoservice_start(tmpuser->ochan);
+			}
+		}
+
+		/* Create all new outgoing calls */
 		ast_copy_string(num, nm->number, sizeof(num));
 		for (number = num; number; number = rest) {
 			struct ast_channel *outbound;
@@ -1009,12 +1027,15 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
 
 			tmpuser->ochan = outbound;
 			tmpuser->state = 0;
+			AST_LIST_INSERT_TAIL(&new_user_list, tmpuser, entry);
+		}
 
+		/* Start all new outgoing calls */
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&new_user_list, tmpuser, entry) {
 			ast_verb(3, "calling Local/%s\n", tmpuser->dialarg);
-			if (!ast_call(tmpuser->ochan, tmpuser->dialarg, 0)) {
-				AST_LIST_INSERT_TAIL(&new_user_list, tmpuser, entry);
-			} else {
+			if (ast_call(tmpuser->ochan, tmpuser->dialarg, 0)) {
 				ast_verb(3, "couldn't reach at this number.\n");
+				AST_LIST_REMOVE_CURRENT(entry);
 
 				/* Destroy this failed new outgoing call. */
 				ast_channel_lock(tmpuser->ochan);
@@ -1025,6 +1046,17 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
 				destroy_calling_node(tmpuser);
 			}
 		}
+		AST_LIST_TRAVERSE_SAFE_END;
+
+		/* Take all active outgoing channels out of autoservice. */
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
+			if (tmpuser->ochan && ast_autoservice_stop(tmpuser->ochan)) {
+				/* Existing outgoing call hungup. */
+				AST_LIST_REMOVE_CURRENT(entry);
+				destroy_calling_node(tmpuser);
+			}
+		}
+		AST_LIST_TRAVERSE_SAFE_END;
 
 		if (AST_LIST_EMPTY(&new_user_list)) {
 			/* No new channels remain at this order level.  If there were any at all. */
@@ -1036,6 +1068,14 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
 
 		winner = wait_for_winner(&findme_user_list, nm, caller, tpargs);
 		if (!winner) {
+			/* Remove all dead outgoing nodes. */
+			AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
+				if (!tmpuser->ochan) {
+					AST_LIST_REMOVE_CURRENT(entry);
+					destroy_calling_node(tmpuser);
+				}
+			}
+			AST_LIST_TRAVERSE_SAFE_END;
 			continue;
 		}
 
diff --git a/configs/followme.conf.sample b/configs/followme.conf.sample
index b11836a5cc..418ec582ea 100644
--- a/configs/followme.conf.sample
+++ b/configs/followme.conf.sample
@@ -40,17 +40,18 @@ context=>default
 ; The context to dial the numbers from
 number=>01233456,25
 ; The a follow-me number to call. The format is:
-; number=> <number to call[&2nd #[&3rd #]]> [, <timeout value in seconds> [, <order in follow-me>] ]
+; number=> <number to call[&2nd #[&...]]>[,<timeout value in seconds>[,<order in follow-me>]]
 ; You can specify as many of these numbers as you like. They will be dialed in the
 ; order that you specify them in the config file OR as specified with the order field
 ; on the number prompt. As you can see from the example, forked dialing of multiple
 ; numbers in the same step is supported with this application if you'd like to dial
 ; multiple numbers in the same followme step.
-; It's also important to note that the timeout value is not the same
-; as the timeout value you would use in app_dial. This timeout value is the amount of
-; time allowed between the time the dialing step starts and the callee makes a choice
-; on whether to take the call or not. That being the case, you may want to account for
-; this time, and make this timeout longer than a timeout you might specify in app_dial.
+;
+; The timeout value is the amount of time allowed between the time the dialing step
+; starts and the callee answers.  The callee then has until the timeout of the last
+; step to make a choice on whether to take the call or not.  That being the case,
+; you may want to make the timeout on the last step longer to give enough time to
+; make the choice to accept or not.
 takecall=>1
 ; The keypress for the callee to take taking the current call. This can be
 ; a single digit or multiple digits. Default is the global default.
-- 
GitLab