Move code from res_features into (new file) main/features.c
authorJason Parker <jparker@digium.com>
Wed, 23 Jan 2008 23:09:11 +0000 (23:09 +0000)
committerJason Parker <jparker@digium.com>
Wed, 23 Jan 2008 23:09:11 +0000 (23:09 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@100039 65c4cc65-6c06-0410-ace0-fbb531ad65f3

.cleancount
CHANGES
include/asterisk/_private.h
include/asterisk/features.h
main/Makefile
main/asterisk.c
main/features.c [moved from res/res_features.c with 98% similarity]
main/loader.c

index bb95160..a787364 100644 (file)
@@ -1 +1 @@
-33
+34
diff --git a/CHANGES b/CHANGES
index c373183..381dbcc 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -409,6 +409,8 @@ Call Features (res_features) Changes
   * Updated the ParkedCall application to allow you to not specify a parking
      extension.  If you don't specify a parking space to pick up, it will grab
      the first one available.
+  * Added cli command 'features reload' to reload call features from features.conf
+  * Moved into core asterisk binary.
 
 Language Support Changes
 ------------------------
@@ -498,4 +500,3 @@ Miscellaneous
      specifying which socket to use to connect to the running Asterisk daemon
      (-s)
   * Added logging to 'make update' command.  See update.log
-
index 06163cd..33ba835 100644 (file)
@@ -32,6 +32,7 @@ void ast_event_init(void);            /*!< Provided by event.c */
 int ast_device_state_engine_init(void);        /*!< Provided by devicestate.c */
 int astobj2_init(void);                        /*!< Provided by astobj2.c */
 int ast_file_init(void);               /*!< Provided by file.c */
+int ast_features_init(void);            /*!< Provided by features.c */
 
 /*!
  * \brief Reload asterisk modules.
index aa145a9..6e2ae1e 100644 (file)
@@ -109,4 +109,7 @@ struct ast_call_feature *ast_find_call_feature(const char *name);
 void ast_rdlock_call_features(void);
 void ast_unlock_call_features(void);
 
+/*! \brief Reload call features from features.conf */
+int ast_features_reload(void);
+
 #endif /* _AST_FEATURES_H */
index 3504b54..15d793f 100644 (file)
@@ -29,7 +29,8 @@ OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \
        netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
        cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
        strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
-       astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o
+       astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o \
+       features.o
 
 # we need to link in the objects statically, not as a library, because
 # otherwise modules will not have them available if none of the static
index 4c903cf..45bfc80 100644 (file)
@@ -96,6 +96,7 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #include "asterisk/network.h"
 #include "asterisk/cli.h"
 #include "asterisk/channel.h"
+#include "asterisk/features.h"
 #include "asterisk/ulaw.h"
 #include "asterisk/alaw.h"
 #include "asterisk/callerid.h"
@@ -3165,6 +3166,8 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       ast_features_init();
+
        if (init_framer()) {
                printf(term_quit());
                exit(1);
similarity index 98%
rename from res/res_features.c
rename to main/features.c
index c6f6812..c0c77c2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Copyright (C) 1999 - 2008, Digium, Inc.
  *
  * Mark Spencer <markster@digium.com>
  *
@@ -31,6 +31,8 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
+#include "asterisk/_private.h"
+
 #include <pthread.h>
 #include <sys/time.h>
 #include <sys/signal.h>
@@ -126,7 +128,7 @@ static unsigned int atxferdropcall;
 static unsigned int atxferloopdelay;
 static unsigned int atxfercallbackretries;
 
-static char *registrar = "res_features";                  /*!< Registrar for operations */
+static char *registrar = "features";              /*!< Registrar for operations */
 
 /* module and CLI command definitions */
 static char *synopsis = "Answer a parked call";
@@ -599,9 +601,6 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
        struct ast_channel *parker;
         struct ast_channel *parkee;
        int res = 0;
-       struct ast_module_user *u;
-
-       u = ast_module_user_add(chan);
 
        set_peers(&parker, &parkee, peer, chan, sense);
        /* Setup the exten/priority to be s/1 since we don't know
@@ -615,8 +614,6 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
        if (!res)
                res = ast_park_call(parkee, parker, 0, NULL);
 
-       ast_module_user_remove(u);
-
        if (!res) {
                if (sense == FEATURE_SENSE_CHAN)
                        res = AST_PBX_NO_HANGUP_PEER;
@@ -675,7 +672,7 @@ static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *pee
        
        if (callee_chan->monitor) {
                ast_verb(4, "User hit '%s' to stop recording call.\n", code);
-               ast_monitor_stop(callee_chan, 1);
+               callee_chan->monitor->stop(callee_chan, 1);
                return FEATURE_RETURN_SUCCESS;
        }
 
@@ -2511,733 +2508,758 @@ static int park_exec(struct ast_channel *chan, void *data)
        return res;
 }
 
-/*!
- * \brief CLI command to list configured features
- * \param e
- * \param cmd
- * \param a
- *
- * \retval CLI_SUCCESS on success.
- * \retval NULL when tab completion is used.
- */
-static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) {
-       int i;
-       struct ast_call_feature *feature;
-       char format[] = "%-25s %-7s %-7s\n";
-
-       switch (cmd) {
-       
-       case CLI_INIT:
-               e->command = "features show";
-               e->usage =
-                       "Usage: features show\n"
-                       "       Lists configured features\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-        }
-
-       ast_cli(a->fd, format, "Builtin Feature", "Default", "Current");
-       ast_cli(a->fd, format, "---------------", "-------", "-------");
-
-       ast_cli(a->fd, format, "Pickup", "*8", ast_pickup_ext());          /* default hardcoded above, so we'll hardcode it here */
-
-       ast_rwlock_rdlock(&features_lock);
-       for (i = 0; i < FEATURES_COUNT; i++)
-               ast_cli(a->fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
-       ast_rwlock_unlock(&features_lock);
-
-       ast_cli(a->fd, "\n");
-       ast_cli(a->fd, format, "Dynamic Feature", "Default", "Current");
-       ast_cli(a->fd, format, "---------------", "-------", "-------");
-       if (AST_LIST_EMPTY(&feature_list))
-               ast_cli(a->fd, "(none)\n");
-       else {
-               AST_LIST_LOCK(&feature_list);
-               AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
-                       ast_cli(a->fd, format, feature->sname, "no def", feature->exten);
-               AST_LIST_UNLOCK(&feature_list);
-       }
-       ast_cli(a->fd, "\nCall parking\n");
-       ast_cli(a->fd, "------------\n");
-       ast_cli(a->fd,"%-20s:      %s\n", "Parking extension", parking_ext);
-       ast_cli(a->fd,"%-20s:      %s\n", "Parking context", parking_con);
-       ast_cli(a->fd,"%-20s:      %d-%d\n", "Parked call extensions", parking_start, parking_stop);
-       ast_cli(a->fd,"\n");
-
-       return CLI_SUCCESS;
-}
-
-static char mandescr_bridge[] =
-"Description: Bridge together two channels already in the PBX\n"
-"Variables: ( Headers marked with * are required )\n"
-"   *Channel1: Channel to Bridge to Channel2\n"
-"   *Channel2: Channel to Bridge to Channel1\n"
-"        Tone: (Yes|No) Play courtesy tone to Channel 2\n"
-"\n";
-
-/*!
- * \brief Actual bridge
- * \param chan
- * \param tmpchan
- * 
- * Stop hold music, lock both channels, masq channels,
- * after bridge return channel to next priority.
-*/
-static void do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *tmpchan)
-{
-       ast_moh_stop(chan);
-       ast_channel_lock(chan);
-       ast_setstate(tmpchan, chan->_state);
-       tmpchan->readformat = chan->readformat;
-       tmpchan->writeformat = chan->writeformat;
-       ast_channel_masquerade(tmpchan, chan);
-       ast_channel_lock(tmpchan);
-       ast_do_masquerade(tmpchan);
-       /* when returning from bridge, the channel will continue at the next priority */
-       ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1);
-       ast_channel_unlock(tmpchan);
-       ast_channel_unlock(chan);
-}
-
-/*!
- * \brief Bridge channels together
- * \param s
- * \param m
- * 
- * Make sure valid channels were specified, 
- * send errors if any of the channels could not be found/locked, answer channels if needed,
- * create the placeholder channels and grab the other channels 
- * make the channels compatible, send error if we fail doing so 
- * setup the bridge thread object and start the bridge.
- * 
- * \retval 0 on success or on incorrect use.
- * \retval 1 on failure to bridge channels.
+/*! 
+ * \brief Add parking hints for all defined parking lots 
+ * \param context
+ * \param start starting parkinglot number
+ * \param stop ending parkinglot number
 */
-static int action_bridge(struct mansession *s, const struct message *m)
+static void park_add_hints(char *context, int start, int stop)
 {
-       const char *channela = astman_get_header(m, "Channel1");
-       const char *channelb = astman_get_header(m, "Channel2");
-       const char *playtone = astman_get_header(m, "Tone");
-       struct ast_channel *chana = NULL, *chanb = NULL;
-       struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
-       struct ast_bridge_thread_obj *tobj = NULL;
-
-       /* make sure valid channels were specified */
-       if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) {
-               chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela));
-               chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb));
-               if (chana)
-                       ast_channel_unlock(chana);
-               if (chanb)
-                       ast_channel_unlock(chanb);
+       int numext;
+       char device[AST_MAX_EXTENSION];
+       char exten[10];
 
