[asterisk-commits] irroot: branch irroot/t38gateway-trunk r321668 - in /team/irroot/t38gateway-t...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jun 3 03:34:09 CDT 2011


Author: irroot
Date: Fri Jun  3 03:34:04 2011
New Revision: 321668

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=321668
Log:
resolve conflict, enable automerge

Modified:
    team/irroot/t38gateway-trunk/   (props changed)
    team/irroot/t38gateway-trunk/channels/chan_local.c
    team/irroot/t38gateway-trunk/channels/chan_sip.c
    team/irroot/t38gateway-trunk/channels/sip/include/sip.h

Propchange: team/irroot/t38gateway-trunk/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/irroot/t38gateway-trunk/
            ('svnmerge-integrated' removed)

Modified: team/irroot/t38gateway-trunk/channels/chan_local.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-trunk/channels/chan_local.c?view=diff&rev=321668&r1=321667&r2=321668
==============================================================================
--- team/irroot/t38gateway-trunk/channels/chan_local.c (original)
+++ team/irroot/t38gateway-trunk/channels/chan_local.c Fri Jun  3 03:34:04 2011
@@ -154,68 +154,124 @@
 #define LOCAL_BRIDGE          (1 << 3) /*!< Report back the "true" channel as being bridged to */
 #define LOCAL_MOH_PASSTHRU    (1 << 4) /*!< Pass through music on hold start/stop frames */
 
-static int local_setoption(struct ast_channel *chan, int option, void * data, int datalen)
-{
-	int res;
-	struct local_pvt *p;
-	struct ast_channel *otherchan;
+/* 
+ * \brief Send a pvt in with no locks held and get all locks
+ *
+ * \note NO locks should be held prior to calling this function
+ * \note The pvt must have a ref held before calling this function
+ * \note if outchan or outowner is set != NULL after calling this function
+ *       those channels are locked and reffed.
+ * \note Batman.
+ */
+static void awesome_locking(struct local_pvt *p, struct ast_channel **outchan, struct ast_channel **outowner)
+{
+	struct ast_channel *chan = NULL;
+	struct ast_channel *owner = NULL;
+
+	for (;;) {
+		ao2_lock(p);
+		if (p->chan) {
+			chan = p->chan;
+			ast_channel_ref(chan);
+		}
+		if (p->owner) {
+			owner = p->owner;
+			ast_channel_ref(owner);
+		}
+		ao2_unlock(p);
+
+		/* if we don't have both channels, then this is very easy */
+		if (!owner || !chan) {
+			if (owner) {
+				ast_channel_lock(owner);
+			} else if(chan) {
+				ast_channel_lock(chan);
+			}
+			ao2_lock(p);
+		} else {
+			/* lock both channels first, then get the pvt lock */
+			ast_channel_lock(chan);
+			while (ast_channel_trylock(owner)) {
+				CHANNEL_DEADLOCK_AVOIDANCE(chan);
+			}
+			ao2_lock(p);
+		}
+
+		/* Now that we have all the locks, validate that nothing changed */
+		if (p->owner != owner || p->chan != chan) {
+			if (owner) {
+				ast_channel_unlock(owner);
+				owner = ast_channel_unref(owner);
+			}
+			if (chan) {
+				ast_channel_unlock(chan);
+				chan = ast_channel_unref(chan);
+			}
+			ao2_unlock(p);
+			continue;
+		}
+
+		break;
+	}
+	*outowner = p->owner;
+	*outchan = p->chan;
+}
+
+static int local_setoption(struct ast_channel *ast, int option, void * data, int datalen)
+{
+	int res = 0;
+	struct local_pvt *p = NULL;
+	struct ast_channel *otherchan = NULL;
 	ast_chan_write_info_t *write_info;
 
 	if (option != AST_OPTION_CHANNEL_WRITE) {
-		return -1;
+		res = -1;
+		goto setoption_cleanup;
 	}
 
 	write_info = data;
 
 	if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) {
 		ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n");
-		return -1;
-	}
-
-
-startover:
-	ast_channel_lock(chan);
-
-	p = chan->tech_pvt;
-	if (!p) {
-		ast_channel_unlock(chan);
-		ast_log(LOG_WARNING, "Could not update other side of %s, local_pvt went away.\n", chan->name);
-		return -1;
-	}
-
-	while (ao2_trylock(p)) {
-		ast_channel_unlock(chan);
-		sched_yield();
-		ast_channel_lock(chan);
-		p = chan->tech_pvt;
-		if (!p) {
-			ast_channel_unlock(chan);
-			ast_log(LOG_WARNING, "Could not update other side of %s, local_pvt went away.\n", chan->name);
-			return -1;
-		}
-	}
-
+		res = -1;
+		goto setoption_cleanup;
+	}
+
+	/* get the tech pvt */
+	ast_channel_lock(ast);
+	if (!(p = ast->tech_pvt)) {
+		ast_channel_unlock(ast);
+		res = -1;
+		goto setoption_cleanup;
+	}
+	ao2_ref(p, 1);
+	ast_channel_unlock(ast);
+
+	/* get the channel we are supposed to write to */
+	ao2_lock(p);
 	otherchan = (write_info->chan == p->owner) ? p->chan : p->owner;
-
 	if (!otherchan || otherchan == write_info->chan) {
+		res = -1;
+		otherchan = NULL;
 		ao2_unlock(p);
-		ast_channel_unlock(chan);
-		ast_log(LOG_WARNING, "Could not update other side of %s, other side went away.\n", chan->name);
-		return 0;
-	}
-
-	if (ast_channel_trylock(otherchan)) {
-		ao2_unlock(p);
-		ast_channel_unlock(chan);
-		goto startover;
-	}
-
+		goto setoption_cleanup;
+	}
+	ast_channel_ref(otherchan);
+
+	/* clear the pvt lock before grabbing the channel */
+	ao2_unlock(p);
+
+	ast_channel_lock(otherchan);
 	res = write_info->write_fn(otherchan, write_info->function, write_info->data, write_info->value);
