summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2011-10-18 21:15:45 +0000
committerRichard Mudgett <rmudgett@digium.com>2011-10-18 21:15:45 +0000
commit10de040b6e89e7c973c23353d477c5dd465a14df (patch)
tree25a7d164a2b8276213348bc5f63fe7c6ed9af4cf
parentd19ddf87410ce65071cf92c298bf048f3ab0f9bb (diff)
More parking issues.
* Fix potential deadlocks in SIP and IAX blind transfer to parking. * Fix SIP, IAX, DAHDI analog, and MGCP channel drivers to respect the parkext_exclusive option with transfers (Park(,,,,,exclusive_lot) parameter). Created ast_park_call_exten() and ast_masq_park_call_exten() to maintian API compatibility. * Made masq_park_call() handle a failed ast_channel_masquerade() setup. * Reduced excessive struct parkeduser.peername[] size. ........ Merged revisions 341254 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 341255 from http://svn.asterisk.org/svn/asterisk/branches/10 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@341256 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--channels/chan_dahdi.c3
-rw-r--r--channels/chan_iax2.c189
-rw-r--r--channels/chan_mgcp.c3
-rw-r--r--channels/chan_sip.c108
-rw-r--r--channels/sig_analog.c4
-rw-r--r--channels/sip/include/sip.h3
-rw-r--r--include/asterisk/features.h88
-rw-r--r--main/features.c209
8 files changed, 410 insertions, 197 deletions
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 05b992402..aa3c71fd8 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -10324,7 +10324,8 @@ static void *analog_ss_thread(void *data)
ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
/* This is a three way call, the main call being a real channel,
and we're parking the first call. */
- ast_masq_park_call(ast_bridged_channel(p->subs[SUB_THREEWAY].owner), chan, 0, NULL);
+ ast_masq_park_call_exten(ast_bridged_channel(p->subs[SUB_THREEWAY].owner),
+ chan, exten, chan->context, 0, NULL);
ast_verb(3, "Parking call to '%s'\n", chan->name);
break;
} else if (p->hidecallerid && !strcmp(exten, "*82")) {
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 3d671bc9a..a8369f554 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -9347,78 +9347,125 @@ static void spawn_dp_lookup(int callno, const char *context, const char *calledn
struct iax_dual {
struct ast_channel *chan1;
struct ast_channel *chan2;
- const char *parkexten;
+ char *park_exten;
+ char *park_context;
};
static void *iax_park_thread(void *stuff)
{
- struct ast_channel *chan1, *chan2;
struct iax_dual *d;
- struct ast_frame *f;
+ int res;
int ext = 0;
d = stuff;
- chan1 = d->chan1;
- chan2 = d->chan2;
+
+ ast_debug(4, "IAX Park: Transferer channel %s, Transferee %s\n",
+ d->chan2->name, d->chan1->name);
+
+ res = ast_park_call_exten(d->chan1, d->chan2, d->park_exten, d->park_context, 0, &ext);
+ if (res) {
+ /* Parking failed. */
+ ast_hangup(d->chan1);
+ } else {
+ ast_log(LOG_NOTICE, "Parked on extension '%d'\n", ext);
+ }
+ ast_hangup(d->chan2);
+
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
ast_free(d);
- f = ast_read(chan1);
- if (f)
- ast_frfree(f);
- ast_park_call(chan1, chan2, 0, d->parkexten, &ext);
- ast_hangup(chan2);
- ast_log(LOG_NOTICE, "Parked on extension '%d'\n", ext);
return NULL;
}
-static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2, const char *parkexten)
+/*! DO NOT hold any locks while calling iax_park */
+static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2, const char *park_exten, const char *park_context)
{
struct iax_dual *d;
- struct ast_channel *chan1m, *chan2m;
+ struct ast_channel *chan1m, *chan2m;/* Chan2m: The transferer, chan1m: The transferee */
pthread_t th;
+
chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan1->exten, chan1->context, chan1->linkedid, chan1->amaflags, "Parking/%s", chan1->name);
chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->linkedid, chan2->amaflags, "IAXPeer/%s", chan2->name);
- if (chan2m && chan1m) {
- /* Make formats okay */
- chan1m->readformat = chan1->readformat;
- chan1m->writeformat = chan1->writeformat;
- ast_channel_masquerade(chan1m, chan1);
- /* Setup the extensions and such */
- ast_copy_string(chan1m->context, chan1->context, sizeof(chan1m->context));
- ast_copy_string(chan1m->exten, chan1->exten, sizeof(chan1m->exten));
- chan1m->priority = chan1->priority;
-
- /* We make a clone of the peer channel too, so we can play
- back the announcement */
- /* Make formats okay */
- chan2m->readformat = chan2->readformat;
- chan2m->writeformat = chan2->writeformat;
- ast_channel_masquerade(chan2m, chan2);
- /* Setup the extensions and such */
- ast_copy_string(chan2m->context, chan2->context, sizeof(chan2m->context));
- ast_copy_string(chan2m->exten, chan2->exten, sizeof(chan2m->exten));
- chan2m->priority = chan2->priority;
- if (ast_do_masquerade(chan2m)) {
- ast_log(LOG_WARNING, "Masquerade failed :(\n");
- ast_hangup(chan2m);
- return -1;
- }
- } else {
- if (chan1m)
+ d = ast_calloc(1, sizeof(*d));
+ if (!chan1m || !chan2m || !d) {
+ if (chan1m) {
ast_hangup(chan1m);
- if (chan2m)
+ }
+ if (chan2m) {
ast_hangup(chan2m);
+ }
+ ast_free(d);
return -1;
}
- if ((d = ast_calloc(1, sizeof(*d)))) {
- d->chan1 = chan1m;
- d->chan2 = chan2m;
- d->parkexten = parkexten;
- if (!ast_pthread_create_detached_background(&th, NULL, iax_park_thread, d)) {
- return 0;
- }
+ d->park_exten = ast_strdup(park_exten);
+ d->park_context = ast_strdup(park_context);
+ if (!d->park_exten || !d->park_context) {
+ ast_hangup(chan1m);
+ ast_hangup(chan2m);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
ast_free(d);
+ return -1;
}
- return -1;
+
+ /* Make formats okay */
+ chan1m->readformat = chan1->readformat;
+ chan1m->writeformat = chan1->writeformat;
+
+ /* Prepare for taking over the channel */
+ if (ast_channel_masquerade(chan1m, chan1)) {
+ ast_hangup(chan1m);
+ ast_hangup(chan2m);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
+ ast_free(d);
+ return -1;
+ }
+
+ /* Setup the extensions and such */
+ ast_copy_string(chan1m->context, chan1->context, sizeof(chan1m->context));
+ ast_copy_string(chan1m->exten, chan1->exten, sizeof(chan1m->exten));
+ chan1m->priority = chan1->priority;
+
+ ast_do_masquerade(chan1m);
+
+ /* We make a clone of the peer channel too, so we can play
+ back the announcement */
+
+ /* Make formats okay */
+ chan2m->readformat = chan2->readformat;
+ chan2m->writeformat = chan2->writeformat;
+ ast_string_field_set(chan2m, parkinglot, chan2->parkinglot);
+
+ /* Prepare for taking over the channel */
+ if (ast_channel_masquerade(chan2m, chan2)) {
+ ast_hangup(chan1m);
+ ast_hangup(chan2m);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
+ ast_free(d);
+ return -1;
+ }
+
+ /* Setup the extensions and such */
+ ast_copy_string(chan2m->context, chan2->context, sizeof(chan2m->context));
+ ast_copy_string(chan2m->exten, chan2->exten, sizeof(chan2m->exten));
+ chan2m->priority = chan2->priority;
+
+ ast_do_masquerade(chan2m);
+
+ d->chan1 = chan1m; /* Transferee */
+ d->chan2 = chan2m; /* Transferer */
+ if (ast_pthread_create_detached_background(&th, NULL, iax_park_thread, d) < 0) {
+ /* Could not start thread */
+ ast_hangup(chan1m);
+ ast_hangup(chan2m);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
+ ast_free(d);
+ return -1;
+ }
+ return 0;
}
static int check_provisioning(struct sockaddr_in *sin, int sockfd, char *si, unsigned int ver)
@@ -10795,43 +10842,45 @@ static int socket_process(struct iax2_thread *thread)
owner = iaxs[fr->callno]->owner;
bridged_chan = owner ? ast_bridged_channel(owner) : NULL;
if (bridged_chan && ies.called_number) {
+ const char *context;
+
+ context = ast_strdupa(iaxs[fr->callno]->context);
+
+ ast_channel_ref(owner);
+ ast_channel_ref(bridged_chan);
+ ast_channel_unlock(owner);
ast_mutex_unlock(&iaxsl[fr->callno]);
/* Set BLINDTRANSFER channel variables */
pbx_builtin_setvar_helper(owner, "BLINDTRANSFER", bridged_chan->name);
pbx_builtin_setvar_helper(bridged_chan, "BLINDTRANSFER", owner->name);
- if (ast_parking_ext_valid(ies.called_number, owner, iaxs[fr->callno]->context)) {
+ /* DO NOT hold any locks while calling ast_parking_ext_valid() */
+ if (ast_parking_ext_valid(ies.called_number, owner, context)) {
ast_debug(1, "Parking call '%s'\n", bridged_chan->name);
- if (iax_park(bridged_chan, owner, ies.called_number)) {
+ if (iax_park(bridged_chan, owner, ies.called_number, context)) {
ast_log(LOG_WARNING, "Failed to park call '%s'\n",
bridged_chan->name);
}
- ast_mutex_lock(&iaxsl[fr->callno]);
} else {
- ast_mutex_lock(&iaxsl[fr->callno]);
-
- if (iaxs[fr->callno]) {
- if (ast_async_goto(bridged_chan, iaxs[fr->callno]->context,
- ies.called_number, 1)) {
- ast_log(LOG_WARNING,
- "Async goto of '%s' to '%s@%s' failed\n",
- bridged_chan->name, ies.called_number,
- iaxs[fr->callno]->context);
- } else {
- ast_debug(1, "Async goto of '%s' to '%s@%s' started\n",
- bridged_chan->name, ies.called_number,
- iaxs[fr->callno]->context);
- }
+ if (ast_async_goto(bridged_chan, context, ies.called_number, 1)) {
+ ast_log(LOG_WARNING,
+ "Async goto of '%s' to '%s@%s' failed\n",
+ bridged_chan->name, ies.called_number, context);
} else {
- /* Initiating call went away before we could transfer. */
+ ast_debug(1, "Async goto of '%s' to '%s@%s' started\n",
+ bridged_chan->name, ies.called_number, context);
}
}
+ ast_channel_unref(owner);
+ ast_channel_unref(bridged_chan);
+
+ ast_mutex_lock(&iaxsl[fr->callno]);
} else {
ast_debug(1, "Async goto not applicable on call %d\n", fr->callno);
- }
- if (owner) {
- ast_channel_unlock(owner);
+ if (owner) {
+ ast_channel_unlock(owner);
+ }
}
break;
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 502b929b6..9df36eb7a 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -3133,7 +3133,8 @@ static void *mgcp_ss(void *data)
sub->next->owner && ast_bridged_channel(sub->next->owner)) {
/* This is a three way call, the main call being a real channel,
and we're parking the first call. */
- ast_masq_park_call(ast_bridged_channel(sub->next->owner), chan, 0, NULL);
+ ast_masq_park_call_exten(ast_bridged_channel(sub->next->owner), chan,
+ p->dtmf_buf, chan->context, 0, NULL);
ast_verb(3, "Parking call to '%s'\n", chan->name);
break;
} else if (!ast_strlen_zero(p->lastcallerid) && !strcmp(p->dtmf_buf, "*60")) {
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index fcc0dfbd5..c7b39b8b6 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1289,7 +1289,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag);
static void check_pendings(struct sip_pvt *p);
static void *sip_park_thread(void *stuff);
-static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, char *parkexten);
+static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, const char *park_exten, const char *park_context);
static void *sip_pickup_thread(void *stuff);
static int sip_pickup(struct ast_channel *chan);
@@ -21438,32 +21438,16 @@ static void *sip_park_thread(void *stuff)
{
struct ast_channel *transferee, *transferer; /* Chan1: The transferee, Chan2: The transferer */
struct sip_dual *d;
- struct sip_request req = {0,};
int ext;
int res;
d = stuff;
transferee = d->chan1;
transferer = d->chan2;
- copy_request(&req, &d->req);
- if (!transferee || !transferer) {
- ast_log(LOG_ERROR, "Missing channels for parking! Transferer %s Transferee %s\n", transferer ? "<available>" : "<missing>", transferee ? "<available>" : "<missing>" );
- deinit_req(&d->req);
- ast_free(d);
- return NULL;
- }
ast_debug(4, "SIP Park: Transferer channel %s, Transferee %s\n", transferer->name, transferee->name);
- if (ast_do_masquerade(transferee)) {
- ast_log(LOG_WARNING, "Masquerade failed.\n");
- transmit_response(transferer->tech_pvt, "503 Internal error", &req);
- deinit_req(&d->req);
- ast_free(d);
- return NULL;
- }
-
- res = ast_park_call(transferee, transferer, 0, d->parkexten, &ext);
+ res = ast_park_call_exten(transferee, transferer, d->park_exten, d->park_context, 0, &ext);
#ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
if (res) {
@@ -21491,31 +21475,40 @@ static void *sip_park_thread(void *stuff)
/* Do not hangup call */
}
deinit_req(&d->req);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
ast_free(d);
return NULL;
}
-/*! \brief Park a call using the subsystem in res_features.c
- This is executed in a separate thread
-*/
-static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, char *parkexten)
+/*! DO NOT hold any locks while calling sip_park */
+static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, const char *park_exten, const char *park_context)
{
struct sip_dual *d;
struct ast_channel *transferee, *transferer;
- /* Chan2m: The transferer, chan1m: The transferee */
pthread_t th;
transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan1->accountcode, chan1->exten, chan1->context, chan1->linkedid, chan1->amaflags, "Parking/%s", chan1->name);
transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->linkedid, chan2->amaflags, "SIPPeer/%s", chan2->name);
- if ((!transferer) || (!transferee)) {
+ d = ast_calloc(1, sizeof(*d));
+ if (!transferee || !transferer || !d) {
if (transferee) {
- transferee->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
ast_hangup(transferee);
}
if (transferer) {
- transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
ast_hangup(transferer);
}
+ ast_free(d);
+ return -1;
+ }
+ d->park_exten = ast_strdup(park_exten);
+ d->park_context = ast_strdup(park_context);
+ if (!d->park_exten || !d->park_context) {
+ ast_hangup(transferee);
+ ast_hangup(transferer);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
+ ast_free(d);
return -1;
}
@@ -21524,67 +21517,56 @@ static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct
transferee->writeformat = chan1->writeformat;
/* Prepare for taking over the channel */
- ast_channel_masquerade(transferee, chan1);
+ if (ast_channel_masquerade(transferee, chan1)) {
+ ast_hangup(transferee);
+ ast_hangup(transferer);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
+ ast_free(d);
+ return -1;
+ }
/* Setup the extensions and such */
ast_copy_string(transferee->context, chan1->context, sizeof(transferee->context));
ast_copy_string(transferee->exten, chan1->exten, sizeof(transferee->exten));
transferee->priority = chan1->priority;
-
+
+ ast_do_masquerade(transferee);
+
/* We make a clone of the peer channel too, so we can play
back the announcement */
/* Make formats okay */
transferer->readformat = chan2->readformat;
transferer->writeformat = chan2->writeformat;
- if (!ast_strlen_zero(chan2->parkinglot))
- ast_string_field_set(transferer, parkinglot, chan2->parkinglot);
-
- /* Prepare for taking over the channel. Go ahead and grab this channel
- * lock here to avoid a deadlock with callbacks into the channel driver
- * that hold the channel lock and want the pvt lock. */
- while (ast_channel_trylock(chan2)) {
- struct sip_pvt *pvt = chan2->tech_pvt;
- sip_pvt_unlock(pvt);
- usleep(1);
- sip_pvt_lock(pvt);
+ ast_string_field_set(transferer, parkinglot, chan2->parkinglot);
+
+ /* Prepare for taking over the channel */
+ if (ast_channel_masquerade(transferer, chan2)) {
+ ast_hangup(transferer);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
+ ast_free(d);
+ return -1;
}
- ast_channel_masquerade(transferer, chan2);
- ast_channel_unlock(chan2);
/* Setup the extensions and such */
ast_copy_string(transferer->context, chan2->context, sizeof(transferer->context));
ast_copy_string(transferer->exten, chan2->exten, sizeof(transferer->exten));
transferer->priority = chan2->priority;
- if (ast_do_masquerade(transferer)) {
- ast_log(LOG_WARNING, "Masquerade failed :(\n");
- transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
- ast_hangup(transferer);
- return -1;
- }
- if (!transferer || !transferee) {
- if (!transferer) {
- ast_debug(1, "No transferer channel, giving up parking\n");
- }
- if (!transferee) {
- ast_debug(1, "No transferee channel, giving up parking\n");
- }
- return -1;
- }
- if (!(d = ast_calloc(1, sizeof(*d)))) {
- return -1;
- }
+ ast_do_masquerade(transferer);
/* Save original request for followup */
copy_request(&d->req, req);
d->chan1 = transferee; /* Transferee */
d->chan2 = transferer; /* Transferer */
d->seqno = seqno;
- d->parkexten = parkexten;
if (ast_pthread_create_detached_background(&th, NULL, sip_park_thread, d) < 0) {
/* Could not start thread */
deinit_req(&d->req);
+ ast_free(d->park_exten);
+ ast_free(d->park_context);
ast_free(d); /* We don't need it anymore. If thread is created, d will be free'd
by sip_park_thread() */
return -1;
@@ -23611,6 +23593,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
callid = ast_strdupa(p->callid);
localtransfer = p->refer->localtransfer;
attendedtransfer = p->refer->attendedtransfer;
+
if (!*nounlock) {
ast_channel_unlock(p->owner);
*nounlock = 1;
@@ -23619,9 +23602,6 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
/* Parking a call. DO NOT hold any locks while calling ast_parking_ext_valid() */
if (localtransfer && ast_parking_ext_valid(refer_to, current.chan1, current.chan1->context)) {
-
- copy_request(&current.req, req);
-
sip_pvt_lock(p);
ast_clear_flag(&p->flags[0], SIP_GOTREFER);
p->refer->status = REFER_200OK;
@@ -23650,7 +23630,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
}
/* DO NOT hold any locks while calling sip_park */
- if (sip_park(current.chan2, current.chan1, req, seqno, refer_to)) {
+ if (sip_park(current.chan2, current.chan1, req, seqno, refer_to, current.chan1->context)) {
sip_pvt_lock(p);
transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", TRUE);
} else {
diff --git a/channels/sig_analog.c b/channels/sig_analog.c
index 4789f964c..003563767 100644
--- a/channels/sig_analog.c
+++ b/channels/sig_analog.c
@@ -2261,7 +2261,9 @@ static void *__analog_ss_thread(void *data)
ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
/* This is a three way call, the main call being a real channel,
and we're parking the first call. */
- ast_masq_park_call(ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), chan, 0, NULL);
+ ast_masq_park_call_exten(
+ ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), chan, exten,
+ chan->context, 0, NULL);
ast_verb(3, "Parking call to '%s'\n", chan->name);
break;
} else if (!ast_strlen_zero(p->lastcid_num) && !strcmp(exten, "*60")) {
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index fc75ff4fa..c2924b710 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -805,7 +805,8 @@ struct sip_dual {
struct ast_channel *chan2; /*!< Second channel involved */
struct sip_request req; /*!< Request that caused the transfer (REFER) */
int seqno; /*!< Sequence number */
- const char *parkexten;
+ char *park_exten;
+ char *park_context;
};
/*! \brief Parameters to the transmit_invite function */
diff --git a/include/asterisk/features.h b/include/asterisk/features.h
index 7e749f2e0..76a141df1 100644
--- a/include/asterisk/features.h
+++ b/include/asterisk/features.h
@@ -78,33 +78,87 @@ struct ast_call_feature {
};
/*!
- * \brief Park a call and read back parked location
- * \param chan the channel to actually be parked
- * \param host the channel which will have the parked location read to.
+ * \brief Park a call and read back parked location
+ *
+ * \param park_me Channel to be parked.
+ * \param parker Channel parking the call.
* \param timeout is a timeout in milliseconds
+ * \param park_exten Parking lot access extension (Not used)
* \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
- *
- * Park the channel chan, and read back the parked location to the host.
- * If the call is not picked up within a specified period of time,
- * then the call will return to the last step that it was in
- * (in terms of exten, priority and context)
+ *
+ * \details
+ * Park the park_me channel, and read back the parked location
+ * to the parker channel. If the call is not picked up within a
+ * specified period of time, then the call will return to the
+ * last step that it was in (in terms of exten, priority and
+ * context).
+ *
+ * \note Use ast_park_call_exten() instead.
+ *
* \retval 0 on success.
* \retval -1 on failure.
-*/
-int ast_park_call(struct ast_channel *chan, struct ast_channel *host, int timeout, const char *parkexten, int *extout);
+ */
+int ast_park_call(struct ast_channel *park_me, struct ast_channel *parker, int timeout, const char *park_exten, int *extout);
-/*!
+/*!
+ * \brief Park a call and read back parked location
+ * \since 1.8.9
+ *
+ * \param park_me Channel to be parked.
+ * \param parker Channel parking the call.
+ * \param park_exten Parking lot access extension
+ * \param park_context Parking lot context
+ * \param timeout is a timeout in milliseconds
+ * \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
+ *
+ * \details
+ * Park the park_me channel, and read back the parked location
+ * to the parker channel. If the call is not picked up within a
+ * specified period of time, then the call will return to the
+ * last step that it was in (in terms of exten, priority and
+ * context).
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_park_call_exten(struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout);
+
+/*!
* \brief Park a call via a masqueraded channel
- * \param rchan the real channel to be parked
- * \param host the channel to have the parking read to.
+ *
+ * \param park_me Channel to be parked.
+ * \param parker Channel parking the call.
* \param timeout is a timeout in milliseconds
* \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
- *
- * Masquerade the channel rchan into a new, empty channel which is then parked with ast_park_call
+ *
+ * \details
+ * Masquerade the park_me channel into a new, empty channel which is then parked.
+ *
+ * \note Use ast_masq_park_call_exten() instead.
+ *
* \retval 0 on success.
* \retval -1 on failure.
-*/
-int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *host, int timeout, int *extout);
+ */
+int ast_masq_park_call(struct ast_channel *park_me, struct ast_channel *parker, int timeout, int *extout);
+
+/*!
+ * \brief Park a call via a masqueraded channel
+ * \since 1.8.9
+ *
+ * \param park_me Channel to be parked.
+ * \param parker Channel parking the call.
+ * \param park_exten Parking lot access extension
+ * \param park_context Parking lot context
+ * \param timeout is a timeout in milliseconds
+ * \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
+ *
+ * \details
+ * Masquerade the park_me channel into a new, empty channel which is then parked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_masq_park_call_exten(struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout);
/*!
* \brief Determine if parking extension exists in a given context
diff --git a/main/features.c b/main/features.c
index 84e427cc7..73974ca56 100644
--- a/main/features.c
+++ b/main/features.c
@@ -480,7 +480,7 @@ struct parkeduser {
enum ast_control_frame_type hold_method;
unsigned int notquiteyet:1;
unsigned int options_specified:1;
- char peername[1024];
+ char peername[AST_CHANNEL_NAME];
unsigned char moh_trys;
/*! Parking lot this entry belongs to. Holds a parking lot reference. */
struct ast_parkinglot *parkinglot;
@@ -1171,6 +1171,32 @@ static struct ast_parkinglot *create_dynamic_parkinglot(const char *name, struct
/*!
* \internal
+ * \brief Abort parking a call that has not completed parking yet.
+ *
+ * \param pu Parked user item to clean up.
+ *
+ * \note The parking lot parkings list is locked on entry.
+ *
+ * \return Nothing
+ */
+static void park_space_abort(struct parkeduser *pu)
+{
+ struct ast_parkinglot *parkinglot;
+
+ parkinglot = pu->parkinglot;
+
+ /* Put back the parking space just allocated. */
+ --parkinglot->next_parking_space;
+
+ AST_LIST_REMOVE(&parkinglot->parkings, pu, list);
+
+ AST_LIST_UNLOCK(&parkinglot->parkings);
+ parkinglot_unref(parkinglot);
+ ast_free(pu);
+}
+
+/*!
+ * \internal
* \brief Reserve a parking space in a parking lot for a call being parked.
*
* \param park_me Channel being parked.
@@ -1525,38 +1551,80 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, st
return 0;
}
-/*! \brief Park a call */
-int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, const char *parkexten, int *extout)
+int ast_park_call_exten(struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout)
{
+ int res;
+ char *parse;
+ const char *app_data;
+ struct ast_exten *exten;
+ struct park_app_args app_args;
struct ast_park_call_args args = {
.timeout = timeout,
.extout = extout,
};
- return park_call_full(chan, peer, &args);
+ if (!park_exten || !park_context) {
+ return park_call_full(park_me, parker, &args);
+ }
+
+ /*
+ * Determiine if the specified park extension has an exclusive
+ * parking lot to use.
+ */
+ if (parker && parker != park_me) {
+ ast_autoservice_start(park_me);
+ }
+ exten = get_parking_exten(park_exten, parker, park_context);
+ if (exten) {
+ app_data = ast_get_extension_app_data(exten);
+ if (!app_data) {
+ app_data = "";
+ }
+ parse = ast_strdupa(app_data);
+ AST_STANDARD_APP_ARGS(app_args, parse);
+
+ if (!ast_strlen_zero(app_args.pl_name)) {
+ /* Find the specified exclusive parking lot */
+ args.parkinglot = find_parkinglot(app_args.pl_name);
+ if (!args.parkinglot && parkeddynamic) {
+ args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
+ }
+ }
+ }
+ if (parker && parker != park_me) {
+ ast_autoservice_stop(park_me);
+ }
+
+ res = park_call_full(park_me, parker, &args);
+ if (args.parkinglot) {
+ parkinglot_unref(args.parkinglot);
+ }
+ return res;
+}
+
+int ast_park_call(struct ast_channel *park_me, struct ast_channel *parker, int timeout, const char *park_exten, int *extout)
+{
+ struct ast_park_call_args args = {
+ .timeout = timeout,
+ .extout = extout,
+ };
+
+ return park_call_full(park_me, parker, &args);
}
/*!
+ * \brief Park call via masqueraded channel and announce parking spot on peer channel.
+ *
* \param rchan the real channel to be parked
* \param peer the channel to have the parking read to.
- * \param timeout is a timeout in milliseconds
- * \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
- * \param play_announcement TRUE if to play which parking space call parked in to peer.
- * \param args Optional additional parking options when parking a call.
+ * \param args Additional parking options when parking a call.
*
* \retval 0 on success.
* \retval -1 on failure.
*/
-static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout, int play_announcement, struct ast_park_call_args *args)
+static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, struct ast_park_call_args *args)
{
struct ast_channel *chan;
- struct ast_park_call_args park_args = {0,};
-
- if (!args) {
- args = &park_args;
- args->timeout = timeout;
- args->extout = extout;
- }
/* Make a new, channel that we'll use to masquerade in the real one */
chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten,
@@ -1577,7 +1645,6 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i
args->pu = park_space_reserve(rchan, peer, args);
if (!args->pu) {
- chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
ast_hangup(chan);
if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
if (peer == rchan) {
@@ -1594,7 +1661,22 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i
/* Make formats okay */
chan->readformat = rchan->readformat;
chan->writeformat = rchan->writeformat;
- ast_channel_masquerade(chan, rchan);
+
+ if (ast_channel_masquerade(chan, rchan)) {
+ park_space_abort(args->pu);
+ args->pu = NULL;
+ ast_hangup(chan);
+ if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
+ if (peer == rchan) {
+ /* Only have one channel to worry about. */
+ ast_stream_and_wait(peer, "pbx-parkingfailed", "");
+ } else if (peer) {
+ /* Have two different channels to worry about. */
+ play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
+ }
+ }
+ return -1;
+ }
/* Setup the extensions and such */
set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
@@ -1611,34 +1693,77 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i
peer = chan;
}
- if (peer && (!play_announcement && args == &park_args)) {
- args->orig_chan_name = ast_strdupa(peer->name);
- }
-
/* parking space reserved, return code check unnecessary */
park_call_full(chan, peer, args);
return 0;
}
-int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
+int ast_masq_park_call_exten(struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout)
{
- return masq_park_call(rchan, peer, timeout, extout, 0, NULL);
+ int res;
+ char *parse;
+ const char *app_data;
+ struct ast_exten *exten;
+ struct park_app_args app_args;
+ struct ast_park_call_args args = {
+ .timeout = timeout,
+ .extout = extout,
+ };
+
+ if (parker) {
+ args.orig_chan_name = ast_strdupa(parker->name);
+ }
+ if (!park_exten || !park_context) {
+ return masq_park_call(park_me, parker, &args);
+ }
+
+ /*
+ * Determiine if the specified park extension has an exclusive
+ * parking lot to use.
+ */
+ if (parker && parker != park_me) {
+ ast_autoservice_start(park_me);
+ }
+ exten = get_parking_exten(park_exten, parker, park_context);
+ if (exten) {
+ app_data = ast_get_extension_app_data(exten);
+ if (!app_data) {
+ app_data = "";
+ }
+ parse = ast_strdupa(app_data);
+ AST_STANDARD_APP_ARGS(app_args, parse);
+
+ if (!ast_strlen_zero(app_args.pl_name)) {
+ /* Find the specified exclusive parking lot */
+ args.parkinglot = find_parkinglot(app_args.pl_name);
+ if (!args.parkinglot && parkeddynamic) {
+ args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
+ }
+ }
+ }
+ if (parker && parker != park_me) {
+ ast_autoservice_stop(park_me);
+ }
+
+ res = masq_park_call(park_me, parker, &args);
+ if (args.parkinglot) {
+ parkinglot_unref(args.parkinglot);
+ }
+ return res;
}
-/*!
- * \brief Park call via masqueraded channel and announce parking spot on peer channel.
- *
- * \param rchan the real channel to be parked
- * \param peer the channel to have the parking read to.
- * \param args Optional additional parking options when parking a call.
- *
- * \retval 0 on success.
- * \retval -1 on failure.
- */
-static int masq_park_call_announce(struct ast_channel *rchan, struct ast_channel *peer, struct ast_park_call_args *args)
+int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
{
- return masq_park_call(rchan, peer, 0, NULL, 1, args);
+ struct ast_park_call_args args = {
+ .timeout = timeout,
+ .extout = extout,
+ };
+
+ if (peer) {
+ args.orig_chan_name = ast_strdupa(peer->name);
+ }
+ return masq_park_call(rchan, peer, &args);
}
static int finishup(struct ast_channel *chan)
@@ -1702,7 +1827,7 @@ static int xfer_park_call_helper(struct ast_channel *park_me, struct ast_channel
parkinglot_unref(args.parkinglot);
return -1;
}
- res = masq_park_call_announce(park_me, parker, &args);
+ res = masq_park_call(park_me, parker, &args);
parkinglot_unref(args.parkinglot);
} else {
/* Parking failed because parking lot does not exist. */
@@ -1751,6 +1876,7 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
{
struct ast_channel *parker;
struct ast_channel *parkee;
+ struct ast_park_call_args args = { 0, };
/*
* We used to set chan's exten and priority to "s" and 1 here,
@@ -1779,8 +1905,7 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
/* one direction used to call park_call.... */
set_peers(&parker, &parkee, peer, chan, sense);
- return masq_park_call_announce(parkee, parker, NULL)
- ? AST_FEATURE_RETURN_SUCCESS : -1;
+ return masq_park_call(parkee, parker, &args) ? AST_FEATURE_RETURN_SUCCESS : -1;
}
/*!
@@ -4827,7 +4952,7 @@ static int park_call_exec(struct ast_channel *chan, const char *data)
}
}
if (args.parkinglot) {
- res = masq_park_call_announce(chan, chan, &args);
+ res = masq_park_call(chan, chan, &args);
parkinglot_unref(args.parkinglot);
} else {
/* Parking failed because the parking lot does not exist. */
@@ -6966,7 +7091,7 @@ static int manager_park(struct mansession *s, const struct message *m)
args.parkinglot = find_parkinglot(parkinglotname);
}
- res = masq_park_call(ch1, ch2, 0, NULL, 0, &args);
+ res = masq_park_call(ch1, ch2, &args);
if (!res) {
ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
astman_send_ack(s, m, "Park successful");
@@ -7889,7 +8014,7 @@ AST_TEST_DEFINE(features_test)
pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNCONTEXT", unique_context_2);
pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNEXTEN", parkinglot_parkext);
pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNPOS", parkinglot_range);
- if (masq_park_call(test_channel1, NULL, 0, NULL, 0, &args)) {
+ if (masq_park_call(test_channel1, NULL, &args)) {
res = -1;
goto exit_features_test;
}