diff --git a/CHANGES b/CHANGES index f942b8c3d4e9c0e97a68a486d14550971041e980..60f297817ef77263aafdc24e26adbe2380dc03fe 100644 --- a/CHANGES +++ b/CHANGES @@ -436,6 +436,7 @@ Asterisk Manager Interface conform more closely to similar events. * Added a new eventfilter option per user to allow whitelisting and blacklisting of events. + * Added optional parkinglot variable for park command. Channel Event Logging --------------------- @@ -1515,6 +1516,8 @@ Call Features (res_features) Changes * Added cli command 'features reload' to reload call features from features.conf * Moved into core asterisk binary. * Changed the default setting for featuredigittimeout to 2000 ms from 500 ms. + * Added the ability for custom parking lots to be configured with their own + parking extension with the parkext option. Language Support Changes ------------------------ diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 7e532fc677b16aae0d969d12e5aa917a30849ca0..9b1537fa836887611c3e4684222bcac287720537 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -9739,7 +9739,7 @@ static void *analog_ss_thread(void *data) tone_zone_play_tone(p->subs[idx].dfd, -1); else tone_zone_play_tone(p->subs[idx].dfd, DAHDI_TONE_DIALTONE); - if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num) && strcmp(exten, ast_parking_ext())) { + if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num) && !ast_parking_ext_valid(exten, chan, chan->context)) { if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) { if (getforward) { /* Record this as the forwarding extension */ @@ -9875,7 +9875,7 @@ static void *analog_ss_thread(void *data) getforward = 0; memset(exten, 0, sizeof(exten)); len = 0; - } else if ((p->transfer || p->canpark) && !strcmp(exten, ast_parking_ext()) && + } else if ((p->transfer || p->canpark) && ast_parking_ext_valid(exten, chan, chan->context) && p->subs[SUB_THREEWAY].owner && ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) { /* This is a three way call, the main call being a real channel, diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index f28bb7fccb1d7fa7e43e7dce0a33eb3840b9bbf1..9e6d4c6a5417aed0bc174c3af3606729e14cc06c 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -9112,7 +9112,7 @@ static void dp_lookup(int callno, const char *context, const char *callednum, co memset(&ied1, 0, sizeof(ied1)); mm = ast_matchmore_extension(NULL, context, callednum, 1, callerid); /* Must be started */ - if (!strcmp(callednum, ast_parking_ext()) || ast_exists_extension(NULL, context, callednum, 1, callerid)) { + if (ast_parking_ext_valid(callednum, NULL, context) || ast_exists_extension(NULL, context, callednum, 1, callerid)) { dpstatus = IAX_DPSTATUS_EXISTS; } else if (ast_canmatch_extension(NULL, context, callednum, 1, callerid)) { dpstatus = IAX_DPSTATUS_CANEXIST; @@ -9167,6 +9167,7 @@ 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; }; static void *iax_park_thread(void *stuff) @@ -9183,13 +9184,13 @@ static void *iax_park_thread(void *stuff) f = ast_read(chan1); if (f) ast_frfree(f); - res = ast_park_call(chan1, chan2, 0, &ext); + res = 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) +static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2, const char *parkexten) { struct iax_dual *d; struct ast_channel *chan1m, *chan2m; @@ -9231,6 +9232,7 @@ static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2) 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; } @@ -10604,10 +10606,10 @@ retryowner: } pbx_builtin_setvar_helper(bridged_chan, "BLINDTRANSFER", iaxs[fr->callno]->owner->name); - if (!strcmp(ies.called_number, ast_parking_ext())) { + if (ast_parking_ext_valid(ies.called_number, c, iaxs[fr->callno]->context)) { struct ast_channel *saved_channel = iaxs[fr->callno]->owner; ast_mutex_unlock(&iaxsl[fr->callno]); - if (iax_park(bridged_chan, saved_channel)) { + if (iax_park(bridged_chan, saved_channel, ies.called_number)) { ast_log(LOG_WARNING, "Failed to park call on '%s'\n", bridged_chan->name); } else { ast_debug(1, "Parked call on '%s'\n", ast_bridged_channel(iaxs[fr->callno]->owner)->name); diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index 2f953a9ae599c5d1012d539d877be358ed2d91e5..499b5e55e6b2f7b7522b6316278d319a71cc8a18 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -3107,7 +3107,7 @@ static void *mgcp_ss(void *data) getforward = 0; memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); len = 0; - } else if (!strcmp(p->dtmf_buf, ast_parking_ext()) && + } else if (ast_parking_ext_valid(p->dtmf_buf, chan, chan->context) && 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. */ diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 1cf6fe795e54410b8b32ed832a707956db1b55d7..4157cc83518e700c17f92226b3a7780b41b5670e 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1260,7 +1260,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); +static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, char *parkexten); static int sip_sipredirect(struct sip_pvt *p, const char *dest); static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method); @@ -4877,7 +4877,9 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) ast_string_field_set(dialog, cid_name, peer->cid_name); ast_string_field_set(dialog, cid_tag, peer->cid_tag); ast_string_field_set(dialog, mwi_from, peer->mwi_from); - ast_string_field_set(dialog, parkinglot, peer->parkinglot); + if (!ast_strlen_zero(peer->parkinglot)) { + ast_string_field_set(dialog, parkinglot, peer->parkinglot); + } ast_string_field_set(dialog, engine, peer->engine); ref_proxy(dialog, obproxy_get(dialog, peer)); dialog->callgroup = peer->callgroup; @@ -14891,7 +14893,9 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, ast_string_field_set(p, subscribecontext, peer->subscribecontext); ast_string_field_set(p, mohinterpret, peer->mohinterpret); ast_string_field_set(p, mohsuggest, peer->mohsuggest); - ast_string_field_set(p, parkinglot, peer->parkinglot); + if (!ast_strlen_zero(peer->parkinglot)) { + ast_string_field_set(p, parkinglot, peer->parkinglot); + } ast_string_field_set(p, engine, peer->engine); p->disallowed_methods = peer->disallowed_methods; set_pvt_allowed_methods(p, req); @@ -20054,7 +20058,7 @@ static void *sip_park_thread(void *stuff) return NULL; } - res = ast_park_call(transferee, transferer, 0, &ext); + res = ast_park_call(transferee, transferer, 0, d->parkexten, &ext); #ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE @@ -20091,7 +20095,7 @@ static void *sip_park_thread(void *stuff) /*! \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) +static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, char *parkexten) { struct sip_dual *d; struct ast_channel *transferee, *transferer; @@ -20172,6 +20176,7 @@ static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct 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); @@ -22064,9 +22069,8 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int /* Fallthrough if we can't find the call leg internally */ } - /* Parking a call */ - if (p->refer->localtransfer && !strcmp(p->refer->refer_to, ast_parking_ext())) { + if (p->refer->localtransfer && ast_parking_ext_valid(p->refer->refer_to, p->owner, p->owner->context)) { /* Must release c's lock now, because it will not longer be accessible after the transfer! */ *nounlock = 1; ast_channel_unlock(current.chan1); @@ -22083,7 +22087,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int p->refer->refer_to); if (sipdebug) ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name); - sip_park(current.chan2, current.chan1, req, seqno); + sip_park(current.chan2, current.chan1, req, seqno, p->refer->refer_to); return res; } @@ -26486,6 +26490,7 @@ static int reload_config(enum channelreloadreason reason) ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */ ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA); /*!< Allow re-invites */ ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine)); + ast_copy_string(default_parkinglot, DEFAULT_PARKINGLOT, sizeof(default_parkinglot)); /* Debugging settings, always default to off */ dumphistory = FALSE; @@ -26994,7 +26999,9 @@ static int reload_config(enum channelreloadreason reason) ast_log(LOG_WARNING, "subscribe_network_change_event value %s is not valid at line %d.\n", v->value, v->lineno); } } else if (!strcasecmp(v->name, "snom_aoc_enabled")) { - ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC); + ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC); + } else if (!strcasecmp(v->name, "parkinglot")) { + ast_copy_string(default_parkinglot, v->value, sizeof(default_parkinglot)); } } diff --git a/channels/sig_analog.c b/channels/sig_analog.c index 7bb3c293c5217c8fef3772f20a4c47500d8f5243..89e4b827f5b11e9f5c7c5c641349a1878d4ff239 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -1939,7 +1939,7 @@ static void *__analog_ss_thread(void *data) } else { analog_play_tone(p, index, ANALOG_TONE_DIALTONE); } - if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num) && strcmp(exten, ast_parking_ext())) { + if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num) && !ast_parking_ext_valid(exten, chan, chan->context)) { if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) { if (getforward) { /* Record this as the forwarding extension */ @@ -2090,7 +2090,7 @@ static void *__analog_ss_thread(void *data) getforward = 0; memset(exten, 0, sizeof(exten)); len = 0; - } else if ((p->transfer || p->canpark) && !strcmp(exten, ast_parking_ext()) && + } else if ((p->transfer || p->canpark) && ast_parking_ext_valid(exten, chan, chan->context) && p->subs[ANALOG_SUB_THREEWAY].owner && ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) { /* This is a three way call, the main call being a real channel, diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 2bd1bc75f6e9b6dd2b220588068c4a0d54f5aa44..385d9663bfdc5ae769a9b3eebd490346f8daffe1 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -768,6 +768,7 @@ 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; }; /*! \brief Parameters to the transmit_invite function */ diff --git a/configs/features.conf.sample b/configs/features.conf.sample index eb04c3c418e74def0783bae10ec0ec0a9d96e7bf..7534d1616eb4119c797ce7945216d9e6a70af645 100644 --- a/configs/features.conf.sample +++ b/configs/features.conf.sample @@ -3,7 +3,7 @@ ; [general] -parkext => 700 ; What extension to dial to park (all parking lots) +parkext => 700 ; What extension to dial to park parkpos => 701-720 ; What extensions to park calls on. (defafult parking lot) ; These needs to be numeric, as Asterisk starts from the start position ; and increments with one for the next parked call. @@ -85,6 +85,7 @@ context => parkedcalls ; Which context parked calls are in (default parking lot ; ;[parkinglot_edvina] ;context => edvinapark +;parkext => 799 ;parkpos => 800-850 ;findslot => next diff --git a/include/asterisk/features.h b/include/asterisk/features.h index c66852a786d82e86bb4499634a95f7a4238e6bab..7a55ff7cfc569451193bc5849f476f1973fd996f 100644 --- a/include/asterisk/features.h +++ b/include/asterisk/features.h @@ -35,6 +35,7 @@ #define FEATURE_MOH_LEN 80 /* same as MAX_MUSICCLASS from channel.h */ #define PARK_APP_NAME "Park" +#define DEFAULT_PARKINGLOT "default" /*!< Default parking lot */ #define AST_FEATURE_RETURN_HANGUP -1 #define AST_FEATURE_RETURN_SUCCESSBREAK 0 @@ -90,7 +91,7 @@ struct ast_call_feature { * \retval 0 on success. * \retval -1 on failure. */ -int ast_park_call(struct ast_channel *chan, struct ast_channel *host, int timeout, int *extout); +int ast_park_call(struct ast_channel *chan, struct ast_channel *host, int timeout, const char *parkexten, int *extout); /*! * \brief Park a call via a masqueraded channel @@ -106,10 +107,11 @@ int ast_park_call(struct ast_channel *chan, struct ast_channel *host, int timeou int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *host, int timeout, int *extout); /*! - * \brief Determine system parking extension - * \returns the call parking extension for drivers that provide special call parking help + * \brief Determine if parking extension exists in a given context + * \retval 0 if extension does not exist + * \retval 1 if extension does exist */ -const char *ast_parking_ext(void); +int ast_parking_ext_valid(const char *exten_str, struct ast_channel *chan, const char *context); /*! \brief Determine system call pickup extension */ const char *ast_pickup_ext(void); diff --git a/main/features.c b/main/features.c index 68d843bc2e89f84cd37bb5de27bb420591a338c6..bd8cb4c1461b53565fe8949f332a61adfa9af2f9 100644 --- a/main/features.c +++ b/main/features.c @@ -257,6 +257,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <parameter name="Timeout"> <para>Number of milliseconds to wait before callback.</para> </parameter> + <parameter name="Parkinglot"> + <para>Parking lot to park channel in.</para> + </parameter> </syntax> <description> <para>Park a channel.</para> @@ -289,10 +292,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") ***/ #define DEFAULT_PARK_TIME 45000 +#define DEFAULT_PARK_EXTENSION "700" #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000 -#define DEFAULT_PARKINGLOT "default" /*!< Default parking lot */ #define DEFAULT_ATXFER_DROP_CALL 0 #define DEFAULT_ATXFER_LOOP_DELAY 10000 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2 @@ -345,6 +348,7 @@ struct parkeduser { /*! \brief Structure for parking lots which are put in a container. */ struct ast_parkinglot { char name[AST_MAX_CONTEXT]; + char parkext[AST_MAX_EXTENSION]; /*!< Parkingextension */ char parking_con[AST_MAX_EXTENSION]; /*!< Context for which parking is made accessible */ char parking_con_dial[AST_MAX_EXTENSION]; /*!< Context for dialback for parking (KLUDGE) */ int parking_start; /*!< First available extension for parking */ @@ -358,6 +362,7 @@ struct ast_parkinglot { int parkedcallreparking; /*!< Enable DTMF based parking on bridge when picking up parked calls */ int parkedcallhangup; /*!< Enable DTMF based hangup on a bridge when pickup up parked calls */ int parkedcallrecording; /*!< Enable DTMF based recording on a bridge when picking up parked calls */ + unsigned short the_mark:1; /*!< Used during reloads, that which bears the_mark shall be deleted! */ AST_LIST_HEAD(parkinglot_parklist, parkeduser) parkings; /*!< List of active parkings in this parkinglot */ }; @@ -443,9 +448,47 @@ struct ast_parkinglot *find_parkinglot(const char *name); static struct ast_parkinglot *create_parkinglot(const char *name); static struct ast_parkinglot *copy_parkinglot(const char *name, const struct ast_parkinglot *parkinglot); -const char *ast_parking_ext(void) +static int find_parkinglot_by_position_cb(void *obj, void *args, int flags) +{ + struct ast_parkinglot *parkinglot = obj; + int *parkpos = args; + + if (*parkpos >= parkinglot->parking_start && *parkpos <= parkinglot->parking_stop) { + return CMP_MATCH | CMP_STOP; + } + + return 0; +} + +static int find_parkinglot_by_exten_cb(void *obj, void *args, int flags) { - return parking_ext; + struct ast_parkinglot *parkinglot = obj; + const char *parkext = args; + + if (!strcmp(parkinglot->parkext, parkext)) { + return CMP_MATCH | CMP_STOP; + } + + return 0; +} + +int ast_parking_ext_valid(const char *exten_str, struct ast_channel *chan, const char *context) +{ + struct ast_exten *exten; + struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ + const char *app_at_exten; + + exten = pbx_find_extension(chan, NULL, &q, context, exten_str, 1, NULL, NULL, E_MATCH); + if (!exten) { + return 0; + } + + app_at_exten = ast_get_extension_app(exten); + if (!app_at_exten || strcmp(PARK_APP_NAME, app_at_exten)) { + return 0; + } + + return 1; } const char *ast_pickup_ext(void) @@ -692,6 +735,7 @@ struct ast_park_call_args { uint32_t flags; /*! Parked user that has already obtained a parking space */ struct parkeduser *pu; + struct ast_parkinglot *parkinglot; /*! parkinglot to be parked in, based on parkext */ }; static struct parkeduser *park_space_reserve(struct ast_channel *chan, struct ast_channel *peer, struct ast_park_call_args *args) @@ -700,17 +744,25 @@ static struct parkeduser *park_space_reserve(struct ast_channel *chan, struct as int i, parking_space = -1, parking_range; const char *parkinglotname = NULL; const char *parkingexten; - struct ast_parkinglot *parkinglot = NULL; + struct ast_parkinglot *parkinglot; - if (peer) + if (args->parkinglot) { + parkinglot = args->parkinglot; + parkinglotname = parkinglot->name; + } else if (peer) { parkinglotname = findparkinglotname(peer); - else /* peer was NULL, check chan (ParkAndAnnounce / res_agi) */ + } else { /* peer was NULL, check chan (ParkAndAnnounce / res_agi) */ parkinglotname = findparkinglotname(chan); + } - if (parkinglotname) { - ast_debug(1, "Found chanvar Parkinglot: %s\n", parkinglotname); - parkinglot = find_parkinglot(parkinglotname); - + if (!args->parkinglot) { + if (parkinglotname) { + parkinglot = find_parkinglot(parkinglotname); + } else { + ast_debug(4, "This could be an indication channel driver needs updating, using default lot.\n"); + parkinglot = parkinglot_addref(default_parkinglot); + } + ast_debug(1, "Found chanvar Parkinglot: %s\n", parkinglot->name); } /* Dynamically create parkinglot */ @@ -761,8 +813,7 @@ static struct parkeduser *park_space_reserve(struct ast_channel *chan, struct as parkinglot = parkinglot_addref(default_parkinglot); } - if (option_debug) - ast_log(LOG_DEBUG, "Parkinglot: %s\n", parkinglot->name); + ast_debug(1, "Parkinglot: %s\n", parkinglot->name); /* Allocate memory for parking data */ if (!(pu = ast_calloc(1, sizeof(*pu)))) { @@ -844,9 +895,8 @@ static struct parkeduser *park_space_reserve(struct ast_channel *chan, struct as pu->notquiteyet = 1; pu->parkingnum = parking_space; - pu->parkinglot = parkinglot_addref(parkinglot); + pu->parkinglot = parkinglot; AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list); - parkinglot_unref(parkinglot); return pu; } @@ -1001,21 +1051,27 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, st } /*! \brief Park a call */ -int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout) +int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, const char *parkexten, int *extout) { + struct ast_parkinglot *found_lot = ao2_callback(parkinglots, 0, find_parkinglot_by_exten_cb, (void *) parkexten); + struct ast_park_call_args args = { .timeout = timeout, .extout = extout, + .parkinglot = found_lot, }; return park_call_full(chan, peer, &args); } +/*! + * \param rchan is the transferee + * \param peer is the transferer + */ 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) { struct ast_channel *chan; struct ast_frame *f; - int park_status; struct ast_park_call_args park_args = {0,}; if (!args) { @@ -1025,8 +1081,9 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i } if ((args->pu = park_space_reserve(rchan, peer, args)) == NULL) { - if (peer) - ast_stream_and_wait(peer, "beeperr", ""); + if (peer) { + ast_stream_and_wait(peer, "pbx-parkingfailed", ""); + } return AST_FEATURE_RETURN_PARKFAILED; } @@ -1061,12 +1118,8 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i args->orig_chan_name = ast_strdupa(peer->name); } - park_status = park_call_full(chan, peer, args); - if (park_status == 1) { - /* would be nice to play "invalid parking extension" */ - ast_hangup(chan); - return -1; - } + /* parking space reserved, return code check unnecessary */ + park_call_full(chan, peer, args); return 0; } @@ -1077,16 +1130,11 @@ int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int return masq_park_call(rchan, peer, timeout, extout, 0, NULL); } -static int masq_park_call_announce_args(struct ast_channel *rchan, struct ast_channel *peer, struct ast_park_call_args *args) +static int masq_park_call_announce(struct ast_channel *rchan, struct ast_channel *peer, struct ast_park_call_args *args) { return masq_park_call(rchan, peer, 0, NULL, 1, args); } -static int masq_park_call_announce(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout) -{ - return masq_park_call(rchan, peer, timeout, extout, 1, NULL); -} - #ifdef TEST_FRAMEWORK static int fake_fixup(struct ast_channel *clonechan, struct ast_channel *original) { @@ -1273,25 +1321,14 @@ static void set_peers(struct ast_channel **caller, struct ast_channel **callee, } } -/*! - * \brief support routing for one touch call parking - * \param chan channel parking call - * \param peer channel to be parked - * \param config unsed - * \param code unused - * \param sense feature options - * - * \param data - * Setup channel, set return exten,priority to 's,1' - * answer chan, sleep chan, park call -*/ -static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data) +static int parkcall_helper(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, struct ast_park_call_args *args) { - struct ast_channel *parker; - struct ast_channel *parkee; int res = 0; - set_peers(&parker, &parkee, peer, chan, sense); + if (args) { + ast_debug(1, "Parkinglot specified for builtin_parkcall: %s\n", args->parkinglot->name); + } + /* we used to set chan's exten and priority to "s" and 1 here, but this generates (in some cases) an invalid extension, and if "s" exists, could errantly @@ -1306,13 +1343,33 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer, res = ast_safe_sleep(chan, 1000); if (!res) { /* one direction used to call park_call.... */ - res = masq_park_call_announce(parkee, parker, 0, NULL); + struct ast_channel *parker; + struct ast_channel *parkee; + set_peers(&parker, &parkee, peer, chan, sense); + res = masq_park_call_announce(parkee, parker, args); /* PBX should hangup zombie channel if a masquerade actually occurred (res=0) */ } return res; } +/*! + * \brief support routing for one touch call parking + * \param chan channel parking call + * \param peer channel to be parked + * \param config unsed + * \param code unused + * \param sense feature options + * + * \param data + * Setup channel, set return exten,priority to 's,1' + * answer chan, sleep chan, park call +*/ +static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data) +{ + return parkcall_helper(chan, peer, config, code, sense, NULL); +} + /*! \brief Play message to both caller and callee in bridged call, plays synchronously, autoservicing the other channel during the message, so please don't use this for very long messages */ @@ -1620,6 +1677,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p struct ast_channel *transferer; struct ast_channel *transferee; const char *transferer_real_context; + struct ast_parkinglot *found_lot = NULL; char xferto[256]; int res, parkstatus = 0; @@ -1647,11 +1705,16 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p finishup(transferee); return res; } - if (!strcmp(xferto, ast_parking_ext())) { + + found_lot = ao2_callback(parkinglots, 0, find_parkinglot_by_exten_cb, &xferto); + if (found_lot) { + struct ast_park_call_args args = { + .parkinglot = found_lot, + }; res = finishup(transferee); if (res) res = -1; - else if (!(parkstatus = masq_park_call_announce(transferee, transferer, 0, NULL))) { /* success */ + else if (!(parkstatus = masq_park_call_announce(transferee, transferer, &args))) { /* success */ /* We return non-zero, but tell the PBX not to hang the channel when the thread dies -- We have to be careful now though. We are responsible for hanging up the channel, else it will never be hung up! */ @@ -1706,7 +1769,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p } else { ast_verb(3, "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context); } - if (parkstatus != AST_FEATURE_RETURN_PARKFAILED && ast_stream_and_wait(transferer, xferfailsound, AST_DIGIT_ANY) < 0) { + if (parkstatus != AST_FEATURE_RETURN_PARKFAILED && ast_stream_and_wait(transferer, xferfailsound, AST_DIGIT_ANY) < 0) { /* Play 'extension does not exist' */ finishup(transferee); return -1; } @@ -1769,6 +1832,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st struct ast_party_connected_line connected_line; struct ast_datastore *features_datastore; struct ast_dial_features *dialfeatures = NULL; + struct ast_parkinglot *parkinglot; ast_debug(1, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense); set_peers(&transferer, &transferee, peer, chan, sense); @@ -1811,12 +1875,16 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st return AST_FEATURE_RETURN_SUCCESS; } - /* If we are attended transfering to parking, just use builtin_parkcall instead of trying to track all of - * the different variables for handling this properly with a builtin_atxfer */ - if (!strcmp(xferto, ast_parking_ext())) { - finishup(transferee); - return builtin_parkcall(chan, peer, config, code, sense, data); - } + /* If we are attended transfering to parking, just use parkcall_helper instead of trying to track all of + * the different variables for handling this properly with a builtin_atxfer */ + parkinglot = ao2_callback(parkinglots, 0, find_parkinglot_by_exten_cb, &xferto); + if (parkinglot) { + struct ast_park_call_args args = { + .parkinglot = parkinglot, + }; + finishup(transferee); + return parkcall_helper(chan, peer, config, code, sense, &args); + } l = strlen(xferto); snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context); /* append context */ @@ -3762,6 +3830,7 @@ int manage_parkinglot(struct ast_parkinglot *curlot, const struct pollfd *pfds, } else ast_log(LOG_WARNING, "Whoa, no parking context for parking lot %s?\n", curlot->name); AST_LIST_REMOVE_CURRENT(list); + parkinglot_unref(pu->parkinglot); free(pu); break; } else { @@ -3847,18 +3916,16 @@ static void *do_parking_thread(void *ignore) /*! \brief Find parkinglot by name */ struct ast_parkinglot *find_parkinglot(const char *name) { - struct ast_parkinglot *parkinglot = NULL; - struct ast_parkinglot tmp_parkinglot; - - if (ast_strlen_zero(name)) - return NULL; - - ast_copy_string(tmp_parkinglot.name, name, sizeof(tmp_parkinglot.name)); + struct ast_parkinglot *parkinglot; - parkinglot = ao2_find(parkinglots, &tmp_parkinglot, OBJ_POINTER); + if (ast_strlen_zero(name)) { + return NULL; + } - if (parkinglot && option_debug) - ast_log(LOG_DEBUG, "Found Parkinglot: %s\n", parkinglot->name); + parkinglot = ao2_find(parkinglots, (void *) name, 0); + if (parkinglot) { + ast_debug(1, "Found Parkinglot: %s\n", parkinglot->name); + } return parkinglot; } @@ -3870,7 +3937,10 @@ struct ast_parkinglot *copy_parkinglot(const char *name, const struct ast_parkin if (ast_strlen_zero(name)) { /* No name specified */ return NULL; } - if (find_parkinglot(name)) { /* Parkinglot with that name allready exists */ + if ((copylot = find_parkinglot(name))) { /* Parkinglot with that name already exists */ + if (copylot) { + ao2_ref(copylot, -1); + } return NULL; } @@ -3962,7 +4032,8 @@ static int park_call_exec(struct ast_channel *chan, const char *data) ast_app_parse_options(park_call_options, &flags, NULL, app_args.options); args.flags = flags.flags; - res = masq_park_call_announce_args(chan, chan, &args); + args.parkinglot = ao2_callback(parkinglots, 0, find_parkinglot_by_exten_cb, &orig_exten); + res = masq_park_call_announce(chan, chan, &args); /* Continue on in the dialplan */ if (res == 1) { ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten)); @@ -3977,7 +4048,7 @@ static int park_call_exec(struct ast_channel *chan, const char *data) } /*! \brief Pickup parked call */ -static int park_exec_full(struct ast_channel *chan, const char *data, struct ast_parkinglot *parkinglot) +static int park_exec_full(struct ast_channel *chan, const char *data) { int res = 0; struct ast_channel *peer=NULL; @@ -3985,11 +4056,13 @@ static int park_exec_full(struct ast_channel *chan, const char *data, struct ast struct ast_context *con; int park = 0; struct ast_bridge_config config; + struct ast_parkinglot *parkinglot; - if (data) + if (data) { park = atoi((char *) data); + } - parkinglot = find_parkinglot(findparkinglotname(chan)); + parkinglot = ao2_callback(parkinglots, 0, find_parkinglot_by_position_cb, (void *) &park); if (!parkinglot) parkinglot = default_parkinglot; @@ -4126,6 +4199,7 @@ static int park_exec_full(struct ast_channel *chan, const char *data, struct ast ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); } + parkinglot_unref(parkinglot); res = ast_bridge_call(chan, peer, &config); pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); @@ -4147,7 +4221,7 @@ static int park_exec_full(struct ast_channel *chan, const char *data, struct ast static int park_exec(struct ast_channel *chan, const char *data) { - return park_exec_full(chan, data, default_parkinglot); + return park_exec_full(chan, data); } /*! \brief Unreference parkinglot object. If no more references, @@ -4155,15 +4229,13 @@ static int park_exec(struct ast_channel *chan, const char *data) static void parkinglot_unref(struct ast_parkinglot *parkinglot) { int refcount = ao2_ref(parkinglot, -1); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount - 1); + ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount - 1); } static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot) { int refcount = ao2_ref(parkinglot, +1); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1); + ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1); return parkinglot; } @@ -4193,10 +4265,28 @@ static void parkinglot_destroy(void *obj) con = ast_context_find(ruin->parking_con); if (con) ast_context_destroy(con, registrar); - ao2_unlink(parkinglots, ruin); } -/*! \brief Build parkinglot from configuration and chain it in */ +/*! + * \brief Add parking hints for all defined parking lots + * \param context + * \param start starting parkinglot number + * \param stop ending parkinglot number +*/ +static void park_add_hints(char *context, int start, int stop) +{ + int numext; + char device[AST_MAX_EXTENSION]; + char exten[10]; + + for (numext = start; numext <= stop; numext++) { + snprintf(exten, sizeof(exten), "%d", numext); + snprintf(device, sizeof(device), "park:%s@%s", exten, context); + ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar); + } +} + +/*! \brief Build parkinglot from configuration and chain it in if it doesn't already exist */ static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable *var) { struct ast_parkinglot *parkinglot; @@ -4224,6 +4314,8 @@ static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable * while(confvar) { if (!strcasecmp(confvar->name, "context")) { ast_copy_string(parkinglot->parking_con, confvar->value, sizeof(parkinglot->parking_con)); + } else if (!strcasecmp(confvar->name, "parkext")) { + ast_copy_string(parkinglot->parkext, confvar->value, sizeof(parkinglot->parkext)); } else if (!strcasecmp(confvar->name, "parkingtime")) { if ((sscanf(confvar->value, "%30d", &parkinglot->parkingtime) != 1) || (parkinglot->parkingtime < 1)) { ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", confvar->value); @@ -4283,6 +4375,10 @@ static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable * if (parkinglot->parkingtime == 0) { parkinglot->parkingtime = DEFAULT_PARK_TIME; } + if (ast_strlen_zero(parkinglot->parkext)) { + ast_debug(2, "no parkext specified for %s - setting it to %s\n", parkinglot->name, DEFAULT_PARK_EXTENSION); + ast_copy_string(parkinglot->parkext, DEFAULT_PARK_EXTENSION, sizeof(parkinglot->parkext)); + } if (!var) { /* Default parking lot */ ast_copy_string(parkinglot->parking_con, "parkedcalls", sizeof(parkinglot->parking_con)); @@ -4304,22 +4400,26 @@ static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable * /* Add a parking extension into the context */ if (!error && !oldparkinglot) { - if (!ast_strlen_zero(ast_parking_ext())) { - if (ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""), ast_free_ptr, registrar) == -1) + if (!ast_strlen_zero(parkinglot->parkext)) { + if (ast_add_extension2(con, 1, parkinglot->parkext, 1, NULL, NULL, parkcall, strdup(""), ast_free_ptr, registrar) == -1) error = 1; } } + /* Add parking hints */ + if (parkinglot->parkaddhints) + park_add_hints(parkinglot->parking_con, parkinglot->parking_start, parkinglot->parking_stop); + ao2_unlock(parkinglot); if (error) { ast_log(LOG_WARNING, "Parking %s not open for business. Configuration error.\n", name); parkinglot_destroy(parkinglot); + parkinglot_unref(parkinglot); return NULL; } - if (option_debug) - ast_log(LOG_DEBUG, "Parking %s now open for business. (start exten %d end %d)\n", name, start, end); - + ast_debug(1, "Parking %s now open for business. (start exten %d end %d)\n", name, start, end); + parkinglot->the_mark = 0; /* Move it into the list, if it wasn't already there */ if (!oldparkinglot) { @@ -4330,26 +4430,6 @@ static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable * return parkinglot; } - -/*! - * \brief Add parking hints for all defined parking lots - * \param context - * \param start starting parkinglot number - * \param stop ending parkinglot number -*/ -static void park_add_hints(char *context, int start, int stop) -{ - int numext; - char device[AST_MAX_EXTENSION]; - char exten[10]; - - for (numext = start; numext <= stop; numext++) { - snprintf(exten, sizeof(exten), "%d", numext); - snprintf(device, sizeof(device), "park:%s@%s", exten, context); - ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar); - } -} - static int load_config(void) { int start = 0, end = 0; @@ -4360,8 +4440,6 @@ static int load_config(void) struct ast_variable *var = NULL; struct feature_group *fg = NULL; struct ast_flags config_flags = { 0 }; - char old_parking_ext[AST_MAX_EXTENSION]; - char old_parking_con[AST_MAX_EXTENSION] = ""; char *ctg; static const char * const categories[] = { /* Categories in features.conf that are not @@ -4372,32 +4450,27 @@ static int load_config(void) "applicationmap" }; + default_parkinglot = build_parkinglot(DEFAULT_PARKINGLOT, NULL); if (default_parkinglot) { - strcpy(old_parking_con, default_parkinglot->parking_con); - strcpy(old_parking_ext, parking_ext); - } else { - default_parkinglot = build_parkinglot(DEFAULT_PARKINGLOT, NULL); - if (default_parkinglot) { - ao2_lock(default_parkinglot); - default_parkinglot->parking_start = 701; - default_parkinglot->parking_stop = 750; - default_parkinglot->parking_offset = 0; - default_parkinglot->parkfindnext = 0; - default_parkinglot->parkingtime = DEFAULT_PARK_TIME; - ao2_unlock(default_parkinglot); - } + ao2_lock(default_parkinglot); + ast_copy_string(default_parkinglot->parkext, DEFAULT_PARK_EXTENSION, sizeof(default_parkinglot->parkext)); + default_parkinglot->parking_start = 701; + default_parkinglot->parking_stop = 750; + default_parkinglot->parking_offset = 0; + default_parkinglot->parkfindnext = 0; + default_parkinglot->parkingtime = DEFAULT_PARK_TIME; + ao2_unlock(default_parkinglot); } + if (default_parkinglot) { - if (option_debug) - ast_log(LOG_DEBUG, "Configuration of default parkinglot done.\n"); + ast_debug(1, "Configuration of default parkinglot done.\n"); } else { ast_log(LOG_ERROR, "Configuration of default parkinglot failed.\n"); return -1; } - /* Reset to defaults */ - strcpy(parking_ext, "700"); + strcpy(default_parkinglot->parkext, DEFAULT_PARK_EXTENSION); strcpy(pickup_ext, "*8"); courtesytone[0] = '\0'; strcpy(xfersound, "beep"); @@ -4428,7 +4501,7 @@ static int load_config(void) } for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { if (!strcasecmp(var->name, "parkext")) { - ast_copy_string(parking_ext, var->value, sizeof(parking_ext)); + ast_copy_string(default_parkinglot->parkext, var->value, sizeof(default_parkinglot->parkext)); } else if (!strcasecmp(var->name, "context")) { ast_copy_string(default_parkinglot->parking_con, var->value, sizeof(default_parkinglot->parking_con)); } else if (!strcasecmp(var->name, "parkingtime")) { @@ -4682,22 +4755,15 @@ static int load_config(void) ast_config_destroy(cfg); - /* Remove the old parking extension */ - if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) { - if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar, 0)) - notify_metermaids(old_parking_ext, old_parking_con, AST_DEVICE_NOT_INUSE); - ast_debug(1, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con); - } - if (!(con = ast_context_find_or_create(NULL, NULL, default_parkinglot->parking_con, registrar))) { ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", default_parkinglot->parking_con); return -1; } - res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar); + res = ast_add_extension2(con, 1, default_parkinglot->parkext, 1, NULL, NULL, parkcall, NULL, NULL, registrar); if (default_parkinglot->parkaddhints) park_add_hints(default_parkinglot->parking_con, default_parkinglot->parking_start, default_parkinglot->parking_stop); if (!res) - notify_metermaids(ast_parking_ext(), default_parkinglot->parking_con, AST_DEVICE_INUSE); + notify_metermaids(default_parkinglot->parkext, default_parkinglot->parking_con, AST_DEVICE_INUSE); return res; } @@ -4776,9 +4842,11 @@ static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cl while ((curlot = ao2_iterator_next(&iter))) { ast_cli(a->fd, "\nCall parking (Parking lot: %s)\n", curlot->name); ast_cli(a->fd, "------------\n"); - ast_cli(a->fd,"%-22s: %s\n", "Parking extension", parking_ext); + ast_cli(a->fd,"%-22s: %s\n", "Parking extension", curlot->parkext); ast_cli(a->fd,"%-22s: %s\n", "Parking context", curlot->parking_con); ast_cli(a->fd,"%-22s: %d-%d\n", "Parked call extensions", curlot->parking_start, curlot->parking_stop); + ast_cli(a->fd,"%-22s: %d\n", "Parkingtime", curlot->parkingtime); + ast_cli(a->fd,"%-22s: %s\n", "MusicOnHold class", curlot->mohclass); ast_cli(a->fd,"\n"); ao2_ref(curlot, -1); } @@ -4787,17 +4855,27 @@ static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cl return CLI_SUCCESS; } +static int parkinglot_markall_cb(void *obj, void *arg, int flags) +{ + struct ast_parkinglot *parkinglot = obj; + parkinglot->the_mark = 1; + return 0; +} + +static int parkinglot_is_marked_cb(void *obj, void *arg, int flags) +{ + struct ast_parkinglot *parkinglot = obj; + return parkinglot->the_mark ? CMP_MATCH : 0; +} + int ast_features_reload(void) { int res; - /* Release parking lot list */ - //ASTOBJ_CONTAINER_MARKALL(&parkinglots); - // TODO: I don't think any marking is necessary - /* Reload configuration */ - res = load_config(); + ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_markall_cb, NULL, "callback to mark all parkinglots"); + res = load_config(); /* Reload configuration */ + ao2_t_callback(parkinglots, OBJ_NODATA | OBJ_UNLINK, parkinglot_is_marked_cb, NULL, "callback to remove all marked parkinglots"); - //ASTOBJ_CONTAINER_PRUNE_MARKED(&parkinglots, parkinglot_destroy); return res; } @@ -5010,7 +5088,8 @@ static char *handle_parkedcalls(struct ast_cli_entry *e, int cmd, struct ast_cli iter = ao2_iterator_init(parkinglots, 0); while ((curlot = ao2_iterator_next(&iter))) { int lotparked = 0; - ast_cli(a->fd, "*** Parking lot: %s\n", curlot->name); + /* subtract ref for iterator and for configured parking lot */ + ast_cli(a->fd, "*** Parking lot: %s (%d)\n", curlot->name, ao2_ref(curlot, 0) - 2); AST_LIST_LOCK(&curlot->parkings); AST_LIST_TRAVERSE(&curlot->parkings, cur, list) { @@ -5106,11 +5185,11 @@ static int manager_park(struct mansession *s, const struct message *m) const char *channel = astman_get_header(m, "Channel"); const char *channel2 = astman_get_header(m, "Channel2"); const char *timeout = astman_get_header(m, "Timeout"); + const char *parkinglotname = astman_get_header(m, "Parkinglot"); char buf[BUFSIZ]; - int to = 0; int res = 0; - int parkExt = 0; struct ast_channel *ch1, *ch2; + struct ast_park_call_args args = {0,}; if (ast_strlen_zero(channel)) { astman_send_error(s, m, "Channel not specified"); @@ -5135,16 +5214,19 @@ static int manager_park(struct mansession *s, const struct message *m) return 0; } + if (!ast_strlen_zero(timeout)) { + sscanf(timeout, "%30d", &args.timeout); + } + if (!ast_strlen_zero(parkinglotname)) { + args.parkinglot = find_parkinglot(parkinglotname); + } + ast_channel_lock(ch1); while (ast_channel_trylock(ch2)) { CHANNEL_DEADLOCK_AVOIDANCE(ch1); } - if (!ast_strlen_zero(timeout)) { - sscanf(timeout, "%30d", &to); - } - - res = ast_masq_park_call(ch1, ch2, to, &parkExt); + res = masq_park_call(ch1, ch2, 0, NULL, 0, &args); if (!res) { ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT); astman_send_ack(s, m, "Park successful");