diff options
author | David Vossel <dvossel@digium.com> | 2009-06-02 21:17:49 +0000 |
---|---|---|
committer | David Vossel <dvossel@digium.com> | 2009-06-02 21:17:49 +0000 |
commit | 3830c415c74dff744d044271453e15d36e6c4bb4 (patch) | |
tree | d601a0e2ddfaa00461f18697efcbf6ee54f01f5b | |
parent | 61bc1854d578cc5a55d36b80bc1afa1b322522b4 (diff) |
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/trunk@198856 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r-- | include/asterisk/channel.h | 12 | ||||
-rw-r--r-- | main/channel.c | 101 | ||||
-rw-r--r-- | main/features.c | 6 |
3 files changed, 114 insertions, 5 deletions
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 6bfb7b6d5..8c0fa8957 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -1081,6 +1081,18 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d 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. * \param tech Structure defining channel technology or "type" diff --git a/main/channel.c b/main/channel.c index d9ce2efbf..9fc89255a 100644 --- a/main/channel.c +++ b/main/channel.c @@ -3931,6 +3931,94 @@ 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; + struct ast_party_redirecting *apr = &orig->redirecting; + 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; + } + + ast_channel_set_redirecting(new, apr); + + /* 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_update_redirecting(oh->parent_channel, apr); + 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_update_redirecting(caller, apr); + 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_string_field_set(new, accountcode, orig->accountcode); + ast_party_caller_copy(&new->cid, &orig->cid); + ast_party_connected_line_copy(&new->connected, &orig->connected); + 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; @@ -3948,11 +4036,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; } @@ -3989,6 +4073,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; diff --git a/main/features.c b/main/features.c index 6c15f258d..d4a5e5db6 100644 --- a/main/features.c +++ b/main/features.c @@ -2380,6 +2380,12 @@ static struct ast_channel *feature_request_and_dial(struct ast_channel *caller, continue; 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; |