-               /* send errors if any of the channels could not be found/locked */
-               if (!chana) {
-                       char buf[256];
-                       snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
-                       astman_send_error(s, m, buf);
-                       return 0;
-               }
-               if (!chanb) {
-                       char buf[256];
-                       snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
-                       astman_send_error(s, m, buf);
-                       return 0;
-               }
-       } else {
-               astman_send_error(s, m, "Missing channel parameter in request");
-               return 0;
+       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);
        }
+}
 
-       /* Answer the channels if needed */
-       if (chana->_state != AST_STATE_UP)
-               ast_answer(chana);
-       if (chanb->_state != AST_STATE_UP)
-               ast_answer(chanb);
-
-       /* create the placeholder channels and grab the other channels */
-       if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
-               NULL, NULL, 0, "Bridge/%s", chana->name))) {
-               astman_send_error(s, m, "Unable to create temporary channel!");
-               return 1;
-       }
+static int load_config(void) 
+{
+       int start = 0, end = 0;
+       int res;
+       int i;
+       struct ast_context *con = NULL;
+       struct ast_config *cfg = NULL;
+       struct ast_variable *var = NULL;
+       struct feature_group *fg = NULL;
+       struct ast_flags config_flags = { 0 };
+       char old_parking_ext[AST_MAX_EXTENSION];
+       char old_parking_con[AST_MAX_EXTENSION] = "";
+       char *ctg; 
+       static const char *categories[] = { 
+               /* Categories in features.conf that are not
+                * to be parsed as group categories
+                */
+               "general",
+               "featuremap",
+               "applicationmap"
+       };
 
-       if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
-               NULL, NULL, 0, "Bridge/%s", chanb->name))) {
-               astman_send_error(s, m, "Unable to create temporary channels!");
-               ast_channel_free(tmpchana);
-               return 1;
-       }
+       if (!ast_strlen_zero(parking_con)) {
+               strcpy(old_parking_ext, parking_ext);
+               strcpy(old_parking_con, parking_con);
+       } 
 
-       do_bridge_masquerade(chana, tmpchana);
-       do_bridge_masquerade(chanb, tmpchanb);
-       
-       /* make the channels compatible, send error if we fail doing so */
-       if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
-               ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name);
-               astman_send_error(s, m, "Could not make channels compatible for manager bridge");
-               ast_hangup(tmpchana);
-               ast_hangup(tmpchanb);
-               return 1;
-       }
+       /* Reset to defaults */
+       strcpy(parking_con, "parkedcalls");
+       strcpy(parking_con_dial, "park-dial");
+       strcpy(parking_ext, "700");
+       strcpy(pickup_ext, "*8");
+       strcpy(parkmohclass, "default");
+       courtesytone[0] = '\0';
+       strcpy(xfersound, "beep");
+       strcpy(xferfailsound, "pbx-invalid");
+       parking_start = 701;
+       parking_stop = 750;
+       parkfindnext = 0;
+       adsipark = 0;
+       comebacktoorigin = 1;
+       parkaddhints = 0;
+       parkedcalltransfers = 0;
+       parkedcallreparking = 0;
 
-       /* setup the bridge thread object and start the bridge */
-       if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
-               ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno));
-               astman_send_error(s, m, "Unable to spawn a new bridge thread");
-               ast_hangup(tmpchana);
-               ast_hangup(tmpchanb);
-               return 1;
-       }
+       transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
+       featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
+       atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
+       atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
+       atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
+       atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
 
-       tobj->chan = tmpchana;
-       tobj->peer = tmpchanb;
-       tobj->return_to_pbx = 1;
-       
-       if (ast_true(playtone)) {
-               if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) {
-                       if (ast_waitstream(tmpchanb, "") < 0)
-                               ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", tmpchanb->name);
-               }
+       cfg = ast_config_load("features.conf", config_flags);
+       if (!cfg) {
+               ast_log(LOG_WARNING,"Could not load features.conf\n");
+               return 0;
        }