-
 	ast_channel_unlock(otherchan);
-	ao2_unlock(p);
-	ast_channel_unlock(chan);
-
+
+setoption_cleanup:
+	if (p) {
+		ao2_ref(p, -1);
+	}
+	if (otherchan) {
+		ast_channel_unref(otherchan);
+	}
 	return res;
 }
 
@@ -294,57 +350,51 @@
 
 static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
 {
-	struct local_pvt *p = ast->tech_pvt;
-	struct ast_channel *chan, *bridged;
-	int res;
-
-	if (!p) {
-		return -1;
-	}
+	struct local_pvt *p;
+	struct ast_channel *bridged = NULL;
+	struct ast_channel *tmp = NULL;
+	int res = 0;
 
 	if (option != AST_OPTION_T38_STATE) {
 		/* AST_OPTION_T38_STATE is the only supported option at this time */
 		return -1;
 	}
 
+	/* for some reason the channel is not locked in channel.c when this function is called */
+	ast_channel_lock(ast);
+	if (!(p = ast->tech_pvt)) {
+		ast_channel_unlock(ast);
+		return -1;
+	}
+
 	ao2_lock(p);
-	chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
-
-try_again:
-	if (!chan) {
+	if (!(tmp = IS_OUTBOUND(ast, p) ? p->owner : p->chan)) {
 		ao2_unlock(p);
-		return -1;
-	}
-
-	if (ast_channel_trylock(chan)) {
-		ao2_unlock(p);
-		sched_yield();
-		ao2_lock(p);
-		chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
-		goto try_again;
-	}
-
-	bridged = ast_bridged_channel(chan);
-	if (!bridged) {
-		/* can't query channel unless we are bridged */
-		ao2_unlock(p);
-		ast_channel_unlock(chan);
-		return -1;
-	}
-
-	if (ast_channel_trylock(bridged)) {
-		ast_channel_unlock(chan);
-		ao2_unlock(p);
-		sched_yield();
-		ao2_lock(p);
-		chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
-		goto try_again;
-	}
-
-	res = ast_channel_queryoption(bridged, option, data, datalen, 0);
+		ast_channel_unlock(ast);
+		return -1;
+	}
+	ast_channel_ref(tmp);
 	ao2_unlock(p);
-	ast_channel_unlock(chan);
-	ast_channel_unlock(bridged);
+	ast_channel_unlock(ast);
+
+	ast_channel_lock(tmp);
+	if (!(bridged = ast_bridged_channel(tmp))) {
+		res = -1;
+		ast_channel_unlock(tmp);
+		goto query_cleanup;
+	}
+	ast_channel_ref(bridged);
+	ast_channel_unlock(tmp);
+
+query_cleanup:
+	if (bridged) {
+		res = ast_channel_queryoption(bridged, option, data, datalen, 0);
+		bridged = ast_channel_unref(bridged);
+	}
+	if (tmp) {
+		tmp = ast_channel_unref(tmp);
+	}
+
 	return res;
 }
 
@@ -373,31 +423,24 @@
 		return 0;
 	}
 
-	/* Ensure that we have both channels locked */
-	while (other && ast_channel_trylock(other)) {
-		int res;
-		if ((res = ao2_unlock(p))) {
-			ast_log(LOG_ERROR, "chan_local bug! '&p->lock' was not locked when entering local_queue_frame! (%s)\n", strerror(res));
-			return -1;
-		}
-		if (us && us_locked) {
-			do {
-				CHANNEL_DEADLOCK_AVOIDANCE(us);
-			} while (ao2_trylock(p));
-		} else {
-			usleep(1);
-			ao2_lock(p);
-		}
-		other = isoutbound ? p->owner : p->chan;
-	}
-
-	if (other) {
-		if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_RINGING) {
-			ast_setstate(other, AST_STATE_RINGING);
-		}
-		ast_queue_frame(other, f);
-		ast_channel_unlock(other);
-	}
+	/* grab a ref on the channel before unlocking the pvt,
+	 * other can not go away from us now regardless of locking */
+	ast_channel_ref(other);
+	if (us && us_locked) {
+		ast_channel_unlock(us);
+	}
+	ao2_unlock(p);
+
+	if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_RINGING) {
+		ast_setstate(other, AST_STATE_RINGING);
+	}
+	ast_queue_frame(other, f);
+
+	other = ast_channel_unref(other);
+	if (us && us_locked) {
+		ast_channel_lock(us);
+	}
+	ao2_lock(p);
 
 	return 0;
 }
