Wed Apr 6 11:29:40 2011

Asterisk developer's documentation


bridge_builtin_features.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2009, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Built in bridging features
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  *
00025  * \ingroup bridges
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <sys/types.h>
00036 #include <sys/stat.h>
00037 
00038 #include "asterisk/module.h"
00039 #include "asterisk/channel.h"
00040 #include "asterisk/bridging.h"
00041 #include "asterisk/bridging_technology.h"
00042 #include "asterisk/frame.h"
00043 #include "asterisk/file.h"
00044 #include "asterisk/app.h"
00045 #include "asterisk/astobj2.h"
00046 
00047 /*! \brief Helper function that presents dialtone and grabs extension */
00048 static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, const char *context)
00049 {
00050    int res;
00051 
00052    /* Play the simple "transfer" prompt out and wait */
00053    res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
00054    ast_stopstream(chan);
00055 
00056    /* If the person hit a DTMF digit while the above played back stick it into the buffer */
00057    if (res) {
00058       exten[0] = (char)res;
00059    }
00060 
00061    /* Drop to dialtone so they can enter the extension they want to transfer to */
00062    res = ast_app_dtget(chan, context, exten, exten_len, 100, 1000);
00063 
00064    return res;
00065 }
00066 
00067 /*! \brief Helper function that creates an outgoing channel and returns it immediately */
00068 static struct ast_channel *dial_transfer(const struct ast_channel *caller, const char *exten, const char *context)
00069 {
00070    char destination[AST_MAX_EXTENSION+AST_MAX_CONTEXT+1] = "";
00071    struct ast_channel *chan = NULL;
00072    int cause;
00073 
00074    /* Fill the variable with the extension and context we want to call */
00075    snprintf(destination, sizeof(destination), "%s@%s", exten, context);
00076 
00077    /* Now we request that chan_local prepare to call the destination */
00078    if (!(chan = ast_request("Local", caller->nativeformats, caller, destination, &cause))) {
00079       return NULL;
00080    }
00081 
00082    /* Before we actually dial out let's inherit the appropriate dialplan variables */
00083    ast_channel_inherit_variables(caller, chan);
00084 
00085    /* Since the above worked fine now we actually call it and return the channel */
00086    if (ast_call(chan, destination, 0)) {
00087       ast_hangup(chan);
00088       return NULL;
00089    }
00090 
00091    return chan;
00092 }
00093 
00094 /*! \brief Internal built in feature for blind transfers */
00095 static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00096 {
00097    char exten[AST_MAX_EXTENSION] = "";
00098    struct ast_channel *chan = NULL;
00099    struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
00100    const char *context = (blind_transfer && !ast_strlen_zero(blind_transfer->context) ? blind_transfer->context : bridge_channel->chan->context);
00101 
00102    /* Grab the extension to transfer to */
00103    if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
00104       ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
00105       ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
00106       return 0;
00107    }
00108 
00109    /* Get a channel that is the destination we wish to call */
00110    if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
00111       ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
00112       ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
00113       return 0;
00114    }
00115 
00116    /* This is sort of the fun part. We impart the above channel onto the bridge, and have it take our place. */
00117    ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
00118 
00119    return 0;
00120 }
00121 
00122 /*! \brief Attended transfer feature to turn it into a threeway call */
00123 static int attended_threeway_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00124 {
00125    /* This is sort of abusing the depart state but in this instance it is only going to be handled in the below function so it is okay */
00126    ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
00127    return 0;
00128 }
00129 
00130 /*! \brief Attended transfer abort feature */
00131 static int attended_abort_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00132 {
00133    struct ast_bridge_channel *called_bridge_channel = NULL;
00134 
00135    /* It is possible (albeit unlikely) that the bridge channels list may change, so we have to ensure we do all of our magic while locked */
00136    ao2_lock(bridge);
00137 
00138    if (AST_LIST_FIRST(&bridge->channels) != bridge_channel) {
00139       called_bridge_channel = AST_LIST_FIRST(&bridge->channels);
00140    } else {
00141       called_bridge_channel = AST_LIST_LAST(&bridge->channels);
00142    }
00143 
00144    /* Now we basically eject the other channel from the bridge. This will cause their thread to hang them up, and our own code to consider the transfer failed. */
00145    if (called_bridge_channel) {
00146       ast_bridge_change_state(called_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
00147    }
00148 
00149    ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
00150 
00151    ao2_unlock(bridge);
00152 
00153    return 0;
00154 }
00155 
00156 /*! \brief Internal built in feature for attended transfers */
00157 static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00158 {
00159    char exten[AST_MAX_EXTENSION] = "";
00160    struct ast_channel *chan = NULL;
00161    struct ast_bridge *attended_bridge = NULL;
00162    struct ast_bridge_features caller_features, called_features;
00163    enum ast_bridge_channel_state attended_bridge_result;
00164    struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
00165    const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : bridge_channel->chan->context);
00166 
00167    /* Grab the extension to transfer to */
00168    if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
00169       ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
00170       ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
00171       return 0;
00172    }
00173 
00174    /* Get a channel that is the destination we wish to call */
00175    if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
00176       ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
00177       ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
00178       return 0;
00179    }
00180 
00181    /* Create a bridge to use to talk to the person we are calling */
00182    if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) {
00183       ast_hangup(chan);
00184       ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
00185       ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
00186       return 0;
00187    }
00188 
00189    /* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */
00190    ast_bridge_features_init(&called_features);
00191    ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE);
00192 
00193    /* This is how this is going down, we are imparting the channel we called above into this bridge first */
00194    ast_bridge_impart(attended_bridge, chan, NULL, &called_features);
00195 
00196    /* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */
00197    ast_bridge_features_init(&caller_features);
00198    ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP,
00199                (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL);
00200    ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"),
00201              attended_threeway_transfer, NULL);
00202    ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"),
00203              attended_abort_transfer, NULL);
00204 
00205    /* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */
00206    attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features);
00207 
00208    /* Since the above returned the caller features structure is of no more use */
00209    ast_bridge_features_cleanup(&caller_features);
00210 
00211    /* Drop the channel we are transferring to out of the above bridge since it has ended */
00212    if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) {
00213       /* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */
00214       if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) {
00215          /* We want to impart them upon the bridge and just have us return to it as normal */
00216          ast_bridge_impart(bridge, chan, NULL, NULL);
00217       } else {
00218          ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
00219       }
00220    } else {
00221       ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
00222       ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
00223    }
00224 
00225    /* Now that all channels are out of it we can destroy the bridge and the called features structure */
00226    ast_bridge_features_cleanup(&called_features);
00227    ast_bridge_destroy(attended_bridge);
00228 
00229    return 0;
00230 }
00231 
00232 /*! \brief Internal built in feature for hangup */
00233 static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
00234 {
00235    /* This is very simple, we basically change the state on the bridge channel to end and the core takes care of the rest */
00236    ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
00237    return 0;
00238 }
00239 
00240 static int unload_module(void)
00241 {
00242    return 0;
00243 }
00244 
00245 static int load_module(void)
00246 {
00247    ast_bridge_features_register(AST_BRIDGE_BUILTIN_BLINDTRANSFER, feature_blind_transfer, NULL);
00248    ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, feature_attended_transfer, NULL);
00249    ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL);
00250 
00251    /* Bump up our reference count so we can't be unloaded */
00252    ast_module_ref(ast_module_info->self);
00253 
00254    return AST_MODULE_LOAD_SUCCESS;
00255 }
00256 
00257 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Built in bridging features");

Generated on Wed Apr 6 11:29:40 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7