-
-       ast_bridge_call_thread_launch(tobj);
-
-       astman_send_ack(s, m, "Launched bridge thread with success");
-
-       return 0;
-}
-
+       for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+               if (!strcasecmp(var->name, "parkext")) {
+                       ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
+               } else if (!strcasecmp(var->name, "context")) {
+                       ast_copy_string(parking_con, var->value, sizeof(parking_con));
+               } else if (!strcasecmp(var->name, "parkingtime")) {
+                       if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
+                               ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
+                               parkingtime = DEFAULT_PARK_TIME;
+                       } else
+                               parkingtime = parkingtime * 1000;
+               } else if (!strcasecmp(var->name, "parkpos")) {
+                       if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
+                               ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno);
+                       } else {
+                               parking_start = start;
+                               parking_stop = end;
+                       }
+               } else if (!strcasecmp(var->name, "findslot")) {
+                       parkfindnext = (!strcasecmp(var->value, "next"));
+               } else if (!strcasecmp(var->name, "parkinghints")) {
+                       parkaddhints = ast_true(var->value);
+               } else if (!strcasecmp(var->name, "parkedcalltransfers")) {
+                       if (!strcasecmp(var->value, "both"))
+                               parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
+                       else if (!strcasecmp(var->value, "caller"))
+                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
+                       else if (!strcasecmp(var->value, "callee"))
+                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
+               } else if (!strcasecmp(var->name, "parkedcallreparking")) {
+                       if (!strcasecmp(var->value, "both"))
+                               parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
+                       else if (!strcasecmp(var->value, "caller"))
+                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
+                       else if (!strcasecmp(var->value, "callee"))
+                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
+               } else if (!strcasecmp(var->name, "adsipark")) {
+                       adsipark = ast_true(var->value);
+               } else if (!strcasecmp(var->name, "transferdigittimeout")) {
+                       if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
+                               ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
+                               transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
+                       } else
+                               transferdigittimeout = transferdigittimeout * 1000;
+               } else if (!strcasecmp(var->name, "featuredigittimeout")) {
+                       if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
+                               ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
+                               featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
+                       }
+               } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
+                       if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
+                               ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
+                               atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
+                       } else
+                               atxfernoanswertimeout = atxfernoanswertimeout * 1000;
+               } else if (!strcasecmp(var->name, "atxferloopdelay")) {
+                       if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
+                               ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
+                               atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
+                       } else 
+                               atxferloopdelay *= 1000;
+               } else if (!strcasecmp(var->name, "atxferdropcall")) {
+                       atxferdropcall = ast_true(var->value);
+               } else if (!strcasecmp(var->name, "atxfercallbackretries")) {
+                       if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
+                               ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
+                               atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
+                       }
+               } else if (!strcasecmp(var->name, "courtesytone")) {
+                       ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
+               }  else if (!strcasecmp(var->name, "parkedplay")) {
+                       if (!strcasecmp(var->value, "both"))
+                               parkedplay = 2;
+                       else if (!strcasecmp(var->value, "parked"))
+                               parkedplay = 1;
+                       else
+                               parkedplay = 0;
+               } else if (!strcasecmp(var->name, "xfersound")) {
+                       ast_copy_string(xfersound, var->value, sizeof(xfersound));
+               } else if (!strcasecmp(var->name, "xferfailsound")) {
+                       ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
+               } else if (!strcasecmp(var->name, "pickupexten")) {
+                       ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
+               } else if (!strcasecmp(var->name, "comebacktoorigin")) {
+                       comebacktoorigin = ast_true(var->value);
+               } else if (!strcasecmp(var->name, "parkedmusicclass")) {
+                       ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
+               }
+       }
+
+       unmap_features();
+       for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
+               if (remap_feature(var->name, var->value))
+                       ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
+       }
+
+       /* Map a key combination to an application*/
+       ast_unregister_features();
+       for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
+               char *tmp_val = ast_strdupa(var->value);
+               char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 
+               struct ast_call_feature *feature;
+
+               /* strsep() sets the argument to NULL if match not found, and it
+                * is safe to use it with a NULL argument, so we don't check
+                * between calls.
+                */
+               exten = strsep(&tmp_val,",");
+               activatedby = strsep(&tmp_val,",");
+               app = strsep(&tmp_val,",");
+               app_args = strsep(&tmp_val,",");
+               moh_class = strsep(&tmp_val,",");
+
+               activateon = strsep(&activatedby, "/"); 
+
+               /*! \todo XXX var_name or app_args ? */
+               if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
+                       ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
+                               app, exten, activateon, var->name);
+                       continue;
+               }
+
+               AST_LIST_LOCK(&feature_list);
+               if ((feature = find_dynamic_feature(var->name))) {
+                       AST_LIST_UNLOCK(&feature_list);
+                       ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
+                       continue;
+               }
+               AST_LIST_UNLOCK(&feature_list);
+                               
+               if (!(feature = ast_calloc(1, sizeof(*feature))))
+                       continue;                                       
+
+               ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
+               ast_copy_string(feature->app, app, FEATURE_APP_LEN);
+               ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
+               
+               if (app_args) 
+                       ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
+
+               if (moh_class)
+                       ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
+                       
+               ast_copy_string(feature->exten, exten, sizeof(feature->exten));
+               feature->operation = feature_exec_app;
+               ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
+
+               /* Allow caller and calle to be specified for backwards compatability */
+               if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
+                       ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
+               else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
+                       ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
+               else {
+                       ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
+                               " must be 'self', or 'peer'\n", var->name);
+                       continue;
+               }
+
+               if (ast_strlen_zero(activatedby))
+                       ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
+               else if (!strcasecmp(activatedby, "caller"))
+                       ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
+               else if (!strcasecmp(activatedby, "callee"))
+                       ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
+               else if (!strcasecmp(activatedby, "both"))
+                       ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
+               else {
+                       ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
+                               " must be 'caller', or 'callee', or 'both'\n", var->name);
+                       continue;
+               }
+
+               ast_register_feature(feature);
+                       
+               ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);
+       }
+
+       ast_unregister_groups();
+       AST_RWLIST_WRLOCK(&feature_groups);
+
+       ctg = NULL;
+       while ((ctg = ast_category_browse(cfg, ctg))) {
+               for (i = 0; i < ARRAY_LEN(categories); i++) {
+                       if (!strcasecmp(categories[i], ctg))
+                               break;
+               }
+
+               if (i < ARRAY_LEN(categories)) 
+                       continue;
+
+               if (!(fg = register_group(ctg)))
+                       continue;
+
+               for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
+                       struct ast_call_feature *feature;
+
+                       AST_LIST_LOCK(&feature_list);
+                       if(!(feature = find_dynamic_feature(var->name)) && 
+                          !(feature = ast_find_call_feature(var->name))) {
+                               AST_LIST_UNLOCK(&feature_list);
+                               ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
+                               continue;
+                       }
+                       AST_LIST_UNLOCK(&feature_list);
+
+                       register_group_feature(fg, var->value, feature);
+               }
+       }
+
+       AST_RWLIST_UNLOCK(&feature_groups);
+
+       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))
+                               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(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
+               ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
+               return -1;
+       }
+       res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
+       if (parkaddhints)
+               park_add_hints(parking_con, parking_start, parking_stop);
+       if (!res)
+               notify_metermaids(ast_parking_ext(), parking_con, AST_DEVICE_INUSE);
+       return res;
+
+}
+
 /*!
- * \brief CLI command to list parked calls
- * \param e 
+ * \brief CLI command to list configured features
+ * \param e
  * \param cmd
  * \param a
- *  
- * Check right usage, lock parking lot, display parked calls, unlock parking lot list.
+ *
  * \retval CLI_SUCCESS on success.
- * \retval CLI_SHOWUSAGE on incorrect number of arguments.
  * \retval NULL when tab completion is used.
-*/
-static char *handle_parkedcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+ */
+static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       struct parkeduser *cur;
-       int numparked = 0;
+       int i;
+       struct ast_call_feature *feature;
+       char format[] = "%-25s %-7s %-7s\n";
 
        switch (cmd) {
+       
        case CLI_INIT:
-               e->command = "parkedcalls show";
+               e->command = "features show";
                e->usage =
-                       "Usage: parkedcalls show\n"
-                       "       List currently parked calls\n";
+                       "Usage: features show\n"
+                       "       Lists configured features\n";
                return NULL;
        case CLI_GENERATE:
                return NULL;
-       }
+        }
 