@@ -429,12 +472,38 @@
 /*!
  * \internal
  * \note This function assumes that we're only called from the "outbound" local channel side
+ *
+ * \note it is assummed p is locked and reffed before entering this function
  */
 static void check_bridge(struct local_pvt *p)
 {
 	struct ast_channel_monitor *tmp;
-	if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner || (p->chan->_bridge != ast_bridged_channel(p->chan)))
+	struct ast_channel *chan = NULL;
+	struct ast_channel *bridged_chan = NULL;
+
+	/* Do a few conditional checks early on just to see if this optimization is possible */
+	if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION)) {
 		return;
+	}
+	if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || !p->chan || !p->owner) {
+		return;
+	}
+
+	/* Safely get the channel bridged to p->chan */
+	chan = ast_channel_ref(p->chan);
+
+	ao2_unlock(p); /* don't call bridged channel with the pvt locked */
+	bridged_chan = ast_bridged_channel(chan);
+	ao2_lock(p);
+
+	chan = ast_channel_unref(chan);
+
+	/* since we had to unlock p to get the bridged chan, validate our
+	 * data once again and verify the bridged channel is what we expect
+	 * it to be in order to perform this optimization */
+	if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || !p->owner || !p->chan || (p->chan->_bridge != bridged_chan)) {
+		return;
+	}
 
 	/* only do the masquerade if we are being called on the outbound channel,
 	   if it has been bridged to another channel and if there are no pending
@@ -518,39 +587,25 @@
 static int local_write(struct ast_channel *ast, struct ast_frame *f)
 {
 	struct local_pvt *p = ast->tech_pvt;
-	struct ast_channel *bridge;
 	int res = -1;
 	int isoutbound;
 
-	if (!p)
-		return -1;
+	if (!p) {
+		return -1;
+	}
 
 	/* Just queue for delivery to the other side */
+	ao2_ref(p, 1); /* ref for local_queue_frame */
 	ao2_lock(p);
-	ao2_ref(p, 1); /* ref for local_queue_frame */
-
 	isoutbound = IS_OUTBOUND(ast, p);
+
 	if (isoutbound && f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
 		check_bridge(p);
-	} else if (!isoutbound) {
-		/* fixup formats nativeformat has changed we must adjust
-		 * ast is p->owner and is locked here*/
-		bridge = ast_bridged_channel(ast);
-		if (bridge && !ast_format_cap_identical(bridge->nativeformats, ast->nativeformats)) {
-			ast_format_cap_copy(bridge->nativeformats, p->owner->nativeformats);
-			ast_set_read_format(ast, &ast->readformat);
-			ast_set_write_format(ast, &ast->writeformat);
-			ast_channel_lock(p->chan);
-			ast_format_cap_copy(bridge->nativeformats, p->chan->nativeformats);
-			ast_set_read_format(p->chan, &p->chan->readformat);
-			ast_set_write_format(p->chan, &p->chan->writeformat);
-			ast_channel_unlock(p->chan);
-		}
-	}
-
-	if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
+	}
+
+	if (!ast_test_flag(p, LOCAL_ALREADY_MASQED)) {
 		res = local_queue_frame(p, isoutbound, f, ast, 1);
-	else {
+	} else {
 		ast_debug(1, "Not posting to queue since already masked on '%s'\n", ast->name);
 		res = 0;
 	}
@@ -749,49 +804,38 @@
 static int local_call(struct ast_channel *ast, char *dest, int timeout)
 {
 	struct local_pvt *p = ast->tech_pvt;
+	int pvt_locked = 0;
+
+	struct ast_channel *owner = NULL;
+	struct ast_channel *chan = NULL;
 	int res;
 	struct ast_var_t *varptr = NULL, *new;
 	size_t len, namelen;
 	char *reduced_dest = ast_strdupa(dest);
 	char *slash;
-	struct ast_channel *chan;
 	const char *exten;
 	const char *context;
 
-	if (!p || p->owner != ast) {
+	if (!p) {
 		return -1;
 	}
 
 	/* since we are letting go of channel locks that were locked coming into
 	 * this function, then we need to give the tech pvt a ref */
 	ao2_ref(p, 1);
-
-	while (ao2_trylock(p)) {
-		ast_channel_unlock(ast);
-		sched_yield();
-		ast_channel_lock(ast);
-	}
-	while ((p->chan && p->owner) && ast_channel_trylock(p->chan)) {
-		ao2_unlock(p);
-		if (p->owner) {
-			ast_channel_unlock(p->owner);
-		}
-		sched_yield();
-		if (p->owner) {
-			ast_channel_lock(p->owner);
-		}
-		ao2_lock(p);
-	}
-
-	if (!p->owner || !p->chan) {
-		/* someone went away during the locking madness.
-		 * time to bail. */
-		if (p->chan) {
-			ast_channel_unlock(p->chan);
-		}
-		ao2_unlock(p);
-		ao2_ref(p,-1);
-		return -1;
+	ast_channel_unlock(ast);
+
+	awesome_locking(p, &chan, &owner);
+	pvt_locked = 1;
+
+	if (owner != ast) {
+		res = -1;
+		goto return_cleanup;
+	}
+
+	if (!owner || !chan) {
+		res = -1;
+		goto return_cleanup;
 	}
 
 	/*
@@ -801,37 +845,37 @@
 	 * All these failure points just return -1. The individual strings will
 	 * be cleared when we destroy the channel.
 	 */
-	ast_party_redirecting_copy(&p->chan->redirecting, &p->owner->redirecting);
-
-	ast_party_dialed_copy(&p->chan->dialed, &p->owner->dialed);
-
-	ast_connected_line_copy_to_caller(&p->chan->caller, &p->owner->connected);
-	ast_connected_line_copy_from_caller(&p->chan->connected, &p->owner->caller);
-
-	ast_string_field_set(p->chan, language, p->owner->language);
-	ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
-	ast_string_field_set(p->chan, musicclass, p->owner->musicclass);
-	ast_cdr_update(p->chan);
-
-	ast_channel_cc_params_init(p->chan, ast_channel_get_cc_config_params(p->owner));
+	ast_party_redirecting_copy(&chan->redirecting, &owner->redirecting);
+
+	ast_party_dialed_copy(&chan->dialed, &owner->dialed);
+
+	ast_connected_line_copy_to_caller(&chan->caller, &owner->connected);
+	ast_connected_line_copy_from_caller(&chan->connected, &owner->caller);
+
+	ast_string_field_set(chan, language, owner->language);
+	ast_string_field_set(chan, accountcode, owner->accountcode);
+	ast_string_field_set(chan, musicclass, owner->musicclass);
+	ast_cdr_update(chan);
+
+	ast_channel_cc_params_init(chan, ast_channel_get_cc_config_params(owner));
 
 	/* Make sure we inherit the ANSWERED_ELSEWHERE flag if it's set on the queue/dial call request in the dialplan */
 	if (ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) {
-		ast_set_flag(p->chan, AST_FLAG_ANSWERED_ELSEWHERE);
+		ast_set_flag(chan, AST_FLAG_ANSWERED_ELSEWHERE);
 	}
 
 	/* copy the channel variables from the incoming channel to the outgoing channel */
 	/* Note that due to certain assumptions, they MUST be in the same order */
-	AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
+	AST_LIST_TRAVERSE(&owner->varshead, varptr, entries) {
 		namelen = strlen(varptr->name);
 		len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
 		if ((new = ast_calloc(1, len))) {
 			memcpy(new, varptr, len);
 			new->value = &(new->name[0]) + namelen + 1;
-			AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
-		}
-	}
-	ast_channel_datastore_inherit(p->owner, p->chan);
+			AST_LIST_INSERT_TAIL(&chan->varshead, new, entries);
+		}
+	}
+	ast_channel_datastore_inherit(owner, chan);
 	/* If the local channel has /n or /b on the end of it,
 	 * we need to lop that off for our argument to setting
 	 * up the CC_INTERFACES variable
@@ -839,18 +883,21 @@
 	if ((slash = strrchr(reduced_dest, '/'))) {
 		*slash = '\0';
 	}
-	ast_set_cc_interfaces_chanvar(p->chan, reduced_dest);
-
-	chan = ast_channel_ref(p->chan);
-	ao2_unlock(p);
+	ast_set_cc_interfaces_chanvar(chan, reduced_dest);
+
 	exten = ast_strdupa(chan->exten);
 	context = ast_strdupa(chan->context);
+
+	ao2_unlock(p);
+	pvt_locked = 0;
+
 	ast_channel_unlock(chan);
 
 	if (!ast_exists_extension(chan, context, exten, 1,
-		S_COR(p->owner->caller.id.number.valid, p->owner->caller.id.number.str, NULL))) {
+		S_COR(owner->caller.id.number.valid, owner->caller.id.number.str, NULL))) {
 		ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", exten, context);
 		res = -1;
+		chan = ast_channel_unref(chan); /* we already unlocked it, so clear it hear so the cleanup label won't touch it. */
 		goto return_cleanup;
 	}
 
@@ -860,10 +907,33 @@
 		ast_set_flag(p, LOCAL_LAUNCHED_PBX);
 		ao2_unlock(p);
 	}
