More parking issues.
authorRichard Mudgett <rmudgett@digium.com>
Tue, 18 Oct 2011 21:15:45 +0000 (21:15 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Tue, 18 Oct 2011 21:15:45 +0000 (21:15 +0000)
* 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

channels/chan_dahdi.c
channels/chan_iax2.c
channels/chan_mgcp.c
channels/chan_sip.c
channels/sig_analog.c
channels/sip/include/sip.h
include/asterisk/features.h
main/features.c

index 05b9924..aa3c71f 100644 (file)
@@ -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")) {
index 3d671bc..a8369f5 100644 (file)
@@ -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;
index 502b929..9df36eb 100644 (file)
@@ -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")) {
index fcc0dfb..c7b39b8 100644 (file)
@@ -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 {
index 4789f96..0035637 100644 (file)
@@ -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")) {
index fc75ff4..c2924b7 100644 (file)
@@ -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 */
index 7e749f2..76a141d 100644 (file)
@@ -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
index 84e427c..73974ca 100644 (file)
@@ -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;
        }