Merged revisions 286931 via svnmerge from

https://origsvn.digium.com/svn/asterisk/branches/1.8

........
  r286931 | jpeeler | 2010-09-15 14:22:15 -0500 (Wed, 15 Sep 2010) | 16 lines
  
  Add parking extension for non-default parking lots.
  
  This is a new feature that allows for parking to custom parking lots to be
  accessed directly, rather than with channel variables or by changing the
  default parking lot. The extension is set with the parkext option just as the
  default parking lot is done. Also, the manager action has been updated to
  optionally allow a specified parking lot.
  
  (closes issue #14882)
  Reported by: vmikhnevych
  Patches: 
        patch_14882.txt uploaded by mnick (license 874)
        modified by me
  
  Review: https://reviewboard.asterisk.org/r/884/
........


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@286939 65c4cc65-6c06-0410-ace0-fbb531ad65f3
10-digiumphones
Jeff Peeler 15 years ago
parent b3fa5ec3be
commit 41b95ee887

@ -436,6 +436,7 @@ Asterisk Manager Interface
conform more closely to similar events. conform more closely to similar events.
* Added a new eventfilter option per user to allow whitelisting and blacklisting * Added a new eventfilter option per user to allow whitelisting and blacklisting
of events. of events.
* Added optional parkinglot variable for park command.
Channel Event Logging Channel Event Logging
--------------------- ---------------------
@ -1515,6 +1516,8 @@ Call Features (res_features) Changes
* Added cli command 'features reload' to reload call features from features.conf * Added cli command 'features reload' to reload call features from features.conf
* Moved into core asterisk binary. * Moved into core asterisk binary.
* Changed the default setting for featuredigittimeout to 2000 ms from 500 ms. * 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 Language Support Changes
------------------------ ------------------------

@ -9739,7 +9739,7 @@ static void *analog_ss_thread(void *data)
tone_zone_play_tone(p->subs[idx].dfd, -1); tone_zone_play_tone(p->subs[idx].dfd, -1);
else else
tone_zone_play_tone(p->subs[idx].dfd, DAHDI_TONE_DIALTONE); 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 (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) {
if (getforward) { if (getforward) {
/* Record this as the forwarding extension */ /* Record this as the forwarding extension */
@ -9875,7 +9875,7 @@ static void *analog_ss_thread(void *data)
getforward = 0; getforward = 0;
memset(exten, 0, sizeof(exten)); memset(exten, 0, sizeof(exten));
len = 0; 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 && p->subs[SUB_THREEWAY].owner &&
ast_bridged_channel(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, /* This is a three way call, the main call being a real channel,

@ -9112,7 +9112,7 @@ static void dp_lookup(int callno, const char *context, const char *callednum, co
memset(&ied1, 0, sizeof(ied1)); memset(&ied1, 0, sizeof(ied1));
mm = ast_matchmore_extension(NULL, context, callednum, 1, callerid); mm = ast_matchmore_extension(NULL, context, callednum, 1, callerid);
/* Must be started */ /* 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; dpstatus = IAX_DPSTATUS_EXISTS;
} else if (ast_canmatch_extension(NULL, context, callednum, 1, callerid)) { } else if (ast_canmatch_extension(NULL, context, callednum, 1, callerid)) {
dpstatus = IAX_DPSTATUS_CANEXIST; 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 iax_dual {
struct ast_channel *chan1; struct ast_channel *chan1;
struct ast_channel *chan2; struct ast_channel *chan2;
const char *parkexten;
}; };
static void *iax_park_thread(void *stuff) static void *iax_park_thread(void *stuff)
@ -9183,13 +9184,13 @@ static void *iax_park_thread(void *stuff)
f = ast_read(chan1); f = ast_read(chan1);
if (f) if (f)
ast_frfree(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_hangup(chan2);
ast_log(LOG_NOTICE, "Parked on extension '%d'\n", ext); ast_log(LOG_NOTICE, "Parked on extension '%d'\n", ext);
return NULL; 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 iax_dual *d;
struct ast_channel *chan1m, *chan2m; 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)))) { if ((d = ast_calloc(1, sizeof(*d)))) {
d->chan1 = chan1m; d->chan1 = chan1m;
d->chan2 = chan2m; d->chan2 = chan2m;
d->parkexten = parkexten;
if (!ast_pthread_create_detached_background(&th, NULL, iax_park_thread, d)) { if (!ast_pthread_create_detached_background(&th, NULL, iax_park_thread, d)) {
return 0; return 0;
} }
@ -10604,10 +10606,10 @@ retryowner:
} }
pbx_builtin_setvar_helper(bridged_chan, "BLINDTRANSFER", iaxs[fr->callno]->owner->name); 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; struct ast_channel *saved_channel = iaxs[fr->callno]->owner;
ast_mutex_unlock(&iaxsl[fr->callno]); 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); ast_log(LOG_WARNING, "Failed to park call on '%s'\n", bridged_chan->name);
} else { } else {
ast_debug(1, "Parked call on '%s'\n", ast_bridged_channel(iaxs[fr->callno]->owner)->name); ast_debug(1, "Parked call on '%s'\n", ast_bridged_channel(iaxs[fr->callno]->owner)->name);

@ -3107,7 +3107,7 @@ static void *mgcp_ss(void *data)
getforward = 0; getforward = 0;
memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
len = 0; 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)) { sub->next->owner && ast_bridged_channel(sub->next->owner)) {
/* This is a three way call, the main call being a real channel, /* This is a three way call, the main call being a real channel,
and we're parking the first call. */ and we're parking the first call. */

@ -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 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 check_pendings(struct sip_pvt *p);
static void *sip_park_thread(void *stuff); 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 sip_sipredirect(struct sip_pvt *p, const char *dest);
static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method); 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_name, peer->cid_name);
ast_string_field_set(dialog, cid_tag, peer->cid_tag); 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, 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); ast_string_field_set(dialog, engine, peer->engine);
ref_proxy(dialog, obproxy_get(dialog, peer)); ref_proxy(dialog, obproxy_get(dialog, peer));
dialog->callgroup = peer->callgroup; 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, subscribecontext, peer->subscribecontext);
ast_string_field_set(p, mohinterpret, peer->mohinterpret); ast_string_field_set(p, mohinterpret, peer->mohinterpret);
ast_string_field_set(p, mohsuggest, peer->mohsuggest); 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); ast_string_field_set(p, engine, peer->engine);
p->disallowed_methods = peer->disallowed_methods; p->disallowed_methods = peer->disallowed_methods;
set_pvt_allowed_methods(p, req); set_pvt_allowed_methods(p, req);
@ -20054,7 +20058,7 @@ static void *sip_park_thread(void *stuff)
return NULL; 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 #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 /*! \brief Park a call using the subsystem in res_features.c
This is executed in a separate thread 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 sip_dual *d;
struct ast_channel *transferee, *transferer; 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->chan1 = transferee; /* Transferee */
d->chan2 = transferer; /* Transferer */ d->chan2 = transferer; /* Transferer */
d->seqno = seqno; d->seqno = seqno;
d->parkexten = parkexten;
if (ast_pthread_create_detached_background(&th, NULL, sip_park_thread, d) < 0) { if (ast_pthread_create_detached_background(&th, NULL, sip_park_thread, d) < 0) {
/* Could not start thread */ /* Could not start thread */
deinit_req(&d->req); 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 */ /* Fallthrough if we can't find the call leg internally */
} }
/* Parking a call */ /* 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! */ /* Must release c's lock now, because it will not longer be accessible after the transfer! */
*nounlock = 1; *nounlock = 1;
ast_channel_unlock(current.chan1); 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); p->refer->refer_to);
if (sipdebug) if (sipdebug)
ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name); 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; 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_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */
ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA); /*!< Allow re-invites */ 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_engine, DEFAULT_ENGINE, sizeof(default_engine));
ast_copy_string(default_parkinglot, DEFAULT_PARKINGLOT, sizeof(default_parkinglot));
/* Debugging settings, always default to off */ /* Debugging settings, always default to off */
dumphistory = FALSE; 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); 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")) { } 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));
} }
} }

