Fix ParkAndAnnounce not respecting parking options.
authorJeff Peeler <jpeeler@digium.com>
Wed, 10 Mar 2010 20:51:23 +0000 (20:51 +0000)
committerJeff Peeler <jpeeler@digium.com>
Wed, 10 Mar 2010 20:51:23 +0000 (20:51 +0000)
The patch ensures that if a peer does not exist, parking settings are read from
the channel. A unit test has been written to ensure proper operation for both
standard parking and parking using masquerades.

(closes issue #16592)
Reported by: mwyres
Patches:
      bug_16592.diff uploaded by snuffy (license 35)

Review: https://reviewboard.asterisk.org/r/539/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@251679 65c4cc65-6c06-0410-ace0-fbb531ad65f3

main/features.c

index 2d7723e..5a2bb02 100644 (file)
@@ -56,6 +56,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/global_datastores.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/cel.h"
+#include "asterisk/test.h"
 
 /*** DOCUMENTATION
        <application name="Bridge" language="en_US">
@@ -702,6 +703,8 @@ static struct parkeduser *park_space_reserve(struct ast_channel *chan, struct as
 
        if (peer)
                parkinglotname = findparkinglotname(peer);
+       else /* peer was NULL, check chan (ParkAndAnnounce / res_agi) */
+               parkinglotname = findparkinglotname(chan);
 
        if (parkinglotname) {
                ast_debug(1, "Found chanvar Parkinglot: %s\n", parkinglotname);
@@ -856,17 +859,17 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, st
        const char *event_from;
 
        if (pu == NULL)
-               pu = park_space_reserve(chan, peer, args);
+               args->pu = pu = park_space_reserve(chan, peer, args);
        if (pu == NULL)
                return 1; /* Continue execution if possible */
 
        snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", pu->parkingnum);
-       
+
        chan->appl = "Parked Call";
-       chan->data = NULL; 
+       chan->data = NULL;
 
        pu->chan = chan;
-       
+
        /* Put the parked channel on hold if we have two different channels */
        if (chan != peer) {
                if (ast_test_flag(args, AST_PARK_OPT_RINGING)) {
@@ -1067,7 +1070,7 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i
        return 0;
 }
 
-/* Park call via masquraded channel */
+/* Park call via masqueraded channel */
 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
 {
        return masq_park_call(rchan, peer, timeout, extout, 0, NULL);
@@ -1083,8 +1086,176 @@ static int masq_park_call_announce(struct ast_channel *rchan, struct ast_channel
        return masq_park_call(rchan, peer, timeout, extout, 1, NULL);
 }
 
-/*! 
- * \brief set caller and callee according to the direction 
+#ifdef TEST_FRAMEWORK
+static int fake_fixup(struct ast_channel *clonechan, struct ast_channel *original)
+{
+       return 0;
+}
+
+static struct ast_channel *create_test_channel(const struct ast_channel_tech *fake_tech)
+{
+       struct ast_channel *test_channel1;
+       if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
+        NULL, NULL, 0, 0, "TestChannel1"))) {
+               return NULL;
+       }
+
+       /* normally this is done in the channel driver */
+       test_channel1->nativeformats = AST_FORMAT_GSM;
+       test_channel1->writeformat = AST_FORMAT_GSM;
+       test_channel1->rawwriteformat = AST_FORMAT_GSM;
+       test_channel1->readformat = AST_FORMAT_GSM;
+       test_channel1->rawreadformat = AST_FORMAT_GSM;
+       test_channel1->tech = fake_tech;
+
+       return test_channel1;
+}
+
+static int unpark_test_channel(struct ast_channel *toremove, struct ast_park_call_args *args)
+{
+       struct ast_context *con;
+       struct parkeduser *pu_toremove;
+       args->pu->notquiteyet = 1; /* go ahead and stop processing the test parking */
+       AST_LIST_LOCK(&args->pu->parkinglot->parkings);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&args->pu->parkinglot->parkings, pu_toremove, list) {
+               con = ast_context_find(args->pu->parkinglot->parking_con);
+               if (con) {
+                       if (ast_context_remove_extension2(con, args->pu->parkingexten, 1, NULL, 0)) {
+                               ast_log(LOG_WARNING, "Whoa, failed to remove the parking extension!\n");
+                               return -1;
+                       } else {
+                               notify_metermaids(args->pu->parkingexten, pu_toremove->parkinglot->parking_con, AST_DEVICE_NOT_INUSE);
+                       }
+               } else {
+                       ast_log(LOG_WARNING, "Whoa, no parking context?\n");
+                       return -1;
+               }
+               if (pu_toremove == args->pu) {
+                       AST_LIST_REMOVE_CURRENT(list);
+                       break;
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+       AST_LIST_UNLOCK(&args->pu->parkinglot->parkings);
+
+       /* the only way this would be unsafe is if a timeout occurred, which is set at 45 sec */
+       ast_free(args->pu);
+       args->pu = NULL;
+
+       ast_hangup(toremove);
+       return 0;
+}
+
+AST_TEST_DEFINE(features_test)
+{
+       int saved_parkeddynamic;
+       struct ast_channel *test_channel1 = NULL;
+       struct ast_channel *parked_chan = NULL;
+       struct ast_parkinglot *dynlot = NULL;
+       struct ast_park_call_args args = {
+               .timeout = DEFAULT_PARK_TIME,
+       };
+
+       int res = -1;
+
+       static const struct ast_channel_tech fake_tech = {
+               .fixup = fake_fixup, /* silence warning from masquerade */
+       };
+
+       static const char unique_parkinglot[] = "myuniquetestparkinglot3141592654";
+       static const char parkinglot_range[] = "750-760";
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "features_test";
+               info->category = "main/features/";
+               info->summary = "Features unit test";
+               info->description =
+                       "Tests whether parking respects PARKINGLOT settings";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       /* changing a config option is a bad practice, but must be done in this case */
+       saved_parkeddynamic = parkeddynamic;
+       parkeddynamic = 1;
+
+       if (!(test_channel1 = create_test_channel(&fake_tech))) {
+               goto exit_features_test;
+       }
+
+       ast_test_status_update(test, "Test parking functionality with defaults\n");
+       if (park_call_full(test_channel1, NULL, &args)) {
+               goto exit_features_test;
+       }
+       if (unpark_test_channel(test_channel1, &args)) {
+               goto exit_features_test;
+       }
+
+       ast_test_status_update(test, "Check that certain parking options are respected\n");
+       if (!(test_channel1 = create_test_channel(&fake_tech))) {
+               goto exit_features_test;
+       }
+       pbx_builtin_setvar_helper(test_channel1, "PARKINGLOT", unique_parkinglot);
+       pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNPOS", parkinglot_range);
+       if (park_call_full(test_channel1, NULL, &args)) {
+               goto exit_features_test;
+       }
+       /* grab newly created parking lot for destruction in the end */
+       dynlot = args.pu->parkinglot;
+       if (!args.pu->parkingnum == 750 || strcasecmp(args.pu->parkinglot->name, unique_parkinglot)) {
+               ast_test_status_update(test, "Parking settings were not respected\n");
+               goto exit_features_test;
+       } else {
+               ast_test_status_update(test, "Parking settings for non-masquerading park verified\n");
+       }
+       if (unpark_test_channel(test_channel1, &args)) {
+               goto exit_features_test;
+       }
+
+       ast_test_status_update(test, "Check #2 that certain parking options are respected\n");
+       if (!(test_channel1 = create_test_channel(&fake_tech))) {
+               goto exit_features_test;
+       }
+       pbx_builtin_setvar_helper(test_channel1, "PARKINGLOT", unique_parkinglot);
+       pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNPOS", parkinglot_range);
+       if (masq_park_call(test_channel1, NULL, 0, NULL, 0, &args) == AST_FEATURE_RETURN_PARKFAILED) {
+               goto exit_features_test;
+       }
+       /* hangup zombie channel */
+       ast_hangup(test_channel1);
+       test_channel1 = NULL;
+       if (!args.pu->parkingnum == 750 || strcasecmp(args.pu->parkinglot->name, unique_parkinglot)) {
+               ast_test_status_update(test, "Parking settings were not respected\n");
+               goto exit_features_test;
+       } else {
+               ast_test_status_update(test, "Parking settings for masquerading park verified\n");
+       }
+       /* find the real channel */
+       parked_chan = ast_channel_get_by_name("TestChannel1");
+       if (unpark_test_channel(parked_chan, &args)) {
+               goto exit_features_test;
+       }
+
+       res = 0;
+
+exit_features_test:
+
+       if (test_channel1) {
+               ast_hangup(test_channel1);
+       }
+
+       /* careful, if PARKINGDYNCONTEXT is tested, need to delete context */
+       ao2_unlink(parkinglots, dynlot);
+       parkeddynamic = saved_parkeddynamic;
+       return res ? AST_TEST_FAIL : AST_TEST_PASS;
+}
+#endif
+
+
+/*!
+ * \brief set caller and callee according to the direction
  * \param caller, callee, peer, chan, sense
  *
  * Detect who triggered feature and set callee/caller variables accordingly
@@ -5258,6 +5429,9 @@ int ast_features_init(void)
        }
 
        res |= ast_devstate_prov_add("Park", metermaidstate);
+#ifdef TEST_FRAMEWORK
+       res |= AST_TEST_REGISTER(features_test);
+#endif
 
        return res;
 }