summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
authorJonathan Rose <jrose@digium.com>2013-06-07 16:07:18 +0000
committerJonathan Rose <jrose@digium.com>2013-06-07 16:07:18 +0000
commit8954661207d94e88ec18d9d9854c8a49d27b8ce9 (patch)
tree611309cf9b1ff3c9c46f3f61fa13246b0ee8d0f6 /res
parentbec2d7948419761c082c7965e847ae4961ee1e27 (diff)
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
Diffstat (limited to 'res')
-rw-r--r--res/parking/parking_bridge.c4
-rw-r--r--res/parking/parking_bridge_features.c86
-rw-r--r--res/parking/parking_controller.c3
-rw-r--r--res/parking/res_parking.h105
-rw-r--r--res/res_parking.c290
5 files changed, 449 insertions, 39 deletions
diff --git a/res/parking/parking_bridge.c b/res/parking/parking_bridge.c
index 31789d8d5..56e51fc9e 100644
--- a/res/parking/parking_bridge.c
+++ b/res/parking/parking_bridge.c
@@ -290,6 +290,8 @@ static int bridge_parking_push(struct ast_bridge_parking *self, struct ast_bridg
COLORIZE(COLOR_BRMAGENTA, 0, self->lot->name),
pu->parking_space);
+ parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_INUSE);
+
return 0;
}
@@ -328,6 +330,8 @@ static void bridge_parking_pull(struct ast_bridge_parking *self, struct ast_brid
}
ao2_unlock(pu);
+ parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_NOT_INUSE);
+
switch (pu->resolution) {
case PARK_UNSET:
/* This should be impossible now since the resolution is forcibly set to abandon if it was unset at this point. Resolution
diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c
index 6462dc527..8e5d739e1 100644
--- a/res/parking/parking_bridge_features.c
+++ b/res/parking/parking_bridge_features.c
@@ -367,6 +367,23 @@ static void parking_duration_cb_destroyer(void *hook_pvt)
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
* \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 ast_channel *chan = user->chan;
+ struct ast_context *park_dial_context;
char *peername;
+ char *peername_flat;
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
to deal with this, lock the parked user, check and set resolution. */
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);
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_FLAT", peername_flat);
+
+ /* 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);
+ }
- /* TODO Dialplan generation for park-dial extensions */
+abandon_extension_creation:
/* async_goto the proper PBX destination - this should happen when we come out of the bridge */
if (!ast_strlen_zero(user->comeback)) {
diff --git a/res/parking/parking_controller.c b/res/parking/parking_controller.c
index 03d7b8861..8f2433b29 100644
--- a/res/parking/parking_controller.c
+++ b/res/parking/parking_controller.c
@@ -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);
- /* 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_peername(peername);
diff --git a/res/parking/res_parking.h b/res/parking/res_parking.h
index 7f66d6e8f..cee93a6ce 100644
--- a/res/parking/res_parking.h
+++ b/res/parking/res_parking.h
@@ -30,6 +30,7 @@
#define DEFAULT_PARKING_LOT "default"
#define DEFAULT_PARKING_EXTEN "700"
+#define BASE_REGISTRAR "res_parking"
#define PARK_DIAL_CONTEXT "park-dial"
enum park_call_resolution {
@@ -75,6 +76,7 @@ struct parking_lot_cfg {
AST_DECLARE_STRING_FIELDS(
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(parkext); /*!< Analogous to parkext 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
* 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.
@@ -125,7 +127,7 @@ struct parked_user {
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
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Create a new parking bridge
*
* \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);
/*!
- * \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.
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Get an available parking space within a parking lot.
*
* \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);
/*!
- * \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.
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Apply features based on the parking lot feature options
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Set necessary bridge roles on a channel that is about to enter a parking lot
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \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.
*/
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
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Get a pointer to the parking lot container for purposes such as iteration
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Find a parking lot based on its name
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Find parking lot name from channel
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \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.
@@ -270,7 +272,7 @@ const char *find_channel_parking_lot_name(struct ast_channel *chan);
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
*
* \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);
/*!
- * \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.
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Publish a stasis parked call message for the channel indicating failure to park.
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \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
@@ -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);
/*!
- * \since 12
+ * \since 12.0.0
* \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
* 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
* 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);
/*!
- * \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
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Execution function for the parked call 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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Execution function for the park and retrieve 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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Register CLI commands
*
* \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);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Unregister CLI commands
*/
void unload_parking_ui(void);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Register manager actions and setup subscriptions for stasis events
*/
int load_parking_manager(void);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Unregister manager actions and remove subscriptions for stasis events
*/
void unload_parking_manager(void);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Register bridge features for parking
*
* \retval 0 on success
@@ -430,7 +465,19 @@ void unload_parking_manager(void);
int load_parking_bridge_features(void);
/*!
- * \since 12
+ * \since 12.0.0
* \brief Unregister features registered by load_parking_bridge_features
*/
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);
diff --git a/res/res_parking.c b/res/res_parking.c
index 72627f164..82037b66c 100644
--- a/res/res_parking.c
+++ b/res/res_parking.c
@@ -191,6 +191,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/features.h"
#include "asterisk/manager.h"
+#include "asterisk/pbx.h"
#define PARKED_CALL_APPLICATION "ParkedCall"
#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)
{
struct parking_lot_cfg *lot_cfg = obj;
-
+ parking_lot_cfg_remove_extensions(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;
}
+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 *lot;
@@ -688,6 +902,54 @@ static int verify_default_parking_lot(void)
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)
{
struct ao2_iterator iter;
@@ -708,7 +970,16 @@ static void mark_lots_as_disabled(void)
static int config_parking_preapply(void)
{
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)
@@ -797,12 +1068,14 @@ static int load_module(void)
goto error;
}
- /* TODO Dialplan generation for parking lots that set parkext */
- /* TODO Generate hints for parking lots that set parkext and have hints enabled */
+ if (load_parking_devstate()) {
+ goto error;
+ }
return AST_MODULE_LOAD_SUCCESS;
error:
+ ao2_cleanup(parking_lot_container);
aco_info_destroy(&cfg_info);
return AST_MODULE_LOAD_DECLINE;
}
@@ -818,10 +1091,17 @@ static int reload_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.
*/
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",