+	chan = ast_channel_unref(chan); /* chan is already unlocked, clear it here so the cleanup lable won't touch it. */
 
 return_cleanup:
-	ast_channel_unref(chan);
-	ao2_ref(p, -1);
+	if (p) {
+		if (pvt_locked) {
+			ao2_unlock(p);
+		}
+		ao2_ref(p, -1);
+	}
+	if (chan) {
+		ast_channel_unlock(chan);
+		chan = ast_channel_unref(chan);
+	}
+
+	/* owner is supposed to be == to ast,  if it
+	 * is, don't unlock it because ast must exit locked */
+	if (owner) {
+		if (owner != ast) {
+			ast_channel_unlock(owner);
+			ast_channel_lock(ast);
+		}
+		owner = ast_channel_unref(owner);
+	} else {
+		/* we have to exit with ast locked */
+		ast_channel_lock(ast);
+	}
+
 	return res;
 }
 
@@ -872,19 +942,31 @@
 {
 	struct local_pvt *p = ast->tech_pvt;
 	int isoutbound;
+	int hangup_chan = 0;
+	int res = 0;
 	struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_HANGUP }, .data.uint32 = ast->hangupcause };
-	struct ast_channel *ochan = NULL;
-
-	if (!p)
-		return -1;
-
-	/* we MUST give the tech_pvt a ref here since we are unlocking the
-	 * channel during deadlock avoidance. */
+	struct ast_channel *owner = NULL;
+	struct ast_channel *chan = NULL;
+
+	if (!p) {
+		return -1;
+	}
+
+	/* give the pvt a ref since we are unlocking the channel. */
 	ao2_ref(p, 1);
 