-       if (a->argc > e->args)
-               return CLI_SHOWUSAGE;
+       ast_cli(a->fd, format, "Builtin Feature", "Default", "Current");
+       ast_cli(a->fd, format, "---------------", "-------", "-------");
 
-       ast_cli(a->fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
-               , "Context", "Extension", "Pri", "Timeout");
+       ast_cli(a->fd, format, "Pickup", "*8", ast_pickup_ext());          /* default hardcoded above, so we'll hardcode it here */
 
-       AST_LIST_LOCK(&parkinglot);
-       AST_LIST_TRAVERSE(&parkinglot, cur, list) {
-               ast_cli(a->fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
-                       ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
-                       ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
+       ast_rwlock_rdlock(&features_lock);
+       for (i = 0; i < FEATURES_COUNT; i++)
+               ast_cli(a->fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
+       ast_rwlock_unlock(&features_lock);
 
-               numparked++;
+       ast_cli(a->fd, "\n");
+       ast_cli(a->fd, format, "Dynamic Feature", "Default", "Current");
+       ast_cli(a->fd, format, "---------------", "-------", "-------");
+       if (AST_LIST_EMPTY(&feature_list))
+               ast_cli(a->fd, "(none)\n");
+       else {
+               AST_LIST_LOCK(&feature_list);
+               AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
+                       ast_cli(a->fd, format, feature->sname, "no def", feature->exten);
+               AST_LIST_UNLOCK(&feature_list);
        }
-       AST_LIST_UNLOCK(&parkinglot);
-       ast_cli(a->fd, "%d parked call%s.\n", numparked, ESS(numparked));
-
+       ast_cli(a->fd, "\nCall parking\n");
+       ast_cli(a->fd, "------------\n");
+       ast_cli(a->fd,"%-20s:      %s\n", "Parking extension", parking_ext);
+       ast_cli(a->fd,"%-20s:      %s\n", "Parking context", parking_con);
+       ast_cli(a->fd,"%-20s:      %d-%d\n", "Parked call extensions", parking_start, parking_stop);
+       ast_cli(a->fd,"\n");
 
        return CLI_SUCCESS;
 }
 
-static char *handle_parkedcalls_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+int ast_features_reload(void)
 {
-       char *res = handle_parkedcalls(e, cmd, a);
-       if (cmd == CLI_INIT)
-               e->command = "show parkedcalls";
-       return res;
+       load_config();
+
+       return RESULT_SUCCESS;
 }
 
-static struct ast_cli_entry cli_show_parkedcalls_deprecated = AST_CLI_DEFINE(handle_parkedcalls_deprecated, "List currently parked calls.");
+static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       switch (cmd) {  
+       case CLI_INIT:
+               e->command = "features reload";
+               e->usage =
+                       "Usage: features reload\n"
+                       "       Reloads configured call features from features.conf\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+        }
+       load_config();
 
-static struct ast_cli_entry cli_features[] = {
-       AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
-       AST_CLI_DEFINE(handle_parkedcalls, "List currently parked calls", .deprecate_cmd = &cli_show_parkedcalls_deprecated),
-};
+       return CLI_SUCCESS;
+}
 
-/*! 
- * \brief Dump parking lot status
- * \param s
- * \param m
+static char mandescr_bridge[] =
+"Description: Bridge together two channels already in the PBX\n"
+"Variables: ( Headers marked with * are required )\n"
+"   *Channel1: Channel to Bridge to Channel2\n"
+"   *Channel2: Channel to Bridge to Channel1\n"
+"        Tone: (Yes|No) Play courtesy tone to Channel 2\n"
+"\n";
+
+/*!
+ * \brief Actual bridge
+ * \param chan
+ * \param tmpchan
  * 
- * Lock parking lot, iterate list and append parked calls status, unlock parking lot.
- * \return Always RESULT_SUCCESS 
+ * Stop hold music, lock both channels, masq channels,
+ * after bridge return channel to next priority.
 */
-static int manager_parking_status(struct mansession *s, const struct message *m)
+static void do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *tmpchan)
 {
-       struct parkeduser *cur;
-       const char *id = astman_get_header(m, "ActionID");
-       char idText[256] = "";
-
-       if (!ast_strlen_zero(id))
-               snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
-
-       astman_send_ack(s, m, "Parked calls will follow");
-
-       AST_LIST_LOCK(&parkinglot);
-
-       AST_LIST_TRAVERSE(&parkinglot, cur, list) {
-               astman_append(s, "Event: ParkedCall\r\n"
-                       "Exten: %d\r\n"
-                       "Channel: %s\r\n"
-                       "From: %s\r\n"
-                       "Timeout: %ld\r\n"
-                       "CallerIDNum: %s\r\n"
-                       "CallerIDName: %s\r\n"
-                       "%s"
-                       "\r\n",
-                       cur->parkingnum, cur->chan->name, cur->peername,
-                       (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
-                       S_OR(cur->chan->cid.cid_num, ""),       /* XXX in other places it is <unknown> */
-                       S_OR(cur->chan->cid.cid_name, ""),
-                       idText);
-       }
-
-       astman_append(s,
-               "Event: ParkedCallsComplete\r\n"
-               "%s"
-               "\r\n",idText);
-
-       AST_LIST_UNLOCK(&parkinglot);
-
-       return RESULT_SUCCESS;
+       ast_moh_stop(chan);
+       ast_channel_lock(chan);
+       ast_setstate(tmpchan, chan->_state);
+       tmpchan->readformat = chan->readformat;
+       tmpchan->writeformat = chan->writeformat;
+       ast_channel_masquerade(tmpchan, chan);
+       ast_channel_lock(tmpchan);
+       ast_do_masquerade(tmpchan);
+       /* when returning from bridge, the channel will continue at the next priority */
+       ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1);
+       ast_channel_unlock(tmpchan);
+       ast_channel_unlock(chan);
 }
 
-static char mandescr_park[] =
-"Description: Park a channel.\n"
-"Variables: (Names marked with * are required)\n"
-"      *Channel: Channel name to park\n"
-"      *Channel2: Channel to announce park info to (and return to if timeout)\n"
-"      Timeout: Number of milliseconds to wait before callback.\n";  
-
 /*!
- * \brief Create manager event for parked calls
+ * \brief Bridge channels together
  * \param s
  * \param m
- *
- * Get channels involved in park, create event.
- * \return Always 0
+ * 
+ * Make sure valid channels were specified, 
+ * send errors if any of the channels could not be found/locked, answer channels if needed,
+ * create the placeholder channels and grab the other channels 
+ * make the channels compatible, send error if we fail doing so 
+ * setup the bridge thread object and start the bridge.
+ * 
+ * \retval 0 on success or on incorrect use.
+ * \retval 1 on failure to bridge channels.
 */
-static int manager_park(struct mansession *s, const struct message *m)
+static int action_bridge(struct mansession *s, const struct message *m)
 {
-       const char *channel = astman_get_header(m, "Channel");
-       const char *channel2 = astman_get_header(m, "Channel2");
-       const char *timeout = astman_get_header(m, "Timeout");
-       char buf[BUFSIZ];
-       int to = 0;
-       int res = 0;
-       int parkExt = 0;
-       struct ast_channel *ch1, *ch2;
+       const char *channela = astman_get_header(m, "Channel1");
+       const char *channelb = astman_get_header(m, "Channel2");
+       const char *playtone = astman_get_header(m, "Tone");
+       struct ast_channel *chana = NULL, *chanb = NULL;
+       struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
+       struct ast_bridge_thread_obj *tobj = NULL;
 
-       if (ast_strlen_zero(channel)) {
-               astman_send_error(s, m, "Channel not specified");
+       /* make sure valid channels were specified */
+       if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) {
+               chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela));
+               chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb));
+               if (chana)
+                       ast_channel_unlock(chana);
+               if (chanb)
+                       ast_channel_unlock(chanb);
+
+               /* send errors if any of the channels could not be found/locked */
+               if (!chana) {
+                       char buf[256];
+                       snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
+                       astman_send_error(s, m, buf);
+                       return 0;
+               }
+               if (!chanb) {
+                       char buf[256];
+                       snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
+                       astman_send_error(s, m, buf);
+                       return 0;
+               }
+       } else {
+               astman_send_error(s, m, "Missing channel parameter in request");
                return 0;
        }
 
