diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 05b992402d..aa3c71fd84 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 67ef917a92..0ee84709ed 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -9358,78 +9358,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) @@ -10806,43 +10853,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 502b929b60..9df36eb7a9 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 b929486457..09e19ecaa8 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1286,7 +1286,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); @@ -21389,32 +21389,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 ? "" : "", transferee ? "" : "" ); - 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) { @@ -21442,31 +21426,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; } @@ -21475,67 +21468,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; @@ -23562,6 +23544,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; @@ -23570,9 +23553,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(¤t.req, req); - sip_pvt_lock(p); ast_clear_flag(&p->flags[0], SIP_GOTREFER); p->refer->status = REFER_200OK; @@ -23601,7 +23581,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 4789f964c2..0035637673 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 8e7fd4865e..ee2270f54b 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -804,7 +804,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 7e749f2e0f..76a141df15 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 b472e0c0ab..e041b6bdec 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; @@ -1169,6 +1169,32 @@ static struct ast_parkinglot *create_dynamic_parkinglot(const char *name, struct return parkinglot; } +/*! + * \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. @@ -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; } /*! @@ -4826,7 +4951,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. */ @@ -6965,7 +7090,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"); @@ -7888,7 +8013,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; }