-	ao2_lock(p);
-
-	isoutbound = IS_OUTBOUND(ast, p);
+	/* the pvt isn't going anywhere, we gave it a ref */
+	ast_channel_unlock(ast);
+
+	/* lock everything */
+	awesome_locking(p, &chan, &owner);
+
+	if (ast != chan && ast != owner) {
+		res = -1;
+		goto local_hangup_cleanup;
+	}
+
+	isoutbound = IS_OUTBOUND(ast, p); /* just comparing pointer of ast */
 
 	if (p->chan && ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) {
 		ast_set_flag(p->chan, AST_FLAG_ANSWERED_ELSEWHERE);
@@ -894,92 +976,59 @@
 	if (isoutbound) {
 		const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
 		if ((status) && (p->owner)) {
-			/* Deadlock avoidance */
-			while (p->owner && ast_channel_trylock(p->owner)) {
-				ao2_unlock(p);
-				if (p->chan) {
-					ast_channel_unlock(p->chan);
-				}
-				sched_yield();
-				if (p->chan) {
-					ast_channel_lock(p->chan);
-				}
-				ao2_lock(p);
-			}
-			if (p->owner) {
-				p->owner->hangupcause = p->chan->hangupcause;
-				pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
-				ast_channel_unlock(p->owner);
-			}
-		}
-		if (!p->chan) {
-			/* chan was == to ast and was !NULL before deadlock avoidance started, if chan
-			 * is NULL now, then we should bail because that channel
-			 * hungup already. This is possible because we let go of the
-			 * lock given to the ast channel passed to this function during
-			 * deadlock avoidance. */
-			ao2_unlock(p);
-			ao2_ref(p, -1);
-			return 0;
-		}
-		p->chan = NULL;
+			p->owner->hangupcause = p->chan->hangupcause;
+			pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
+		}
+
 		ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
 		ast_module_user_remove(p->u_chan);
+		p->chan = NULL;
 	} else {
 		ast_module_user_remove(p->u_owner);
-		while (p->chan && ast_channel_trylock(p->chan)) {
-				ao2_unlock(p);
-				if (p->owner) {
-					ast_channel_unlock(p->owner);
-				}
-				sched_yield();
-				if (p->owner) {
-					ast_channel_lock(p->owner);
-				}
-				ao2_lock(p);
-		}
 		if (p->chan) {
 			ast_queue_hangup(p->chan);
-			ast_channel_unlock(p->chan);
-		}
-
-		if (!p->owner) {
-			/* owner was == to ast and was !NULL before deadlock avoidance started, if
-			 * owner is NULL now, then we should bail because that channel
-			 * hungup already. This is possible because we let go of the
-			 * lock given to the ast channel passed to this function during
-			 * deadlock avoidance. */
-			ao2_unlock(p);
-			ao2_ref(p, -1);
-			return 0;
 		}
 		p->owner = NULL;
 	}
 
-	ast->tech_pvt = NULL;
+	ast->tech_pvt = NULL; /* this is one of our locked channels, doesn't matter which */
 
 	if (!p->owner && !p->chan) {
 		ao2_unlock(p);
-
 		/* Remove from list */
 		ao2_unlink(locals, p);
 		ao2_ref(p, -1);
-		return 0;
+		p = NULL;
+		res = 0;
+		goto local_hangup_cleanup;
 	}
 	if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX)) {
 		/* Need to actually hangup since there is no PBX */
-		ochan = p->chan;
+		hangup_chan = 1;
 	} else {
-		local_queue_frame(p, isoutbound, &f, NULL, 1);
-	}
-
-	ao2_unlock(p);
-	if (ochan) {
-		ast_hangup(ochan);
-	}
-
-	ao2_ref(p, -1);
-	return 0;
+		local_queue_frame(p, isoutbound, &f, NULL, 0);
+	}
+
+local_hangup_cleanup:
+	if (p) {
+		ao2_unlock(p);
+		ao2_ref(p, -1);
+	}
+	if (chan) {
+		ast_channel_unlock(chan);
+		if (hangup_chan) {
+			ast_hangup(chan);
+		}
+		chan = ast_channel_unref(chan);
+	}
+	if (owner) {
+		ast_channel_unlock(owner);
+		owner = ast_channel_unref(owner);
+	}
+
+	/* leave with the same stupid channel locked that came in */
+	ast_channel_lock(ast);
+	return res;
 }
 
 static void local_destroy(void *obj)

Modified: team/irroot/t38gateway-trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-trunk/channels/chan_sip.c?view=diff&rev=321668&r1=321667&r2=321668
==============================================================================
--- team/irroot/t38gateway-trunk/channels/chan_sip.c (original)
+++ team/irroot/t38gateway-trunk/channels/chan_sip.c Fri Jun  3 03:34:04 2011
@@ -263,6 +263,7 @@
 #include "asterisk/cel.h"
 #include "asterisk/data.h"
 #include "asterisk/aoc.h"
+#include "asterisk/message.h"
 #include "sip/include/sip.h"
 #include "sip/include/globals.h"
 #include "sip/include/config_parser.h"
@@ -1252,7 +1253,8 @@
 static int transmit_info_with_aoc(struct sip_pvt *p, struct ast_aoc_decoded *decoded);
 static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
 static int transmit_info_with_vidupdate(struct sip_pvt *p);
-static int transmit_message_with_text(struct sip_pvt *p, const char *text);
+static int transmit_message_with_text(struct sip_pvt *p, const char *text, int init, int auth);
+static int transmit_message_with_msg(struct sip_pvt *p, const struct ast_msg *msg);
 static int transmit_refer(struct sip_pvt *p, const char *dest);
 static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten);
 static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate);
@@ -1261,7 +1263,7 @@
 static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
 static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
 static void copy_request(struct sip_request *dst, const struct sip_request *src);
-static void receive_message(struct sip_pvt *p, struct sip_request *req);
+static void receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
 static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward);
 static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *event, int cache_only);
 
@@ -1532,7 +1534,7 @@
 static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
 static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *sin, const char *e);
 static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
-static int handle_request_message(struct sip_pvt *p, struct sip_request *req);
+static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
 static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, int seqno, const char *e);
 static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
 static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
@@ -1547,6 +1549,7 @@
 static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
 static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
 static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
+static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
 static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
 
 /*------ SRTP Support -------- */
@@ -4444,7 +4447,7 @@
 	}
 	if (debug)
 		ast_verbose("Sending text %s on %s\n", text, ast->name);
-	transmit_message_with_text(dialog, text);
+	transmit_message_with_text(dialog, text, 0, 0);
 	return 0;
 }
 
@@ -13117,14 +13120,47 @@
 	return res;
 }
 