-       if (ast_strlen_zero(channel2)) {
-               astman_send_error(s, m, "Channel2 not specified");
-               return 0;
+       /* Answer the channels if needed */
+       if (chana->_state != AST_STATE_UP)
+               ast_answer(chana);
+       if (chanb->_state != AST_STATE_UP)
+               ast_answer(chanb);
+
+       /* create the placeholder channels and grab the other channels */
+       if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
+               NULL, NULL, 0, "Bridge/%s", chana->name))) {
+               astman_send_error(s, m, "Unable to create temporary channel!");
+               return 1;
        }
 
-       ch1 = ast_get_channel_by_name_locked(channel);
-       if (!ch1) {
-               snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
-               astman_send_error(s, m, buf);
-               return 0;
+       if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
+               NULL, NULL, 0, "Bridge/%s", chanb->name))) {
+               astman_send_error(s, m, "Unable to create temporary channels!");
+               ast_channel_free(tmpchana);
+               return 1;
        }
 
-       ch2 = ast_get_channel_by_name_locked(channel2);
-       if (!ch2) {
-               snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
-               astman_send_error(s, m, buf);
-               ast_channel_unlock(ch1);
-               return 0;
+       do_bridge_masquerade(chana, tmpchana);
+       do_bridge_masquerade(chanb, tmpchanb);
+       
+       /* make the channels compatible, send error if we fail doing so */
+       if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
+               ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name);
+               astman_send_error(s, m, "Could not make channels compatible for manager bridge");
+               ast_hangup(tmpchana);
+               ast_hangup(tmpchanb);
+               return 1;
        }
 
-       if (!ast_strlen_zero(timeout)) {
-               sscanf(timeout, "%d", &to);
+       /* setup the bridge thread object and start the bridge */
+       if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
+               ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno));
+               astman_send_error(s, m, "Unable to spawn a new bridge thread");
+               ast_hangup(tmpchana);
+               ast_hangup(tmpchanb);
+               return 1;
        }
 
-       res = ast_masq_park_call(ch1, ch2, to, &parkExt);
-       if (!res) {
-               ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
-               astman_send_ack(s, m, "Park successful");
-       } else {
-               astman_send_error(s, m, "Park failure");
+       tobj->chan = tmpchana;
+       tobj->peer = tmpchanb;
+       tobj->return_to_pbx = 1;
+       
+       if (ast_true(playtone)) {
+               if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) {
+                       if (ast_waitstream(tmpchanb, "") < 0)
+                               ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", tmpchanb->name);
+               }
        }
 
