From 4828759ab85a1b9198a4d8dc7eff3da99d0a86c5 Mon Sep 17 00:00:00 2001
From: Martin Pycko <martinp@digium.com>
Date: Fri, 12 Sep 2003 16:51:35 +0000
Subject: [PATCH] Add distinguishing between BUSY and FAILURE for outgoing
 spool calls. Always save CDR record (even if the call fails). If the call
 fails try to see if there is "failed" extension in the specified context
 (only if you use context,extension,priority syntax) and execute it.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1499 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 cdr.c                      | 46 ++++++++++++++++++++++++++++++++++++--
 channel.c                  | 37 +++++++++++++++++++++---------
 channels/chan_zap.c        | 19 ++++++++++++++++
 include/asterisk/cdr.h     | 16 +++++++++++++
 include/asterisk/channel.h |  3 +++
 pbx.c                      | 23 ++++++++++++++++++-
 6 files changed, 130 insertions(+), 14 deletions(-)

diff --git a/cdr.c b/cdr.c
index 20b3e2cfaa..3cbaac47ee 100755
--- a/cdr.c
+++ b/cdr.c
@@ -19,6 +19,7 @@
 #include <asterisk/cdr.h>
 #include <asterisk/logger.h>
 #include <asterisk/callerid.h>
+#include <asterisk/causes.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
@@ -163,6 +164,41 @@ void ast_cdr_busy(struct ast_cdr *cdr)
 	}
 }
 
+void ast_cdr_failed(struct ast_cdr *cdr)
+{
+	char *chan; 
+	if (cdr) {
+		chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
+		if (cdr->posted)
+			ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
+			cdr->disposition = AST_CDR_FAILED;
+	}
+}
+
+int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
+{
+	int res = 0;
+	if (cdr) {
+		switch(cause) {
+			case AST_CAUSE_BUSY:
+				ast_cdr_busy(cdr);
+				break;
+			case AST_CAUSE_FAILURE:
+				ast_cdr_failed(cdr);
+				break;
+			case AST_CAUSE_NORMAL:
+				break;
+			case AST_CAUSE_NOTDEFINED:
+				res = -1;
+				break;
+			default:
+				res = -1;
+				ast_log(LOG_WARNING, "We don't handle that cause yet\n");
+		}
+	}
+	return res;
+}
+
 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
 {
 	char *chan; 
@@ -275,6 +311,8 @@ char *ast_cdr_disp2str(int disposition)
 	switch (disposition) {
 	case AST_CDR_NOANSWER:
 		return "NO ANSWER";
+	case AST_CDR_FAILED:
+		return "FAILED";		
 	case AST_CDR_BUSY:
 		return "BUSY";		
 	case AST_CDR_ANSWERED:
@@ -313,11 +351,15 @@ int ast_cdr_update(struct ast_channel *c)
 	char *name, *num;
 	char tmp[AST_MAX_EXTENSION] = "";
 	/* Grab source from ANI or normal Caller*ID */
+	if (!cdr) {
+		ast_log(LOG_NOTICE, "The cdr pointer is not set\n");
+		return -1;
+	}
 	if (c->ani)
 		strncpy(tmp, c->ani, sizeof(tmp) - 1);
-	else if (c->callerid)
+	else if (c->callerid && strlen(c->callerid))
 		strncpy(tmp, c->callerid, sizeof(tmp) - 1);
-	if (c->callerid)
+	if (c->callerid && strlen(c->callerid))
 		strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
 	else
 		strcpy(cdr->clid, "");
diff --git a/channel.c b/channel.c
index 2ecde4feef..6d704d7424 100755
--- a/channel.c
+++ b/channel.c
@@ -34,6 +34,7 @@
 #include <asterisk/linkedlists.h>
 #include <asterisk/indications.h>
 #include <asterisk/monitor.h>
+#include <asterisk/causes.h>
 #ifdef ZAPTEL_OPTIMIZATIONS
 #include <sys/ioctl.h>
 #include <linux/zaptel.h>
@@ -1493,8 +1494,7 @@ struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int
 	int state = 0;
 	struct ast_channel *chan;
 	struct ast_frame *f;
-	int res;
-	
+	int res = 0;
 	chan = ast_request(type, format, data);
 	if (chan) {
 		if (callerid)
@@ -1504,8 +1504,6 @@ struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int
 				res = ast_waitfor(chan, timeout);
 				if (res < 0) {
 					/* Something not cool, or timed out */
-					ast_hangup(chan);
-					chan = NULL;
 					break;
 				}
 				/* If done, break out */
@@ -1516,8 +1514,7 @@ struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int
 				f = ast_read(chan);
 				if (!f) {
 					state = AST_CONTROL_HANGUP;
-					ast_hangup(chan);
-					chan = NULL;
+					res = 0;
 					break;
 				}
 				if (f->frametype == AST_FRAME_CONTROL) {
@@ -1537,17 +1534,36 @@ struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int
 				}
 				ast_frfree(f);
 			}
-		} else {
-			ast_hangup(chan);
-			chan = NULL;
+		} else
 			ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
-		}
 	} else
 		ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
 	if (chan && (chan->_state == AST_STATE_UP))
 		state = AST_CONTROL_ANSWER;
 	if (outstate)
 		*outstate = state;