+/*! \brief Transmit text with SIP MESSAGE method based on an ast_msg */
+static int transmit_message_with_msg(struct sip_pvt *p, const struct ast_msg *msg)
+{
+	struct sip_request req;
+	struct ast_msg_var_iterator *i;
+	const char *var, *val;
+
+	initreqprep(&req, p, SIP_MESSAGE, NULL);
+	ast_string_field_set(p, msg_body, ast_msg_get_body(msg));
+	initialize_initreq(p, &req);
+
+	i = ast_msg_var_iterator_init(msg);
+	while (ast_msg_var_iterator_next(msg, i, &var, &val)) {
+		add_header(&req, var, val);
+		ast_msg_var_unref_current(i);
+	}
+	ast_msg_var_iterator_destroy(i);
+
+	add_text(&req, ast_msg_get_body(msg));
+
+	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+}
+
 /*! \brief Transmit text with SIP MESSAGE method */
-static int transmit_message_with_text(struct sip_pvt *p, const char *text)
+static int transmit_message_with_text(struct sip_pvt *p, const char *text, int init, int auth)
 {
 	struct sip_request req;
-	
-	reqprep(&req, p, SIP_MESSAGE, 0, 1);
-	add_text(&req, text);
-	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+
+	if (init) {
+		initreqprep(&req, p, SIP_MESSAGE, NULL);
+		ast_string_field_set(p, msg_body, text);
+		initialize_initreq(p, &req);
+	} else {
+		reqprep(&req, p, SIP_MESSAGE, 0, 1);
+	}
+	if (auth) {
+		return transmit_request_with_auth(p, SIP_MESSAGE, p->ocseq, XMIT_RELIABLE, 0);
+	} else {
+		add_text(&req, text);
+		return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+	}
 }
 
 /*! \brief Allocate SIP refer structure */
@@ -13355,6 +13391,10 @@
 		add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->hangupcause));
 		snprintf(buf, sizeof(buf), "%d", p->hangupcause);
 		add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
+	}
+
+	if (sipmethod == SIP_MESSAGE) {
+		add_text(&resp, p->msg_body);
 	}
 
 	return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);	
@@ -15912,15 +15952,52 @@
 	return 0;
 }
 
+static int get_msg_text2(struct ast_str **buf, struct sip_request *req, int addnewline)
+{
+	int i, res = 0;
+
+	ast_str_reset(*buf);
+
+	for (i = 0; res >= 0 && i < req->lines; i++) {
+		const char *line = REQ_OFFSET_TO_STR(req, line[i]);
+
+		res = ast_str_append(buf, 0, "%s%s", line, addnewline ? "\n" : "");
+	}
+
+	return res < 0 ? -1 : 0;
+}
+
+static void set_message_vars_from_req(struct ast_msg *msg, struct sip_request *req)
+{
+	size_t x;
+	char name_buf[1024] = "";
+	char val_buf[1024] = "";
+	char *c;
+
+	for (x = 0; x < req->headers; x++) {
+		const char *header = REQ_OFFSET_TO_STR(req, header[x]);
+		if ((c = strchr(header, ':'))) {
+			ast_copy_string(name_buf, header, MIN((c - header + 1), sizeof(name_buf)));
+			ast_copy_string(val_buf, ast_skip_blanks(c + 1), sizeof(val_buf));
+			ast_trim_blanks(name_buf);
+			ast_msg_set_var(msg, name_buf, val_buf);
+		}
+	}
+}
+
+AST_THREADSTORAGE(sip_msg_buf);
 
 /*! \brief  Receive SIP MESSAGE method messages
 \note	We only handle messages within current calls currently
 	Reference: RFC 3428 */
