]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Allow disconnect feature before a call is bridged
authorDavid Vossel <dvossel@digium.com>
Thu, 19 Mar 2009 16:15:16 +0000 (16:15 +0000)
committerDavid Vossel <dvossel@digium.com>
Thu, 19 Mar 2009 16:15:16 +0000 (16:15 +0000)
feature.conf has a disconnect option.  By default this option is set to '*', but it could be anything.  If a user wishes to disconnect a call before the other side answers, only '*' will work, regardless if the disconnect option is set to something else.  This is because features are unavailable until bridging takes place.  The default disconnect option, '*', was hardcoded in app_dial, which doesn't make any sense from a user perspective since they may expect it to be something different.  This patch allows features to be detected from outside of the bridge, but not operated on.  In this case, the disconnect feature can be detected before briding and handled outside of features.c.

(closes issue #11583)
Reported by: sobomax
Patches:
patch-apps__app_dial.c uploaded by sobomax (license 359)
11583.latest-patch uploaded by murf (license 17)
detect_disconnect.diff uploaded by dvossel (license 671)
Tested by: sobomax, dvossel
Review: http://reviewboard.digium.com/r/195/

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

apps/app_dial.c
include/asterisk/features.h
res/res_features.c
res/res_features.exports

index 6178b346c369be0543874ac9b26be3e333c3188b..63d00a7d427c42d633bb85e1a648c51d810d2ac6 100644 (file)
@@ -383,6 +383,7 @@ static int onedigit_goto(struct ast_channel *chan, const char *context, char ext
        return 0;
 }
 
+static int detect_disconnect(struct ast_channel *chan, char code, char *featurecode, int len);
 
 static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan)
 {
@@ -417,7 +418,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
        struct ast_channel *peer = NULL;
        /* single is set if only one destination is enabled */
        int single = outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
-       
+
+       char featurecode[FEATURE_MAX_LEN + 1] = { 0, };
+
        if (single) {
                /* Turn off hold music, etc */
                ast_deactivate_generator(in);
@@ -742,10 +745,10 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
                                        }
                                }
 
