]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Merged revisions 198856 via svnmerge from
authorDavid Vossel <dvossel@digium.com>
Wed, 3 Jun 2009 15:26:16 +0000 (15:26 +0000)
committerDavid Vossel <dvossel@digium.com>
Wed, 3 Jun 2009 15:26:16 +0000 (15:26 +0000)
https://origsvn.digium.com/svn/asterisk/trunk

........
  r198856 | dvossel | 2009-06-02 16:17:49 -0500 (Tue, 02 Jun 2009) | 10 lines

  Generic call forward api, ast_call_forward()

  The function ast_call_forward() forwards a call to an extension specified in an ast_channel's call_forward string.  After an ast_channel is called, if the channel's call_forward string is set this function can be used to forward the call to a new channel and terminate the original one.  I have included this api call in both channel.c's ast_request_and_dial() and feature.c's feature_request_and_dial().  App_dial and app_queue already contain call forward logic specific for their application and options.

  (closes issue #13630)
  Reported by: festr

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

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

include/asterisk/channel.h
main/channel.c
main/features.c

index de3670f97c9b0394f1e25bcf792d6cf03c318381..d022a8816b3a0896e57970f26c36e3af5162a937 100644 (file)
@@ -863,6 +863,17 @@ struct ast_channel *ast_request_and_dial(const char *type, int format, void *dat
  */
 struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data,
        int timeout, int *reason, const char *cid_num, const char *cid_name, struct outgoing_helper *oh);
+/*!
+* \brief Forwards a call to a new channel specified by the original channel's call_forward str.  If possible, the new forwarded channel is created and returned while the original one is terminated.
+* \param caller in channel that requested orig
+* \param orig channel being replaced by the call forward channel
+* \param timeout maximum amount of time to wait for setup of new forward channel
+* \param format requested channel format
+* \param oh outgoing helper used with original channel
+* \param outstate reason why unsuccessful (if uncuccessful)
+* \return Returns the forwarded call's ast_channel on success or NULL on failure
+*/
+struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_channel *orig, int *timeout, int format, struct outgoing_helper *oh, int *outstate);
 
 /*!\brief Register a channel technology (a new channel driver)
  * Called by a channel module to register the kind of channels it supports.
index 15768b659677233a874083989e7dbc13a5bf4f1f..1e908533dd411e2ce6ef752fb1c203f0748cf654 100644 (file)
@@ -3535,6 +3535,91 @@ const char *ast_channel_reason2str(int reason)
        }
 }
 
+static void handle_cause(int cause, int *outstate)
+{
+       if (outstate) {
+               /* compute error and return */
+               if (cause == AST_CAUSE_BUSY)
+                       *outstate = AST_CONTROL_BUSY;
+               else if (cause == AST_CAUSE_CONGESTION)
+                       *outstate = AST_CONTROL_CONGESTION;
+               else
+                       *outstate = 0;
+       }
+}
+
+struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_channel *orig, int *timeout, int format, struct outgoing_helper *oh, int *outstate)
+{
+       char tmpchan[256];
+       struct ast_channel *new = NULL;
+       char *data, *type;
+       int cause = 0;
+
+       /* gather data and request the new forward channel */
+       ast_copy_string(tmpchan, orig->call_forward, sizeof(tmpchan));
+       if ((data = strchr(tmpchan, '/'))) {
+               *data++ = '\0';
+               type = tmpchan;
+       } else {
+               const char *forward_context;
+               ast_channel_lock(orig);
+               forward_context = pbx_builtin_getvar_helper(orig, "FORWARD_CONTEXT");
+               snprintf(tmpchan, sizeof(tmpchan), "%s@%s", orig->call_forward, S_OR(forward_context, orig->context));
+               ast_channel_unlock(orig);
+               data = tmpchan;
+               type = "Local";
+       }
+       if (!(new = ast_request(type, format, data, &cause))) {
+               ast_log(LOG_NOTICE, "Unable to create channel for call forward to '%s/%s' (cause = %d)\n", type, data, cause);
+               handle_cause(cause, outstate);
+               ast_hangup(orig);
+               return NULL;
+       }
+
+       /* Copy/inherit important information into new channel */
+       if (oh) {
+               if (oh->vars) {
+                       ast_set_variables(new, oh->vars);
+               }
+               if (!ast_strlen_zero(oh->cid_num) && !ast_strlen_zero(oh->cid_name)) {
+                       ast_set_callerid(new, oh->cid_num, oh->cid_name, oh->cid_num);
+               }
+               if (oh->parent_channel) {
+                       ast_channel_inherit_variables(oh->parent_channel, new);
+                       ast_channel_datastore_inherit(oh->parent_channel, new);
+               }
+               if (oh->account) {
+                       ast_cdr_setaccount(new, oh->account);
+               }
+       } else if (caller) { /* no outgoing helper so use caller if avaliable */
+               ast_channel_inherit_variables(caller, new);
+               ast_channel_datastore_inherit(caller, new);
+       }
+
+       ast_channel_lock(orig);
+       while (ast_channel_trylock(new)) {
+               CHANNEL_DEADLOCK_AVOIDANCE(orig);
+       }
+       ast_copy_flags(new->cdr, orig->cdr, AST_CDR_FLAG_ORIGINATED);
+       ast_string_field_set(new, accountcode, orig->accountcode);
+       if (!ast_strlen_zero(orig->cid.cid_num) && !ast_strlen_zero(new->cid.cid_name)) {
+               ast_set_callerid(new, orig->cid.cid_num, orig->cid.cid_name, orig->cid.cid_num);
+       }
+       ast_channel_unlock(new);
+       ast_channel_unlock(orig);
+
+       /* call new channel */
+       if ((*timeout = ast_call(new, data, 0))) {
+               ast_log(LOG_NOTICE, "Unable to call forward to channel %s/%s\n", type, (char *)data);
+               ast_hangup(orig);
+               ast_hangup(new);
+               return NULL;
+       }
+       ast_hangup(orig);
+
+       return new;
+}
+
 struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
 {
        int dummy_outstate;
@@ -3551,11 +3636,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
        chan = ast_request(type, format, data, &cause);
        if (!chan) {
                ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
-               /* compute error and return */
-               if (cause == AST_CAUSE_BUSY)
-                       *outstate = AST_CONTROL_BUSY;
-               else if (cause == AST_CAUSE_CONGESTION)
-                       *outstate = AST_CONTROL_CONGESTION;
+               handle_cause(cause, outstate);
                return NULL;
        }
 
@@ -3586,6 +3667,13 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
                                break;
                        if (timeout > -1)
                                timeout = res;
+                       if (!ast_strlen_zero(chan->call_forward)) {
+                               if (!(chan = ast_call_forward(NULL, chan, &timeout, format, oh, outstate))) {
+                                       return NULL;
+                               }
+                               continue;
+                       }
+
                        f = ast_read(chan);
                        if (!f) {
                                *outstate = AST_CONTROL_HANGUP;
index 86347bb46942431cedaa3d1e693b7674c9be39d6..57fc12c0cd75f5c2045b5b38d47e4dea32d253d4 100644 (file)
@@ -2155,7 +2155,13 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *call
                                if (!active_channel)
                                        continue;
 
-                               if (chan && (chan == active_channel)){
+                               if (chan && (chan == active_channel)) {
+                                       if (!ast_strlen_zero(chan->call_forward)) {
+                                               if (!(chan = ast_call_forward(caller, chan, &to, format, NULL, outstate))) {
+                                                       return NULL;
+                                               }
+                                               continue;
+                                       }
                                        f = ast_read(chan);
                                        if (f == NULL) { /*doh! where'd he go?*/
                                                state = AST_CONTROL_HANGUP;