-static void receive_message(struct sip_pvt *p, struct sip_request *req)
-{
-	char buf[1400];	
+static void receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
+{
+	struct ast_str *buf;
 	struct ast_frame f;
 	const char *content_type = get_header(req, "Content-Type");
+	struct ast_msg *msg;
+	int res;
+	char *from, *to;
 
 	if (strncmp(content_type, "text/plain", strlen("text/plain"))) { /* No text/plain attachment */
 		transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */
@@ -15929,7 +16006,15 @@
 		return;
 	}
 
-	if (get_msg_text(buf, sizeof(buf), req, FALSE)) {
+	if (!(buf = ast_str_thread_get(&sip_msg_buf, 128))) {
+		transmit_response(p, "500 Internal Server Error", req);
+		if (!p->owner) {
+			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		}
+		return;
+	}
+
+	if (get_msg_text2(&buf, req, FALSE)) {
 		ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid);
 		transmit_response(p, "202 Accepted", req);
 		if (!p->owner)
@@ -15939,23 +16024,96 @@
 
 	if (p->owner) {
 		if (sip_debug_test_pvt(p))
-			ast_verbose("SIP Text message received: '%s'\n", buf);
+			ast_verbose("SIP Text message received: '%s'\n", ast_str_buffer(buf));
 		memset(&f, 0, sizeof(f));
 		f.frametype = AST_FRAME_TEXT;
 		f.subclass.integer = 0;
 		f.offset = 0;
-		f.data.ptr = buf;
-		f.datalen = strlen(buf) + 1;
+		f.data.ptr = ast_str_buffer(buf);
+		f.datalen = ast_str_strlen(buf) + 1;
 		ast_queue_frame(p->owner, &f);
 		transmit_response(p, "202 Accepted", req); /* We respond 202 accepted, since we relay the message */
 		return;
 	}
 
-	/* Message outside of a call, we do not support that */
-	ast_log(LOG_WARNING, "Received message to %s from %s, dropped it...\n  Content-Type:%s\n  Message: %s\n", get_header(req, "To"), get_header(req, "From"), content_type, buf);
-	transmit_response(p, "405 Method Not Allowed", req);
+	if (!sip_cfg.accept_outofcall_message) {
+		/* Message outside of a call, we do not support that */
+		ast_debug(1, "MESSAGE outside of a call administratively disabled.\n");
+		transmit_response(p, "405 Method Not Allowed", req);
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		return;
+	}
+
+	copy_request(&p->initreq, req);
+
+	if (sip_cfg.auth_message_requests) {
+		int res;
+
+		set_pvt_allowed_methods(p, req);
+		res = check_user(p, req, SIP_MESSAGE, e, XMIT_UNRELIABLE, addr);
+		if (res == AUTH_CHALLENGE_SENT) {
+			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+			return;
+		}
+		if (res < 0) { /* Something failed in authentication */
+			if (res == AUTH_FAKE_AUTH) {
+				ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From"));
+				transmit_fake_auth_response(p, SIP_OPTIONS, req, XMIT_UNRELIABLE);
+			} else {
+				ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From"));
+				transmit_response(p, "403 Forbidden", req);
+			}
+			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+			return;
+		}
+		/* Auth was successful.  Proceed. */
+	} else {
+		struct sip_peer *peer;
+
+		/*
+		 * MESSAGE outside of a call, not authenticating it.
+		 * Check to see if we match a peer anyway so that we can direct
+		 * it to the right context.
+		 */
+
+		peer = find_peer(NULL, &p->recv, TRUE, FINDPEERS, 0, p->socket.type);
+		if (peer) {
+			/* Only if no auth is required. */
+			if (ast_strlen_zero(peer->secret) && ast_strlen_zero(peer->md5secret)) {
+				ast_string_field_set(p, context, peer->context);
+			}
+			peer = unref_peer(peer, "from find_peer() in receive_message");
+		}
+	}
+
+	get_destination(p, NULL, NULL);
+
+	if (!(msg = ast_msg_alloc())) {
+		transmit_response(p, "500 Internal Server Error", req);
+		if (!p->owner) {
+			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		}
+		return;
+	}
+
+	to = ast_strdupa(REQ_OFFSET_TO_STR(req, rlPart2));
+	from = ast_strdupa(get_header(req, "From"));
+
+	res = ast_msg_set_to(msg, "%s", to);
+	res |= ast_msg_set_from(msg, "%s", get_in_brackets(from));
+	res |= ast_msg_set_body(msg, "%s", ast_str_buffer(buf));
+	res |= ast_msg_set_context(msg, "%s", p->context);
+	res |= ast_msg_set_exten(msg, "%s", p->exten);
+
+	if (res) {
+		ast_msg_destroy(msg);
+	} else {
+		set_message_vars_from_req(msg, req);
+		ast_msg_queue(msg);
+	}
+
+	transmit_response(p, "202 Accepted", req);
 	sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-	return;
 }
 
 /*! \brief  CLI Command to show calls within limits set by call_limit */
@@ -20549,6 +20707,8 @@
 				handle_response_register(p, resp, rest, req, seqno);
 			else if (sipmethod == SIP_UPDATE) {
 				handle_response_update(p, resp, rest, req, seqno);
+			} else if (sipmethod == SIP_MESSAGE) {
+				handle_response_message(p, resp, rest, req, seqno);
 			} else if (sipmethod == SIP_BYE) {
 				if (p->options)
 					p->options->auth_type = resp;
@@ -20894,11 +21054,11 @@
 
 #ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
 	if (!res) {
-		transmit_message_with_text(transferer->tech_pvt, "Unable to park call.\n");
+		transmit_message_with_text(transferer->tech_pvt, "Unable to park call.\n", 0, 0);
 	} else {
 		/* Then tell the transferer what happened */
 		sprintf(buf, "Call parked on extension '%d'", ext);
-		transmit_message_with_text(transferer->tech_pvt, buf);
+		transmit_message_with_text(transferer->tech_pvt, buf, 0, 0);
 	}
 #endif
 
@@ -22185,9 +22345,9 @@
 					char *decoded_exten = ast_strdupa(p->exten);
 					transmit_response_reliable(p, "404 Not Found", req);
 					ast_uri_decode(decoded_exten, ast_uri_sip_user);
-					ast_log(LOG_NOTICE, "Call from '%s' to extension"
+					ast_log(LOG_NOTICE, "Call from '%s' (%s) to extension"
 						" '%s' rejected because extension not found in context '%s'.\n",
-						S_OR(p->username, p->peername), decoded_exten, p->context);
+						S_OR(p->username, p->peername), ast_sockaddr_stringify(&p->recv), decoded_exten, p->context);
 				}
 			} /* end switch */
 
@@ -23378,16 +23538,127 @@
 	return 1;
 }
 
+/*!
+ * \internal
+ * \brief Handle auth requests to a MESSAGE request
+ */
+static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
+{
+	char *header, *respheader;
+	char digest[1024];
+
+	if (p->options) {
+		p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
+	}
+
+	if ((p->authtries == MAX_AUTHTRIES)) {
+		ast_log(LOG_NOTICE, "Failed to authenticate on MESSAGE to '%s'\n", get_header(&p->initreq, "From"));
+		pvt_set_needdestroy(p, "MESSAGE authentication failed");
+		return;
+	}
+
+	p->authtries++;
+	auth_headers((resp == 401 ? WWW_AUTH : PROXY_AUTH), &header, &respheader);
+	memset(digest, 0, sizeof(digest));
+	if (reply_digest(p, req, header, SIP_MESSAGE, digest, sizeof(digest))) {
+		/* There's nothing to use for authentication */
+		ast_debug(1, "Nothing to use for MESSAGE authentication\n");
+		pvt_set_needdestroy(p, "MESSAGE authentication failed");
+		return;
+	}
+
+	if (p->do_history) {
+		append_history(p, "MessageAuth", "Try: %d", p->authtries);
+	}
+
+	transmit_message_with_text(p, p->msg_body, 0, 1);
+}
+
 /*! \brief Handle incoming MESSAGE request */