-                               if (ast_test_flag(peerflags, OPT_CALLER_HANGUP) && 
-                                                 (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */
+                               if (ast_test_flag(peerflags, OPT_CALLER_HANGUP) &&
+                                       detect_disconnect(in, f->subclass, featurecode, sizeof(featurecode))) {
                                        if (option_verbose > 2)
-                                               ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
+                                               ast_verbose(VERBOSE_PREFIX_3 "User requested call disconnect.\n");
                                        *to=0;
                                        ast_cdr_noanswer(in->cdr);
                                        strcpy(status, "CANCEL");
@@ -786,6 +789,34 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
        return peer;
 }
 
+static int detect_disconnect(struct ast_channel *chan, char code, char *featurecode, int len)
+{
+       struct ast_flags features = { AST_FEATURE_DISCONNECT }; /* only concerned with disconnect feature */
+       struct ast_call_feature feature;
+       char *tmp;
+       int res;
+
+       if ((strlen(featurecode)) < (len - 2)) { 
+               tmp = &featurecode[strlen(featurecode)];
+               tmp[0] = code;
+               tmp[1] = '\0';
+       } else {
+               featurecode[0] = 0;
+               return -1; /* no room in featurecode buffer */
+       }
+
+       res = ast_feature_detect(chan, &features, featurecode, &feature);
+
+       if (res != FEATURE_RETURN_STOREDIGITS) {
+               featurecode[0] = '\0';
+       }
+       if (feature.feature_mask & AST_FEATURE_DISCONNECT) {
+               return 1;
+       }
+
+       return 0;
+}
+
 static void replace_macro_delimiter(char *s)
 {
        for (; *s; s++)
index c67bd0380b16d6ba3193684d885255713990b880..7e4de197267b7e8090fa509eccb9c31e722c20fe 100644 (file)
 
 #define PARK_APP_NAME "Park"
 
+#define FEATURE_RETURN_HANGUP           -1
+#define FEATURE_RETURN_SUCCESSBREAK     0
+#define FEATURE_RETURN_PBX_KEEPALIVE    AST_PBX_KEEPALIVE
+#define FEATURE_RETURN_NO_HANGUP_PEER   AST_PBX_NO_HANGUP_PEER
+#define FEATURE_RETURN_PASSDIGITS       21
+#define FEATURE_RETURN_STOREDIGITS      22
+#define FEATURE_RETURN_SUCCESS          23
+#define FEATURE_RETURN_KEEPTRYING       24
+#define FEATURE_RETURN_PARKFAILED       25
+
+#define FEATURE_SENSE_CHAN     (1 << 0)
+#define FEATURE_SENSE_PEER     (1 << 1)
+
+typedef int (*ast_feature_operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data);
+
 /*! \brief main call feature structure */
 struct ast_call_feature {
        int feature_mask;
@@ -85,6 +100,10 @@ char *ast_pickup_ext(void);
 /*! \brief Bridge a call, optionally allowing redirection */
 int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
 
+/*! \brief detect a feature before bridging 
+    \param chan, ast_flags ptr, code, ast_call_feature ptr to be set if found */
+int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, char *code, struct ast_call_feature *feature);
+
 /*! \brief Pickup a call */
 int ast_pickup_call(struct ast_channel *chan);
 
index 8cde876f528bc4d331dc978d1044c9a2dade9d9f..631d8ebca8ede2f9903fbde922ed7e886c1612dc 100644 (file)
@@ -614,9 +614,6 @@ static int masq_park_call_announce(struct ast_channel *rchan, struct ast_channel
        return masq_park_call(rchan, peer, timeout, extout, 1, orig_chan_name);
 }
 
-#define FEATURE_SENSE_CHAN     (1 << 0)
-#define FEATURE_SENSE_PEER     (1 << 1)
-
 /*! \brief
  * set caller and callee according to the direction
  */
@@ -1223,46 +1220,43 @@ static int remap_feature(const char *name, const char *value)
        return res;
 }
 
-static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
+/*!
+ * \brief Helper function for feature_interpret and ast_feature_detect
+ * \param chan,peer,config,code,sense,dynamic_features char buf,feature flags,operation,feature
+ *
+ * Lock features list, browse for code, unlock list
+ * If a feature is found and the operation variable is set, that feature's
+ * operation is executed.  The first feature found is copied to the feature parameter.
+ * \retval res on success.
+ * \retval -1 on failure.
+*/
+static int feature_interpret_helper(struct ast_channel *chan, struct ast_channel *peer,
+       struct ast_bridge_config *config, char *code, int sense, char *dynamic_features_buf,
+       struct ast_flags *features, int operation, struct ast_call_feature *feature)
 {
        int x;
-       struct ast_flags features;
-       struct ast_call_feature *feature;
-       const char *peer_dynamic_features, *chan_dynamic_features;
-       char dynamic_features_buf[128];
+       struct ast_call_feature *tmpfeature;
        char *tmp, *tok;
        int res = FEATURE_RETURN_PASSDIGITS;
        int feature_detected = 0;
 
-       if (sense == FEATURE_SENSE_CHAN) {
-               ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
-       } else {
-               ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);   
+       if (!(peer && chan && config) && operation) {
+               return -1; /* can not run feature operation */
        }
 
-       ast_channel_lock(peer);
-       peer_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"),""));
-       ast_channel_unlock(peer);
-
-       ast_channel_lock(chan);
-       chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
-       ast_channel_unlock(chan);
-
-       snprintf(dynamic_features_buf, sizeof(dynamic_features_buf), "%s%s%s", S_OR(chan_dynamic_features, ""), chan_dynamic_features && peer_dynamic_features ? "#" : "", S_OR(peer_dynamic_features,""));
-
-       if (option_debug > 2)
-               ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d dynamic=%s\n", chan->name, peer->name, code, sense, features.flags, dynamic_features_buf);
-
        ast_rwlock_rdlock(&features_lock);
        for (x = 0; x < FEATURES_COUNT; x++) {
-               if ((ast_test_flag(&features, builtin_features[x].feature_mask)) &&
+               if ((ast_test_flag(features, builtin_features[x].feature_mask)) &&
                    !ast_strlen_zero(builtin_features[x].exten)) {
                        /* Feature is up for consideration */
                        if (!strcmp(builtin_features[x].exten, code)) {
                                if (option_debug > 2) {
                                        ast_log(LOG_DEBUG, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
                                }
-                               res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
+                               if (operation) {
+                                       res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
+                               }
+                               memcpy(feature, &builtin_features[x], sizeof(feature));
                                feature_detected = 1;
                                break;
                        } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
@@ -1281,34 +1275,87 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p
 
        while ((tok = strsep(&tmp, "#"))) {
                AST_RWLIST_RDLOCK(&feature_list);
-               if (!(feature = find_dynamic_feature(tok))) {
+               if (!(tmpfeature = find_dynamic_feature(tok))) {
                        AST_RWLIST_UNLOCK(&feature_list);
                        continue;
                }
-                       
+
                /* Feature is up for consideration */
-               if (!strcmp(feature->exten, code)) {
-                       if (option_verbose > 2)
-                               ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok);
-                       res = feature->operation(chan, peer, config, code, sense, feature);
+               if (!strcmp(tmpfeature->exten, code)) {
+                       if (option_debug > 2) {
+                               ast_log(LOG_NOTICE, " Feature Found: %s exten: %s\n",tmpfeature->sname, tok);
+                       }
+                       if (operation) {
+                               res = tmpfeature->operation(chan, peer, config, code, sense, tmpfeature);
+                       }
+                       memcpy(feature, tmpfeature, sizeof(feature));
                        if (res != FEATURE_RETURN_KEEPTRYING) {
                                AST_RWLIST_UNLOCK(&feature_list);
                                break;
                        }
                        res = FEATURE_RETURN_PASSDIGITS;
-               } else if (!strncmp(feature->exten, code, strlen(code)))
+               } else if (!strncmp(tmpfeature->exten, code, strlen(code)))
                        res = FEATURE_RETURN_STOREDIGITS;
 
                AST_RWLIST_UNLOCK(&feature_list);
        }
-       
+
        return res;
 }
 
+/*!
+ * \brief Check the dynamic features
+ * \param chan,peer,config,code,sense
+ *
+ * \retval res on success.
+ * \retval -1 on failure.
+*/
+
+static int feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense) {
+
+       char dynamic_features_buf[128];
+       const char *peer_dynamic_features, *chan_dynamic_features;
+       struct ast_flags features;
+       struct ast_call_feature feature;
+       if (sense == FEATURE_SENSE_CHAN) {
+               ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
+       }
+       else {
+               ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
+       }
+
+       ast_channel_lock(peer);
+       peer_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES"),""));
+       ast_channel_unlock(peer);
+
+       ast_channel_lock(chan);
+       chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
+       ast_channel_unlock(chan);
+
+       snprintf(dynamic_features_buf, sizeof(dynamic_features_buf), "%s%s%s", S_OR(chan_dynamic_features, ""), chan_dynamic_features && peer_dynamic_features ? "#" : "", S_OR(peer_dynamic_features,""));
+
+       if (option_debug > 2) {
+               ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d, dynamic=%s\n", chan->name, peer->name, code, sense, features.flags, dynamic_features_buf);
+       }
+
+       return feature_interpret_helper(chan, peer, config, code, sense, dynamic_features_buf, &features, 1, &feature);
+}
+
+
+int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, char *code, struct ast_call_feature *feature) {
+
+       char *dynamic_features;
+       ast_channel_lock(chan);
+       dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
+       ast_channel_unlock(chan);
+
+       return feature_interpret_helper(chan, NULL, NULL, code, 0, dynamic_features, features, 0, feature);
+}
+
 static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
 {
        int x;
-       
+
        ast_clear_flag(config, AST_FLAGS_ALL);
 
        ast_rwlock_rdlock(&features_lock);
@@ -1323,7 +1370,7 @@ static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer,
                        ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
        }
        ast_rwlock_unlock(&features_lock);
-       
+
        if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
                const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
 
@@ -1870,7 +1917,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
                        ast_frfree(f);
                        f = NULL;
                        config->feature_timer = backup_config.feature_timer;
-                       res = ast_feature_interpret(chan, peer, config, featurecode, sense);
+                       res = feature_interpret(chan, peer, config, featurecode, sense);
                        switch(res) {
                        case FEATURE_RETURN_PASSDIGITS:
                                ast_dtmf_stream(other, who, featurecode, 0);
index 344a652c8ec05f5f7a050a2bb9ba8d8f4a20f5f9..59a90b7822e4032e1e86973e59119d1c23ea8f96 100644 (file)
@@ -8,6 +8,7 @@
                ast_pickup_ext;
                ast_register_feature;
                ast_unregister_feature;
+               ast_feature_detect;
        local:
                *;
 };