+	if (chan && res <= 0) {
+		if (!chan->cdr) {
+			chan->cdr = ast_cdr_alloc();
+			if (chan->cdr)
+				ast_cdr_init(chan->cdr, chan);
+		}
+		if (chan->cdr) {
+			char tmp[256];
+			sprintf(tmp, "%s/%s",type,(char *)data);
+			ast_cdr_setapp(chan->cdr,"Dial",tmp);
+			ast_cdr_update(chan);
+			ast_cdr_start(chan->cdr);
+			ast_cdr_end(chan->cdr);
+			/* If the cause wasn't handled properly */
+			if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
+				ast_cdr_failed(chan->cdr);
+			ast_cdr_reset(chan->cdr,1);
+		} else 
+			ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+		ast_hangup(chan);
+		chan = NULL;
+	}
 	return chan;
 }
 
@@ -2431,4 +2447,3 @@ int ast_tonepair(struct ast_channel *chan, int freq1, int freq2, int duration, i
 	return 0;
 }
 
-
diff --git a/channels/chan_zap.c b/channels/chan_zap.c
index a0dded75b5..284f70f435 100755
--- a/channels/chan_zap.c
+++ b/channels/chan_zap.c
@@ -36,6 +36,7 @@
 #include <asterisk/dsp.h>
 #include <asterisk/astdb.h>
 #include <asterisk/manager.h>
+#include <asterisk/causes.h>
 #include <sys/signal.h>
 #include <sys/select.h>
 #include <errno.h>
