mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-19 19:20:35 +00:00
res_parking: Automatically generate extensions, hints, etc.
(closes issue ASTERISK-21645) Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/2545/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390849 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
10
CHANGES
10
CHANGES
@@ -187,6 +187,11 @@ Parking
|
|||||||
The preferred way to configure parking is now through res_parking.conf while
|
The preferred way to configure parking is now through res_parking.conf while
|
||||||
configuration through features.conf is not currently supported.
|
configuration through features.conf is not currently supported.
|
||||||
|
|
||||||
|
* res_parking uses the configuration framework. If an invalid configuration is
|
||||||
|
supplied, res_parking will fail to load or fail to reload. Previously parking
|
||||||
|
lots that were misconfigured would generally be accepted with certain
|
||||||
|
configuration problems leading to individual disabled parking lots.
|
||||||
|
|
||||||
* Parked calls are now placed in bridges. This is a largely architectural change,
|
* Parked calls are now placed in bridges. This is a largely architectural change,
|
||||||
but it could have some implications in allowing for new parked call retrieval
|
but it could have some implications in allowing for new parked call retrieval
|
||||||
methods and the contents of parking lots will be visible though certain bridge
|
methods and the contents of parking lots will be visible though certain bridge
|
||||||
@@ -226,6 +231,11 @@ Parking
|
|||||||
by default. Instead, it will follow the timeout rules of the parking lot. The
|
by default. Instead, it will follow the timeout rules of the parking lot. The
|
||||||
old behavior can be reproduced by using the 'c' option.
|
old behavior can be reproduced by using the 'c' option.
|
||||||
|
|
||||||
|
* Added a channel variable PARKER_FLAT which stores the name of the extension
|
||||||
|
that would be used to come back to if comebacktoorigin was set to use. This can
|
||||||
|
be useful when comebacktoorigin is off if you still want to use the extensions
|
||||||
|
in the park-dial context that are generated to redial the parker on timeout.
|
||||||
|
|
||||||
Queue
|
Queue
|
||||||
-------------------
|
-------------------
|
||||||
* Add queue available hint. exten => 8501,hint,Queue:markq_avail
|
* Add queue available hint. exten => 8501,hint,Queue:markq_avail
|
||||||
|
@@ -469,6 +469,17 @@ int ast_add_extension2(struct ast_context *con, int replace, const char *extensi
|
|||||||
int priority, const char *label, const char *callerid,
|
int priority, const char *label, const char *callerid,
|
||||||
const char *application, void *data, void (*datad)(void *), const char *registrar);
|
const char *application, void *data, void (*datad)(void *), const char *registrar);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Same as ast_add_extension2, but assumes you have already locked context
|
||||||
|
* \since 12.0.0
|
||||||
|
*
|
||||||
|
* \note con must be write locked prior to calling. For details about the arguments,
|
||||||
|
* check ast_add_extension2()
|
||||||
|
*/
|
||||||
|
int ast_add_extension2_nolock(struct ast_context *con, int replace, const char *extension,
|
||||||
|
int priority, const char *label, const char *callerid,
|
||||||
|
const char *application, void *data, void (*datad)(void *), const char *registrar);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Map devstate to an extension state.
|
* \brief Map devstate to an extension state.
|
||||||
*
|
*
|
||||||
|
@@ -362,8 +362,6 @@ typedef enum {
|
|||||||
FEATURE_INTERPRET_CHECK, /* Used by feature_check */
|
FEATURE_INTERPRET_CHECK, /* Used by feature_check */
|
||||||
} feature_interpret_op;
|
} feature_interpret_op;
|
||||||
|
|
||||||
static const char *parkedcall = "ParkedCall";
|
|
||||||
|
|
||||||
/*! Parking lot access ramp dialplan usage entry. */
|
/*! Parking lot access ramp dialplan usage entry. */
|
||||||
struct parking_dp_ramp {
|
struct parking_dp_ramp {
|
||||||
/*! Next node in the parking lot spaces dialplan list. */
|
/*! Next node in the parking lot spaces dialplan list. */
|
||||||
@@ -1482,13 +1480,6 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, st
|
|||||||
|
|
||||||
snprintf(app_data, sizeof(app_data), "%s,%s", pu->parkingexten,
|
snprintf(app_data, sizeof(app_data), "%s,%s", pu->parkingexten,
|
||||||
pu->parkinglot->name);
|
pu->parkinglot->name);
|
||||||
if (ast_add_extension(pu->parkinglot->cfg.parking_con, 1, pu->parkingexten, 1,
|
|
||||||
NULL, NULL, parkedcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
|
|
||||||
ast_log(LOG_ERROR, "Could not create parked call exten: %s@%s\n",
|
|
||||||
pu->parkingexten, pu->parkinglot->cfg.parking_con);
|
|
||||||
} else {
|
|
||||||
notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con, AST_DEVICE_INUSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
AST_LIST_UNLOCK(&pu->parkinglot->parkings);
|
AST_LIST_UNLOCK(&pu->parkinglot->parkings);
|
||||||
|
|
||||||
@@ -3939,12 +3930,6 @@ static int manage_parked_call(struct parkeduser *pu, const struct pollfd *pfds,
|
|||||||
|
|
||||||
pbx_builtin_setvar_helper(chan, "PARKER", peername);
|
pbx_builtin_setvar_helper(chan, "PARKER", peername);
|
||||||
|
|
||||||
if (ast_add_extension(parking_con_dial, 1, peername_flat, 1, NULL, NULL,
|
|
||||||
"Dial", ast_strdup(returnexten), ast_free_ptr, registrar)) {
|
|
||||||
ast_log(LOG_ERROR,
|
|
||||||
"Could not create parking return dial exten: %s@%s\n",
|
|
||||||
peername_flat, parking_con_dial);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
|
snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
|
||||||
@@ -4274,25 +4259,6 @@ static struct ast_parkinglot *create_parkinglot(const char *name)
|
|||||||
return newlot;
|
return newlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Add parking hints for all defined parking spaces.
|
|
||||||
* \param context Dialplan context to add the hints.
|
|
||||||
* \param start Starting space in parkinglot.
|
|
||||||
* \param stop Ending space in parkinglot.
|
|
||||||
*/
|
|
||||||
static void park_add_hints(const 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Default configuration for default parking lot. */
|
/*! Default configuration for default parking lot. */
|
||||||
static const struct parkinglot_cfg parkinglot_cfg_default_default = {
|
static const struct parkinglot_cfg parkinglot_cfg_default_default = {
|
||||||
.mohclass = "default",
|
.mohclass = "default",
|
||||||
@@ -4330,50 +4296,9 @@ static const struct parkinglot_cfg parkinglot_cfg_default = {
|
|||||||
*/
|
*/
|
||||||
static int parkinglot_activate(struct ast_parkinglot *parkinglot)
|
static int parkinglot_activate(struct ast_parkinglot *parkinglot)
|
||||||
{
|
{
|
||||||
int disabled = 0;
|
/* XXX All parking stuff is being replaced by res_parking */
|
||||||
char app_data[5 + AST_MAX_CONTEXT];
|
parkinglot->disabled = 1;
|
||||||
|
return -1;
|
||||||
/* Create Park option list. Must match with struct park_app_args options. */
|
|
||||||
if (parkinglot->cfg.parkext_exclusive) {
|
|
||||||
/* Specify the parking lot this parking extension parks calls. */
|
|
||||||
snprintf(app_data, sizeof(app_data), ",,,,,%s", parkinglot->name);
|
|
||||||
} else {
|
|
||||||
/* The dialplan must specify which parking lot to use. */
|
|
||||||
app_data[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create context */
|
|
||||||
if (!ast_context_find_or_create(NULL, NULL, parkinglot->cfg.parking_con, registrar)) {
|
|
||||||
ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n",
|
|
||||||
parkinglot->cfg.parking_con);
|
|
||||||
disabled = 1;
|
|
||||||
|
|
||||||
/* Add a parking extension into the context */
|
|
||||||
} else if (ast_add_extension(parkinglot->cfg.parking_con, 1, parkinglot->cfg.parkext,
|
|
||||||
1, NULL, NULL, parkcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
|
|
||||||
ast_log(LOG_ERROR, "Could not create parking lot %s access exten %s@%s\n",
|
|
||||||
parkinglot->name, parkinglot->cfg.parkext, parkinglot->cfg.parking_con);
|
|
||||||
disabled = 1;
|
|
||||||
} else {
|
|
||||||
/* Add parking hints */
|
|
||||||
if (parkinglot->cfg.parkaddhints) {
|
|
||||||
park_add_hints(parkinglot->cfg.parking_con, parkinglot->cfg.parking_start,
|
|
||||||
parkinglot->cfg.parking_stop);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX Not sure why we should need to notify the metermaids for
|
|
||||||
* this exten. It was originally done for the default parking
|
|
||||||
* lot entry exten only but should be done for all entry extens
|
|
||||||
* if we do it for one.
|
|
||||||
*/
|
|
||||||
/* Notify metermaids about parking lot entry exten state. */
|
|
||||||
notify_metermaids(parkinglot->cfg.parkext, parkinglot->cfg.parking_con,
|
|
||||||
AST_DEVICE_INUSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
parkinglot->disabled = disabled;
|
|
||||||
return disabled ? -1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ast_features_reload(void)
|
int ast_features_reload(void)
|
||||||
|
10
main/pbx.c
10
main/pbx.c
@@ -9612,6 +9612,16 @@ int ast_add_extension2(struct ast_context *con,
|
|||||||
application, data, datad, registrar, 1);
|
application, data, datad, registrar, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ast_add_extension2_nolock(struct ast_context *con,
|
||||||
|
int replace, const char *extension, int priority, const char *label, const char *callerid,
|
||||||
|
const char *application, void *data, void (*datad)(void *),
|
||||||
|
const char *registrar)
|
||||||
|
{
|
||||||
|
return ast_add_extension2_lockopt(con, replace, extension, priority, label, callerid,
|
||||||
|
application, data, datad, registrar, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Same as ast_add_extension2() but controls the context locking.
|
* \brief Same as ast_add_extension2() but controls the context locking.
|
||||||
*
|
*
|
||||||
|
@@ -290,6 +290,8 @@ static int bridge_parking_push(struct ast_bridge_parking *self, struct ast_bridg
|
|||||||
COLORIZE(COLOR_BRMAGENTA, 0, self->lot->name),
|
COLORIZE(COLOR_BRMAGENTA, 0, self->lot->name),
|
||||||
pu->parking_space);
|
pu->parking_space);
|
||||||
|
|
||||||
|
parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_INUSE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,6 +330,8 @@ static void bridge_parking_pull(struct ast_bridge_parking *self, struct ast_brid
|
|||||||
}
|
}
|
||||||
ao2_unlock(pu);
|
ao2_unlock(pu);
|
||||||
|
|
||||||
|
parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_NOT_INUSE);
|
||||||
|
|
||||||
switch (pu->resolution) {
|
switch (pu->resolution) {
|
||||||
case PARK_UNSET:
|
case PARK_UNSET:
|
||||||
/* This should be impossible now since the resolution is forcibly set to abandon if it was unset at this point. Resolution
|
/* This should be impossible now since the resolution is forcibly set to abandon if it was unset at this point. Resolution
|
||||||
|
@@ -367,6 +367,23 @@ static void parking_duration_cb_destroyer(void *hook_pvt)
|
|||||||
ao2_ref(user, -1);
|
ao2_ref(user, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Removes the identification information from a channel name string
|
||||||
|
* \since 12.0
|
||||||
|
*
|
||||||
|
* \param channel name string that you wish to turn into a dial string. This will be edited in place.
|
||||||
|
*/
|
||||||
|
static void channel_name_to_dial_string(char *peername)
|
||||||
|
{
|
||||||
|
char *dash;
|
||||||
|
|
||||||
|
/* Truncate after the dash */
|
||||||
|
dash = strrchr(peername, '-');
|
||||||
|
if (dash) {
|
||||||
|
*dash = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*! \internal
|
/*! \internal
|
||||||
* \brief Interval hook. Pulls a parked call from the parking bridge after the timeout is passed and sets the resolution to timeout.
|
* \brief Interval hook. Pulls a parked call from the parking bridge after the timeout is passed and sets the resolution to timeout.
|
||||||
*
|
*
|
||||||
@@ -378,9 +395,17 @@ static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridg
|
|||||||
{
|
{
|
||||||
struct parked_user *user = hook_pvt;
|
struct parked_user *user = hook_pvt;
|
||||||
struct ast_channel *chan = user->chan;
|
struct ast_channel *chan = user->chan;
|
||||||
|
struct ast_context *park_dial_context;
|
||||||
char *peername;
|
char *peername;
|
||||||
|
char *peername_flat;
|
||||||
char parking_space[AST_MAX_EXTENSION];
|
char parking_space[AST_MAX_EXTENSION];
|
||||||
|
|
||||||
|
char returnexten[AST_MAX_EXTENSION];
|
||||||
|
char *duplicate_returnexten;
|
||||||
|
struct ast_exten *existing_exten;
|
||||||
|
struct pbx_find_info pbx_finder = { .stacklen = 0 }; /* The rest is reset in pbx_find_extension */
|
||||||
|
|
||||||
|
|
||||||
/* We are still in the bridge, so it's possible for other stuff to mess with the parked call before we leave the bridge
|
/* We are still in the bridge, so it's possible for other stuff to mess with the parked call before we leave the bridge
|
||||||
to deal with this, lock the parked user, check and set resolution. */
|
to deal with this, lock the parked user, check and set resolution. */
|
||||||
ao2_lock(user);
|
ao2_lock(user);
|
||||||
@@ -402,11 +427,68 @@ static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridg
|
|||||||
pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot->name);
|
pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot->name);
|
||||||
|
|
||||||
peername = ast_strdupa(user->parker->name);
|
peername = ast_strdupa(user->parker->name);
|
||||||
flatten_peername(peername);
|
channel_name_to_dial_string(peername);
|
||||||
|
|
||||||
|
peername_flat = ast_strdupa(user->parker->name);
|
||||||
|
flatten_peername(peername_flat);
|
||||||
|
|
||||||
pbx_builtin_setvar_helper(chan, "PARKER", peername);
|
pbx_builtin_setvar_helper(chan, "PARKER", peername);
|
||||||
|
pbx_builtin_setvar_helper(chan, "PARKER_FLAT", peername_flat);
|
||||||
|
|
||||||
/* TODO Dialplan generation for park-dial extensions */
|
/* Dialplan generation for park-dial extensions */
|
||||||
|
|
||||||
|
if (ast_wrlock_contexts()) {
|
||||||
|
ast_log(LOG_ERROR, "Failed to lock the contexts list. Can't add the park-dial extension.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(park_dial_context = ast_context_find_or_create(NULL, NULL, PARK_DIAL_CONTEXT, BASE_REGISTRAR))) {
|
||||||
|
ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", PARK_DIAL_CONTEXT);
|
||||||
|
if (ast_unlock_contexts()) {
|
||||||
|
ast_assert(0);
|
||||||
|
}
|
||||||
|
goto abandon_extension_creation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_wrlock_context(park_dial_context)) {
|
||||||
|
ast_log(LOG_ERROR, "failed to obtain write lock on context '%s'\n", PARK_DIAL_CONTEXT);
|
||||||
|
if (ast_unlock_contexts()) {
|
||||||
|
ast_assert(0);
|
||||||
|
}
|
||||||
|
goto abandon_extension_creation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_unlock_contexts()) {
|
||||||
|
ast_assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(returnexten, sizeof(returnexten), "%s,%u", peername,
|
||||||
|
user->lot->cfg->comebackdialtime);
|
||||||
|
|
||||||
|
duplicate_returnexten = ast_strdup(returnexten);
|
||||||
|
|
||||||
|
if (!duplicate_returnexten) {
|
||||||
|
ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
|
||||||
|
peername_flat, PARK_DIAL_CONTEXT, returnexten);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If an extension already exists here because we registered it for another parked call timing out, then we may overwrite it. */
|
||||||
|
if ((existing_exten = pbx_find_extension(NULL, NULL, &pbx_finder, PARK_DIAL_CONTEXT, peername_flat, 1, NULL, NULL, E_MATCH)) &&
|
||||||
|
(strcmp(ast_get_extension_registrar(existing_exten), BASE_REGISTRAR))) {
|
||||||
|
ast_debug(3, "An extension for '%s@%s' was already registered by another registrar '%s'\n",
|
||||||
|
peername_flat, PARK_DIAL_CONTEXT, ast_get_extension_registrar(existing_exten));
|
||||||
|
} else if (ast_add_extension2_nolock(park_dial_context, 1, peername_flat, 1, NULL, NULL,
|
||||||
|
"Dial", duplicate_returnexten, ast_free_ptr, BASE_REGISTRAR)) {
|
||||||
|
ast_free(duplicate_returnexten);
|
||||||
|
ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
|
||||||
|
peername_flat, PARK_DIAL_CONTEXT, returnexten);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_unlock_context(park_dial_context)) {
|
||||||
|
ast_assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
abandon_extension_creation:
|
||||||
|
|
||||||
/* async_goto the proper PBX destination - this should happen when we come out of the bridge */
|
/* async_goto the proper PBX destination - this should happen when we come out of the bridge */
|
||||||
if (!ast_strlen_zero(user->comeback)) {
|
if (!ast_strlen_zero(user->comeback)) {
|
||||||
|
@@ -254,9 +254,6 @@ int comeback_goto(struct parked_user *pu, struct parking_lot *lot)
|
|||||||
|
|
||||||
peername = blindtransfer ? ast_strdupa(blindtransfer) : ast_strdupa(pu->parker->name);
|
peername = blindtransfer ? ast_strdupa(blindtransfer) : ast_strdupa(pu->parker->name);
|
||||||
|
|
||||||
/* XXX Comeback to origin mode: Generate an extension in park-dial to Dial the peer */
|
|
||||||
|
|
||||||
|
|
||||||
/* Flatten the peername so that it can be used for performing the timeout PBX operations */
|
/* Flatten the peername so that it can be used for performing the timeout PBX operations */
|
||||||
flatten_peername(peername);
|
flatten_peername(peername);
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#define DEFAULT_PARKING_LOT "default"
|
#define DEFAULT_PARKING_LOT "default"
|
||||||
#define DEFAULT_PARKING_EXTEN "700"
|
#define DEFAULT_PARKING_EXTEN "700"
|
||||||
|
#define BASE_REGISTRAR "res_parking"
|
||||||
#define PARK_DIAL_CONTEXT "park-dial"
|
#define PARK_DIAL_CONTEXT "park-dial"
|
||||||
|
|
||||||
enum park_call_resolution {
|
enum park_call_resolution {
|
||||||
@@ -75,6 +76,7 @@ struct parking_lot_cfg {
|
|||||||
|
|
||||||
AST_DECLARE_STRING_FIELDS(
|
AST_DECLARE_STRING_FIELDS(
|
||||||
AST_STRING_FIELD(name); /*!< Name of the parking lot configuration object */
|
AST_STRING_FIELD(name); /*!< Name of the parking lot configuration object */
|
||||||
|
AST_STRING_FIELD(registrar); /*!< Which registrar the lot uses if it isn't the default registrar */
|
||||||
AST_STRING_FIELD(mohclass); /*!< Analogous to mohclass config option */
|
AST_STRING_FIELD(mohclass); /*!< Analogous to mohclass config option */
|
||||||
AST_STRING_FIELD(parkext); /*!< Analogous to parkext config option */
|
AST_STRING_FIELD(parkext); /*!< Analogous to parkext config option */
|
||||||
AST_STRING_FIELD(parking_con); /*!< Analogous to context config option */
|
AST_STRING_FIELD(parking_con); /*!< Analogous to context config option */
|
||||||
@@ -109,7 +111,7 @@ struct parked_user {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief If a parking lot exists in the parking lot list already, update its status to match the provided
|
* \brief If a parking lot exists in the parking lot list already, update its status to match the provided
|
||||||
* configuration and return a reference return a reference to it. Otherwise, create a parking lot
|
* configuration and return a reference return a reference to it. Otherwise, create a parking lot
|
||||||
* struct based on a parking lot configuration and return a reference to the new one.
|
* struct based on a parking lot configuration and return a reference to the new one.
|
||||||
@@ -125,7 +127,7 @@ struct parked_user {
|
|||||||
struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *cfg);
|
struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *cfg);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Remove a parking lot from the usable lists if it is no longer involved in any calls and no configuration currently claims it
|
* \brief Remove a parking lot from the usable lists if it is no longer involved in any calls and no configuration currently claims it
|
||||||
*
|
*
|
||||||
* \param lot Which parking lot is being checked for elimination
|
* \param lot Which parking lot is being checked for elimination
|
||||||
@@ -136,7 +138,7 @@ struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *cfg);
|
|||||||
void parking_lot_remove_if_unused(struct parking_lot *lot);
|
void parking_lot_remove_if_unused(struct parking_lot *lot);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Create a new parking bridge
|
* \brief Create a new parking bridge
|
||||||
*
|
*
|
||||||
* \param bridge_lot Parking lot which the new bridge should be based on
|
* \param bridge_lot Parking lot which the new bridge should be based on
|
||||||
@@ -147,7 +149,7 @@ void parking_lot_remove_if_unused(struct parking_lot *lot);
|
|||||||
struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot);
|
struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Get a reference to a parking lot's bridge. If it doesn't exist, create it and get a reference.
|
* \brief Get a reference to a parking lot's bridge. If it doesn't exist, create it and get a reference.
|
||||||
*
|
*
|
||||||
* \param lot Which parking lot we need the bridge from. This parking lot must be locked before calling this function.
|
* \param lot Which parking lot we need the bridge from. This parking lot must be locked before calling this function.
|
||||||
@@ -160,7 +162,7 @@ struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot);
|
|||||||
struct ast_bridge *parking_lot_get_bridge(struct parking_lot *lot);
|
struct ast_bridge *parking_lot_get_bridge(struct parking_lot *lot);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Get an available parking space within a parking lot.
|
* \brief Get an available parking space within a parking lot.
|
||||||
*
|
*
|
||||||
* \param lot Which parking lot we are getting a space from
|
* \param lot Which parking lot we are getting a space from
|
||||||
@@ -175,7 +177,7 @@ struct ast_bridge *parking_lot_get_bridge(struct parking_lot *lot);
|
|||||||
int parking_lot_get_space(struct parking_lot *lot, int target_override);
|
int parking_lot_get_space(struct parking_lot *lot, int target_override);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Determine if there is a parked user in a parking space and pull it from the parking lot if there is.
|
* \brief Determine if there is a parked user in a parking space and pull it from the parking lot if there is.
|
||||||
*
|
*
|
||||||
* \param lot Parking lot being pulled from
|
* \param lot Parking lot being pulled from
|
||||||
@@ -191,7 +193,7 @@ int parking_lot_get_space(struct parking_lot *lot, int target_override);
|
|||||||
struct parked_user *parking_lot_retrieve_parked_user(struct parking_lot *lot, int target);
|
struct parked_user *parking_lot_retrieve_parked_user(struct parking_lot *lot, int target);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Apply features based on the parking lot feature options
|
* \brief Apply features based on the parking lot feature options
|
||||||
*
|
*
|
||||||
* \param chan Which channel's feature set is being modified
|
* \param chan Which channel's feature set is being modified
|
||||||
@@ -202,7 +204,7 @@ struct parked_user *parking_lot_retrieve_parked_user(struct parking_lot *lot, in
|
|||||||
void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parking_lot *lot, int recipient_mode);
|
void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parking_lot *lot, int recipient_mode);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Set necessary bridge roles on a channel that is about to enter a parking lot
|
* \brief Set necessary bridge roles on a channel that is about to enter a parking lot
|
||||||
*
|
*
|
||||||
* \param chan Entering channel
|
* \param chan Entering channel
|
||||||
@@ -212,14 +214,14 @@ void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parki
|
|||||||
void parking_channel_set_roles(struct ast_channel *chan, struct parking_lot *lot, int force_ringing);
|
void parking_channel_set_roles(struct ast_channel *chan, struct parking_lot *lot, int force_ringing);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief custom callback function for ast_bridge_channel_queue_playfile which plays a parking space
|
* \brief custom callback function for ast_bridge_channel_queue_playfile which plays a parking space
|
||||||
* and optionally hangs up the call afterwards based on the payload in playfile.
|
* and optionally hangs up the call afterwards based on the payload in playfile.
|
||||||
*/
|
*/
|
||||||
void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *payload);
|
void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *payload);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Setup timeout interval feature on an ast_bridge_features for parking
|
* \brief Setup timeout interval feature on an ast_bridge_features for parking
|
||||||
*
|
*
|
||||||
* \param features The ast_bridge_features we are establishing the interval hook on
|
* \param features The ast_bridge_features we are establishing the interval hook on
|
||||||
@@ -228,7 +230,7 @@ void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *pa
|
|||||||
void parking_set_duration(struct ast_bridge_features *features, struct parked_user *user);
|
void parking_set_duration(struct ast_bridge_features *features, struct parked_user *user);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Get a pointer to the parking lot container for purposes such as iteration
|
* \brief Get a pointer to the parking lot container for purposes such as iteration
|
||||||
*
|
*
|
||||||
* \retval pointer to the parking lot container.
|
* \retval pointer to the parking lot container.
|
||||||
@@ -236,7 +238,7 @@ void parking_set_duration(struct ast_bridge_features *features, struct parked_us
|
|||||||
struct ao2_container *get_parking_lot_container(void);
|
struct ao2_container *get_parking_lot_container(void);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Find a parking lot based on its name
|
* \brief Find a parking lot based on its name
|
||||||
*
|
*
|
||||||
* \param lot_name Name of the parking lot sought
|
* \param lot_name Name of the parking lot sought
|
||||||
@@ -249,7 +251,7 @@ struct ao2_container *get_parking_lot_container(void);
|
|||||||
struct parking_lot *parking_lot_find_by_name(const char *lot_name);
|
struct parking_lot *parking_lot_find_by_name(const char *lot_name);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Find parking lot name from channel
|
* \brief Find parking lot name from channel
|
||||||
*
|
*
|
||||||
* \param chan The channel we want the parking lot name for
|
* \param chan The channel we want the parking lot name for
|
||||||
@@ -262,7 +264,7 @@ struct parking_lot *parking_lot_find_by_name(const char *lot_name);
|
|||||||
const char *find_channel_parking_lot_name(struct ast_channel *chan);
|
const char *find_channel_parking_lot_name(struct ast_channel *chan);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Flattens a peer name so that it can be written to/found from PBX extensions
|
* \brief Flattens a peer name so that it can be written to/found from PBX extensions
|
||||||
*
|
*
|
||||||
* \param peername unflattened peer name. This will be flattened in place, so expect it to change.
|
* \param peername unflattened peer name. This will be flattened in place, so expect it to change.
|
||||||
@@ -270,7 +272,7 @@ const char *find_channel_parking_lot_name(struct ast_channel *chan);
|
|||||||
void flatten_peername(char *peername);
|
void flatten_peername(char *peername);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Set a channel's position in the PBX after timeout using the parking lot settings
|
* \brief Set a channel's position in the PBX after timeout using the parking lot settings
|
||||||
*
|
*
|
||||||
* \param pu Parked user who is entering/reentering the PBX
|
* \param pu Parked user who is entering/reentering the PBX
|
||||||
@@ -282,7 +284,30 @@ void flatten_peername(char *peername);
|
|||||||
int comeback_goto(struct parked_user *pu, struct parking_lot *lot);
|
int comeback_goto(struct parked_user *pu, struct parking_lot *lot);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
|
* \brief Add extensions for a parking lot configuration
|
||||||
|
*
|
||||||
|
* \param lot_cfg parking lot configuration to generate extensions for
|
||||||
|
*
|
||||||
|
* \retval 0 on success
|
||||||
|
* \retval non-zero on failure
|
||||||
|
*/
|
||||||
|
int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \since 12.0.0
|
||||||
|
* \brief Remove extensions belonging to a parking lot configuration
|
||||||
|
*
|
||||||
|
* \param lot_cfg parking lot configuratin to remove extensions from
|
||||||
|
*
|
||||||
|
* \note This will not remove extensions registered non-exclusively even
|
||||||
|
* if those extensions were registered by lot_cfg. Those are only
|
||||||
|
* purged on a res_parking module reload.
|
||||||
|
*/
|
||||||
|
void parking_lot_cfg_remove_extensions(struct parking_lot_cfg *lot_cfg);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \since 12.0.0
|
||||||
* \brief Pull a parked user out of its parking lot. Use this when you don't want to use the parked user afterwards.
|
* \brief Pull a parked user out of its parking lot. Use this when you don't want to use the parked user afterwards.
|
||||||
* \param user The parked user being pulled.
|
* \param user The parked user being pulled.
|
||||||
*
|
*
|
||||||
@@ -292,7 +317,7 @@ int comeback_goto(struct parked_user *pu, struct parking_lot *lot);
|
|||||||
int unpark_parked_user(struct parked_user *user);
|
int unpark_parked_user(struct parked_user *user);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Publish a stasis parked call message for the channel indicating failure to park.
|
* \brief Publish a stasis parked call message for the channel indicating failure to park.
|
||||||
*
|
*
|
||||||
* \param parkee channel belonging to the failed parkee
|
* \param parkee channel belonging to the failed parkee
|
||||||
@@ -300,7 +325,7 @@ int unpark_parked_user(struct parked_user *user);
|
|||||||
void publish_parked_call_failure(struct ast_channel *parkee);
|
void publish_parked_call_failure(struct ast_channel *parkee);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Publish a stasis parked call message for a given parked user
|
* \brief Publish a stasis parked call message for a given parked user
|
||||||
*
|
*
|
||||||
* \param pu pointer to a parked_user that we are generating the message for
|
* \param pu pointer to a parked_user that we are generating the message for
|
||||||
@@ -309,7 +334,7 @@ void publish_parked_call_failure(struct ast_channel *parkee);
|
|||||||
void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type);
|
void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Function to prepare a channel for parking by determining which parking bridge should
|
* \brief Function to prepare a channel for parking by determining which parking bridge should
|
||||||
* be used, setting up a park common datastore so that the parking bridge will have access
|
* be used, setting up a park common datastore so that the parking bridge will have access
|
||||||
* to necessary parking information when joining, and applying various bridge roles to the
|
* to necessary parking information when joining, and applying various bridge roles to the
|
||||||
@@ -338,7 +363,7 @@ struct park_common_datastore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Function that pulls data from the park common datastore on a channel in order to apply it to
|
* \brief Function that pulls data from the park common datastore on a channel in order to apply it to
|
||||||
* the parked user struct upon bridging.
|
* the parked user struct upon bridging.
|
||||||
*
|
*
|
||||||
@@ -354,7 +379,17 @@ void get_park_common_datastore_data(struct ast_channel *parkee,
|
|||||||
int *randomize, int *time_limit, int *silence_announce);
|
int *randomize, int *time_limit, int *silence_announce);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
|
* \brief Notify metermaids that we've changed an extension
|
||||||
|
*
|
||||||
|
* \param exten Extension of the call parked/unparked
|
||||||
|
* \param context Context of the call parked/unparked
|
||||||
|
* \param state new device state
|
||||||
|
*/
|
||||||
|
void parking_notify_metermaids(int exten, const char *context, enum ast_device_state state);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \since 12.0.0
|
||||||
* \brief Execution function for the parking application
|
* \brief Execution function for the parking application
|
||||||
*
|
*
|
||||||
* \param chan ast_channel entering the application
|
* \param chan ast_channel entering the application
|
||||||
@@ -368,7 +403,7 @@ void get_park_common_datastore_data(struct ast_channel *parkee,
|
|||||||
int park_app_exec(struct ast_channel *chan, const char *data);
|
int park_app_exec(struct ast_channel *chan, const char *data);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Execution function for the parked call application
|
* \brief Execution function for the parked call application
|
||||||
*
|
*
|
||||||
* \param chan ast_channel entering the application
|
* \param chan ast_channel entering the application
|
||||||
@@ -380,7 +415,7 @@ int park_app_exec(struct ast_channel *chan, const char *data);
|
|||||||
int parked_call_app_exec(struct ast_channel *chan, const char *data);
|
int parked_call_app_exec(struct ast_channel *chan, const char *data);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Execution function for the park and retrieve application
|
* \brief Execution function for the park and retrieve application
|
||||||
*
|
*
|
||||||
* \param chan ast_channel entering the application
|
* \param chan ast_channel entering the application
|
||||||
@@ -394,7 +429,7 @@ int parked_call_app_exec(struct ast_channel *chan, const char *data);
|
|||||||
int park_and_announce_app_exec(struct ast_channel *chan, const char *data);
|
int park_and_announce_app_exec(struct ast_channel *chan, const char *data);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Register CLI commands
|
* \brief Register CLI commands
|
||||||
*
|
*
|
||||||
* \retval 0 if successful
|
* \retval 0 if successful
|
||||||
@@ -403,25 +438,25 @@ int park_and_announce_app_exec(struct ast_channel *chan, const char *data);
|
|||||||
int load_parking_ui(void);
|
int load_parking_ui(void);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Unregister CLI commands
|
* \brief Unregister CLI commands
|
||||||
*/
|
*/
|
||||||
void unload_parking_ui(void);
|
void unload_parking_ui(void);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Register manager actions and setup subscriptions for stasis events
|
* \brief Register manager actions and setup subscriptions for stasis events
|
||||||
*/
|
*/
|
||||||
int load_parking_manager(void);
|
int load_parking_manager(void);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Unregister manager actions and remove subscriptions for stasis events
|
* \brief Unregister manager actions and remove subscriptions for stasis events
|
||||||
*/
|
*/
|
||||||
void unload_parking_manager(void);
|
void unload_parking_manager(void);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Register bridge features for parking
|
* \brief Register bridge features for parking
|
||||||
*
|
*
|
||||||
* \retval 0 on success
|
* \retval 0 on success
|
||||||
@@ -430,7 +465,19 @@ void unload_parking_manager(void);
|
|||||||
int load_parking_bridge_features(void);
|
int load_parking_bridge_features(void);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \since 12
|
* \since 12.0.0
|
||||||
* \brief Unregister features registered by load_parking_bridge_features
|
* \brief Unregister features registered by load_parking_bridge_features
|
||||||
*/
|
*/
|
||||||
void unload_parking_bridge_features(void);
|
void unload_parking_bridge_features(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \since 12.0.0
|
||||||
|
* \brief Register Parking devstate handler
|
||||||
|
*/
|
||||||
|
int load_parking_devstate(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \since 12.0.0
|
||||||
|
* \brief Unregister Parking devstate handler
|
||||||
|
*/
|
||||||
|
void unload_parking_devstate(void);
|
||||||
|
@@ -191,6 +191,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include "asterisk/astobj2.h"
|
#include "asterisk/astobj2.h"
|
||||||
#include "asterisk/features.h"
|
#include "asterisk/features.h"
|
||||||
#include "asterisk/manager.h"
|
#include "asterisk/manager.h"
|
||||||
|
#include "asterisk/pbx.h"
|
||||||
|
|
||||||
#define PARKED_CALL_APPLICATION "ParkedCall"
|
#define PARKED_CALL_APPLICATION "ParkedCall"
|
||||||
#define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
|
#define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
|
||||||
@@ -375,7 +376,7 @@ static void parking_lot_disable(struct parking_lot *lot)
|
|||||||
static void parking_lot_cfg_destructor(void *obj)
|
static void parking_lot_cfg_destructor(void *obj)
|
||||||
{
|
{
|
||||||
struct parking_lot_cfg *lot_cfg = obj;
|
struct parking_lot_cfg *lot_cfg = obj;
|
||||||
|
parking_lot_cfg_remove_extensions(lot_cfg);
|
||||||
ast_string_field_free_memory(lot_cfg);
|
ast_string_field_free_memory(lot_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,6 +608,219 @@ static struct parking_lot *alloc_new_parking_lot(struct parking_lot_cfg *lot_cfg
|
|||||||
return lot;
|
return lot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void parking_lot_cfg_remove_extensions(struct parking_lot_cfg *lot_cfg)
|
||||||
|
{
|
||||||
|
if (!ast_strlen_zero(lot_cfg->registrar)) {
|
||||||
|
/* Although the function is called ast_context_destroy, the use of this funtion is
|
||||||
|
* intended only to remove extensions, hints, etc registered by the parking lot's registrar.
|
||||||
|
* It won't actually destroy the context unless that context is empty afterwards and it is
|
||||||
|
* unreferenced.
|
||||||
|
*/
|
||||||
|
ast_context_destroy(NULL, lot_cfg->registrar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_all_configured_parking_lot_extensions(void)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
|
||||||
|
struct parking_lot_cfg *lot_cfg;
|
||||||
|
struct ao2_iterator iter;
|
||||||
|
|
||||||
|
if (!cfg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
|
||||||
|
parking_lot_cfg_remove_extensions(lot_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_context_destroy(NULL, BASE_REGISTRAR);
|
||||||
|
|
||||||
|
ao2_iterator_destroy(&iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \since 12
|
||||||
|
* \brief Create an extension using ast_add_extension2_nolock. This function automatically allocates a duplicate
|
||||||
|
* of the data string so that whatever calls it doesn't have to deal with freeing it if the ast_add_extension2_nolock
|
||||||
|
* fails.
|
||||||
|
*
|
||||||
|
* \param context a write locked ast_context. Make certain it is write locked prior to calling this function
|
||||||
|
* \param replace whether the extension should replace existing extensions
|
||||||
|
* \param extension name of the extension desired
|
||||||
|
* \param priority priority of the extension we are registering
|
||||||
|
* \param application name of the application being used for the extension
|
||||||
|
* \param data application arguments
|
||||||
|
* \param registrar name of the registrar you should use for the extension.
|
||||||
|
* Make sure this string doesn't go anywhere while there are still extensions using it.
|
||||||
|
*/
|
||||||
|
static int parking_add_extension(struct ast_context *context, int replace, const char *extension,
|
||||||
|
int priority, const char *application, const char *data, const char *registrar)
|
||||||
|
{
|
||||||
|
char *data_duplicate = ast_strdup(data);
|
||||||
|
|
||||||
|
if (!data_duplicate) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_add_extension2_nolock(context, replace, extension, priority, NULL, NULL,
|
||||||
|
application, data_duplicate, ast_free_ptr, registrar)) {
|
||||||
|
ast_free(data_duplicate);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int extension_is_compatible(struct parking_lot_cfg *lot_cfg, const char *app_type, struct ast_exten *extension)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct parking_lot_cfg *, owner, NULL, ao2_cleanup);
|
||||||
|
const char *extension_registrar = ast_get_extension_registrar(extension);
|
||||||
|
const char *extension_context = ast_get_context_name(ast_get_extension_context(extension));
|
||||||
|
const char *extension_name = ast_get_extension_name(extension);
|
||||||
|
const char *extension_application = ast_get_extension_app(extension);
|
||||||
|
|
||||||
|
ast_assert(extension_registrar && extension_context && extension_name && extension_application);
|
||||||
|
|
||||||
|
if (strcmp(extension_registrar, BASE_REGISTRAR)) {
|
||||||
|
ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s', but that extension is already owned by %s.\n",
|
||||||
|
lot_cfg->name, extension_name, extension_context, extension_registrar);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(extension_application, app_type)) {
|
||||||
|
ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s' with a non-exclusive %s application, "
|
||||||
|
"but a/an %s application is already registered to that extension by %s.\n",
|
||||||
|
lot_cfg->name, extension_name, extension_context, app_type,
|
||||||
|
extension_application, BASE_REGISTRAR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_debug(3, "Parking lot '%s' -- extension '%s@%s' with application %s is compatible.\n",
|
||||||
|
lot_cfg->name, extension_name, extension_context, app_type);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg)
|
||||||
|
{
|
||||||
|
int parkingspace;
|
||||||
|
struct ast_exten *existing_exten;
|
||||||
|
struct ast_context *lot_context;
|
||||||
|
struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
|
||||||
|
const char *registrar_pointer;
|
||||||
|
|
||||||
|
if (ast_strlen_zero(lot_cfg->parkext)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lot_cfg->parkext_exclusive) {
|
||||||
|
ast_string_field_build(lot_cfg, registrar, "%s/%s", BASE_REGISTRAR, lot_cfg->name);
|
||||||
|
registrar_pointer = lot_cfg->registrar;
|
||||||
|
} else {
|
||||||
|
registrar_pointer = BASE_REGISTRAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need the contexts list locked to safely be able to both read and lock the specific context within */
|
||||||
|
if (ast_wrlock_contexts()) {
|
||||||
|
ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(lot_context = ast_context_find_or_create(NULL, NULL, lot_cfg->parking_con, registrar_pointer))) {
|
||||||
|
ast_log(LOG_ERROR, "Parking lot '%s' -- Needs a context '%s' which does not exist and Asterisk was unable to create\n",
|
||||||
|
lot_cfg->name, lot_cfg->parking_con);
|
||||||
|
if (ast_unlock_contexts()) {
|
||||||
|
ast_assert(0);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Once we know what context we will be modifying, we need to write lock it because we will be reading extensions
|
||||||
|
* and we don't want something else to destroy them while we are looking at them.
|
||||||
|
*/
|
||||||
|
if (ast_wrlock_context(lot_context)) {
|
||||||
|
ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_unlock_contexts()) {
|
||||||
|
ast_assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle generation/confirmation for the Park extension */
|
||||||
|
if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, lot_cfg->parkext, 1, NULL, NULL, E_MATCH))) {
|
||||||
|
if (lot_cfg->parkext_exclusive || !extension_is_compatible(lot_cfg, PARK_APPLICATION, existing_exten)) {
|
||||||
|
ast_unlock_context(lot_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (parking_add_extension(lot_context, 0, lot_cfg->parkext, 1, PARK_APPLICATION,
|
||||||
|
lot_cfg->parkext_exclusive ? lot_cfg->name : "", registrar_pointer)) {
|
||||||
|
ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
|
||||||
|
lot_cfg->name, PARK_APPLICATION, lot_cfg->parkext, lot_cfg->parking_con);
|
||||||
|
ast_unlock_context(lot_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle generation/confirmation for the ParkedCall extensions and hints */
|
||||||
|
for (parkingspace = lot_cfg->parking_start; parkingspace <= lot_cfg->parking_stop; parkingspace++) {
|
||||||
|
char space[AST_MAX_EXTENSION];
|
||||||
|
RAII_VAR(struct ast_str *, arguments_string, NULL, ast_free);
|
||||||
|
find_info.stacklen = 0; /* reset for pbx_find_exten */
|
||||||
|
|
||||||
|
snprintf(space, sizeof(space), "%d", parkingspace);
|
||||||
|
|
||||||
|
/* Unlike the Park extensions, ParkedCall extensions and their hints may never be shared for any reason. */
|
||||||
|
if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, 1, NULL, NULL, E_MATCH))) {
|
||||||
|
ast_unlock_context(lot_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments_string = ast_str_create(32);
|
||||||
|
if (!arguments_string) {
|
||||||
|
ast_unlock_context(lot_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_str_set(&arguments_string, 0, "%s,%s", lot_cfg->name, space);
|
||||||
|
if (parking_add_extension(lot_context, 0, space, 1, PARKED_CALL_APPLICATION,
|
||||||
|
ast_str_buffer(arguments_string), registrar_pointer)) {
|
||||||
|
ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
|
||||||
|
lot_cfg->name, PARKED_CALL_APPLICATION, space, lot_cfg->parking_con);
|
||||||
|
ast_unlock_context(lot_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
find_info.stacklen = 0; /* reset for pbx_find_exten */
|
||||||
|
|
||||||
|
if (lot_cfg->parkaddhints) {
|
||||||
|
char hint_device[AST_MAX_EXTENSION];
|
||||||
|
|
||||||
|
snprintf(hint_device, sizeof(hint_device), "park:%s@%s", space, lot_cfg->parking_con);
|
||||||
|
|
||||||
|
if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, PRIORITY_HINT, NULL, NULL, E_MATCH))) {
|
||||||
|
ast_log(LOG_ERROR, "Parking lot '%s' -- Needs to add a hint '%s' at '%s@%s' but one already exists owned by %s\n",
|
||||||
|
lot_cfg->name, hint_device, space, lot_cfg->parking_con, ast_get_extension_registrar(existing_exten));
|
||||||
|
ast_unlock_context(lot_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parking_add_extension(lot_context, 0, space, PRIORITY_HINT, hint_device, "", registrar_pointer)) {
|
||||||
|
ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add hint '%s@%s' to the PBX.\n",
|
||||||
|
lot_cfg->name, space, lot_cfg->parking_con);
|
||||||
|
ast_unlock_context(lot_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_unlock_context(lot_context)) {
|
||||||
|
ast_assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg)
|
struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg)
|
||||||
{
|
{
|
||||||
struct parking_lot *lot;
|
struct parking_lot *lot;
|
||||||
@@ -688,6 +902,54 @@ static int verify_default_parking_lot(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void remove_pending_parking_lot_extensions(struct parking_config *cfg_pending)
|
||||||
|
{
|
||||||
|
struct parking_lot_cfg *lot_cfg;
|
||||||
|
struct ao2_iterator iter;
|
||||||
|
|
||||||
|
for (iter = ao2_iterator_init(cfg_pending->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
|
||||||
|
parking_lot_cfg_remove_extensions(lot_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ao2_iterator_destroy(&iter);
|
||||||
|
|
||||||
|
ast_context_destroy(NULL, BASE_REGISTRAR);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int configure_parking_extensions(void)
|
||||||
|
{
|
||||||
|
struct parking_config *cfg = aco_pending_config(&cfg_info);
|
||||||
|
struct ao2_iterator iter;
|
||||||
|
RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if (!cfg) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear existing extensions */
|
||||||
|
remove_all_configured_parking_lot_extensions();
|
||||||
|
|
||||||
|
/* Attempt to build new extensions for each lot */
|
||||||
|
for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
|
||||||
|
if (parking_lot_cfg_create_extensions(lot_cfg)) {
|
||||||
|
ao2_cleanup(lot_cfg);
|
||||||
|
lot_cfg = NULL;
|
||||||
|
res = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ao2_iterator_destroy(&iter);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
remove_pending_parking_lot_extensions(cfg);
|
||||||
|
ast_log(LOG_ERROR, "Extension registration failed. Previously configured lot extensions were removed and can not be safely restored.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static void mark_lots_as_disabled(void)
|
static void mark_lots_as_disabled(void)
|
||||||
{
|
{
|
||||||
struct ao2_iterator iter;
|
struct ao2_iterator iter;
|
||||||
@@ -708,7 +970,16 @@ static void mark_lots_as_disabled(void)
|
|||||||
static int config_parking_preapply(void)
|
static int config_parking_preapply(void)
|
||||||
{
|
{
|
||||||
mark_lots_as_disabled();
|
mark_lots_as_disabled();
|
||||||
return verify_default_parking_lot();
|
|
||||||
|
if (verify_default_parking_lot()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configure_parking_extensions()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disable_marked_lots(void)
|
static void disable_marked_lots(void)
|
||||||
@@ -797,12 +1068,14 @@ static int load_module(void)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO Dialplan generation for parking lots that set parkext */
|
if (load_parking_devstate()) {
|
||||||
/* TODO Generate hints for parking lots that set parkext and have hints enabled */
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
return AST_MODULE_LOAD_SUCCESS;
|
return AST_MODULE_LOAD_SUCCESS;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
ao2_cleanup(parking_lot_container);
|
||||||
aco_info_destroy(&cfg_info);
|
aco_info_destroy(&cfg_info);
|
||||||
return AST_MODULE_LOAD_DECLINE;
|
return AST_MODULE_LOAD_DECLINE;
|
||||||
}
|
}
|
||||||
@@ -818,10 +1091,17 @@ static int reload_module(void)
|
|||||||
|
|
||||||
static int unload_module(void)
|
static int unload_module(void)
|
||||||
{
|
{
|
||||||
/* XXX Parking is currently unloadable due to the fact that it loads features which could cause
|
/* XXX Parking is currently not unloadable due to the fact that it loads features which could cause
|
||||||
* significant problems if they disappeared while a channel still had access to them.
|
* significant problems if they disappeared while a channel still had access to them.
|
||||||
*/
|
*/
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/* TODO Things we will need to do here:
|
||||||
|
*
|
||||||
|
* destroy existing parking lots
|
||||||
|
* uninstall parking related bridge features
|
||||||
|
* remove extensions owned by the parking registrar
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
|
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
|
||||||
|
Reference in New Issue
Block a user