-static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
+static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
 {
 	if (!req->ignore) {
 		if (req->debug)
 			ast_verbose("Receiving message!\n");
-		receive_message(p, req);
+		receive_message(p, req, addr, e);
 	} else
 		transmit_response(p, "202 Accepted", req);
 	return 1;
+}
+
+static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from);
+
+static const struct ast_msg_tech sip_msg_tech = {
+	.name = "sip",
+	.msg_send = sip_msg_send,
+};
+
+static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from)
+{
+	struct sip_pvt *pvt;
+	int res;
+	char *peer;
+	struct sip_peer *peer_ptr;
+
+	if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_MESSAGE, NULL))) {
+		return -1;
+	}
+
+	peer = ast_strdupa(to);
+	if (strchr(peer, '@')) {
+		strsep(&peer, "@");
+	} else {
+		strsep(&peer, ":");
+	}
+	if (ast_strlen_zero(peer)) {
+		ast_log(LOG_WARNING, "MESSAGE(to) is invalid for SIP - '%s'\n", to);
+		return -1;
+	}
+
+	if (!ast_strlen_zero(from)) {
+		if ((peer_ptr = find_peer(from, NULL, 0, 1, 0, 0))) {
+			ast_string_field_set(pvt, fromname, S_OR(peer_ptr->cid_name, peer_ptr->name));
+			ast_string_field_set(pvt, fromuser, S_OR(peer_ptr->cid_num, peer_ptr->name));
+			unref_peer(peer_ptr, "unref_peer, from sip_msg_send, find_peer");
+		} else if (strchr(from, '<')) { /* from is callerid-style */
+			char *sender;
+			char *name = NULL, *location = NULL, *user = NULL, *domain = NULL;
+
+			sender = ast_strdupa(from);
+			ast_callerid_parse(sender, &name, &location);
+			ast_string_field_set(pvt, fromname, name);
+			if (strchr(location, ':')) { /* Must be a URI */
+				parse_uri(location, "sip:,sips:", &user, NULL, &domain, NULL);
+				ast_string_field_set(pvt, fromuser, user);
+				ast_string_field_set(pvt, fromdomain, domain);
+			} else { /* Treat it as an exten/user */
+				ast_string_field_set(pvt, fromuser, location);
+			}
+		} else { /* assume we just have the name, use defaults for the rest */
+			ast_string_field_set(pvt, fromname, from);
+		}
+	}
+
+	sip_pvt_lock(pvt);
+
+	if (create_addr(pvt, peer, NULL, TRUE, NULL)) {
+		sip_pvt_unlock(pvt);
+		dialog_unlink_all(pvt, TRUE, TRUE);
+		dialog_unref(pvt, "create_addr failed sending a MESSAGE");
+		return -1;
+	}
+	ast_sip_ouraddrfor(&pvt->sa, &pvt->ourip, pvt);
+	ast_set_flag(&pvt->flags[0], SIP_OUTGOING);
+
+	/* XXX Does pvt->expiry need to be set? */
+
+	res = transmit_message_with_msg(pvt, msg);
+
+	sip_pvt_unlock(pvt);
+	sip_scheddestroy(pvt, DEFAULT_TRANS_TIMEOUT);
+	dialog_unref(pvt, "sent a MESSAGE");
+
+	return res;
 }
 
 static enum sip_publish_type determine_sip_publish_type(struct sip_request *req, const char * const event, const char * const etag, const char * const expires, int *expires_int)
@@ -24589,7 +24860,7 @@
 		res = handle_request_bye(p, req);
 		break;
 	case SIP_MESSAGE:
-		res = handle_request_message(p, req);
+		res = handle_request_message(p, req, addr, e);
 		break;
 	case SIP_PUBLISH:
 		res = handle_request_publish(p, req, addr, seqno, e);
@@ -27368,6 +27639,8 @@
 	sip_cfg.directrtpsetup = FALSE;		/* Experimental feature, disabled by default */
 	sip_cfg.alwaysauthreject = DEFAULT_ALWAYSAUTHREJECT;
 	sip_cfg.auth_options_requests = DEFAULT_AUTH_OPTIONS;
+	sip_cfg.auth_message_requests = DEFAULT_AUTH_MESSAGE;
+	sip_cfg.accept_outofcall_message = DEFAULT_ACCEPT_OUTOFCALL_MESSAGE;
 	sip_cfg.allowsubscribe = FALSE;
 	sip_cfg.disallowed_methods = SIP_UNKNOWN;
 	sip_cfg.contact_ha = NULL;		/* Reset the contact ACL */
@@ -27616,6 +27889,10 @@
 			if (ast_true(v->value)) {
 				sip_cfg.auth_options_requests = 1;
 			}
+		} else if (!strcasecmp(v->name, "auth_message_requests")) {
+			sip_cfg.auth_message_requests = ast_true(v->value) ? 1 : 0;
+		} else if (!strcasecmp(v->name, "accept_outofcall_message")) {
+			sip_cfg.accept_outofcall_message = ast_true(v->value) ? 1 : 0;
 		} else if (!strcasecmp(v->name, "mohinterpret")) {
 			ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret));
 		} else if (!strcasecmp(v->name, "mohsuggest")) {
@@ -29586,6 +29863,11 @@
 	memcpy(&sip_tech_info, &sip_tech, sizeof(sip_tech));
 	memset((void *) &sip_tech_info.send_digit_begin, 0, sizeof(sip_tech_info.send_digit_begin));
 
+	if (ast_msg_tech_register(&sip_msg_tech)) {
+		/* LOAD_FAILURE stops Asterisk, so cleanup is a moot point. */
+		return AST_MODULE_LOAD_FAILURE;
+	}
+

[... 45 lines stripped ...]



More information about the asterisk-commits mailing list