@@ -498,6 +499,23 @@ static int cidrings[] = {
 #define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */)
 #define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_SF)) /* || (p->sig & __ZT_SIG_FXO) */)
 
+/* translate between PRI causes and asterisk's */
+int hangup_pri2cause(int cause)
+{
+	switch(cause) {
+#ifdef ZAPATA_PRI
+		case PRI_CAUSE_USER_BUSY:
+			return AST_CAUSE_BUSY;
+		case PRI_CAUSE_NORMAL_CLEARING:
+			return AST_CAUSE_NORMAL;
+#endif
+		default:
+			return AST_CAUSE_FAILURE;
+	}
+	/* never reached */
+	return 0;
+}
+
 static int zt_get_index(struct ast_channel *ast, struct zt_pvt *p, int nullok)
 {
 	int res;
@@ -6049,6 +6067,7 @@ static void *pri_dchannel(void *vpri)
 					if (chan) {
 						ast_mutex_lock(&pri->pvt[chan]->lock);
 						if (pri->pvt[chan]->owner) {
+							pri->pvt[chan]->owner->hangupcause = hangup_pri2cause(e->hangup.cause);
 							pri->pvt[chan]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
 							if (option_verbose > 2) 
 								ast_verbose(VERBOSE_PREFIX_3 "Channel %d, span %d got hangup\n", chan, pri->span);
diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h
index 0b9681035b..3988832df4 100755
--- a/include/asterisk/cdr.h
+++ b/include/asterisk/cdr.h
@@ -23,6 +23,7 @@
 #define AST_CDR_NOANSWER			(1 << 0)
 #define AST_CDR_BUSY				(1 << 1)
 #define AST_CDR_ANSWERED			(1 << 2)
+#define AST_CDR_FAILED				(1 << 3)
 
 //! AMA Flags
 #define AST_CDR_OMIT				(1)
@@ -142,6 +143,21 @@ extern void ast_cdr_answer(struct ast_cdr *cdr);
  */
 extern void ast_cdr_busy(struct ast_cdr *cdr);
 
+//! Fail a call
+/*!
+ * \param cdr the cdr you wish to associate with the call
+ * Returns nothing important
+ */
+extern void ast_cdr_failed(struct ast_cdr *cdr);
+
+//! Save the result of the call based on the AST_CAUSE_*
+/*!
+ * \param cdr the cdr you wish to associate with the call
+ * Returns nothing important
+ * \param cause the AST_CAUSE_*
+ */
+extern int ast_cdr_disposition(struct ast_cdr *cdr, int cause);
+	
 //! End a call
 /*!
  * \param cdr the cdr you have associated the call with
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 815f882f47..f33bb62d32 100755
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -213,6 +213,9 @@ struct ast_channel {
 	/* Unique Channel Identifier */
 	char uniqueid[32];
 
+	/* Why is the channel hanged up */
+	int hangupcause;
+	
 	/* A linked list for variables */
 	struct ast_var_t *vars;	
 	AST_LIST_HEAD(varshead,ast_var_t) varshead;
diff --git a/pbx.c b/pbx.c
index 5ad920948f..8a7dd2fd7c 100755
--- a/pbx.c
+++ b/pbx.c
@@ -3779,7 +3779,7 @@ static void *async_wait(void *data)
 	return NULL;
 }
 
-int ast_pbx_outgoing_exten(char *type, int format, void *data, int timeout, char *context, char *exten, int priority, int *reason, int sync, char *callerid, char *variable )
+int ast_pbx_outgoing_exten(char *type, int format, void *data, int timeout, char *context, char *exten, int priority, int *reason, int sync, char *callerid, char *variable)
 {
 	struct ast_channel *chan;
 	struct async_stat *as;
@@ -3828,6 +3828,27 @@ int ast_pbx_outgoing_exten(char *type, int format, void *data, int timeout, char
 					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
 				ast_hangup(chan);
 			}
+		} else {
+			/* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
+			/* check if "failed" exists */
+			if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
+				chan = ast_channel_alloc(0);
+				if (chan) {
+					strncpy(chan->name, "OutgoingSpoolFailed", sizeof(chan->name) - 1);
+					if (context && strlen(context))
+						strncpy(chan->context, context, sizeof(chan->context) - 1);
+					strncpy(chan->exten, "failed", sizeof(chan->exten) - 1);
+					chan->priority = 1;
+					/* JDG chanvar */
+					tmp = variable;
+					/* FIXME replace this call with strsep  NOT*/
+					while( (var = strtok_r(NULL, "|", &tmp)) ) {
+						pbx_builtin_setvar( chan, var );
+					} /* /JDG */
+					ast_pbx_run(chan);	
+				} else
+					ast_log(LOG_WARNING, "Can't allocate the channel structure, skipping execution of extension 'failed'\n");
+			}
 		}
 	} else {
 		as = malloc(sizeof(struct async_stat));
-- 
GitLab