-       ast_channel_unlock(ch1);
-       ast_channel_unlock(ch2);
+       ast_bridge_call_thread_launch(tobj);
+
+       astman_send_ack(s, m, "Launched bridge thread with success");
 
        return 0;
 }
 
 /*!
- * \brief Pickup a call
- * \param chan channel that initiated pickup.
- *
- * Walk list of channels, checking it is not itself, channel is pbx one,
- * check that the callgroup for both channels are the same and the channel is ringing.
- * Answer calling channel, flag channel as answered on queue, masq channels together.
-*/
-int ast_pickup_call(struct ast_channel *chan)
-{
-       struct ast_channel *cur = NULL;
-       int res = -1;
-
-       while ((cur = ast_channel_walk_locked(cur)) != NULL) {
-               if (!cur->pbx && 
-                       (cur != chan) &&
-                       (chan->pickupgroup & cur->callgroup) &&
-                       ((cur->_state == AST_STATE_RINGING) ||
-                        (cur->_state == AST_STATE_RING))) {
-                               break;
-               }
-               ast_channel_unlock(cur);
-       }
-       if (cur) {
-               ast_debug(1, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
-               res = ast_answer(chan);
-               if (res)
-                       ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
-               res = ast_queue_control(chan, AST_CONTROL_ANSWER);
-               if (res)
-                       ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
-               res = ast_channel_masquerade(cur, chan);
-               if (res)
-                       ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);           /* Done */
-               ast_channel_unlock(cur);
-       } else  {
-               ast_debug(1, "No call pickup possible...\n");
-       }
-       return res;
-}
-
-/*! 
- * \brief Add parking hints for all defined parking lots 
- * \param context
- * \param start starting parkinglot number
- * \param stop ending parkinglot number
+ * \brief CLI command to list parked calls
+ * \param e 
+ * \param cmd
+ * \param a
+ *  
+ * Check right usage, lock parking lot, display parked calls, unlock parking lot list.
+ * \retval CLI_SUCCESS on success.
+ * \retval CLI_SHOWUSAGE on incorrect number of arguments.
+ * \retval NULL when tab completion is used.
 */
-static void park_add_hints(char *context, int start, int stop)
+static char *handle_parkedcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       int numext;
-       char device[AST_MAX_EXTENSION];
-       char exten[10];
+       struct parkeduser *cur;
+       int numparked = 0;
 
-       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);
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "parkedcalls show";
+               e->usage =
+                       "Usage: parkedcalls show\n"
+                       "       List currently parked calls\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
        }