@ -1939,7 +1939,7 @@ static void *__analog_ss_thread(void *data)
} else { } else {
analog_play_tone(p, index, ANALOG_TONE_DIALTONE); 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 (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) {
if (getforward) { if (getforward) {
/* Record this as the forwarding extension */ /* Record this as the forwarding extension */
@ -2090,7 +2090,7 @@ static void *__analog_ss_thread(void *data)
getforward = 0; getforward = 0;
memset(exten, 0, sizeof(exten)); memset(exten, 0, sizeof(exten));
len = 0; 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 && p->subs[ANALOG_SUB_THREEWAY].owner &&
ast_bridged_channel(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, /* This is a three way call, the main call being a real channel,

@ -768,6 +768,7 @@ struct sip_dual {
struct ast_channel *chan2; /*!< Second channel involved */ struct ast_channel *chan2; /*!< Second channel involved */
struct sip_request req; /*!< Request that caused the transfer (REFER) */ struct sip_request req; /*!< Request that caused the transfer (REFER) */
int seqno; /*!< Sequence number */ int seqno; /*!< Sequence number */
const char *parkexten;
}; };
/*! \brief Parameters to the transmit_invite function */ /*! \brief Parameters to the transmit_invite function */

@ -3,7 +3,7 @@
; ;
[general] [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) parkpos => 701-720 ; What extensions to park calls on. (defafult parking lot)
; These needs to be numeric, as Asterisk starts from the start position ; These needs to be numeric, as Asterisk starts from the start position
; and increments with one for the next parked call. ; 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] ;[parkinglot_edvina]
;context => edvinapark ;context => edvinapark
;parkext => 799
;parkpos => 800-850 ;parkpos => 800-850
;findslot => next ;findslot => next

@ -35,6 +35,7 @@
#define FEATURE_MOH_LEN 80 /* same as MAX_MUSICCLASS from channel.h */ #define FEATURE_MOH_LEN 80 /* same as MAX_MUSICCLASS from channel.h */
#define PARK_APP_NAME "Park" #define PARK_APP_NAME "Park"
#define DEFAULT_PARKINGLOT "default" /*!< Default parking lot */
#define AST_FEATURE_RETURN_HANGUP -1 #define AST_FEATURE_RETURN_HANGUP -1
#define AST_FEATURE_RETURN_SUCCESSBREAK 0 #define AST_FEATURE_RETURN_SUCCESSBREAK 0
@ -90,7 +91,7 @@ struct ast_call_feature {
* \retval 0 on success. * \retval 0 on success.
* \retval -1 on failure. * \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 * \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); int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *host, int timeout, int *extout);
/*! /*!
* \brief Determine system parking extension * \brief Determine if parking extension exists in a given context
* \returns the call parking extension for drivers that provide special call parking help * \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 */ /*! \brief Determine system call pickup extension */
const char *ast_pickup_ext(void); const char *ast_pickup_ext(void);

@ -257,6 +257,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Timeout"> <parameter name="Timeout">
<para>Number of milliseconds to wait before callback.</para> <para>Number of milliseconds to wait before callback.</para>
</parameter> </parameter>
<parameter name="Parkinglot">
<para>Parking lot to park channel in.</para>
</parameter>
</syntax> </syntax>
<description> <description>
<para>Park a channel.</para> <para>Park a channel.</para>
@ -289,10 +292,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
***/ ***/
#define DEFAULT_PARK_TIME 45000 #define DEFAULT_PARK_TIME 45000
#define DEFAULT_PARK_EXTENSION "700"
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
#define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000
#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
#define DEFAULT_PARKINGLOT "default" /*!< Default parking lot */
#define DEFAULT_ATXFER_DROP_CALL 0 #define DEFAULT_ATXFER_DROP_CALL 0
#define DEFAULT_ATXFER_LOOP_DELAY 10000 #define DEFAULT_ATXFER_LOOP_DELAY 10000
#define DEFAULT_ATXFER_CALLBACK_RETRIES 2 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2
@ -345,6 +348,7 @@ struct parkeduser {
/*! \brief Structure for parking lots which are put in a container. */ /*! \brief Structure for parking lots which are put in a container. */
struct ast_parkinglot { struct ast_parkinglot {
char name[AST_MAX_CONTEXT]; 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[AST_MAX_EXTENSION]; /*!< Context for which parking is made accessible */
char parking_con_dial[AST_MAX_EXTENSION]; /*!< Context for dialback for parking (KLUDGE) */ char parking_con_dial[AST_MAX_EXTENSION]; /*!< Context for dialback for parking (KLUDGE) */
int parking_start; /*!< First available extension for parking */ 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 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 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 */ 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 */ 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 *create_parkinglot(const char *name);
static struct ast_parkinglot *copy_parkinglot(const char *name, const struct ast_parkinglot *parkinglot); 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) const char *ast_pickup_ext(void)
@ -692,6 +735,7 @@ struct ast_park_call_args {
uint32_t flags; uint32_t flags;
/*! Parked user that has already obtained a parking space */ /*! Parked user that has already obtained a parking space */
struct parkeduser *pu; 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) 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; int i, parking_space = -1, parking_range;
const char *parkinglotname = NULL; const char *parkinglotname = NULL;
const char *parkingexten; 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); parkinglotname = findparkinglotname(peer);
else /* peer was NULL, check chan (ParkAndAnnounce / res_agi) */ } else { /* peer was NULL, check chan (ParkAndAnnounce / res_agi) */
parkinglotname = findparkinglotname(chan); parkinglotname = findparkinglotname(chan);
}
if (parkinglotname) { if (!args->parkinglot) {
ast_debug(1, "Found chanvar Parkinglot: %s\n", parkinglotname); if (parkinglotname) {
parkinglot = find_parkinglot(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 */ /* Dynamically create parkinglot */
@ -761,8 +813,7 @@ static struct parkeduser *park_space_reserve(struct ast_channel *chan, struct as
parkinglot = parkinglot_addref(default_parkinglot); parkinglot = parkinglot_addref(default_parkinglot);
} }
if (option_debug) ast_debug(1, "Parkinglot: %s\n", parkinglot->name);
ast_log(LOG_DEBUG, "Parkinglot: %s\n", parkinglot->name);
/* Allocate memory for parking data */ /* Allocate memory for parking data */
if (!(pu = ast_calloc(1, sizeof(*pu)))) { 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->notquiteyet = 1;
pu->parkingnum = parking_space; pu->parkingnum = parking_space;
pu->parkinglot = parkinglot_addref(parkinglot); pu->parkinglot = parkinglot;
AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list); AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list);
parkinglot_unref(parkinglot);
return pu; return pu;
} }
@ -1001,21 +1051,27 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, st
} }
/*! \brief Park a call */ /*! \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 = { struct ast_park_call_args args = {
.timeout = timeout, .timeout = timeout,
.extout = extout, .extout = extout,
.parkinglot = found_lot,
}; };
return park_call_full(chan, peer, &args); 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) 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_channel *chan;
struct ast_frame *f; struct ast_frame *f;
int park_status;
struct ast_park_call_args park_args = {0,}; struct ast_park_call_args park_args = {0,};
if (!args) { 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 ((args->pu = park_space_reserve(rchan, peer, args)) == NULL) {
if (peer) if (peer) {
ast_stream_and_wait(peer, "beeperr", ""); ast_stream_and_wait(peer, "pbx-parkingfailed", "");
}
return AST_FEATURE_RETURN_PARKFAILED; 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); args->orig_chan_name = ast_strdupa(peer->name);
} }
park_status = park_call_full(chan, peer, args); /* parking space reserved, return code check unnecessary */
if (park_status == 1) { park_call_full(chan, peer, args);
/* would be nice to play "invalid parking extension" */
ast_hangup(chan);
return -1;
}
return 0; 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); 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); 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 #ifdef TEST_FRAMEWORK
static int fake_fixup(struct ast_channel *clonechan, struct ast_channel *original) 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,
} }
} }
/*! 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)
* \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)
{ {
struct ast_channel *parker;
struct ast_channel *parkee;
int res = 0; 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 /* we used to set chan's exten and priority to "s" and 1
here, but this generates (in some cases) an invalid here, but this generates (in some cases) an invalid
extension, and if "s" exists, could errantly 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); res = ast_safe_sleep(chan, 1000);
if (!res) { /* one direction used to call park_call.... */ 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) */ /* PBX should hangup zombie channel if a masquerade actually occurred (res=0) */
} }
return res; 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 /*! \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 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 *transferer;
struct ast_channel *transferee; struct ast_channel *transferee;
const char *transferer_real_context; const char *transferer_real_context;
struct ast_parkinglot *found_lot = NULL;
char xferto[256]; char xferto[256];
int res, parkstatus = 0; int res, parkstatus = 0;
@ -1647,11 +1705,16 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p
finishup(transferee); finishup(transferee);
return res; 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); res = finishup(transferee);
if (res) if (res)
res = -1; 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 /* 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 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! */ 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 { } else {
ast_verb(3, "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context); 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); finishup(transferee);
return -1; 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_party_connected_line connected_line;
struct ast_datastore *features_datastore; struct ast_datastore *features_datastore;
struct ast_dial_features *dialfeatures = NULL; 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); ast_debug(1, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
set_peers(&transferer, &transferee, peer, chan, 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; return AST_FEATURE_RETURN_SUCCESS;
} }
/* If we are attended transfering to parking, just use builtin_parkcall instead of trying to track all of /* 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 */ * the different variables for handling this properly with a builtin_atxfer */
if (!strcmp(xferto, ast_parking_ext())) { parkinglot = ao2_callback(parkinglots, 0, find_parkinglot_by_exten_cb, &xferto);
finishup(transferee); if (parkinglot) {
return builtin_parkcall(chan, peer, config, code, sense, data); struct ast_park_call_args args = {
} .parkinglot = parkinglot,
};
finishup(transferee);
return parkcall_helper(chan, peer, config, code, sense, &args);
}
l = strlen(xferto); l = strlen(xferto);
snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context); /* append context */ 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 } else
ast_log(LOG_WARNING, "Whoa, no parking context for parking lot %s?\n", curlot->name); ast_log(LOG_WARNING, "Whoa, no parking context for parking lot %s?\n", curlot->name);
AST_LIST_REMOVE_CURRENT(list); AST_LIST_REMOVE_CURRENT(list);
parkinglot_unref(pu->parkinglot);
free(pu); free(pu);
break; break;
} else { } else {
@ -3847,18 +3916,16 @@ static void *do_parking_thread(void *ignore)
/*! \brief Find parkinglot by name */ /*! \brief Find parkinglot by name */
struct ast_parkinglot *find_parkinglot(const char *name) struct ast_parkinglot *find_parkinglot(const char *name)
{ {
struct ast_parkinglot *parkinglot = NULL; struct ast_parkinglot *parkinglot;
struct ast_parkinglot tmp_parkinglot;
if (ast_strlen_zero(name))
return NULL;
ast_copy_string(tmp_parkinglot.name, name, sizeof(tmp_parkinglot.name));
parkinglot = ao2_find(parkinglots, &tmp_parkinglot, OBJ_POINTER); if (ast_strlen_zero(name)) {
return NULL;
}
if (parkinglot && option_debug) parkinglot = ao2_find(parkinglots, (void *) name, 0);
ast_log(LOG_DEBUG, "Found Parkinglot: %s\n", parkinglot->name); if (parkinglot) {
ast_debug(1, "Found Parkinglot: %s\n", parkinglot->name);
}
return parkinglot; 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 */ if (ast_strlen_zero(name)) { /* No name specified */
return NULL; 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; 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); ast_app_parse_options(park_call_options, &flags, NULL, app_args.options);
args.flags = flags.flags; 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 */ /* Continue on in the dialplan */
if (res == 1) { if (res == 1) {
ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten)); 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 */ /*! \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; int res = 0;
struct ast_channel *peer=NULL; 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; struct ast_context *con;
int park = 0; int park = 0;
struct ast_bridge_config config; struct ast_bridge_config config;
struct ast_parkinglot *parkinglot;
if (data) if (data) {
park = atoi((char *) data); park = atoi((char *) data);
}
parkinglot = find_parkinglot(findparkinglotname(chan)); parkinglot = ao2_callback(parkinglots, 0, find_parkinglot_by_position_cb, (void *) &park);
if (!parkinglot) if (!parkinglot)
parkinglot = default_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); ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
} }
parkinglot_unref(parkinglot);
res = ast_bridge_call(chan, peer, &config); res = ast_bridge_call(chan, peer, &config);
pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); 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) 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, /*! \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) static void parkinglot_unref(struct ast_parkinglot *parkinglot)
{ {
int refcount = ao2_ref(parkinglot, -1); int refcount = ao2_ref(parkinglot, -1);
if (option_debug > 2) ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount - 1);
ast_log(LOG_DEBUG, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount - 1);
} }
static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot) static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot)
{ {
int refcount = ao2_ref(parkinglot, +1); int refcount = ao2_ref(parkinglot, +1);
if (option_debug > 2) ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1);
ast_log(LOG_DEBUG, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1);
return parkinglot; return parkinglot;
} }
@ -4193,10 +4265,28 @@ static void parkinglot_destroy(void *obj)
con = ast_context_find(ruin->parking_con); con = ast_context_find(ruin->parking_con);
if (con) if (con)
ast_context_destroy(con, registrar); 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) static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable *var)
{ {
struct ast_parkinglot *parkinglot; struct ast_parkinglot *parkinglot;
@ -4224,6 +4314,8 @@ static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable *
while(confvar) { while(confvar) {
if (!strcasecmp(confvar->name, "context")) { if (!strcasecmp(confvar->name, "context")) {
ast_copy_string(parkinglot->parking_con, confvar->value, sizeof(parkinglot->parking_con)); 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")) { } else if (!strcasecmp(confvar->name, "parkingtime")) {
if ((sscanf(confvar->value, "%30d", &parkinglot->parkingtime) != 1) || (parkinglot->parkingtime < 1)) { if ((sscanf(confvar->value, "%30d", &parkinglot->parkingtime) != 1) || (parkinglot->parkingtime < 1)) {
ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", confvar->value); 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) { if (parkinglot->parkingtime == 0) {
parkinglot->parkingtime = DEFAULT_PARK_TIME; 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 */ if (!var) { /* Default parking lot */
ast_copy_string(parkinglot->parking_con, "parkedcalls", sizeof(parkinglot->parking_con)); 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 */ /* Add a parking extension into the context */
if (!error && !oldparkinglot) { if (!error && !oldparkinglot) {
if (!ast_strlen_zero(ast_parking_ext())) { if (!ast_strlen_zero(parkinglot->parkext)) {
if (ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""), ast_free_ptr, registrar) == -1) if (ast_add_extension2(con, 1, parkinglot->parkext, 1, NULL, NULL, parkcall, strdup(""), ast_free_ptr, registrar) == -1)
error = 1; error = 1;
} }
} }
/* Add parking hints */
if (parkinglot->parkaddhints)
park_add_hints(parkinglot->parking_con, parkinglot->parking_start, parkinglot->parking_stop);
ao2_unlock(parkinglot); ao2_unlock(parkinglot);
if (error) { if (error) {
ast_log(LOG_WARNING, "Parking %s not open for business. Configuration error.\n", name); ast_log(LOG_WARNING, "Parking %s not open for business. Configuration error.\n", name);
parkinglot_destroy(parkinglot); parkinglot_destroy(parkinglot);
parkinglot_unref(parkinglot);
return NULL; return NULL;
} }
if (option_debug) ast_debug(1, "Parking %s now open for business. (start exten %d end %d)\n", name, start, end);
ast_log(LOG_DEBUG, "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 */ /* Move it into the list, if it wasn't already there */
if (!oldparkinglot) { if (!oldparkinglot) {
@ -4330,26 +4430,6 @@ static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable *
return parkinglot; 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) static int load_config(void)
{ {
int start = 0, end = 0; int start = 0, end = 0;
@ -4360,8 +4440,6 @@ static int load_config(void)
struct ast_variable *var = NULL; struct ast_variable *var = NULL;
struct feature_group *fg = NULL; struct feature_group *fg = NULL;
struct ast_flags config_flags = { 0 }; struct ast_flags config_flags = { 0 };
char old_parking_ext[AST_MAX_EXTENSION];
char old_parking_con[AST_MAX_EXTENSION] = "";
char *ctg; char *ctg;
static const char * const categories[] = { static const char * const categories[] = {
/* Categories in features.conf that are not /* Categories in features.conf that are not
@ -4372,32 +4450,27 @@ static int load_config(void)
"applicationmap" "applicationmap"
}; };
default_parkinglot = build_parkinglot(DEFAULT_PARKINGLOT, NULL);
if (default_parkinglot) { if (default_parkinglot) {
strcpy(old_parking_con, default_parkinglot->parking_con); ao2_lock(default_parkinglot);
strcpy(old_parking_ext, parking_ext); ast_copy_string(default_parkinglot->parkext, DEFAULT_PARK_EXTENSION, sizeof(default_parkinglot->parkext));
} else { default_parkinglot->parking_start = 701;
default_parkinglot = build_parkinglot(DEFAULT_PARKINGLOT, NULL); default_parkinglot->parking_stop = 750;
if (default_parkinglot) { default_parkinglot->parking_offset = 0;
ao2_lock(default_parkinglot); default_parkinglot->parkfindnext = 0;
default_parkinglot->parking_start = 701; default_parkinglot->parkingtime = DEFAULT_PARK_TIME;
default_parkinglot->parking_stop = 750; ao2_unlock(default_parkinglot);
default_parkinglot->parking_offset = 0;
default_parkinglot->parkfindnext = 0;
default_parkinglot->parkingtime = DEFAULT_PARK_TIME;
ao2_unlock(default_parkinglot);
}
} }
if (default_parkinglot) { if (default_parkinglot) {
if (option_debug) ast_debug(1, "Configuration of default parkinglot done.\n");
ast_log(LOG_DEBUG, "Configuration of default parkinglot done.\n");
} else { } else {
ast_log(LOG_ERROR, "Configuration of default parkinglot failed.\n"); ast_log(LOG_ERROR, "Configuration of default parkinglot failed.\n");
return -1; return -1;
} }
/* Reset to defaults */ /* Reset to defaults */
strcpy(parking_ext, "700"); strcpy(default_parkinglot->parkext, DEFAULT_PARK_EXTENSION);
strcpy(pickup_ext, "*8"); strcpy(pickup_ext, "*8");
courtesytone[0] = '\0'; courtesytone[0] = '\0';
strcpy(xfersound, "beep"); strcpy(xfersound, "beep");
@ -4428,7 +4501,7 @@ static int load_config(void)
} }
for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
if (!strcasecmp(var->name, "parkext")) { 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")) { } else if (!strcasecmp(var->name, "context")) {
ast_copy_string(default_parkinglot->parking_con, var->value, sizeof(default_parkinglot->parking_con)); ast_copy_string(default_parkinglot->parking_con, var->value, sizeof(default_parkinglot->parking_con));
} else if (!strcasecmp(var->name, "parkingtime")) { } else if (!strcasecmp(var->name, "parkingtime")) {
@ -4682,22 +4755,15 @@ static int load_config(void)
ast_config_destroy(cfg); 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))) { 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); ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", default_parkinglot->parking_con);
return -1; 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) if (default_parkinglot->parkaddhints)
park_add_hints(default_parkinglot->parking_con, default_parkinglot->parking_start, default_parkinglot->parking_stop); park_add_hints(default_parkinglot->parking_con, default_parkinglot->parking_start, default_parkinglot->parking_stop);
if (!res) 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; 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))) { while ((curlot = ao2_iterator_next(&iter))) {
ast_cli(a->fd, "\nCall parking (Parking lot: %s)\n", curlot->name); ast_cli(a->fd, "\nCall parking (Parking lot: %s)\n", curlot->name);
ast_cli(a->fd, "------------\n"); 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: %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-%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"); ast_cli(a->fd,"\n");
ao2_ref(curlot, -1); 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; 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 ast_features_reload(void)
{ {
int res; int res;
/* Release parking lot list */
//ASTOBJ_CONTAINER_MARKALL(&parkinglots);
// TODO: I don't think any marking is necessary
/* Reload configuration */ ao2_t_callback(parkinglots, OBJ_NODATA, parkinglot_markall_cb, NULL, "callback to mark all parkinglots");
res = load_config(); 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; 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); iter = ao2_iterator_init(parkinglots, 0);
while ((curlot = ao2_iterator_next(&iter))) { while ((curlot = ao2_iterator_next(&iter))) {
int lotparked = 0; 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_LOCK(&curlot->parkings);
AST_LIST_TRAVERSE(&curlot->parkings, cur, list) { 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 *channel = astman_get_header(m, "Channel");
const char *channel2 = astman_get_header(m, "Channel2"); const char *channel2 = astman_get_header(m, "Channel2");
const char *timeout = astman_get_header(m, "Timeout"); const char *timeout = astman_get_header(m, "Timeout");
const char *parkinglotname = astman_get_header(m, "Parkinglot");
char buf[BUFSIZ]; char buf[BUFSIZ];
int to = 0;
int res = 0; int res = 0;
int parkExt = 0;
struct ast_channel *ch1, *ch2; struct ast_channel *ch1, *ch2;
struct ast_park_call_args args = {0,};
if (ast_strlen_zero(channel)) { if (ast_strlen_zero(channel)) {
astman_send_error(s, m, "Channel not specified"); 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; 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); ast_channel_lock(ch1);
while (ast_channel_trylock(ch2)) { while (ast_channel_trylock(ch2)) {
CHANNEL_DEADLOCK_AVOIDANCE(ch1); CHANNEL_DEADLOCK_AVOIDANCE(ch1);
} }
if (!ast_strlen_zero(timeout)) { res = masq_park_call(ch1, ch2, 0, NULL, 0, &args);
sscanf(timeout, "%30d", &to);
}
res = ast_masq_park_call(ch1, ch2, to, &parkExt);
if (!res) { if (!res) {
ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT); ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
astman_send_ack(s, m, "Park successful"); astman_send_ack(s, m, "Park successful");

Loading…
Cancel
Save