-}
-
-
-static int load_config(void) 
-{
-       int start = 0, end = 0;
-       int res;
-       int i;
-       struct ast_context *con = NULL;
-       struct ast_config *cfg = NULL;
-       struct ast_variable *var = NULL;
-       struct feature_group *fg = NULL;
-       struct ast_flags config_flags = { 0 };
-       char old_parking_ext[AST_MAX_EXTENSION];
-       char old_parking_con[AST_MAX_EXTENSION] = "";
-       char *ctg; 
-       static const char *categories[] = { 
-               /* Categories in features.conf that are not
-                * to be parsed as group categories
-                */
-               "general",
-               "featuremap",
-               "applicationmap"
-       };
-
-       if (!ast_strlen_zero(parking_con)) {
-               strcpy(old_parking_ext, parking_ext);
-               strcpy(old_parking_con, parking_con);
-       } 
 
-       /* Reset to defaults */
-       strcpy(parking_con, "parkedcalls");
-       strcpy(parking_con_dial, "park-dial");
-       strcpy(parking_ext, "700");
-       strcpy(pickup_ext, "*8");
-       strcpy(parkmohclass, "default");
-       courtesytone[0] = '\0';
-       strcpy(xfersound, "beep");
-       strcpy(xferfailsound, "pbx-invalid");
-       parking_start = 701;
-       parking_stop = 750;
-       parkfindnext = 0;
-       adsipark = 0;
-       comebacktoorigin = 1;
-       parkaddhints = 0;
-       parkedcalltransfers = 0;
-       parkedcallreparking = 0;
+       if (a->argc > e->args)
+               return CLI_SHOWUSAGE;
 
-       transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
-       featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
-       atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
-       atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
-       atxferdropcall = DEFAULT_ATXFER_DROP_CALL;
-       atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
+       ast_cli(a->fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
+               , "Context", "Extension", "Pri", "Timeout");
 
-       cfg = ast_config_load("features.conf", config_flags);
-       if (!cfg) {
-               ast_log(LOG_WARNING,"Could not load features.conf\n");
-               return AST_MODULE_LOAD_DECLINE;
-       }
-       for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
-               if (!strcasecmp(var->name, "parkext")) {
-                       ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
-               } else if (!strcasecmp(var->name, "context")) {
-                       ast_copy_string(parking_con, var->value, sizeof(parking_con));
-               } else if (!strcasecmp(var->name, "parkingtime")) {
-                       if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
-                               ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
-                               parkingtime = DEFAULT_PARK_TIME;
-                       } else
-                               parkingtime = parkingtime * 1000;
-               } else if (!strcasecmp(var->name, "parkpos")) {
-                       if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
-                               ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno);
-                       } else {
-                               parking_start = start;
-                               parking_stop = end;
-                       }
-               } else if (!strcasecmp(var->name, "findslot")) {
-                       parkfindnext = (!strcasecmp(var->value, "next"));
-               } else if (!strcasecmp(var->name, "parkinghints")) {
-                       parkaddhints = ast_true(var->value);
-               } else if (!strcasecmp(var->name, "parkedcalltransfers")) {
-                       if (!strcasecmp(var->value, "both"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
-                       else if (!strcasecmp(var->value, "caller"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
-                       else if (!strcasecmp(var->value, "callee"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
-               } else if (!strcasecmp(var->name, "parkedcallreparking")) {
-                       if (!strcasecmp(var->value, "both"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
-                       else if (!strcasecmp(var->value, "caller"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
-                       else if (!strcasecmp(var->value, "callee"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
-               } else if (!strcasecmp(var->name, "adsipark")) {
-                       adsipark = ast_true(var->value);
-               } else if (!strcasecmp(var->name, "transferdigittimeout")) {
-                       if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
-                               ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
-                               transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
-                       } else
-                               transferdigittimeout = transferdigittimeout * 1000;
-               } else if (!strcasecmp(var->name, "featuredigittimeout")) {
-                       if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
-                               ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
-                               featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
-                       }
-               } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
-                       if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
-                               ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
-                               atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
-                       } else
-                               atxfernoanswertimeout = atxfernoanswertimeout * 1000;
-               } else if (!strcasecmp(var->name, "atxferloopdelay")) {
-                       if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
-                               ast_log(LOG_WARNING, "%s is not a valid atxferloopdelay\n", var->value);
-                               atxferloopdelay = DEFAULT_ATXFER_LOOP_DELAY;
-                       } else 
-                               atxferloopdelay *= 1000;
-               } else if (!strcasecmp(var->name, "atxferdropcall")) {
-                       atxferdropcall = ast_true(var->value);
-               } else if (!strcasecmp(var->name, "atxfercallbackretries")) {
-                       if ((sscanf(var->value, "%u", &atxferloopdelay) != 1)) {
-                               ast_log(LOG_WARNING, "%s is not a valid atxfercallbackretries\n", var->value);
-                               atxfercallbackretries = DEFAULT_ATXFER_CALLBACK_RETRIES;
-                       }
-               } else if (!strcasecmp(var->name, "courtesytone")) {
-                       ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
-               }  else if (!strcasecmp(var->name, "parkedplay")) {
-                       if (!strcasecmp(var->value, "both"))
-                               parkedplay = 2;
-                       else if (!strcasecmp(var->value, "parked"))
-                               parkedplay = 1;
-                       else
-                               parkedplay = 0;
-               } else if (!strcasecmp(var->name, "xfersound")) {
-                       ast_copy_string(xfersound, var->value, sizeof(xfersound));
-               } else if (!strcasecmp(var->name, "xferfailsound")) {
-                       ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
-               } else if (!strcasecmp(var->name, "pickupexten")) {
-                       ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
-               } else if (!strcasecmp(var->name, "comebacktoorigin")) {
-                       comebacktoorigin = ast_true(var->value);
-               } else if (!strcasecmp(var->name, "parkedmusicclass")) {
-                       ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
-               }
-       }
+       AST_LIST_LOCK(&parkinglot);
+       AST_LIST_TRAVERSE(&parkinglot, cur, list) {
+               ast_cli(a->fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
+                       ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
+                       ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
 
-       unmap_features();
-       for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
-               if (remap_feature(var->name, var->value))
-                       ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
+               numparked++;
        }
+       AST_LIST_UNLOCK(&parkinglot);
+       ast_cli(a->fd, "%d parked call%s.\n", numparked, ESS(numparked));
 
-       /* Map a key combination to an application*/
-       ast_unregister_features();
-       for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
-               char *tmp_val = ast_strdupa(var->value);
-               char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 
-               struct ast_call_feature *feature;
 
-               /* strsep() sets the argument to NULL if match not found, and it
-                * is safe to use it with a NULL argument, so we don't check
-                * between calls.
-                */
-               exten = strsep(&tmp_val,",");
-               activatedby = strsep(&tmp_val,",");
-               app = strsep(&tmp_val,",");
-               app_args = strsep(&tmp_val,",");
-               moh_class = strsep(&tmp_val,",");
+       return CLI_SUCCESS;
+}
 
-               activateon = strsep(&activatedby, "/"); 
+static char *handle_parkedcalls_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       char *res = handle_parkedcalls(e, cmd, a);
+       if (cmd == CLI_INIT)
+               e->command = "show parkedcalls";
+       return res;
+}
 
-               /*! \todo XXX var_name or app_args ? */
-               if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
-                       ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
-                               app, exten, activateon, var->name);
-                       continue;
-               }
+static struct ast_cli_entry cli_show_parkedcalls_deprecated = AST_CLI_DEFINE(handle_parkedcalls_deprecated, "List currently parked calls.");
 
-               AST_LIST_LOCK(&feature_list);
-               if ((feature = find_dynamic_feature(var->name))) {
-                       AST_LIST_UNLOCK(&feature_list);
-                       ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
-                       continue;
-               }
-               AST_LIST_UNLOCK(&feature_list);
-                               
-               if (!(feature = ast_calloc(1, sizeof(*feature))))
-                       continue;                                       
+static struct ast_cli_entry cli_features[] = {
+       AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
+       AST_CLI_DEFINE(handle_features_reload, "Reloads configured features"),
+       AST_CLI_DEFINE(handle_parkedcalls, "List currently parked calls", .deprecate_cmd = &cli_show_parkedcalls_deprecated),
+};
 
-               ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
-               ast_copy_string(feature->app, app, FEATURE_APP_LEN);
-               ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
-               
-               if (app_args) 
-                       ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
+/*! 
+ * \brief Dump parking lot status
+ * \param s
+ * \param m
+ * 
+ * Lock parking lot, iterate list and append parked calls status, unlock parking lot.
+ * \return Always RESULT_SUCCESS 
+*/
+static int manager_parking_status(struct mansession *s, const struct message *m)
+{
+       struct parkeduser *cur;
+       const char *id = astman_get_header(m, "ActionID");
+       char idText[256] = "";
 
-               if (moh_class)
-                       ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
-                       
-               ast_copy_string(feature->exten, exten, sizeof(feature->exten));
-               feature->operation = feature_exec_app;
-               ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
+       if (!ast_strlen_zero(id))
+               snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
 
-               /* Allow caller and calle to be specified for backwards compatability */
-               if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
-                       ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
-               else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
-                       ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
-               else {
-                       ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
-                               " must be 'self', or 'peer'\n", var->name);
-                       continue;
-               }
+       astman_send_ack(s, m, "Parked calls will follow");
 
-               if (ast_strlen_zero(activatedby))
-                       ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
-               else if (!strcasecmp(activatedby, "caller"))
-                       ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
-               else if (!strcasecmp(activatedby, "callee"))
-                       ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
-               else if (!strcasecmp(activatedby, "both"))
-                       ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
-               else {
-                       ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
-                               " must be 'caller', or 'callee', or 'both'\n", var->name);
-                       continue;
-               }
+       AST_LIST_LOCK(&parkinglot);
 
-               ast_register_feature(feature);
-                       
-               ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);
+       AST_LIST_TRAVERSE(&parkinglot, cur, list) {
+               astman_append(s, "Event: ParkedCall\r\n"
+                       "Exten: %d\r\n"
+                       "Channel: %s\r\n"
+                       "From: %s\r\n"
+                       "Timeout: %ld\r\n"
+                       "CallerIDNum: %s\r\n"
+                       "CallerIDName: %s\r\n"
+                       "%s"
+                       "\r\n",
+                       cur->parkingnum, cur->chan->name, cur->peername,
+                       (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
+                       S_OR(cur->chan->cid.cid_num, ""),       /* XXX in other places it is <unknown> */
+                       S_OR(cur->chan->cid.cid_name, ""),
+                       idText);
        }
 
-       ast_unregister_groups();
-       AST_RWLIST_WRLOCK(&feature_groups);
+       astman_append(s,
+               "Event: ParkedCallsComplete\r\n"
+               "%s"
+               "\r\n",idText);
 
-       ctg = NULL;
-       while ((ctg = ast_category_browse(cfg, ctg))) {
-               for (i = 0; i < ARRAY_LEN(categories); i++) {
-                       if (!strcasecmp(categories[i], ctg))
-                               break;
-               }
+       AST_LIST_UNLOCK(&parkinglot);
 
-               if (i < ARRAY_LEN(categories)) 
-                       continue;
+       return RESULT_SUCCESS;
+}
 
-               if (!(fg = register_group(ctg)))
-                       continue;
+static char mandescr_park[] =
+"Description: Park a channel.\n"
+"Variables: (Names marked with * are required)\n"
+"      *Channel: Channel name to park\n"
+"      *Channel2: Channel to announce park info to (and return to if timeout)\n"
+"      Timeout: Number of milliseconds to wait before callback.\n";  
 
-               for (var = ast_variable_browse(cfg, ctg); var; var = var->next) {
-                       struct ast_call_feature *feature;
+/*!
+ * \brief Create manager event for parked calls
+ * \param s
+ * \param m
+ *
+ * Get channels involved in park, create event.
+ * \return Always 0
+*/
+static int manager_park(struct mansession *s, const struct message *m)
+{
+       const char *channel = astman_get_header(m, "Channel");
+       const char *channel2 = astman_get_header(m, "Channel2");
+       const char *timeout = astman_get_header(m, "Timeout");
+       char buf[BUFSIZ];
+       int to = 0;
+       int res = 0;
+       int parkExt = 0;
+       struct ast_channel *ch1, *ch2;
 
-                       AST_LIST_LOCK(&feature_list);
-                       if(!(feature = find_dynamic_feature(var->name)) && 
-                          !(feature = ast_find_call_feature(var->name))) {
-                               AST_LIST_UNLOCK(&feature_list);
-                               ast_log(LOG_WARNING, "Feature '%s' was not found.\n", var->name);
-                               continue;
-                       }
-                       AST_LIST_UNLOCK(&feature_list);
+       if (ast_strlen_zero(channel)) {
+               astman_send_error(s, m, "Channel not specified");
+               return 0;
+       }
 
-                       register_group_feature(fg, var->value, feature);
-               }
+       if (ast_strlen_zero(channel2)) {
+               astman_send_error(s, m, "Channel2 not specified");
+               return 0;
        }
 
-       AST_RWLIST_UNLOCK(&feature_groups);
+       ch1 = ast_get_channel_by_name_locked(channel);
+       if (!ch1) {
+               snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
+               astman_send_error(s, m, buf);
+               return 0;
+       }
 
-       ast_config_destroy(cfg);
+       ch2 = ast_get_channel_by_name_locked(channel2);
+       if (!ch2) {
+               snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
+               astman_send_error(s, m, buf);
+               ast_channel_unlock(ch1);
+               return 0;
+       }
 
-       /* 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))
-                               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 (!ast_strlen_zero(timeout)) {
+               sscanf(timeout, "%d", &to);
        }
-       
-       if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
-               ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
-               return -1;
+
+       res = ast_masq_park_call(ch1, ch2, to, &parkExt);
+       if (!res) {
+               ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
+               astman_send_ack(s, m, "Park successful");
+       } else {
+               astman_send_error(s, m, "Park failure");
        }
-       res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
-       if (parkaddhints)
-               park_add_hints(parking_con, parking_start, parking_stop);
-       if (!res)
-               notify_metermaids(ast_parking_ext(), parking_con, AST_DEVICE_INUSE);
-       return res;
 
+       ast_channel_unlock(ch1);
+       ast_channel_unlock(ch2);
+
+       return 0;
+}
+
+/*!
+ * \brief Pickup a call
+ * \param chan channel that initiated pickup.
+ *
+ * Walk list of channels, checking it is not itself, channel is pbx one,
+ * check that the callgroup for both channels are the same and the channel is ringing.
+ * Answer calling channel, flag channel as answered on queue, masq channels together.
+*/
+int ast_pickup_call(struct ast_channel *chan)
+{
+       struct ast_channel *cur = NULL;
+       int res = -1;
+
+       while ((cur = ast_channel_walk_locked(cur)) != NULL) {
+               if (!cur->pbx && 
+                       (cur != chan) &&
+                       (chan->pickupgroup & cur->callgroup) &&
+                       ((cur->_state == AST_STATE_RINGING) ||
+                        (cur->_state == AST_STATE_RING))) {
+                               break;
+               }
+               ast_channel_unlock(cur);
+       }
+       if (cur) {
+               ast_debug(1, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
+               res = ast_answer(chan);
+               if (res)
+                       ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
+               res = ast_queue_control(chan, AST_CONTROL_ANSWER);
+               if (res)
+                       ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
+               res = ast_channel_masquerade(cur, chan);
+               if (res)
+                       ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);           /* Done */
+               ast_channel_unlock(cur);
+       } else  {
+               ast_debug(1, "No call pickup possible...\n");
+       }
+       return res;
 }
 
 static char *app_bridge = "Bridge";
@@ -3388,16 +3410,11 @@ static int bridge_exec(struct ast_channel *chan, void *data)
        return 0;
 }
 
-static int reload(void)
-{
-       return load_config();
-}
-
-static int load_module(void)
+int ast_features_init(void)
 {
        int res;
 
-       ast_register_application(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip);     
+       ast_register_application2(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip, NULL);
 
        memset(parking_ext, 0, sizeof(parking_ext));
        memset(parking_con, 0, sizeof(parking_con));
@@ -3406,9 +3423,9 @@ static int load_module(void)
                return res;
        ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
        ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
-       res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
+       res = ast_register_application2(parkedcall, park_exec, synopsis, descrip, NULL);
        if (!res)
-               res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
+               res = ast_register_application2(parkcall, park_call_exec, synopsis2, descrip2, NULL);
        if (!res) {
                ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls");
                ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
@@ -3420,29 +3437,3 @@ static int load_module(void)
 
        return res;
 }
-
-
-static int unload_module(void)
-{
-       struct ast_context *con;
-       ast_manager_unregister("ParkedCalls");
-       ast_manager_unregister("Bridge");
-       ast_manager_unregister("Park");
-       ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
-       ast_unregister_application(parkcall);
-       ast_unregister_application(app_bridge);
-       ast_devstate_prov_del("Park");
-       con = ast_context_find(parking_con);
-       if (con)
-               ast_context_destroy(con, registrar);
-       con = ast_context_find(parking_con_dial);
-       if (con)
-               ast_context_destroy(con, registrar);    
-       return ast_unregister_application(parkedcall);
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Features Resource",
-               .load = load_module,
-               .unload = unload_module,
-               .reload = reload,
-             );
index 1d07977..45fc7be 100644 (file)
@@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/rtp.h"
 #include "asterisk/http.h"
 #include "asterisk/lock.h"
+#include "asterisk/features.h"
 
 #ifdef DLFCNCOMPAT
 #include "asterisk/dlfcn-compat.h"
@@ -247,6 +248,7 @@ static struct reload_classes {
        { "rtp",        ast_rtp_reload },
        { "http",       ast_http_reload },
        { "logger",     logger_reload },
+       { "features",   ast_features_reload },
        { NULL,         NULL }
 };