Sun Dec 18 20:55:53 2011

Asterisk developer's documentation


res_features.c File Reference

Routines implementing call features as call pickup, parking and transfer. More...

#include "asterisk.h"
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/causes.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/app.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/utils.h"
#include "asterisk/adsi.h"
#include "asterisk/devicestate.h"
#include "asterisk/monitor.h"
#include "asterisk/indications.h"

Include dependency graph for res_features.c:

Go to the source code of this file.

Data Structures

struct  aauser
struct  ast_bridge_thread_obj
struct  holdeduser
struct  parkeduser

Defines

#define AST_MAX_WATCHERS   256
#define DEFAULT_FEATURE_DIGIT_TIMEOUT   500
#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER   15000
#define DEFAULT_PARK_TIME   45000
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT   3000
#define FEATURE_RETURN_HANGUP   -1
#define FEATURE_RETURN_KEEPTRYING   24
#define FEATURE_RETURN_NO_HANGUP_PEER   AST_PBX_NO_HANGUP_PEER
#define FEATURE_RETURN_PASSDIGITS   21
#define FEATURE_RETURN_PBX_KEEPALIVE   AST_PBX_KEEPALIVE
#define FEATURE_RETURN_STOREDIGITS   22
#define FEATURE_RETURN_SUCCESS   23
#define FEATURE_RETURN_SUCCESSBREAK   0
#define FEATURE_SENSE_CHAN   (1 << 0)
#define FEATURE_SENSE_PEER   (1 << 1)
#define FEATURES_COUNT   (sizeof(builtin_features) / sizeof(builtin_features[0]))

Enumerations

enum  {
  AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0), AST_FEATURE_FLAG_ONPEER = (1 << 1), AST_FEATURE_FLAG_ONSELF = (1 << 2), AST_FEATURE_FLAG_BYCALLEE = (1 << 3),
  AST_FEATURE_FLAG_BYCALLER = (1 << 4), AST_FEATURE_FLAG_BYBOTH = (3 << 3)
}
enum  { BRIDGE_OPT_PLAYTONE = (1 << 0) }

Functions

static int action_bridge (struct mansession *s, const struct message *m)
static int adsi_announce_park (struct ast_channel *chan, char *parkingexten)
 AST_APP_OPTIONS (bridge_exec_options, BEGIN_OPTIONS END_OPTIONS)
int ast_autoanswer_login (struct ast_channel *chan, void *data)
int ast_bridge_call (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
 Bridge a call, optionally allowing redirection.
static void * ast_bridge_call_thread (void *data)
static void ast_bridge_call_thread_launch (void *data)
static int ast_feature_interpret (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
static struct ast_channelast_feature_request_and_dial (struct ast_channel *caller, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, const char *language)
struct ast_channelast_get_holded_call (char *uniqueid)
int ast_hold_call (struct ast_channel *chan, struct ast_channel *peer)
static AST_LIST_HEAD_STATIC (feature_list, ast_call_feature)
int ast_masq_autoanswer_login (struct ast_channel *rchan, void *data)
int ast_masq_hold_call (struct ast_channel *rchan, struct ast_channel *peer)
int ast_masq_park_call (struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
 Park a call via a masqueraded channel.
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS,"Call Features Resource",.load=load_module,.unload=unload_module,.reload=reload,)
 AST_MUTEX_DEFINE_STATIC (holding_lock)
 AST_MUTEX_DEFINE_STATIC (parking_lock)
 AST_MUTEX_DEFINE_STATIC (autoanswer_lock)
int ast_park_call (struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
 Park a call.
char * ast_parking_ext (void)
 Determine system parking extension Returns the call parking extension for drivers that provide special call parking help.
int ast_pickup_call (struct ast_channel *chan)
 Pickup a call.
char * ast_pickup_ext (void)
 Determine system call pickup extension.
void ast_register_feature (struct ast_call_feature *feature)
 register new feature into feature_list
int ast_retrieve_call (struct ast_channel *chan, char *uniqueid)
int ast_retrieve_call_to_death (char *uniqueid)
 AST_RWLOCK_DEFINE_STATIC (features_lock)
void ast_unregister_feature (struct ast_call_feature *feature)
 unregister feature from feature_list
static void ast_unregister_features (void)
 Remove all features in the list.
static int autoanswer_exec (struct ast_channel *chan, void *data)
static int autoanswer_login_exec (struct ast_channel *chan, void *data)
static void autoanswer_reregister_extensions (void)
static int bridge_exec (struct ast_channel *chan, void *data)
static int builtin_atxfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
static int builtin_automonitor (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
static int builtin_blindtransfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
static int builtin_disconnect (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
static int builtin_parkcall (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
 support routing for one touch call parking
static int check_compat (struct ast_channel *c, struct ast_channel *newchan)
static void check_goto_on_transfer (struct ast_channel *chan)
static void * do_autoanswer_thread (void *ignore)
static void do_bridge_masquerade (struct ast_channel *chan, struct ast_channel *tmpchan)
static void * do_holding_thread (void *ignore)
static void * do_parking_thread (void *ignore)
 Take care of parked calls and unpark them if needed.
static int feature_exec_app (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
 exec an app by feature
static struct ast_call_featurefind_dynamic_feature (const char *name)
 find a feature by name
static int finishup (struct ast_channel *chan)
static int handle_autoanswer (int fd, int argc, char *argv[])
static int handle_parkedcalls (int fd, int argc, char *argv[])
static int handle_showfeatures (int fd, int argc, char *argv[])
static int load_config (void)
static int load_module (void)
static int manager_park (struct mansession *s, const struct message *m)
static int manager_parking_status (struct mansession *s, const struct message *m)
 Dump lot status.
static int metermaidstate (const char *data)
 metermaids callback from devicestate.c
static void notify_metermaids (char *exten, char *context)
 Notify metermaids that we've changed an extension.
static void park_add_hints (char *context, int start, int stop)
 Add parking hints for all defined parking lots.
static int park_call_exec (struct ast_channel *chan, void *data)
 Park a call.
static int park_call_full (struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, char *orig_chan_name)
static int park_exec (struct ast_channel *chan, void *data)
 Pickup parked call.
static void post_manager_event (const char *s, char *parkingexten, struct ast_channel *chan)
static const char * real_ctx (struct ast_channel *transferer, struct ast_channel *transferee)
 Find the context for the transfer.
static int reload (void)
static int remap_feature (const char *name, const char *value)
static int retrieve_call_exec (struct ast_channel *chan, void *data)
static void set_c_e_p (struct ast_channel *chan, const char *context, const char *ext, int pri)
 store context, priority and extension
static void set_config_flags (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
static void set_peers (struct ast_channel **caller, struct ast_channel **callee, struct ast_channel *peer, struct ast_channel *chan, int sense)
 set caller and callee according to the direction
static int unload_module (void)
static void unmap_features (void)

Variables

static struct aauseraalot
static int adsipark
static char * app_bridge = "Bridge"
static int atxfernoanswertimeout
static char * autoanswer = "Autoanswer"
static pthread_t autoanswer_thread
static char * autoanswerlogin = "AutoanswerLogin"
static char * bridge_descrip
static char * bridge_synopsis = "Bridge two channels"
static struct ast_call_feature builtin_features []
static struct ast_cli_entry cli_features []
static struct ast_cli_entry cli_show_features_deprecated
static char courtesytone [256]
static char * descrip
static char * descrip2
static char * descrip3
static char * descrip4
static int featuredigittimeout
static char * holdedcall = "HoldedCall"
static pthread_t holding_thread
static struct holdeduserholdlist
static char mandescr_bridge []
static char mandescr_park []
static struct ast_appmonitor_app = NULL
static int monitor_ok = 1
static int parkaddhints = 0
static char * parkcall = "Park"
static char * parkedcall = "ParkedCall"
static int parkedplay = 0
static int parkfindnext
static char parking_con [AST_MAX_EXTENSION]
static char parking_con_dial [AST_MAX_EXTENSION]
static char parking_ext [AST_MAX_EXTENSION]
static int parking_offset
static int parking_start
static int parking_stop
static pthread_t parking_thread
static struct parkeduserparkinglot
static int parkingtime = DEFAULT_PARK_TIME
static char parkmohclass [MAX_MUSICCLASS]
static char pickup_ext [AST_MAX_EXTENSION]
static char * registrar = "res_features"
static char showautoanswer_help []
static char showfeatures_help []
static char showparked_help []
static char * synopsis = "Answer a parked call"
static char * synopsis2 = "Park yourself"
static char * synopsis3 = "Log in for autoanswer"
static char * synopsis4 = "Autoanswer a call"
static int transferdigittimeout
static char xferfailsound [256]
static char xfersound [256]


Detailed Description

Routines implementing call features as call pickup, parking and transfer.

Author:
Mark Spencer <markster@digium.com>

Definition in file res_features.c.


Define Documentation

#define AST_MAX_WATCHERS   256

Definition at line 76 of file res_features.c.

#define DEFAULT_FEATURE_DIGIT_TIMEOUT   500

Definition at line 73 of file res_features.c.

Referenced by load_config().

#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER   15000

Definition at line 74 of file res_features.c.

Referenced by load_config().

#define DEFAULT_PARK_TIME   45000

Definition at line 71 of file res_features.c.

Referenced by load_config().

#define DEFAULT_TRANSFER_DIGIT_TIMEOUT   3000

Definition at line 72 of file res_features.c.

Referenced by load_config().

#define FEATURE_RETURN_HANGUP   -1

Definition at line 571 of file res_features.c.

Referenced by builtin_disconnect().

#define FEATURE_RETURN_KEEPTRYING   24

Definition at line 578 of file res_features.c.

Referenced by ast_feature_interpret(), and feature_exec_app().

#define FEATURE_RETURN_NO_HANGUP_PEER   AST_PBX_NO_HANGUP_PEER

Definition at line 574 of file res_features.c.

Referenced by feature_exec_app().

#define FEATURE_RETURN_PASSDIGITS   21

Definition at line 575 of file res_features.c.

Referenced by ast_bridge_call(), and ast_feature_interpret().

#define FEATURE_RETURN_PBX_KEEPALIVE   AST_PBX_KEEPALIVE

Definition at line 573 of file res_features.c.

Referenced by feature_exec_app().

#define FEATURE_RETURN_STOREDIGITS   22

Definition at line 576 of file res_features.c.

Referenced by ast_feature_interpret().

#define FEATURE_RETURN_SUCCESS   23

#define FEATURE_RETURN_SUCCESSBREAK   0

Definition at line 572 of file res_features.c.

Referenced by feature_exec_app().

#define FEATURE_SENSE_CHAN   (1 << 0)

#define FEATURE_SENSE_PEER   (1 << 1)

Definition at line 581 of file res_features.c.

Referenced by ast_bridge_call(), and set_peers().

#define FEATURES_COUNT   (sizeof(builtin_features) / sizeof(builtin_features[0]))


Enumeration Type Documentation

anonymous enum

Enumerator:
AST_FEATURE_FLAG_NEEDSDTMF 
AST_FEATURE_FLAG_ONPEER 
AST_FEATURE_FLAG_ONSELF 
AST_FEATURE_FLAG_BYCALLEE 
AST_FEATURE_FLAG_BYCALLER 
AST_FEATURE_FLAG_BYBOTH 

Definition at line 78 of file res_features.c.

00078      {
00079    AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0),
00080    AST_FEATURE_FLAG_ONPEER =    (1 << 1),
00081    AST_FEATURE_FLAG_ONSELF =    (1 << 2),
00082    AST_FEATURE_FLAG_BYCALLEE =  (1 << 3),
00083    AST_FEATURE_FLAG_BYCALLER =  (1 << 4),
00084    AST_FEATURE_FLAG_BYBOTH  =   (3 << 3),
00085 };

anonymous enum

Enumerator:
BRIDGE_OPT_PLAYTONE 

Definition at line 3379 of file res_features.c.

03379      {
03380    BRIDGE_OPT_PLAYTONE = (1 << 0),
03381 };


Function Documentation

static int action_bridge ( struct mansession s,
const struct message m 
) [static]

Definition at line 2447 of file res_features.c.

References ast_channel::_state, ast_answer(), ast_bridge_call_thread_launch(), ast_calloc, ast_channel_alloc(), ast_channel_free(), ast_channel_make_compatible(), ast_get_channel_by_name_prefix_locked(), ast_hangup(), ast_log(), ast_mutex_unlock(), AST_STATE_DOWN, AST_STATE_UP, ast_streamfile(), ast_strlen_zero(), ast_true(), ast_waitstream(), astman_get_header(), astman_send_ack(), astman_send_error(), ast_bridge_thread_obj::chan, do_bridge_masquerade(), errno, ast_channel::lock, LOG_WARNING, ast_bridge_thread_obj::peer, playtone(), and ast_bridge_thread_obj::return_to_pbx.

Referenced by load_module().

02448 {
02449    const char *channela = astman_get_header(m, "Channel1");
02450    const char *channelb = astman_get_header(m, "Channel2");
02451    const char *playtone = astman_get_header(m, "Tone");
02452    struct ast_channel *chana = NULL, *chanb = NULL;
02453    struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
02454    struct ast_bridge_thread_obj *tobj = NULL;
02455 
02456    /* make sure valid channels were specified */
02457    if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) {
02458       chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela));
02459       chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb));
02460       if (chana)
02461          ast_mutex_unlock(&chana->lock);
02462       if (chanb)
02463          ast_mutex_unlock(&chanb->lock);
02464 
02465       /* send errors if any of the channels could not be found/locked */
02466       if (!chana) {
02467          char buf[256];
02468          snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
02469          astman_send_error(s, m, buf);
02470          return 0;
02471       }
02472       if (!chanb) {
02473          char buf[256];
02474          snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
02475          astman_send_error(s, m, buf);
02476          return 0;
02477       }
02478    } else {
02479       astman_send_error(s, m, "Missing channel parameter in request");
02480       return 0;
02481    }
02482 
02483    /* Answer the channels if needed */
02484    if (chana->_state != AST_STATE_UP)
02485       ast_answer(chana);
02486    if (chanb->_state != AST_STATE_UP)
02487       ast_answer(chanb);
02488 
02489    /* create the placeholder channels and grab the other channels */
02490    if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
02491       NULL, NULL, 0, "Bridge/%s", chana->name))) {
02492       astman_send_error(s, m, "Unable to create temporary channel!");
02493       return 1;
02494    }
02495 
02496    if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
02497       NULL, NULL, 0, "Bridge/%s", chanb->name))) {
02498       astman_send_error(s, m, "Unable to create temporary channels!");
02499       ast_channel_free(tmpchana);
02500       return 1;
02501    }
02502 
02503    do_bridge_masquerade(chana, tmpchana);
02504    do_bridge_masquerade(chanb, tmpchanb);
02505    
02506    /* make the channels compatible, send error if we fail doing so */
02507    if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
02508       ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name);
02509       astman_send_error(s, m, "Could not make channels compatible for manager bridge");
02510       ast_hangup(tmpchana);
02511       ast_hangup(tmpchanb);
02512       return 1;
02513    }
02514 
02515    /* setup the bridge thread object and start the bridge */
02516    if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
02517       ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno));
02518       astman_send_error(s, m, "Unable to spawn a new bridge thread");
02519       ast_hangup(tmpchana);
02520       ast_hangup(tmpchanb);
02521       return 1;
02522    }
02523 
02524    tobj->chan = tmpchana;
02525    tobj->peer = tmpchanb;
02526    tobj->return_to_pbx = 1;
02527    
02528    if (ast_true(playtone)) {
02529       if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) {
02530          if (ast_waitstream(tmpchanb, "") < 0)
02531             ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", tmpchanb->name);
02532       }
02533    }
02534 
02535    ast_bridge_call_thread_launch(tobj);
02536 
02537    astman_send_ack(s, m, "Launched bridge thread with success");
02538 
02539    return 0;
02540 }

static int adsi_announce_park ( struct ast_channel chan,
char *  parkingexten 
) [static]

Definition at line 345 of file res_features.c.

References ADSI_JUST_CENT, ast_adsi_load_session(), ast_adsi_print(), and justify.

Referenced by park_call_full().

00346 {
00347    int res;
00348    int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
00349    char tmp[256];
00350    char *message[5] = {NULL, NULL, NULL, NULL, NULL};
00351 
00352    snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
00353    message[0] = tmp;
00354    res = ast_adsi_load_session(chan, NULL, 0, 1);
00355    if (res == -1)
00356       return res;
00357    return ast_adsi_print(chan, message, justify, 1);
00358 }

AST_APP_OPTIONS ( bridge_exec_options  ,
BEGIN_OPTIONS  END_OPTIONS 
)

int ast_autoanswer_login ( struct ast_channel chan,
void *  data 
)

Definition at line 2767 of file res_features.c.

References ast_channel::_state, ast_channel::appl, ast_add_extension2(), ast_answer(), ast_context_create(), ast_context_find(), ast_hangup(), ast_log(), AST_MAX_EXTENSION, ast_moh_start(), ast_mutex_lock(), ast_mutex_unlock(), AST_STATE_UP, ast_strdupa, ast_verbose(), aauser::chan, aauser::context, ast_channel::data, EVENT_FLAG_CALL, aauser::exten, exten, free, LOG_ERROR, LOG_NOTICE, LOG_WARNING, malloc, manager_event(), aauser::next, option_verbose, s, aauser::start, strdup, strsep(), and VERBOSE_PREFIX_2.

Referenced by ast_masq_autoanswer_login(), and autoanswer_exec().

02768 {
02769    /* We put the user in the parking list, then wake up the parking thread to be sure it looks
02770       after these channels too */
02771    struct ast_context *con;
02772    char exten[AST_MAX_EXTENSION];
02773    struct aauser *pu,*pl = NULL;
02774    char *s, *stringp, *aacontext, *aaexten = NULL;
02775 
02776    s = ast_strdupa((void *) data);
02777    stringp=s;
02778    aacontext = strsep(&stringp, "|");
02779    aaexten = strsep(&stringp, "|");
02780    if (!aaexten) {
02781        aaexten = aacontext;
02782        aacontext = NULL;
02783    }
02784    if (!aaexten) {
02785       ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n");
02786       return -1;
02787    } else {
02788       if (!aacontext) {
02789          aacontext = "default";
02790       }
02791    }
02792 
02793    ast_mutex_lock(&autoanswer_lock);
02794    pu = aalot;
02795    while(pu) {
02796       if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){
02797          if (pl)
02798             pl->next = pu->next;
02799          else
02800             aalot = pu->next;
02801          break;
02802       }
02803       pl = pu;
02804       pu = pu->next;
02805    }
02806    ast_mutex_unlock(&autoanswer_lock);
02807    if (pu) {
02808        ast_log(LOG_NOTICE, "Logout old Channel %s for %s@%s.\n",pu->chan->name, pu->exten, pu->context);
02809        manager_event(EVENT_FLAG_CALL, "AutoanswerLogout",
02810                                "Channel: %s\r\n"
02811                                "Uniqueid: %s\r\n"
02812                                "Context: %s\r\n"
02813                                "Exten: %s\r\n"
02814                            ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten);
02815        ast_hangup(pu->chan);
02816        free(pu);
02817    }
02818    pu = malloc(sizeof(struct aauser));
02819    if (pu) {
02820       memset(pu, 0, sizeof(pu));
02821       ast_mutex_lock(&autoanswer_lock);
02822       chan->appl = "Autoanswer";
02823       chan->data = NULL;
02824 
02825       pu->chan = chan;
02826       if (chan->_state != AST_STATE_UP) {
02827           ast_answer(chan);
02828       }
02829 
02830       /* Start music on hold */
02831       ast_moh_start(pu->chan, NULL, NULL);
02832       gettimeofday(&pu->start, NULL);
02833       strncpy(pu->exten, aaexten, sizeof(pu->exten)-1);
02834       strncpy(pu->context, aacontext, sizeof(pu->exten)-1);
02835       pu->next = aalot;
02836       aalot = pu;
02837       con = ast_context_find(aacontext);
02838       if (!con) {
02839          con = ast_context_create(NULL,aacontext, registrar);
02840          if (!con) {
02841             ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", aacontext);
02842          }
02843       }
02844       if (con) {
02845          snprintf(exten, sizeof(exten), "%s", aaexten);
02846          ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)data), free, registrar);
02847       }
02848 
02849       ast_mutex_unlock(&autoanswer_lock);
02850       /* Wake up the (presumably select()ing) thread */
02851       pthread_kill(autoanswer_thread, SIGURG);
02852       if (option_verbose > 1)
02853          ast_verbose(VERBOSE_PREFIX_2 "Autoanswer login from %s for %s@%s.\n", pu->chan->name, pu->exten, pu->context);
02854          manager_event(EVENT_FLAG_CALL, "AutoanswerLogin",
02855                                 "Channel: %s\r\n"
02856                                 "Uniqueid: %s\r\n"
02857                "Context: %s\r\n"
02858                         "Exten: %s\r\n"
02859                         ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten);
02860 
02861          return 0;
02862    } else {
02863       ast_log(LOG_WARNING, "Out of memory\n");
02864       return -1;
02865    }
02866    return 0;
02867 }

int ast_bridge_call ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config 
)

Bridge a call, optionally allowing redirection.

append the event to featurecode. we rely on the string being zero-filled, and not overflowing it.

Todo:
XXX how do we guarantee the latter ?

Definition at line 1437 of file res_features.c.

References ast_channel::appl, ast_answer(), ast_cdr_alloc(), ast_cdr_appenduserfield(), ast_cdr_discard(), AST_CDR_FLAG_LOCKED, ast_cdr_init(), ast_cdr_merge(), ast_cdr_setdestchan(), ast_cdr_setuserfield(), ast_cdr_start(), ast_channel_bridge(), ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OPTION, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_dtmf_stream(), ast_feature_interpret(), AST_FEATURE_PLAY_WARNING, AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_DTMF_BEGIN, ast_frfree, ast_indicate(), ast_indicate_data(), ast_log(), AST_OPTION_AUDIO_MODE, AST_OPTION_FLAG_REQUEST, AST_OPTION_RELAXDTMF, AST_OPTION_TDD, AST_OPTION_TONE_VERIFY, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_channel::cdr, ast_cdr::channel, ast_option_header::data, ast_frame::data, ast_channel::data, ast_frame::datalen, ast_cdr::dstchannel, ast_bridge_config::end_sound, f, FEATURE_MAX_LEN, FEATURE_RETURN_PASSDIGITS, FEATURE_RETURN_SUCCESS, FEATURE_SENSE_CHAN, FEATURE_SENSE_PEER, ast_bridge_config::feature_timer, ast_bridge_config::features_callee, ast_bridge_config::features_caller, ast_bridge_config::firstpass, ast_option_header::flag, ast_frame::frametype, free, LOG_DEBUG, LOG_WARNING, ast_option_header::option, option_debug, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), ast_bridge_config::play_warning, set_config_flags(), ast_bridge_config::start_sound, ast_bridge_config::start_time, ast_frame::subclass, ast_cdr::userfield, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound.

Referenced by app_exec(), ast_bridge_call_thread(), ast_retrieve_call(), autoanswer_exec(), bridge_exec(), builtin_atxfer(), park_exec(), and try_calling().

01438 {
01439    /* Copy voice back and forth between the two channels.  Give the peer
01440       the ability to transfer calls with '#<extension' syntax. */
01441    struct ast_frame *f;
01442    struct ast_channel *who;
01443    char chan_featurecode[FEATURE_MAX_LEN + 1]="";
01444    char peer_featurecode[FEATURE_MAX_LEN + 1]="";
01445    int res;
01446    int diff;
01447    int hasfeatures=0;
01448    int hadfeatures=0;
01449    struct ast_option_header *aoh;
01450    struct ast_bridge_config backup_config;
01451    struct ast_cdr *bridge_cdr;
01452 
01453    memset(&backup_config, 0, sizeof(backup_config));
01454 
01455    config->start_time = ast_tvnow();
01456 
01457    if (chan && peer) {
01458       pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
01459       pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
01460    } else if (chan)
01461       pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
01462 
01463    if (monitor_ok) {
01464       const char *monitor_exec;
01465       struct ast_channel *src = NULL;
01466       if (!monitor_app) { 
01467          if (!(monitor_app = pbx_findapp("Monitor")))
01468             monitor_ok=0;
01469       }
01470       if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 
01471          src = chan;
01472       else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
01473          src = peer;
01474       if (monitor_app && src) {
01475          char *tmp = ast_strdupa(monitor_exec);
01476          pbx_exec(src, monitor_app, tmp);
01477       }
01478    }
01479    
01480    set_config_flags(chan, peer, config);
01481    config->firstpass = 1;
01482 
01483    /* Answer if need be */
01484    if (ast_answer(chan))
01485       return -1;
01486    peer->appl = "Bridged Call";
01487    peer->data = chan->name;
01488 
01489    /* copy the userfield from the B-leg to A-leg if applicable */
01490    if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
01491       char tmp[256];
01492       if (!ast_strlen_zero(chan->cdr->userfield)) {
01493          snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield);
01494          ast_cdr_appenduserfield(chan, tmp);
01495       } else
01496          ast_cdr_setuserfield(chan, peer->cdr->userfield);
01497       /* free the peer's cdr without ast_cdr_free complaining */
01498       free(peer->cdr);
01499       peer->cdr = NULL;
01500    }
01501 
01502    for (;;) {
01503       struct ast_channel *other; /* used later */
01504 
01505       res = ast_channel_bridge(chan, peer, config, &f, &who);
01506 
01507       if (config->feature_timer) {
01508          /* Update time limit for next pass */
01509          diff = ast_tvdiff_ms(ast_tvnow(), config->start_time);
01510          config->feature_timer -= diff;
01511          if (hasfeatures) {
01512             /* Running on backup config, meaning a feature might be being
01513                activated, but that's no excuse to keep things going 
01514                indefinitely! */
01515             if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) {
01516                if (option_debug)
01517                   ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
01518                config->feature_timer = 0;
01519                who = chan;
01520                if (f)
01521                   ast_frfree(f);
01522                f = NULL;
01523                res = 0;
01524             } else if (config->feature_timer <= 0) {
01525                /* Not *really* out of time, just out of time for
01526                   digits to come in for features. */
01527                if (option_debug)
01528                   ast_log(LOG_DEBUG, "Timed out for feature!\n");
01529                if (!ast_strlen_zero(peer_featurecode)) {
01530                   ast_dtmf_stream(chan, peer, peer_featurecode, 0);
01531                   memset(peer_featurecode, 0, sizeof(peer_featurecode));
01532                }
01533                if (!ast_strlen_zero(chan_featurecode)) {
01534                   ast_dtmf_stream(peer, chan, chan_featurecode, 0);
01535                   memset(chan_featurecode, 0, sizeof(chan_featurecode));
01536                }
01537                if (f)
01538                   ast_frfree(f);
01539                hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
01540                if (!hasfeatures) {
01541                   /* Restore original (possibly time modified) bridge config */
01542                   memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
01543                   memset(&backup_config, 0, sizeof(backup_config));
01544                }
01545                hadfeatures = hasfeatures;
01546                /* Continue as we were */
01547                continue;
01548             } else if (!f) {
01549                /* The bridge returned without a frame and there is a feature in progress.
01550                 * However, we don't think the feature has quite yet timed out, so just
01551                 * go back into the bridge. */
01552                continue;
01553             }
01554          } else {
01555             if (config->feature_timer <=0) {
01556                /* We ran out of time */
01557                config->feature_timer = 0;
01558                who = chan;
01559                if (f)
01560                   ast_frfree(f);
01561                f = NULL;
01562                res = 0;
01563             }
01564          }
01565       }
01566       if (res < 0) {
01567          if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer))
01568             ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
01569          return -1;
01570       }
01571       
01572       if (!f || (f->frametype == AST_FRAME_CONTROL &&
01573             (f->subclass == AST_CONTROL_HANGUP || f->subclass == AST_CONTROL_BUSY || 
01574                f->subclass == AST_CONTROL_CONGESTION ) ) ) {
01575          res = -1;
01576          break;
01577       }
01578       /* many things should be sent to the 'other' channel */
01579       other = (who == chan) ? peer : chan;
01580       if (f->frametype == AST_FRAME_CONTROL) {
01581          switch (f->subclass) {
01582          case AST_CONTROL_RINGING:
01583          case AST_CONTROL_FLASH:
01584          case -1:
01585             ast_indicate(other, f->subclass);
01586             break;
01587          case AST_CONTROL_HOLD:
01588          case AST_CONTROL_UNHOLD:
01589             ast_indicate_data(other, f->subclass, f->data, f->datalen);
01590             break;
01591          case AST_CONTROL_OPTION:
01592             aoh = f->data;
01593             /* Forward option Requests, but only ones we know are safe
01594              * These are ONLY sent by chan_iax2 and I'm not convinced that
01595              * they are useful. I haven't deleted them entirely because I
01596              * just am not sure of the ramifications of removing them. */
01597             if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
01598                   switch (ntohs(aoh->option)) {
01599                case AST_OPTION_TONE_VERIFY:
01600                case AST_OPTION_TDD:
01601                case AST_OPTION_RELAXDTMF:
01602                case AST_OPTION_AUDIO_MODE:
01603                   ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 
01604                      f->datalen - sizeof(struct ast_option_header), 0);
01605                }
01606             }
01607             break;
01608          }
01609       } else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
01610          /* eat it */
01611       } else if (f->frametype == AST_FRAME_DTMF) {
01612          char *featurecode;
01613          int sense;
01614 
01615          hadfeatures = hasfeatures;
01616          /* This cannot overrun because the longest feature is one shorter than our buffer */
01617          if (who == chan) {
01618             sense = FEATURE_SENSE_CHAN;
01619             featurecode = chan_featurecode;
01620          } else  {
01621             sense = FEATURE_SENSE_PEER;
01622             featurecode = peer_featurecode;
01623          }
01624          /*! append the event to featurecode. we rely on the string being zero-filled, and
01625           * not overflowing it. 
01626           * \todo XXX how do we guarantee the latter ?
01627           */
01628          featurecode[strlen(featurecode)] = f->subclass;
01629          /* Get rid of the frame before we start doing "stuff" with the channels */
01630          ast_frfree(f);
01631          f = NULL;
01632          config->feature_timer = backup_config.feature_timer;
01633          res = ast_feature_interpret(chan, peer, config, featurecode, sense);
01634          switch(res) {
01635          case FEATURE_RETURN_PASSDIGITS:
01636             ast_dtmf_stream(other, who, featurecode, 0);
01637             /* Fall through */
01638          case FEATURE_RETURN_SUCCESS:
01639             memset(featurecode, 0, sizeof(chan_featurecode));
01640             break;
01641          }
01642          if (res >= FEATURE_RETURN_PASSDIGITS) {
01643             res = 0;
01644          } else 
01645             break;
01646          hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
01647          if (hadfeatures && !hasfeatures) {
01648             /* Restore backup */
01649             memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
01650             memset(&backup_config, 0, sizeof(struct ast_bridge_config));
01651          } else if (hasfeatures) {
01652             if (!hadfeatures) {
01653                /* Backup configuration */
01654                memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
01655                /* Setup temporary config options */
01656                config->play_warning = 0;
01657                ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
01658                ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
01659                config->warning_freq = 0;
01660                config->warning_sound = NULL;
01661                config->end_sound = NULL;
01662                config->start_sound = NULL;
01663                config->firstpass = 0;
01664             }
01665             config->start_time = ast_tvnow();
01666             config->feature_timer = featuredigittimeout;
01667             if (option_debug)
01668                ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->feature_timer);
01669          }
01670       }
01671       if (f)
01672          ast_frfree(f);
01673 
01674    }
01675 
01676    /* arrange the cdrs */
01677    bridge_cdr = ast_cdr_alloc();
01678    if (bridge_cdr) {
01679       if (chan->cdr && peer->cdr) { /* both of them? merge */
01680          ast_channel_lock(chan);  /* lock the channel before modifing cdrs */
01681          ast_cdr_init(bridge_cdr,chan); /* seems more logicaller to use the  destination as a base, but, really, it's random */
01682          ast_cdr_start(bridge_cdr); /* now is the time to start */
01683 
01684          /* absorb the channel cdr */
01685          ast_cdr_merge(bridge_cdr, chan->cdr);
01686          if (!ast_test_flag(chan->cdr, AST_CDR_FLAG_LOCKED)) 
01687             ast_cdr_discard(chan->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
01688 
01689          chan->cdr = NULL; /* remove pointer to freed memory before releasing the lock */
01690 
01691          ast_channel_unlock(chan);
01692          
01693          /* absorb the peer cdr */
01694          ast_channel_lock(peer);
01695          ast_cdr_merge(bridge_cdr, peer->cdr);
01696          if (!ast_test_flag(peer->cdr, AST_CDR_FLAG_LOCKED))
01697             ast_cdr_discard(peer->cdr); /* if locked cdrs are in peer, they are taken over in the merge */
01698          
01699          peer->cdr = NULL;
01700          ast_channel_unlock(peer);
01701 
01702          ast_channel_lock(chan);
01703          chan->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
01704          ast_channel_unlock(chan);
01705 
01706       } else if (chan->cdr) {
01707 
01708          ast_channel_lock(chan); /* Lock before modifying CDR */
01709          /* take the cdr from the channel - literally */
01710          ast_cdr_init(bridge_cdr,chan);
01711          /* absorb this data */
01712          ast_cdr_merge(bridge_cdr, chan->cdr);
01713          if (!ast_test_flag(chan->cdr, AST_CDR_FLAG_LOCKED))
01714             ast_cdr_discard(chan->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
01715          chan->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
01716          ast_channel_unlock(chan);
01717       } else if (peer->cdr) {
01718          ast_channel_lock(peer); /* Lock before modifying CDR */
01719          /* take the cdr from the peer - literally */
01720          ast_cdr_init(bridge_cdr,peer);
01721          /* absorb this data */
01722          ast_cdr_merge(bridge_cdr, peer->cdr);
01723          if (!ast_test_flag(peer->cdr, AST_CDR_FLAG_LOCKED))
01724             ast_cdr_discard(peer->cdr); /* if locked cdrs are in chan, they are taken over in the merge */
01725          peer->cdr = NULL;
01726          peer->cdr = bridge_cdr; /* make this available to the rest of the world via the chan while the call is in progress */
01727          ast_channel_unlock(peer);
01728       } else {
01729          ast_channel_lock(chan); /* Lock before modifying CDR */
01730          /* make up a new cdr */
01731          ast_cdr_init(bridge_cdr,chan); /* eh, just pick one of them */
01732          chan->cdr = bridge_cdr; /*  */
01733          ast_channel_unlock(chan);
01734       }
01735       if (ast_strlen_zero(bridge_cdr->dstchannel)) {
01736          if (strcmp(bridge_cdr->channel, peer->name) != 0)
01737             ast_cdr_setdestchan(bridge_cdr, peer->name);
01738          else
01739             ast_cdr_setdestchan(bridge_cdr, chan->name);
01740       }
01741    }
01742    return res;
01743 }

static void* ast_bridge_call_thread ( void *  data  )  [static]

Definition at line 285 of file res_features.c.

References ast_channel::appl, ast_bridge_call(), ast_cdr_reset(), ast_cdr_setdestchan(), ast_check_hangup(), ast_hangup(), ast_log(), ast_pbx_start(), AST_PBX_SUCCESS, ast_bridge_thread_obj::bconfig, ast_channel::cdr, ast_bridge_thread_obj::chan, ast_channel::data, free, LOG_VERBOSE, LOG_WARNING, ast_bridge_thread_obj::peer, and ast_bridge_thread_obj::return_to_pbx.

Referenced by ast_bridge_call_thread_launch().

00286 {
00287    struct ast_bridge_thread_obj *tobj = data;
00288    int res;
00289 
00290    tobj->chan->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge";
00291    tobj->chan->data = tobj->peer->name;
00292    tobj->peer->appl = !tobj->return_to_pbx ? "Transferred Call" : "ManagerBridge";
00293    tobj->peer->data = tobj->chan->name;
00294 
00295    if (tobj->chan->cdr) {
00296       ast_cdr_reset(tobj->chan->cdr, NULL);
00297       ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name);
00298    }
00299    if (tobj->peer->cdr) {
00300       ast_cdr_reset(tobj->peer->cdr, NULL);
00301       ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name);
00302    }
00303 
00304    ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
00305 
00306    if (tobj->return_to_pbx) {
00307       if (!ast_check_hangup(tobj->peer)) {
00308          ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", tobj->peer->name);
00309          res = ast_pbx_start(tobj->peer);
00310          if (res != AST_PBX_SUCCESS)
00311             ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", tobj->peer->name);
00312       } else
00313          ast_hangup(tobj->peer);
00314       if (!ast_check_hangup(tobj->chan)) {
00315          ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", tobj->chan->name);
00316          res = ast_pbx_start(tobj->chan);
00317          if (res != AST_PBX_SUCCESS)
00318             ast_log(LOG_WARNING, "FAILED continuing PBX on chan %s\n", tobj->chan->name);
00319       } else
00320          ast_hangup(tobj->chan);
00321    } else {
00322       ast_hangup(tobj->chan);
00323       ast_hangup(tobj->peer);
00324    }
00325 
00326    free(tobj);
00327 
00328    return NULL;
00329 }

static void ast_bridge_call_thread_launch ( void *  data  )  [static]

Definition at line 331 of file res_features.c.

References ast_bridge_call_thread(), ast_pthread_create, and thread.

Referenced by action_bridge(), and builtin_atxfer().

00332 {
00333    pthread_t thread;
00334    pthread_attr_t attr;
00335    struct sched_param sched;
00336 
00337    pthread_attr_init(&attr);
00338    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00339    ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
00340    pthread_attr_destroy(&attr);
00341    memset(&sched, 0, sizeof(sched));
00342    pthread_setschedparam(thread, SCHED_RR, &sched);
00343 }

static int ast_feature_interpret ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
char *  code,
int  sense 
) [static]

Definition at line 1147 of file res_features.c.

References ast_copy_flags, AST_FLAGS_ALL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_rwlock_rdlock(), ast_rwlock_unlock(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verbose(), ast_call_feature::exten, exten, ast_call_feature::feature_mask, FEATURE_RETURN_KEEPTRYING, FEATURE_RETURN_PASSDIGITS, FEATURE_RETURN_STOREDIGITS, FEATURE_SENSE_CHAN, ast_bridge_config::features_callee, ast_bridge_config::features_caller, FEATURES_COUNT, find_dynamic_feature(), ast_flags::flags, LOG_DEBUG, ast_call_feature::operation, option_debug, option_verbose, pbx_builtin_getvar_helper(), ast_call_feature::sname, strsep(), and VERBOSE_PREFIX_3.

Referenced by ast_bridge_call().

01148 {
01149    int x;
01150    struct ast_flags features;
01151    int res = FEATURE_RETURN_PASSDIGITS;
01152    struct ast_call_feature *feature;
01153    const char *dynamic_features;
01154    char *tmp, *tok;
01155 
01156    if (sense == FEATURE_SENSE_CHAN) {
01157       ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);   
01158       dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
01159    } else {
01160       ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);   
01161       dynamic_features = pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES");
01162    }
01163    if (option_debug > 2)
01164       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);
01165 
01166    ast_rwlock_rdlock(&features_lock);
01167    for (x = 0; x < FEATURES_COUNT; x++) {
01168       if ((ast_test_flag(&features, builtin_features[x].feature_mask)) &&
01169           !ast_strlen_zero(builtin_features[x].exten)) {
01170          /* Feature is up for consideration */
01171          if (!strcmp(builtin_features[x].exten, code)) {
01172             res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
01173             break;
01174          } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
01175             if (res == FEATURE_RETURN_PASSDIGITS)
01176                res = FEATURE_RETURN_STOREDIGITS;
01177          }
01178       }
01179    }
01180    ast_rwlock_unlock(&features_lock);
01181 
01182    if (ast_strlen_zero(dynamic_features))
01183       return res;
01184 
01185    tmp = ast_strdupa(dynamic_features);
01186 
01187    while ((tok = strsep(&tmp, "#"))) {
01188       AST_LIST_LOCK(&feature_list); 
01189       if (!(feature = find_dynamic_feature(tok))) {
01190          AST_LIST_UNLOCK(&feature_list);
01191          continue;
01192       }
01193          
01194       /* Feature is up for consideration */
01195       if (!strcmp(feature->exten, code)) {
01196          if (option_verbose > 2)
01197             ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok);
01198          res = feature->operation(chan, peer, config, code, sense, feature);
01199          if (res != FEATURE_RETURN_KEEPTRYING) {
01200             AST_LIST_UNLOCK(&feature_list);
01201             break;
01202          }
01203          res = FEATURE_RETURN_PASSDIGITS;
01204       } else if (!strncmp(feature->exten, code, strlen(code)))
01205          res = FEATURE_RETURN_STOREDIGITS;
01206 
01207       AST_LIST_UNLOCK(&feature_list);
01208    }
01209    
01210    return res;
01211 }

static struct ast_channel * ast_feature_request_and_dial ( struct ast_channel caller,
const char *  type,
int  format,
void *  data,
int  timeout,
int *  outstate,
const char *  cid_num,
const char *  cid_name,
const char *  language 
) [static, read]

Todo:
XXX Check - this is very similar to the code in channel.c

Definition at line 1256 of file res_features.c.

References ast_channel::_softhangup, ast_channel::_state, ast_call(), AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_inherit_variables(), ast_check_hangup(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_TEXT, ast_frfree, ast_hangup(), ast_indicate(), ast_log(), ast_read(), ast_request(), ast_rwlock_rdlock(), ast_rwlock_unlock(), ast_set_callerid(), AST_STATE_UP, ast_string_field_set, ast_verbose(), ast_waitfor_n(), ast_channel::cdr, ast_call_feature::exten, f, FEATURES_COUNT, ast_frame::frametype, ast_channel::hangupcause, len, LOG_NOTICE, LOG_WARNING, option_verbose, pbx_builtin_setvar_helper(), ast_frame::subclass, and VERBOSE_PREFIX_3.

Referenced by builtin_atxfer().

01257 {
01258    int state = 0;
01259    int cause = 0;
01260    int to;
01261    struct ast_channel *chan;
01262    struct ast_channel *monitor_chans[2];
01263    struct ast_channel *active_channel;
01264    int res = 0, ready = 0;
01265    
01266    if ((chan = ast_request(type, format, data, &cause))) {
01267       ast_set_callerid(chan, cid_num, cid_name, cid_num);
01268       ast_string_field_set(chan, language, language);
01269       ast_channel_inherit_variables(caller, chan); 
01270       pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", caller->name);
01271       if (!chan->cdr) {
01272          chan->cdr=ast_cdr_alloc();
01273          if (chan->cdr) {
01274             ast_cdr_init(chan->cdr, chan); /* initilize our channel's cdr */
01275             ast_cdr_start(chan->cdr);
01276          }
01277       }
01278          
01279       if (!ast_call(chan, data, timeout)) {
01280          struct timeval started;
01281          int x, len = 0;
01282          char *disconnect_code = NULL, *dialed_code = NULL;
01283 
01284          ast_indicate(caller, AST_CONTROL_RINGING);
01285          /* support dialing of the featuremap disconnect code while performing an attended tranfer */
01286          ast_rwlock_rdlock(&features_lock);
01287          for (x = 0; x < FEATURES_COUNT; x++) {
01288             if (strcasecmp(builtin_features[x].sname, "disconnect"))
01289                continue;
01290 
01291             disconnect_code = builtin_features[x].exten;
01292             len = strlen(disconnect_code) + 1;
01293             dialed_code = alloca(len);
01294             memset(dialed_code, 0, len);
01295             break;
01296          }
01297          ast_rwlock_unlock(&features_lock);
01298          x = 0;
01299          started = ast_tvnow();
01300          to = timeout;
01301          while (!ast_check_hangup(caller) && timeout && (chan->_state != AST_STATE_UP)) {
01302             struct ast_frame *f = NULL;
01303 
01304             monitor_chans[0] = caller;
01305             monitor_chans[1] = chan;
01306             active_channel = ast_waitfor_n(monitor_chans, 2, &to);
01307 
01308             /* see if the timeout has been violated */
01309             if(ast_tvdiff_ms(ast_tvnow(), started) > timeout) {
01310                state = AST_CONTROL_UNHOLD;
01311                ast_log(LOG_NOTICE, "We exceeded our AT-timeout\n");
01312                break; /*doh! timeout*/
01313             }
01314 
01315             if (!active_channel)
01316                continue;
01317 
01318             if (chan && (chan == active_channel)){
01319                f = ast_read(chan);
01320                if (f == NULL) { /*doh! where'd he go?*/
01321                   state = AST_CONTROL_HANGUP;
01322                   res = 0;
01323                   break;
01324                }
01325                
01326                if (f->frametype == AST_FRAME_CONTROL || f->frametype == AST_FRAME_DTMF || f->frametype == AST_FRAME_TEXT) {
01327                   if (f->subclass == AST_CONTROL_RINGING) {
01328                      state = f->subclass;
01329                      if (option_verbose > 2)
01330                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", chan->name);
01331                      ast_indicate(caller, AST_CONTROL_RINGING);
01332                   } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
01333                      state = f->subclass;
01334                      if (option_verbose > 2)
01335                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", chan->name);
01336                      ast_indicate(caller, AST_CONTROL_BUSY);
01337                      ast_frfree(f);
01338                      f = NULL;
01339                      break;
01340                   } else if (f->subclass == AST_CONTROL_ANSWER) {
01341                      /* This is what we are hoping for */
01342                      state = f->subclass;
01343                      ast_frfree(f);
01344                      f = NULL;
01345                      ready=1;
01346                      break;
01347                   } else if (f->subclass != -1) {
01348                      ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass);
01349                   }
01350                   /* else who cares */
01351                }
01352 
01353             } else if (caller && (active_channel == caller)) {
01354                f = ast_read(caller);
01355                if (f == NULL) { /*doh! where'd he go?*/
01356                   if (caller->_softhangup && !chan->_softhangup) {
01357                      /* make this a blind transfer */
01358                      ready = 1;
01359                      break;
01360                   }
01361                   state = AST_CONTROL_HANGUP;
01362                   res = 0;
01363                   break;
01364                }
01365                
01366                if (f->frametype == AST_FRAME_DTMF) {
01367                   dialed_code[x++] = f->subclass;
01368                   dialed_code[x] = '\0';
01369                   if (strlen(dialed_code) == len) {
01370                      x = 0;
01371                   } else if (x && strncmp(dialed_code, disconnect_code, x)) {
01372                      x = 0;
01373                      dialed_code[x] = '\0';
01374                   }
01375                   if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
01376                      /* Caller Canceled the call */
01377                      state = AST_CONTROL_UNHOLD;
01378                      ast_frfree(f);
01379                      f = NULL;
01380                      break;
01381                   }
01382                }
01383             }
01384             if (f)
01385                ast_frfree(f);
01386          } /* end while */
01387       } else
01388          ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
01389    } else {
01390       ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
01391       switch(cause) {
01392       case AST_CAUSE_BUSY:
01393          state = AST_CONTROL_BUSY;
01394          break;
01395       case AST_CAUSE_CONGESTION:
01396          state = AST_CONTROL_CONGESTION;
01397          break;
01398       }
01399    }
01400    
01401    ast_indicate(caller, -1);
01402    if (chan && ready) {
01403       if (chan->_state == AST_STATE_UP) 
01404          state = AST_CONTROL_ANSWER;
01405       res = 0;
01406    } else if(chan) {
01407       res = -1;
01408       ast_hangup(chan);
01409       chan = NULL;
01410    } else {
01411       res = -1;
01412    }
01413    
01414    if (outstate)
01415       *outstate = state;
01416 
01417    if (chan && res <= 0) {
01418       if (chan->cdr || (chan->cdr = ast_cdr_alloc())) {
01419          char tmp[256];
01420          ast_cdr_init(chan->cdr, chan);
01421          snprintf(tmp, 256, "%s/%s", type, (char *)data);
01422          ast_cdr_setapp(chan->cdr,"Dial",tmp);
01423          ast_cdr_update(chan);
01424          ast_cdr_start(chan->cdr);
01425          ast_cdr_end(chan->cdr);
01426          /* If the cause wasn't handled properly */
01427          if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
01428             ast_cdr_failed(chan->cdr);
01429       } else {
01430          ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
01431       }
01432    }
01433    
01434    return chan;
01435 }

struct ast_channel* ast_get_holded_call ( char *  uniqueid  )  [read]

Definition at line 2252 of file res_features.c.

References ast_get_channel_by_uniqueid_locked(), ast_log(), ast_moh_stop(), ast_mutex_lock(), ast_mutex_unlock(), ast_verbose(), free, LOG_WARNING, holdeduser::next, option_verbose, holdeduser::uniqueid, and VERBOSE_PREFIX_3.

Referenced by ast_retrieve_call(), and ast_retrieve_call_to_death().

02253 {
02254    int res=-1;
02255    struct ast_channel *peer=NULL;
02256    struct holdeduser *pu, *pl=NULL;
02257 
02258    ast_mutex_lock(&holding_lock);
02259    pu = holdlist;
02260    while(pu) {
02261       if (!strncmp(uniqueid,pu->uniqueid,sizeof(pu->uniqueid))) {
02262          if (pl)
02263             pl->next = pu->next;
02264          else
02265             holdlist = pu->next;
02266          break;
02267       }
02268       pl = pu;
02269       pu = pu->next;
02270    }
02271    ast_mutex_unlock(&holding_lock);
02272    if (pu) {
02273       peer = ast_get_channel_by_uniqueid_locked(pu->uniqueid);
02274       free(pu);
02275       if (peer) {
02276           res=0;
02277           if (option_verbose > 2)
02278          ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name);
02279           ast_moh_stop(peer);
02280           return peer;
02281       } else {
02282           if (option_verbose > 2)
02283          ast_verbose(VERBOSE_PREFIX_3 "Could not find channel with uniqueid %s.\n", uniqueid);
02284           return NULL;
02285       }
02286    } else {
02287       ast_log(LOG_WARNING, "Could not find held channel with uniqueid %s to retrieve.\n", uniqueid);
02288    }
02289    return NULL;
02290 }

int ast_hold_call ( struct ast_channel chan,
struct ast_channel peer 
)

Definition at line 2110 of file res_features.c.

References ast_channel::appl, ast_log(), ast_moh_start(), ast_mutex_lock(), ast_mutex_unlock(), holdeduser::chan, ast_channel::data, EVENT_FLAG_CALL, LOG_WARNING, malloc, manager_event(), holdeduser::next, holdeduser::start, holdeduser::uniqueid, and holdeduser::uniqueidpeer.

Referenced by ast_masq_hold_call().

02111 {
02112    /* We put the user in the parking list, then wake up the parking thread to be sure it looks
02113       after these channels too */
02114    struct holdeduser *pu;
02115    pu = malloc(sizeof(struct holdeduser));
02116    if (pu) {
02117       memset(pu, 0, sizeof(pu));
02118       ast_mutex_lock(&holding_lock);
02119       chan->appl = "Holded Call";
02120       chan->data = NULL;
02121 
02122       pu->chan = chan;
02123       strncpy(pu->uniqueid, chan->uniqueid, sizeof(pu->uniqueid));
02124       strncpy(pu->uniqueidpeer, peer->uniqueid, sizeof(pu->uniqueidpeer));
02125       /* Start music on hold */
02126       ast_moh_start(pu->chan, NULL, NULL);
02127       gettimeofday(&pu->start, NULL);
02128       pu->next = holdlist;
02129       holdlist = pu;
02130       ast_mutex_unlock(&holding_lock);
02131       /* Wake up the (presumably select()ing) thread */
02132       pthread_kill(holding_thread, SIGURG);
02133 
02134       manager_event(EVENT_FLAG_CALL, "HoldedCall",
02135                          "Channel1: %s\r\n"
02136                          "Channel2: %s\r\n"
02137                       "Uniqueid1: %s\r\n"
02138                       "Uniqueid2: %s\r\n"
02139                              ,pu->chan->name, peer->name, pu->chan->uniqueid, peer->uniqueid);
02140 
02141    } else {
02142       ast_log(LOG_WARNING, "Out of memory\n");
02143       return -1;
02144    }
02145    return 0;
02146 }

static AST_LIST_HEAD_STATIC ( feature_list  ,
ast_call_feature   
) [static]

int ast_masq_autoanswer_login ( struct ast_channel rchan,
void *  data 
)

Definition at line 2721 of file res_features.c.

References ast_autoanswer_login(), ast_channel_alloc(), ast_channel_masquerade(), ast_frfree, ast_log(), ast_read(), AST_STATE_DOWN, ast_string_field_build, ast_channel::context, ast_channel::exten, f, LOG_WARNING, name, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat.

Referenced by autoanswer_login_exec().

02722 {
02723    struct ast_channel *chan;
02724    struct ast_frame *f;
02725    /* Make a new, fake channel that we'll use to masquerade in the real one */
02726    chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Autoanswer/%s", rchan->name);
02727    if (chan) {
02728       /* Let us keep track of the channel name */
02729       ast_string_field_build(chan, name, "Autoanswer/%s",rchan->name);
02730       /* Make formats okay */
02731       chan->readformat = rchan->readformat;
02732       chan->writeformat = rchan->writeformat;
02733       ast_channel_masquerade(chan, rchan);
02734       /* Setup the extensions and such */
02735       strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
02736       strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
02737       chan->priority = rchan->priority;
02738       /* might be dirty but we want trackable channels */
02739       ast_string_field_build(chan, uniqueid, "%s",rchan->uniqueid);
02740       /* Make the masq execute */
02741       f = ast_read(chan);
02742       if (f)
02743          ast_frfree(f);
02744       ast_autoanswer_login(chan, data);
02745    } else {
02746       ast_log(LOG_WARNING, "Unable to create aa channel\n");
02747       return -1;
02748    }
02749    return 0;
02750 }

int ast_masq_hold_call ( struct ast_channel rchan,
struct ast_channel peer 
)

Definition at line 2148 of file res_features.c.

References ast_channel_alloc(), ast_channel_masquerade(), ast_frfree, ast_hold_call(), ast_log(), ast_read(), AST_STATE_DOWN, ast_string_field_build, ast_channel::context, ast_channel::exten, f, LOG_WARNING, name, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat.

02149 {
02150    struct ast_channel *chan;
02151    struct ast_frame *f;
02152    /* Make a new, fake channel that we'll use to masquerade in the real one */
02153    chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Onhold/%s",rchan->name);
02154    if (chan) {
02155       /* Let us keep track of the channel name */
02156       ast_string_field_build(chan, name, "Onhold/%s",rchan->name);
02157       /* Make formats okay */
02158       chan->readformat = rchan->readformat;
02159       chan->writeformat = rchan->writeformat;
02160       ast_channel_masquerade(chan, rchan);
02161       /* Setup the extensions and such */
02162       strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
02163       strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
02164       chan->priority = rchan->priority;
02165       /* this might be dirty, but we need to preserve the uniqueid */
02166       ast_string_field_build(chan, uniqueid, "%s",rchan->uniqueid);
02167       /* Make the masq execute */
02168       f = ast_read(chan);
02169       if (f)
02170          ast_frfree(f);
02171       ast_hold_call(chan, peer);
02172       return -1;
02173    } else {
02174       ast_log(LOG_WARNING, "Unable to create holded channel\n");
02175       return -1;
02176    }
02177    return 0;
02178 }

int ast_masq_park_call ( struct ast_channel rchan,
struct ast_channel host,
int  timeout,
int *  extout 
)

Park a call via a masqueraded channel.

Parameters:
rchan the real channel to be parked
host the channel to have the parking read to Masquerade the channel rchan into a new, empty channel which is then parked with ast_park_call
timeout is a timeout in milliseconds
extout is a parameter to an int that will hold the parked location, or NULL if you want

Definition at line 538 of file res_features.c.

References ast_channel::amaflags, ast_channel_alloc(), ast_channel_masquerade(), ast_frfree, ast_log(), ast_read(), AST_STATE_DOWN, ast_strdupa, ast_channel::context, ast_channel::exten, f, LOG_WARNING, park_call_full(), ast_channel::priority, ast_channel::readformat, set_c_e_p(), and ast_channel::writeformat.

Referenced by manager_park(), mgcp_ss(), parkandannounce_exec(), rpt_exec(), and ss_thread().

00539 {
00540    struct ast_channel *chan;
00541    struct ast_frame *f;
00542    char *orig_chan_name = NULL;
00543 
00544    /* Make a new, fake channel that we'll use to masquerade in the real one */
00545    if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) {
00546       ast_log(LOG_WARNING, "Unable to create parked channel\n");
00547       return -1;
00548    }
00549 
00550    /* Make formats okay */
00551    chan->readformat = rchan->readformat;
00552    chan->writeformat = rchan->writeformat;
00553    ast_channel_masquerade(chan, rchan);
00554 
00555    /* Setup the extensions and such */
00556    set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
00557 
00558    /* Make the masq execute */
00559    f = ast_read(chan);
00560    if (f)
00561       ast_frfree(f);
00562 
00563    orig_chan_name = ast_strdupa(chan->name);
00564 
00565    park_call_full(chan, peer, timeout, extout, orig_chan_name);
00566 
00567    return 0;
00568 }

AST_MODULE_INFO ( ASTERISK_GPL_KEY  ,
AST_MODFLAG_GLOBAL_SYMBOLS  ,
"Call Features Resource"  ,
load = load_module,
unload = unload_module,
reload = reload 
)

AST_MUTEX_DEFINE_STATIC ( holding_lock   ) 

AST_MUTEX_DEFINE_STATIC ( parking_lock   ) 

protects all static variables above

AST_MUTEX_DEFINE_STATIC ( autoanswer_lock   ) 

int ast_park_call ( struct ast_channel chan,
struct ast_channel peer,
int  timeout,
int *  extout 
)

Park a call.

Park a call and read back parked location.

Note:
We put the user in the parking list, then wake up the parking thread to be sure it looks after these channels too

Definition at line 533 of file res_features.c.

References park_call_full().

Referenced by builtin_blindtransfer(), builtin_parkcall(), iax_park_thread(), and sip_park_thread().

00534 {
00535    return park_call_full(chan, peer, timeout, extout, NULL);
00536 }

char* ast_parking_ext ( void   ) 

Determine system parking extension Returns the call parking extension for drivers that provide special call parking help.

Definition at line 218 of file res_features.c.

Referenced by builtin_blindtransfer(), dp_lookup(), handle_request_refer(), load_config(), mgcp_ss(), socket_process(), and ss_thread().

00219 {
00220    return parking_ext;
00221 }

int ast_pickup_call ( struct ast_channel chan  ) 

Pickup a call.

Definition at line 3109 of file res_features.c.

References ast_channel::_state, ast_answer(), ast_channel_masquerade(), ast_channel_unlock, ast_channel_walk_locked(), AST_CONTROL_ANSWER, ast_log(), ast_queue_control(), AST_STATE_RING, AST_STATE_RINGING, ast_channel::callgroup, LOG_DEBUG, LOG_WARNING, option_debug, ast_channel::pbx, and ast_channel::pickupgroup.

Referenced by cb_events(), handle_request_invite(), mgcp_ss(), and ss_thread().

03110 {
03111    struct ast_channel *cur = NULL;
03112    int res = -1;
03113 
03114    while ( (cur = ast_channel_walk_locked(cur)) != NULL) {
03115       if (!cur->pbx && 
03116          (cur != chan) &&
03117          (chan->pickupgroup & cur->callgroup) &&
03118          ((cur->_state == AST_STATE_RINGING) ||
03119           (cur->_state == AST_STATE_RING))) {
03120             break;
03121       }
03122       ast_channel_unlock(cur);
03123    }
03124    if (cur) {
03125       if (option_debug)
03126          ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
03127       res = ast_answer(chan);
03128       if (res)
03129          ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
03130       res = ast_queue_control(chan, AST_CONTROL_ANSWER);
03131       if (res)
03132          ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
03133       res = ast_channel_masquerade(cur, chan);
03134       if (res)
03135          ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);     /* Done */
03136       ast_channel_unlock(cur);
03137    } else   {
03138       if (option_debug)
03139          ast_log(LOG_DEBUG, "No call pickup possible...\n");
03140    }
03141    return res;
03142 }

char* ast_pickup_ext ( void   ) 

Determine system call pickup extension.

Definition at line 223 of file res_features.c.

Referenced by cb_events(), get_destination(), handle_request_invite(), handle_showfeatures(), mgcp_ss(), and ss_thread().

00224 {
00225    return pickup_ext;
00226 }

void ast_register_feature ( struct ast_call_feature feature  ) 

register new feature into feature_list

register new feature into feature_set

Definition at line 1006 of file res_features.c.

References AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_verbose(), LOG_NOTICE, option_verbose, ast_call_feature::sname, and VERBOSE_PREFIX_2.

Referenced by load_config().

01007 {
01008    if (!feature) {
01009       ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
01010          return;
01011    }
01012   
01013    AST_LIST_LOCK(&feature_list);
01014    AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry);
01015    AST_LIST_UNLOCK(&feature_list);
01016 
01017    if (option_verbose >= 2) 
01018       ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname);
01019 }

int ast_retrieve_call ( struct ast_channel chan,
char *  uniqueid 
)

Definition at line 2180 of file res_features.c.

References ast_channel::_state, ast_answer(), ast_bridge_call(), ast_channel_make_compatible(), AST_FEATURE_REDIRECT, ast_get_holded_call(), ast_hangup(), ast_log(), ast_moh_stop(), ast_mutex_unlock(), AST_PBX_NO_HANGUP_PEER, ast_set_flag, AST_STATE_UP, ast_streamfile(), ast_verbose(), ast_waitstream(), ast_bridge_config::features_callee, ast_bridge_config::features_caller, LOG_WARNING, option_verbose, ast_bridge_config::play_warning, ast_bridge_config::timelimit, VERBOSE_PREFIX_3, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound.

Referenced by retrieve_call_exec().

02181 {
02182    int res=-1, dres=-1;
02183    struct ast_channel *peer=NULL;
02184    struct ast_bridge_config config;
02185 
02186    peer = ast_get_holded_call(uniqueid);
02187 
02188    /* JK02: it helps to answer the channel if not already up */
02189    if (chan->_state != AST_STATE_UP) {
02190       ast_answer(chan);
02191    }
02192 
02193    if (peer) {
02194       ast_mutex_unlock(&peer->lock);
02195       ast_moh_stop(peer);
02196       res = ast_channel_make_compatible(chan, peer);
02197       if (res < 0) {
02198          ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
02199          ast_hangup(peer);
02200          return -1;
02201       }
02202       /* This runs sorta backwards, since we give the incoming channel control, as if it
02203          were the person called. */
02204       if (option_verbose > 2)
02205          ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to holded call %s\n", chan->name, peer->name);
02206 
02207       memset(&config,0,sizeof(struct ast_bridge_config));
02208       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
02209       ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
02210       config.timelimit = 0;
02211       config.play_warning = 0;
02212       config.warning_freq = 0;
02213       config.warning_sound=NULL;
02214       res = ast_bridge_call(chan,peer,&config);
02215 
02216       /* Simulate the PBX hanging up */
02217       if (res != AST_PBX_NO_HANGUP_PEER)
02218          ast_hangup(peer);
02219       return res;
02220    } else {
02221       /* XXX Play a message XXX */
02222      dres = ast_streamfile(chan, "pbx-invalidpark", chan->language);
02223      if (!dres)
02224        dres = ast_waitstream(chan, "");
02225      else {
02226        ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
02227        dres = 0;
02228      }
02229    }
02230    return res;
02231 }

int ast_retrieve_call_to_death ( char *  uniqueid  ) 

Definition at line 2233 of file res_features.c.

References ast_get_holded_call(), ast_hangup(), ast_log(), ast_mutex_unlock(), ast_verbose(), ast_channel::lock, LOG_WARNING, option_verbose, and VERBOSE_PREFIX_3.

02234 {
02235    int res=-1;
02236    struct ast_channel *peer=NULL;
02237 
02238    peer = ast_get_holded_call(uniqueid);
02239 
02240    if (peer) {
02241       res=0;
02242       if (option_verbose > 2)
02243          ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name);
02244       ast_mutex_unlock(&peer->lock);
02245       ast_hangup(peer);
02246    } else {
02247       ast_log(LOG_WARNING, "Could not find channel with uniqueid %s to retrieve.\n", uniqueid);
02248    }
02249    return res;
02250 }

AST_RWLOCK_DEFINE_STATIC ( features_lock   ) 

void ast_unregister_feature ( struct ast_call_feature feature  ) 

unregister feature from feature_list

unregister feature from feature_set

Definition at line 1022 of file res_features.c.

References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, and free.

01023 {
01024    if (!feature)
01025       return;
01026 
01027    AST_LIST_LOCK(&feature_list);
01028    AST_LIST_REMOVE(&feature_list,feature,feature_entry);
01029    AST_LIST_UNLOCK(&feature_list);
01030    free(feature);
01031 }

static void ast_unregister_features ( void   )  [static]

Remove all features in the list.

Definition at line 1034 of file res_features.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by load_config().

01035 {
01036    struct ast_call_feature *feature;
01037 
01038    AST_LIST_LOCK(&feature_list);
01039    while ((feature = AST_LIST_REMOVE_HEAD(&feature_list,feature_entry)))
01040       free(feature);
01041    AST_LIST_UNLOCK(&feature_list);
01042 }

static int autoanswer_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 2994 of file res_features.c.

References ast_channel::_state, ast_answer(), ast_autoanswer_login(), ast_bridge_call(), ast_channel_make_compatible(), AST_FEATURE_REDIRECT, ast_hangup(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_moh_stop(), ast_mutex_lock(), ast_mutex_unlock(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_verbose(), ast_waitstream(), aauser::chan, aauser::context, EVENT_FLAG_CALL, aauser::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, LOG_WARNING, manager_event(), aauser::next, option_verbose, ast_bridge_config::play_warning, s, strsep(), ast_bridge_config::timelimit, VERBOSE_PREFIX_3, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound.

Referenced by load_module().

02995 {
02996    int res=0;
02997    struct ast_channel *peer=NULL;
02998    struct aauser *pu, *pl=NULL;
02999    struct ast_bridge_config config;
03000    char *s, *stringp, *aacontext, *aaexten = NULL;
03001    char datastring[80];
03002    struct ast_module_user *u;
03003 
03004 
03005    if (!data) {
03006       ast_log(LOG_WARNING, "Autoanswer requires an argument (extension number)\n");
03007       return -1;
03008    }
03009    s = ast_strdupa((void *) data);
03010    stringp=s;
03011    aacontext = strsep(&stringp, "|");
03012    aaexten = strsep(&stringp, "|");
03013    if (!aaexten) {
03014        aaexten = aacontext;
03015        aacontext = NULL;
03016    }
03017    if (!aaexten) {
03018       ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n");
03019       return -1;
03020    } else {
03021       if (!aacontext) {
03022          aacontext = "default";
03023       }
03024    }
03025 
03026    u = ast_module_user_add(chan);
03027    ast_mutex_lock(&autoanswer_lock);
03028    pu = aalot;
03029    while(pu) {
03030       if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){
03031          if (pl)
03032             pl->next = pu->next;
03033          else
03034             aalot = pu->next;
03035          break;
03036       }
03037       pl = pu;
03038       pu = pu->next;
03039    }
03040    ast_mutex_unlock(&autoanswer_lock);
03041    if (pu) {
03042       peer = pu->chan;
03043       free(pu);
03044       pu = NULL;
03045    }
03046    /* JK02: it helps to answer the channel if not already up */
03047    if (chan->_state != AST_STATE_UP) {
03048       ast_answer(chan);
03049    }
03050 
03051    if (peer) {
03052       ast_moh_stop(peer);
03053       /* Play a courtesy beep in the callED channel to prefix the bridge connecting */
03054       if (!ast_strlen_zero(courtesytone)) {
03055          if (!ast_streamfile(peer, courtesytone, peer->language)) {
03056             if (ast_waitstream(peer, "") < 0) {
03057                ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
03058                ast_hangup(peer);
03059                return -1;
03060             }
03061          }
03062       }
03063 
03064       res = ast_channel_make_compatible(chan, peer);
03065       if (res < 0) {
03066          ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
03067          ast_hangup(peer);
03068          return -1;
03069       }
03070       /* This runs sorta backwards, since we give the incoming channel control, as if it
03071          were the person called. */
03072       if (option_verbose > 2)
03073          ast_verbose(VERBOSE_PREFIX_3 "Channel %s autoanswered  %s\n", peer->name, chan->name);
03074       manager_event(EVENT_FLAG_CALL, "Autoanswer",
03075                     "Channel: %s\r\n"
03076                     "Uniqueid: %s\r\n"
03077                     "Channel2: %s\r\n"
03078                     "Uniqueid2: %s\r\n"
03079                     "Context: %s\r\n"
03080                     "Exten: %s\r\n"
03081                 ,chan->name, chan->uniqueid, peer->name, peer->uniqueid, aacontext, aaexten);
03082 
03083 
03084       memset(&config,0,sizeof(struct ast_bridge_config));
03085       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
03086       ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
03087       config.timelimit = 0;
03088       config.play_warning = 0;
03089       config.warning_freq = 0;
03090       config.warning_sound=NULL;
03091       res = ast_bridge_call(chan,peer,&config);
03092 
03093       if (option_verbose > 2)
03094          ast_verbose(VERBOSE_PREFIX_3 "returning from bridge %s\n", peer->name);
03095          /* relogin */
03096       snprintf(datastring, sizeof(datastring) - 1, "%s|%s", aacontext, aaexten);
03097       ast_autoanswer_login(peer, datastring);
03098       return res;
03099    } else {
03100       if (option_verbose > 2)
03101          ast_verbose(VERBOSE_PREFIX_3 "Nobody logged in for autoanswer %s@%s\n", aaexten, aacontext);
03102       res = -1;
03103    }
03104    ast_module_user_remove(u);
03105    return res;
03106 }

static int autoanswer_login_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 2752 of file res_features.c.

References ast_log(), ast_masq_autoanswer_login(), ast_module_user_add, ast_module_user_remove, and LOG_WARNING.

Referenced by load_module().

02753 {
02754    int res=0;
02755    struct ast_module_user *u;
02756 
02757    u = ast_module_user_add(chan);
02758    if (!data) {
02759       ast_log(LOG_WARNING, "AutoanswerLogin requires an argument (extension number)\n");
02760       return -1;
02761    }
02762    res = ast_masq_autoanswer_login(chan, data);
02763    ast_module_user_remove(u);
02764    return res;
02765 }

static void autoanswer_reregister_extensions ( void   )  [static]

Definition at line 2869 of file res_features.c.

References ast_add_extension2(), ast_context_create(), ast_context_find(), ast_log(), AST_MAX_EXTENSION, ast_mutex_lock(), ast_mutex_unlock(), aauser::context, aauser::exten, exten, free, LOG_ERROR, aauser::next, and strdup.

Referenced by reload().

02870 {
02871    struct aauser *cur;
02872    struct ast_context *con;
02873    char exten[AST_MAX_EXTENSION];
02874    char args[AST_MAX_EXTENSION];
02875 
02876    ast_mutex_lock(&autoanswer_lock);
02877 
02878    cur=aalot;
02879    while(cur) {
02880       con = ast_context_find(cur->context);
02881       if (!con) {
02882          con = ast_context_create(NULL,cur->context, registrar);
02883          if (!con) {
02884             ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", cur->context);
02885          }
02886       }
02887       if (con) {
02888          snprintf(exten, sizeof(exten), "%s", cur->exten);
02889          snprintf(args, sizeof(args), "%s|%s", cur->context, cur->exten);
02890          ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)args), free, registrar);
02891       }
02892       cur = cur->next;
02893    }
02894 
02895    ast_mutex_unlock(&autoanswer_lock);
02896 }

static int bridge_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3387 of file res_features.c.

References ast_channel::_state, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_bridge_call(), ast_channel_alloc(), ast_channel_make_compatible(), ast_check_hangup(), AST_DECLARE_APP_ARGS, ast_get_channel_by_name_prefix_locked(), ast_hangup(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_unlock(), ast_pbx_start(), AST_PBX_SUCCESS, AST_STANDARD_APP_ARGS, AST_STATE_DOWN, AST_STATE_UP, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), BRIDGE_OPT_PLAYTONE, ast_channel::context, do_bridge_masquerade(), EVENT_FLAG_CALL, ast_channel::exten, ast_channel::lock, LOG_DEBUG, LOG_WARNING, manager_event(), option_debug, pbx_builtin_setvar_helper(), and ast_channel::priority.

Referenced by load_module().

03388 {
03389    struct ast_module_user *u;
03390    struct ast_channel *current_dest_chan, *final_dest_chan;
03391    char *tmp_data  = NULL;
03392    struct ast_flags opts = { 0, };
03393    struct ast_bridge_config bconfig = { { 0, }, };
03394 
03395    AST_DECLARE_APP_ARGS(args,
03396       AST_APP_ARG(dest_chan);
03397       AST_APP_ARG(options);
03398    );
03399    
03400    if (ast_strlen_zero(data)) {
03401       ast_log(LOG_WARNING, "Bridge require at least 1 argument specifying the other end of the bridge\n");
03402       return -1;
03403    }
03404    
03405    u = ast_module_user_add(chan);
03406 
03407    tmp_data = ast_strdupa(data);
03408    AST_STANDARD_APP_ARGS(args, tmp_data);
03409    if (!ast_strlen_zero(args.options))
03410       ast_app_parse_options(bridge_exec_options, &opts, NULL, args.options);
03411 
03412    /* avoid bridge with ourselves */
03413    if (!strncmp(chan->name, args.dest_chan, 
03414       strlen(chan->name) < strlen(args.dest_chan) ? 
03415       strlen(chan->name) : strlen(args.dest_chan))) {
03416       ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", chan->name);
03417       manager_event(EVENT_FLAG_CALL, "BridgeExec",
03418                "Response: Failed\r\n"
03419                "Reason: Unable to bridge channel to itself\r\n"
03420                "Channel1: %s\r\n"
03421                "Channel2: %s\r\n",
03422                chan->name, args.dest_chan);
03423       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP");
03424       ast_module_user_remove(u);
03425       return 0;
03426    }
03427 
03428    /* make sure we have a valid end point */
03429    if (!(current_dest_chan = ast_get_channel_by_name_prefix_locked(args.dest_chan, 
03430       strlen(args.dest_chan)))) {
03431       ast_log(LOG_WARNING, "Bridge failed because channel %s does not exists or we "
03432          "cannot get its lock\n", args.dest_chan);
03433       manager_event(EVENT_FLAG_CALL, "BridgeExec",
03434                "Response: Failed\r\n"
03435                "Reason: Cannot grab end point\r\n"
03436                "Channel1: %s\r\n"
03437                "Channel2: %s\r\n", chan->name, args.dest_chan);
03438       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT");
03439       ast_module_user_remove(u);
03440       return 0;
03441    }
03442    ast_mutex_unlock(&current_dest_chan->lock);
03443 
03444    /* answer the channel if needed */
03445    if (current_dest_chan->_state != AST_STATE_UP)
03446       ast_answer(current_dest_chan);
03447 
03448    /* try to allocate a place holder where current_dest_chan will be placed */
03449    if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, 
03450       NULL, NULL, 0, "Bridge/%s", current_dest_chan->name))) {
03451       ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan);
03452       manager_event(EVENT_FLAG_CALL, "BridgeExec",
03453                "Response: Failed\r\n"
03454                "Reason: cannot create placeholder\r\n"
03455                "Channel1: %s\r\n"
03456                "Channel2: %s\r\n", chan->name, args.dest_chan);
03457    }
03458    do_bridge_masquerade(current_dest_chan, final_dest_chan);
03459 
03460    /* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */
03461    /* try to make compatible, send error if we fail */
03462    if (ast_channel_make_compatible(chan, final_dest_chan) < 0) {
03463       ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, final_dest_chan->name);
03464       manager_event(EVENT_FLAG_CALL, "BridgeExec",
03465                "Response: Failed\r\n"
03466                "Reason: Could not make channels compatible for bridge\r\n"
03467                "Channel1: %s\r\n"
03468                "Channel2: %s\r\n", chan->name, final_dest_chan->name);
03469       ast_hangup(final_dest_chan); /* may be we should return this channel to the PBX? */
03470       pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE");
03471       ast_module_user_remove(u);
03472       return 0;
03473    }
03474 
03475    /* Report that the bridge will be successfull */
03476    manager_event(EVENT_FLAG_CALL, "BridgeExec",
03477             "Response: Success\r\n"
03478             "Channel1: %s\r\n"
03479             "Channel2: %s\r\n", chan->name, final_dest_chan->name);
03480 
03481    /* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */  
03482    if (ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE) && !ast_strlen_zero(xfersound)) {
03483       if (!ast_streamfile(final_dest_chan, xfersound, final_dest_chan->language)) {
03484          if (ast_waitstream(final_dest_chan, "") < 0)
03485             ast_log(LOG_WARNING, "Failed to play courtesy tone on %s\n", final_dest_chan->name);
03486       }
03487    }
03488    
03489    /* do the bridge */
03490    ast_bridge_call(chan, final_dest_chan, &bconfig);
03491 
03492    /* the bridge has ended, set BRIDGERESULT to SUCCESS. If the other channel has not been hung up, return it to the PBX */
03493    pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS");
03494    if (!ast_check_hangup(final_dest_chan)) {
03495       if (option_debug) {
03496          ast_log(LOG_DEBUG, "starting new PBX in %s,%s,%d for chan %s\n", 
03497          final_dest_chan->context, final_dest_chan->exten, 
03498          final_dest_chan->priority, final_dest_chan->name);
03499       }
03500 
03501       if (ast_pbx_start(final_dest_chan) != AST_PBX_SUCCESS) {
03502          ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", final_dest_chan->name);
03503          ast_hangup(final_dest_chan);
03504       } else if (option_debug)
03505          ast_log(LOG_DEBUG, "SUCCESS continuing PBX on chan %s\n", final_dest_chan->name);
03506    } else {
03507       if (option_debug)
03508          ast_log(LOG_DEBUG, "hangup chan %s since the other endpoint has hung up\n", final_dest_chan->name);
03509       ast_hangup(final_dest_chan);
03510    }
03511 
03512    ast_module_user_remove(u);
03513 
03514    return 0;
03515 }

static int builtin_atxfer ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
char *  code,
int  sense,
void *  data 
) [static]

Definition at line 846 of file res_features.c.

References ast_channel::_softhangup, ast_channel::_state, ast_app_dtget(), ast_autoservice_start(), ast_autoservice_stop(), ast_best_codec(), ast_bridge_call(), ast_bridge_call_thread_launch(), ast_calloc, ast_channel_alloc(), ast_channel_masquerade(), ast_check_hangup(), ast_clear_flag, AST_CONTROL_BUSY, AST_CONTROL_HOLD, AST_CONTROL_UNHOLD, AST_DIGIT_ANY, ast_exists_extension(), ast_explicit_goto(), AST_FEATURE_DISCONNECT, ast_feature_request_and_dial(), AST_FLAGS_ALL, ast_frfree, ast_hangup(), ast_indicate(), ast_log(), ast_read(), ast_set_flag, AST_STATE_DOWN, AST_STATE_UP, ast_stream_and_wait(), ast_waitfordigit(), ast_bridge_thread_obj::bconfig, ast_bridge_thread_obj::chan, check_compat(), ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, ast_channel::context, ast_channel::exten, f, FEATURE_RETURN_SUCCESS, ast_bridge_config::features_callee, ast_bridge_config::features_caller, finishup(), LOG_DEBUG, LOG_WARNING, ast_channel::nativeformats, option_debug, ast_bridge_thread_obj::peer, ast_channel::priority, ast_channel::readformat, real_ctx(), set_peers(), ast_channel::visible_indication, and ast_channel::writeformat.

00847 {
00848    struct ast_channel *transferer;
00849    struct ast_channel *transferee;
00850    const char *transferer_real_context;
00851    char xferto[256] = "";
00852    int res;
00853    int outstate=0;
00854    struct ast_channel *newchan;
00855    struct ast_channel *xferchan;
00856    struct ast_bridge_thread_obj *tobj;
00857    struct ast_bridge_config bconfig;
00858    struct ast_frame *f;
00859    int l;
00860 
00861    if (option_debug)
00862       ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
00863    set_peers(&transferer, &transferee, peer, chan, sense);
00864         transferer_real_context = real_ctx(transferer, transferee);
00865    /* Start autoservice on chan while we talk to the originator */
00866    ast_autoservice_start(transferee);
00867    ast_indicate(transferee, AST_CONTROL_HOLD);
00868    
00869    /* Transfer */
00870    res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
00871    if (res < 0) {
00872       finishup(transferee);
00873       return res;
00874    }
00875    if (res > 0) /* If they've typed a digit already, handle it */
00876       xferto[0] = (char) res;
00877 
00878    /* this is specific of atxfer */
00879    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
00880         if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
00881                 finishup(transferee);
00882                 return res;
00883         }
00884    if (res == 0) {
00885       ast_log(LOG_WARNING, "Did not read data.\n");
00886       finishup(transferee);
00887       if (ast_stream_and_wait(transferer, "beeperr", transferer->language, ""))
00888          return -1;
00889       return FEATURE_RETURN_SUCCESS;
00890    }
00891 
00892    /* valid extension, res == 1 */
00893    if (!ast_exists_extension(transferer, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
00894       ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
00895       finishup(transferee);
00896       if (ast_stream_and_wait(transferer, "beeperr", transferer->language, ""))
00897          return -1;
00898       return FEATURE_RETURN_SUCCESS;
00899    }
00900 
00901    l = strlen(xferto);
00902    snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context);   /* append context */
00903    newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats),
00904       xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, transferer->language);
00905    ast_indicate(transferer, -1);
00906    if (!newchan) {
00907       finishup(transferee);
00908       /* any reason besides user requested cancel and busy triggers the failed sound */
00909       if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
00910             ast_stream_and_wait(transferer, xferfailsound, transferer->language, ""))
00911          return -1;
00912       return FEATURE_RETURN_SUCCESS;
00913    }
00914 
00915    if (check_compat(transferer, newchan)) {
00916       /* we do mean transferee here, NOT transferer */
00917       finishup(transferee);
00918       return -1;
00919    }
00920    memset(&bconfig,0,sizeof(struct ast_bridge_config));
00921    ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
00922    ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
00923    res = ast_bridge_call(transferer, newchan, &bconfig);
00924    if (newchan->_softhangup || !transferer->_softhangup) {
00925       ast_hangup(newchan);
00926       if (ast_stream_and_wait(transferer, xfersound, transferer->language, ""))
00927          ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
00928       finishup(transferee);
00929       transferer->_softhangup = 0;
00930       return FEATURE_RETURN_SUCCESS;
00931    }
00932    
00933    if (check_compat(transferee, newchan)) {
00934       finishup(transferee);
00935       return -1;
00936    }
00937 
00938    ast_indicate(transferee, AST_CONTROL_UNHOLD);
00939    
00940    if ((ast_autoservice_stop(transferee) < 0)
00941       || (ast_waitfordigit(transferee, 100) < 0)
00942       || (ast_waitfordigit(newchan, 100) < 0) 
00943       || ast_check_hangup(transferee) 
00944       || ast_check_hangup(newchan)) {
00945       ast_hangup(newchan);
00946       return -1;
00947    }
00948 
00949    xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name);
00950    if (!xferchan) {
00951       ast_hangup(newchan);
00952       return -1;
00953    }
00954    /* Make formats okay */
00955    xferchan->visible_indication = transferer->visible_indication;
00956    xferchan->readformat = transferee->readformat;
00957    xferchan->writeformat = transferee->writeformat;
00958    ast_channel_masquerade(xferchan, transferee);
00959    ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
00960    xferchan->_state = AST_STATE_UP;
00961    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
00962    xferchan->_softhangup = 0;
00963 
00964    if ((f = ast_read(xferchan)))
00965       ast_frfree(f);
00966 
00967    newchan->_state = AST_STATE_UP;
00968    ast_clear_flag(newchan, AST_FLAGS_ALL);   
00969    newchan->_softhangup = 0;
00970 
00971    tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj));
00972    if (!tobj) {
00973       ast_hangup(xferchan);
00974       ast_hangup(newchan);
00975       return -1;
00976    }
00977    tobj->chan = newchan;
00978    tobj->peer = xferchan;
00979    tobj->bconfig = *config;
00980 
00981    if (ast_stream_and_wait(newchan, xfersound, newchan->language, ""))
00982       ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
00983    ast_bridge_call_thread_launch(tobj);
00984    return -1;  /* XXX meaning the channel is bridged ? */
00985 }

static int builtin_automonitor ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
char *  code,
int  sense,
void *  data 
) [static]

Definition at line 632 of file res_features.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_log(), ast_monitor_stop(), ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_verbose(), ast_channel::cid, ast_callerid::cid_num, FEATURE_RETURN_SUCCESS, len, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::monitor, option_verbose, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), S_OR, set_peers(), and VERBOSE_PREFIX_3.

00633 {
00634    char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
00635    int x = 0;
00636    size_t len;
00637    struct ast_channel *caller_chan, *callee_chan;
00638 
00639    if (!monitor_ok) {
00640       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
00641       return -1;
00642    }
00643 
00644    if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
00645       monitor_ok = 0;
00646       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
00647       return -1;
00648    }
00649 
00650    set_peers(&caller_chan, &callee_chan, peer, chan, sense);
00651 
00652    if (!ast_strlen_zero(courtesytone)) {
00653       if (ast_autoservice_start(callee_chan))
00654          return -1;
00655       if (ast_stream_and_wait(caller_chan, courtesytone, caller_chan->language, "")) {
00656          ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
00657          ast_autoservice_stop(callee_chan);
00658          return -1;
00659       }
00660       if (ast_autoservice_stop(callee_chan))
00661          return -1;
00662    }
00663    
00664    if (callee_chan->monitor) {
00665       if (option_verbose > 3)
00666          ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code);
00667       ast_monitor_stop(callee_chan, 1);
00668       return FEATURE_RETURN_SUCCESS;
00669    }
00670 
00671    if (caller_chan && callee_chan) {
00672       const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
00673       const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
00674 
00675       if (!touch_format)
00676          touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
00677 
00678       if (!touch_monitor)
00679          touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
00680    
00681       if (touch_monitor) {
00682          len = strlen(touch_monitor) + 50;
00683          args = alloca(len);
00684          touch_filename = alloca(len);
00685          snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
00686          snprintf(args, len, "%s|%s|m", (touch_format) ? touch_format : "wav", touch_filename);
00687       } else {
00688          caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name));
00689          callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name));
00690          len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
00691          args = alloca(len);
00692          touch_filename = alloca(len);
00693          snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
00694          snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename);
00695       }
00696 
00697       for( x = 0; x < strlen(args); x++) {
00698          if (args[x] == '/')
00699             args[x] = '-';
00700       }
00701       
00702       if (option_verbose > 3)
00703          ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args);
00704 
00705       pbx_exec(callee_chan, monitor_app, args);
00706       pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
00707       pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
00708    
00709       return FEATURE_RETURN_SUCCESS;
00710    }
00711    
00712    ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");  
00713    return -1;
00714 }

static int builtin_blindtransfer ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
char *  code,
int  sense,
void *  data 
) [static]

Todo:
XXX Maybe we should have another message here instead of invalid extension XXX

Definition at line 743 of file res_features.c.

References ast_app_dtget(), ast_async_goto(), ast_autoservice_start(), ast_cdr_alloc(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_setdestchan(), ast_cdr_start(), AST_CONTROL_HOLD, AST_DIGIT_ANY, ast_exists_extension(), ast_indicate(), ast_log(), ast_park_call(), ast_parking_ext(), AST_PBX_KEEPALIVE, AST_PBX_NO_HANGUP_PEER, ast_stopstream(), ast_stream_and_wait(), ast_verbose(), ast_channel::cdr, check_goto_on_transfer(), ast_channel::cid, ast_callerid::cid_num, FEATURE_RETURN_SUCCESS, finishup(), LOG_WARNING, option_verbose, ast_channel::pbx, pbx_builtin_setvar_helper(), real_ctx(), set_c_e_p(), set_peers(), VERBOSE_PREFIX_2, and VERBOSE_PREFIX_3.

00744 {
00745    struct ast_channel *transferer;
00746    struct ast_channel *transferee;
00747    const char *transferer_real_context;
00748    char xferto[256];
00749    int res;
00750 
00751    set_peers(&transferer, &transferee, peer, chan, sense);
00752    transferer_real_context = real_ctx(transferer, transferee);
00753    /* Start autoservice on chan while we talk to the originator */
00754    ast_autoservice_start(transferee);
00755    ast_indicate(transferee, AST_CONTROL_HOLD);
00756 
00757    memset(xferto, 0, sizeof(xferto));
00758    
00759    /* Transfer */
00760    res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
00761    if (res < 0) {
00762       finishup(transferee);
00763       return -1; /* error ? */
00764    }
00765    if (res > 0)   /* If they've typed a digit already, handle it */
00766       xferto[0] = (char) res;
00767 
00768    ast_stopstream(transferer);
00769    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
00770    if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
00771       finishup(transferee);
00772       return res;
00773    }
00774    if (!strcmp(xferto, ast_parking_ext())) {
00775       res = finishup(transferee);
00776       if (res)
00777          res = -1;
00778       else if (!ast_park_call(transferee, transferer, 0, NULL)) { /* success */
00779          /* We return non-zero, but tell the PBX not to hang the channel when
00780             the thread dies -- We have to be careful now though.  We are responsible for 
00781             hanging up the channel, else it will never be hung up! */
00782 
00783          return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER;
00784       } else {
00785          ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
00786       }
00787       /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
00788    } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
00789       pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", transferee->name);
00790       pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
00791       res=finishup(transferee);
00792       if (!transferer->cdr) {
00793          transferer->cdr=ast_cdr_alloc();
00794          if (transferer) {
00795             ast_cdr_init(transferer->cdr, transferer); /* initilize our channel's cdr */
00796             ast_cdr_start(transferer->cdr);
00797          }
00798       }
00799       if (transferer->cdr) {
00800          ast_cdr_setdestchan(transferer->cdr, transferee->name);
00801          ast_cdr_setapp(transferer->cdr, "BLINDTRANSFER","");
00802       }
00803       if (!transferee->pbx) {
00804          /* Doh!  Use our handy async_goto functions */
00805          if (option_verbose > 2) 
00806             ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
00807                         ,transferee->name, xferto, transferer_real_context);
00808          if (ast_async_goto(transferee, transferer_real_context, xferto, 1))
00809             ast_log(LOG_WARNING, "Async goto failed :-(\n");
00810          res = -1;
00811       } else {
00812          /* Set the channel's new extension, since it exists, using transferer context */
00813          set_c_e_p(transferee, transferer_real_context, xferto, 0);
00814       }
00815       check_goto_on_transfer(transferer);
00816       return res;
00817    } else {
00818       if (option_verbose > 2) 
00819          ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context);
00820    }
00821    if (ast_stream_and_wait(transferer, xferfailsound, transferer->language, AST_DIGIT_ANY) < 0 ) {
00822       finishup(transferee);
00823       return -1;
00824    }
00825    ast_stopstream(transferer);
00826    res = finishup(transferee);
00827    if (res) {
00828       if (option_verbose > 1)
00829          ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
00830       return res;
00831    }
00832    return FEATURE_RETURN_SUCCESS;
00833 }

static int builtin_disconnect ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
char *  code,
int  sense,
void *  data 
) [static]

Definition at line 716 of file res_features.c.

References ast_verbose(), FEATURE_RETURN_HANGUP, option_verbose, and VERBOSE_PREFIX_3.

00717 {
00718    if (option_verbose > 3)
00719       ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
00720    return FEATURE_RETURN_HANGUP;
00721 }

static int builtin_parkcall ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
char *  code,
int  sense,
void *  data 
) [static]

support routing for one touch call parking

Definition at line 599 of file res_features.c.

References ast_channel::_state, ast_answer(), ast_module_user_add, ast_module_user_remove, ast_park_call(), AST_PBX_KEEPALIVE, AST_PBX_NO_HANGUP_PEER, ast_safe_sleep(), AST_STATE_UP, ast_channel::exten, FEATURE_SENSE_CHAN, ast_channel::priority, and set_peers().

00600 {
00601    struct ast_channel *parker;
00602         struct ast_channel *parkee;
00603    int res = 0;
00604    struct ast_module_user *u;
00605 
00606    u = ast_module_user_add(chan);
00607 
00608    set_peers(&parker, &parkee, peer, chan, sense);
00609    /* Setup the exten/priority to be s/1 since we don't know
00610       where this call should return */
00611    strcpy(chan->exten, "s");
00612    chan->priority = 1;
00613    if (chan->_state != AST_STATE_UP)
00614       res = ast_answer(chan);
00615    if (!res)
00616       res = ast_safe_sleep(chan, 1000);
00617    if (!res)
00618       res = ast_park_call(parkee, parker, 0, NULL);
00619 
00620    ast_module_user_remove(u);
00621 
00622    if (!res) {
00623       if (sense == FEATURE_SENSE_CHAN)
00624          res = AST_PBX_NO_HANGUP_PEER;
00625       else
00626          res = AST_PBX_KEEPALIVE;
00627    }
00628    return res;
00629 
00630 }

static int check_compat ( struct ast_channel c,
struct ast_channel newchan 
) [static]

Definition at line 835 of file res_features.c.

References ast_channel_make_compatible(), ast_hangup(), ast_log(), and LOG_WARNING.

Referenced by builtin_atxfer().

00836 {
00837    if (ast_channel_make_compatible(c, newchan) < 0) {
00838       ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
00839          c->name, newchan->name);
00840       ast_hangup(newchan);
00841       return -1;
00842    }
00843    return 0;
00844 }

static void check_goto_on_transfer ( struct ast_channel chan  )  [static]

Definition at line 246 of file res_features.c.

References ast_channel::_softhangup, ast_channel::_state, ast_channel_alloc(), ast_channel_masquerade(), ast_clear_flag, AST_FLAGS_ALL, ast_frfree, ast_hangup(), ast_parseable_goto(), ast_pbx_start(), ast_read(), AST_STATE_DOWN, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), f, pbx_builtin_getvar_helper(), ast_channel::readformat, and ast_channel::writeformat.

Referenced by builtin_blindtransfer().

00247 {
00248    struct ast_channel *xferchan;
00249    const char *val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
00250    char *x, *goto_on_transfer;
00251    struct ast_frame *f;
00252 
00253    if (ast_strlen_zero(val))
00254       return;
00255 
00256    goto_on_transfer = ast_strdupa(val);
00257 
00258    if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, chan->name)))
00259       return;
00260 
00261    for (x = goto_on_transfer; x && *x; x++) {
00262       if (*x == '^')
00263          *x = '|';
00264    }
00265    /* Make formats okay */
00266    xferchan->readformat = chan->readformat;
00267    xferchan->writeformat = chan->writeformat;
00268    ast_channel_masquerade(xferchan, chan);
00269    ast_parseable_goto(xferchan, goto_on_transfer);
00270    xferchan->_state = AST_STATE_UP;
00271    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
00272    xferchan->_softhangup = 0;
00273    if ((f = ast_read(xferchan))) {
00274       ast_frfree(f);
00275       f = NULL;
00276       ast_pbx_start(xferchan);
00277    } else {
00278       ast_hangup(xferchan);
00279    }
00280 }

static void* do_autoanswer_thread ( void *  ignore  )  [static]

Definition at line 2897 of file res_features.c.

References ast_clear_flag, ast_context_find(), ast_context_remove_extension2(), AST_CONTROL_HANGUP, AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, ast_frfree, ast_hangup(), ast_log(), AST_MAX_EXTENSION, AST_MAX_FDS, ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_select(), ast_set_flag, ast_verbose(), aauser::chan, aauser::context, EVENT_FLAG_CALL, aauser::exten, exten, f, ast_channel::fdno, ast_channel::fds, ast_frame::frametype, free, LOG_WARNING, manager_event(), aauser::next, option_verbose, aauser::start, ast_frame::subclass, and VERBOSE_PREFIX_2.

Referenced by load_module().

02898 {
02899    int ms, tms, max;
02900    struct ast_context *con;
02901    char exten[AST_MAX_EXTENSION];
02902    struct aauser *pu, *pl, *pt = NULL;
02903    struct timeval tv;
02904    struct ast_frame *f;
02905    int x;
02906    fd_set rfds, efds;
02907    fd_set nrfds, nefds;
02908    FD_ZERO(&rfds);
02909    FD_ZERO(&efds);
02910    for (;;) {
02911       ms = -1;
02912       max = -1;
02913       ast_mutex_lock(&autoanswer_lock);
02914       pl = NULL;
02915       pu = aalot;
02916       gettimeofday(&tv, NULL);
02917       FD_ZERO(&nrfds);
02918       FD_ZERO(&nefds);
02919       while(pu) {
02920          tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
02921          for (x=0;x<AST_MAX_FDS;x++) {
02922             if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
02923                if (FD_ISSET(pu->chan->fds[x], &efds))
02924                   ast_set_flag(pu->chan, AST_FLAG_EXCEPTION);
02925                else
02926                   ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION);
02927                pu->chan->fdno = x;
02928                /* See if they need servicing */
02929                f = ast_read(pu->chan);
02930                if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
02931                   /* There's a problem, hang them up*/
02932                   if (option_verbose > 1)
02933                      ast_verbose(VERBOSE_PREFIX_2 "%s logged out of autoanswer app\n", pu->chan->name);
02934                   manager_event(EVENT_FLAG_CALL, "AutoanswerLogout",
02935                                         "Channel: %s\r\n"
02936                                         "Uniqueid: %s\r\n"
02937                                   "Context: %s\r\n"
02938                                   "Exten: %s\r\n"
02939                               ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten);
02940                   ast_hangup(pu->chan);
02941                   con = ast_context_find(pu->context);
02942                   if (con) {
02943                       snprintf(exten, sizeof(exten), "%s", pu->exten);
02944                       if (ast_context_remove_extension2(con, exten, 1, registrar))
02945                      ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
02946                   } else {
02947                      ast_log(LOG_WARNING, "Whoa, no %s context?\n", pu->exten);
02948                   }
02949                   /* And take them out of the parking lot */
02950                   if (pl)
02951                      pl->next = pu->next;
02952                   else
02953                      aalot = pu->next;
02954                   pt = pu;
02955                   pu = pu->next;
02956                   free(pt);
02957                   break;
02958                } else {
02959                   /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
02960                   ast_frfree(f);
02961                   goto std;   /* XXX Ick: jumping into an else statement??? XXX */
02962                }
02963             }
02964          }
02965          if (x >= AST_MAX_FDS) {
02966 std:           for (x=0;x<AST_MAX_FDS;x++) {
02967                /* Keep this one for next one */
02968                if (pu->chan->fds[x] > -1) {
02969                   FD_SET(pu->chan->fds[x], &nrfds);
02970                   FD_SET(pu->chan->fds[x], &nefds);
02971                   if (pu->chan->fds[x] > max)
02972                      max = pu->chan->fds[x];
02973                }
02974             }
02975             /* Keep track of our longest wait */
02976             if ((tms < ms) || (ms < 0))
02977                ms = tms;
02978             pl = pu;
02979             pu = pu->next;
02980          }
02981       }
02982       ast_mutex_unlock(&autoanswer_lock);
02983       rfds = nrfds;
02984       efds = nefds;
02985       tv.tv_sec = ms / 1000;
02986       tv.tv_usec = (ms % 1000) * 1000;
02987       /* Wait for something to happen */
02988       ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
02989       pthread_testcancel();
02990    }
02991    return NULL;   /* Never reached */
02992 }

static void do_bridge_masquerade ( struct ast_channel chan,
struct ast_channel tmpchan 
) [static]

Definition at line 2431 of file res_features.c.

References ast_channel::_state, ast_channel_masquerade(), ast_do_masquerade(), ast_explicit_goto(), ast_moh_stop(), ast_mutex_lock(), ast_mutex_unlock(), ast_setstate(), ast_channel::context, ast_channel::exten, ast_channel::lock, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat.

Referenced by action_bridge(), and bridge_exec().

02432 {
02433    ast_moh_stop(chan);
02434    ast_mutex_lock(&chan->lock);
02435    ast_setstate(tmpchan, chan->_state);
02436    tmpchan->readformat = chan->readformat;
02437    tmpchan->writeformat = chan->writeformat;
02438    ast_channel_masquerade(tmpchan, chan);
02439    ast_mutex_lock(&tmpchan->lock);
02440    ast_do_masquerade(tmpchan);
02441    /* when returning from bridge, the channel will continue at the next priority */
02442    ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1);
02443    ast_mutex_unlock(&tmpchan->lock);
02444    ast_mutex_unlock(&chan->lock);
02445 }

static void* do_holding_thread ( void *  ignore  )  [static]

Definition at line 2293 of file res_features.c.

References ast_clear_flag, AST_CONTROL_HANGUP, AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, ast_frfree, ast_hangup(), AST_MAX_FDS, ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_select(), ast_set_flag, ast_verbose(), holdeduser::chan, f, ast_channel::fdno, ast_channel::fds, ast_frame::frametype, free, holdeduser::next, option_verbose, holdeduser::start, ast_frame::subclass, and VERBOSE_PREFIX_2.

Referenced by load_module().

02294 {
02295    int ms, tms, max;
02296    struct holdeduser *pu, *pl, *pt = NULL;
02297    struct timeval tv;
02298    struct ast_frame *f;
02299    int x;
02300    fd_set rfds, efds;
02301    fd_set nrfds, nefds;
02302    FD_ZERO(&rfds);
02303    FD_ZERO(&efds);
02304    for (;;) {
02305       ms = -1;
02306       max = -1;
02307       ast_mutex_lock(&holding_lock);
02308       pl = NULL;
02309       pu = holdlist;
02310       gettimeofday(&tv, NULL);
02311       FD_ZERO(&nrfds);
02312       FD_ZERO(&nefds);
02313       while(pu) {
02314          tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
02315             for (x=0;x<AST_MAX_FDS;x++) {
02316                if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
02317                   if (FD_ISSET(pu->chan->fds[x], &efds))
02318                      ast_set_flag(pu->chan, AST_FLAG_EXCEPTION);
02319                   else
02320                      ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION);
02321                   pu->chan->fdno = x;
02322                   /* See if they need servicing */
02323                   f = ast_read(pu->chan);
02324                   if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
02325                      /* There's a problem, hang them up*/
02326                      if (option_verbose > 1)
02327                         ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being onhold\n", pu->chan->name);
02328                      ast_hangup(pu->chan);
02329                      /* find the corresponding channel and hang them up too! */
02330                      /* but only if it is not bridged yet! */
02331                      /* And take them out of the parking lot */
02332                      if (pl)
02333                         pl->next = pu->next;
02334                      else
02335                         holdlist = pu->next;
02336                      pt = pu;
02337                      pu = pu->next;
02338                      free(pt);
02339                      break;
02340                   } else {
02341                      /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
02342                      ast_frfree(f);
02343                      goto std;   /* XXX Ick: jumping into an else statement??? XXX */
02344                   }
02345                }
02346             }
02347             if (x >= AST_MAX_FDS) {
02348 std:              for (x=0;x<AST_MAX_FDS;x++) {
02349                   /* Keep this one for next one */
02350                   if (pu->chan->fds[x] > -1) {
02351                      FD_SET(pu->chan->fds[x], &nrfds);
02352                      FD_SET(pu->chan->fds[x], &nefds);
02353                      if (pu->chan->fds[x] > max)
02354                         max = pu->chan->fds[x];
02355                   }
02356                }
02357                /* Keep track of our longest wait */
02358                if ((tms < ms) || (ms < 0))
02359                   ms = tms;
02360                pl = pu;
02361                pu = pu->next;
02362             }
02363       }
02364       ast_mutex_unlock(&holding_lock);
02365       rfds = nrfds;
02366       efds = nefds;
02367       tv.tv_sec = ms / 1000;
02368       tv.tv_usec = (ms % 1000) * 1000;
02369       /* Wait for something to happen */
02370       ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
02371       pthread_testcancel();
02372    }
02373    return NULL;   /* Never reached */
02374 }

static void* do_parking_thread ( void *  ignore  )  [static]

Take care of parked calls and unpark them if needed.

Todo:
XXX Maybe we could do something with packets, like dial "0" for operator or something XXX

Todo:
XXX Ick: jumping into an else statement??? XXX

Definition at line 1762 of file res_features.c.

References ast_add_extension2(), ast_clear_flag, ast_context_create(), ast_context_find(), ast_context_remove_extension2(), AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_UNHOLD, AST_FLAG_EXCEPTION, AST_FRAME_CONTROL, ast_free, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_EXTENSION, AST_MAX_FDS, ast_mutex_lock(), ast_mutex_unlock(), ast_pbx_start(), ast_read(), ast_select(), ast_set_flag, ast_strdupa, ast_strlen_zero(), ast_verbose(), parkeduser::chan, ast_channel::context, parkeduser::context, ast_channel::exten, parkeduser::exten, f, ast_channel::fdno, ast_channel::fds, ast_frame::frametype, free, ast_channel::generatordata, LOG_DEBUG, LOG_ERROR, LOG_WARNING, parkeduser::moh_trys, parkeduser::next, notify_metermaids(), parkeduser::notquiteyet, option_debug, option_verbose, parkeduser::parkingexten, parkeduser::parkingnum, parkeduser::parkingtime, parkeduser::peername, post_manager_event(), ast_channel::priority, parkeduser::priority, S_OR, set_c_e_p(), parkeduser::start, strdup, ast_frame::subclass, and VERBOSE_PREFIX_2.

Referenced by load_module().

01763 {
01764    fd_set rfds, efds;   /* results from previous select, to be preserved across loops. */
01765    FD_ZERO(&rfds);
01766    FD_ZERO(&efds);
01767 
01768    for (;;) {
01769       struct parkeduser *pu, *pl, *pt = NULL;
01770       int ms = -1;   /* select timeout, uninitialized */
01771       int max = -1;  /* max fd, none there yet */
01772       fd_set nrfds, nefds; /* args for the next select */
01773       FD_ZERO(&nrfds);
01774       FD_ZERO(&nefds);
01775 
01776       ast_mutex_lock(&parking_lock);
01777       pl = NULL;
01778       pu = parkinglot;
01779       /* navigate the list with prev-cur pointers to support removals */
01780       while (pu) {
01781          struct ast_channel *chan = pu->chan;   /* shorthand */
01782          int tms;        /* timeout for this item */
01783          int x;          /* fd index in channel */
01784          struct ast_context *con;
01785 
01786          if (pu->notquiteyet) { /* Pretend this one isn't here yet */
01787             pl = pu;
01788             pu = pu->next;
01789             continue;
01790          }
01791          tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
01792          if (tms > pu->parkingtime) {
01793             ast_indicate(chan, AST_CONTROL_UNHOLD);
01794             /* Get chan, exten from derived kludge */
01795             if (pu->peername[0]) {
01796                char *peername = ast_strdupa(pu->peername);
01797                char *cp = strrchr(peername, '-');
01798                if (cp) 
01799                   *cp = 0;
01800                con = ast_context_find(parking_con_dial);
01801                if (!con) {
01802                   con = ast_context_create(NULL, parking_con_dial, registrar);
01803                   if (!con)
01804                      ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
01805                }
01806                if (con) {
01807                   char returnexten[AST_MAX_EXTENSION];
01808                   snprintf(returnexten, sizeof(returnexten), "%s|30|t", peername);
01809                   ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), ast_free, registrar);
01810                }
01811                set_c_e_p(chan, parking_con_dial, peername, 1);
01812             } else {
01813                /* They've been waiting too long, send them back to where they came.  Theoretically they
01814                   should have their original extensions and such, but we copy to be on the safe side */
01815                set_c_e_p(chan, pu->context, pu->exten, pu->priority);
01816             }
01817 
01818             post_manager_event("ParkedCallTimeOut", pu->parkingexten, chan);
01819 
01820             if (option_verbose > 1) 
01821                ast_verbose(VERBOSE_PREFIX_2 "Timeout for %s parked on %d. Returning to %s,%s,%d\n", chan->name, pu->parkingnum, chan->context, chan->exten, chan->priority);
01822             /* Start up the PBX, or hang them up */
01823             if (ast_pbx_start(chan))  {
01824                ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name);
01825                ast_hangup(chan);
01826             }
01827             /* And take them out of the parking lot */
01828             if (pl) 
01829                pl->next = pu->next;
01830             else
01831                parkinglot = pu->next;
01832             pt = pu;
01833             pu = pu->next;
01834             con = ast_context_find(parking_con);
01835             if (con) {
01836                if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL))
01837                   ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
01838                else
01839                   notify_metermaids(pt->parkingexten, parking_con);
01840             } else
01841                ast_log(LOG_WARNING, "Whoa, no parking context?\n");
01842             free(pt);
01843          } else { /* still within parking time, process descriptors */
01844             for (x = 0; x < AST_MAX_FDS; x++) {
01845                struct ast_frame *f;
01846 
01847                if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds)))
01848                   continue;   /* nothing on this descriptor */
01849 
01850                if (FD_ISSET(chan->fds[x], &efds))
01851                   ast_set_flag(chan, AST_FLAG_EXCEPTION);
01852                else
01853                   ast_clear_flag(chan, AST_FLAG_EXCEPTION);
01854                chan->fdno = x;
01855 
01856                /* See if they need servicing */
01857                f = ast_read(chan);
01858                if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass ==  AST_CONTROL_HANGUP)) {
01859                   if (f)
01860                      ast_frfree(f);
01861                   post_manager_event("ParkedCallGiveUp", pu->parkingexten, chan);
01862 
01863                   /* There's a problem, hang them up*/
01864                   if (option_verbose > 1) 
01865                      ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name);
01866                   ast_hangup(chan);
01867                   /* And take them out of the parking lot */
01868                   if (pl) 
01869                      pl->next = pu->next;
01870                   else
01871                      parkinglot = pu->next;
01872                   pt = pu;
01873                   pu = pu->next;
01874                   con = ast_context_find(parking_con);
01875                   if (con) {
01876                      if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL))
01877                         ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
01878                   else
01879                      notify_metermaids(pt->parkingexten, parking_con);
01880                   } else
01881                      ast_log(LOG_WARNING, "Whoa, no parking context?\n");
01882                   free(pt);
01883                   break;
01884                } else {
01885                   /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
01886                   ast_frfree(f);
01887                   if (pu->moh_trys < 3 && !chan->generatordata) {
01888                      if (option_debug)
01889                         ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source.  Restarting.\n");
01890                      ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
01891                         S_OR(parkmohclass, NULL),
01892                         !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
01893                      pu->moh_trys++;
01894                   }
01895                   goto std;   /*! \todo XXX Ick: jumping into an else statement??? XXX */
01896                }
01897 
01898             } /* end for */
01899             if (x >= AST_MAX_FDS) {
01900 std:              for (x=0; x<AST_MAX_FDS; x++) {  /* mark fds for next round */
01901                   if (chan->fds[x] > -1) {
01902                      FD_SET(chan->fds[x], &nrfds);
01903                      FD_SET(chan->fds[x], &nefds);
01904                      if (chan->fds[x] > max)
01905                         max = chan->fds[x];
01906                   }
01907                }
01908                /* Keep track of our shortest wait */
01909                if (tms < ms || ms < 0)
01910                   ms = tms;
01911                pl = pu;
01912                pu = pu->next;
01913             }
01914          }
01915       } /* end while */
01916       ast_mutex_unlock(&parking_lock);
01917       rfds = nrfds;
01918       efds = nefds;
01919       {
01920          struct timeval tv = ast_samp2tv(ms, 1000);
01921          /* Wait for something to happen */
01922          ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
01923       }
01924       pthread_testcancel();
01925    }
01926    return NULL;   /* Never reached */
01927 }

static int feature_exec_app ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
char *  code,
int  sense,
void *  data 
) [static]

exec an app by feature

Todo:
XXX should probably return res

Definition at line 1058 of file res_features.c.

References ast_call_feature::app, app, ast_call_feature::app_args, ast_autoservice_start(), ast_autoservice_stop(), AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_FLAG_ONSELF, ast_log(), ast_moh_start(), ast_moh_stop(), AST_PBX_KEEPALIVE, AST_PBX_NO_HANGUP_PEER, ast_strlen_zero(), ast_test_flag, FEATURE_RETURN_KEEPTRYING, FEATURE_RETURN_NO_HANGUP_PEER, FEATURE_RETURN_PBX_KEEPALIVE, FEATURE_RETURN_SUCCESS, FEATURE_RETURN_SUCCESSBREAK, FEATURE_SENSE_CHAN, LOG_NOTICE, LOG_WARNING, ast_call_feature::moh_class, pbx_exec(), and pbx_findapp().

Referenced by load_config().

01059 {
01060    struct ast_app *app;
01061    struct ast_call_feature *feature = data;
01062    struct ast_channel *work, *idle;
01063    int res;
01064 
01065    if (!feature) { /* shouldn't ever happen! */
01066       ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
01067       return -1; 
01068    }
01069 
01070    if (sense == FEATURE_SENSE_CHAN) {
01071       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
01072          return FEATURE_RETURN_KEEPTRYING;
01073       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
01074          work = chan;
01075          idle = peer;
01076       } else {
01077          work = peer;
01078          idle = chan;
01079       }
01080    } else {
01081       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
01082          return FEATURE_RETURN_KEEPTRYING;
01083       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
01084          work = peer;
01085          idle = chan;
01086       } else {
01087          work = chan;
01088          idle = peer;
01089       }
01090    }
01091 
01092    if (!(app = pbx_findapp(feature->app))) {
01093       ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
01094       return -2;
01095    }
01096 
01097    ast_autoservice_start(idle);
01098    
01099    if (!ast_strlen_zero(feature->moh_class))
01100       ast_moh_start(idle, feature->moh_class, NULL);
01101 
01102    res = pbx_exec(work, app, feature->app_args);
01103 
01104    if (!ast_strlen_zero(feature->moh_class))
01105       ast_moh_stop(idle);
01106 
01107    ast_autoservice_stop(idle);
01108 
01109    if (res == AST_PBX_KEEPALIVE)
01110       return FEATURE_RETURN_PBX_KEEPALIVE;
01111    else if (res == AST_PBX_NO_HANGUP_PEER)
01112       return FEATURE_RETURN_NO_HANGUP_PEER;
01113    else if (res)
01114       return FEATURE_RETURN_SUCCESSBREAK;
01115    
01116    return FEATURE_RETURN_SUCCESS;   /*! \todo XXX should probably return res */
01117 }

static struct ast_call_feature* find_dynamic_feature ( const char *  name  )  [static, read]

find a feature by name

Definition at line 1045 of file res_features.c.

References AST_LIST_TRAVERSE, and ast_call_feature::sname.

Referenced by ast_feature_interpret(), load_config(), and set_config_flags().

01046 {
01047    struct ast_call_feature *tmp;
01048 
01049    AST_LIST_TRAVERSE(&feature_list, tmp, feature_entry) {
01050       if (!strcasecmp(tmp->sname, name))
01051          break;
01052    }
01053 
01054    return tmp;
01055 }

static int finishup ( struct ast_channel chan  )  [static]

Definition at line 723 of file res_features.c.

References ast_autoservice_stop(), AST_CONTROL_UNHOLD, and ast_indicate().

Referenced by builtin_atxfer(), and builtin_blindtransfer().

00724 {
00725         ast_indicate(chan, AST_CONTROL_UNHOLD);
00726   
00727         return ast_autoservice_stop(chan);
00728 }

static int handle_autoanswer ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 2687 of file res_features.c.

References ast_cli(), ast_mutex_lock(), ast_mutex_unlock(), aauser::chan, aauser::context, aauser::exten, aauser::next, and RESULT_SUCCESS.

02688 {
02689    struct aauser *cur;
02690 
02691    ast_cli(fd, "%25s %10s %15s \n", "Channel"
02692       , "Extension", "Context");
02693 
02694    ast_mutex_lock(&autoanswer_lock);
02695 
02696    cur=aalot;
02697    while(cur) {
02698       ast_cli(fd, "%25s %10s %15s\n",cur->chan->name, cur->exten, cur->context);
02699 
02700       cur = cur->next;
02701    }
02702 
02703    ast_mutex_unlock(&autoanswer_lock);
02704 
02705    return RESULT_SUCCESS;
02706 }

static int handle_parkedcalls ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 2546 of file res_features.c.

References ast_cli(), ast_mutex_lock(), ast_mutex_unlock(), parkeduser::chan, parkeduser::context, parkeduser::exten, parkeduser::next, parkeduser::parkingexten, parkeduser::parkingtime, parkeduser::priority, RESULT_SUCCESS, and parkeduser::start.

02547 {
02548    struct parkeduser *cur;
02549    int numparked = 0;
02550 
02551    ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
02552       , "Context", "Extension", "Pri", "Timeout");
02553 
02554    ast_mutex_lock(&parking_lock);
02555 
02556    for (cur = parkinglot; cur; cur = cur->next) {
02557       ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
02558          ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
02559          ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
02560 
02561       numparked++;
02562    }
02563    ast_mutex_unlock(&parking_lock);
02564    ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : "");
02565 
02566 
02567    return RESULT_SUCCESS;
02568 }

static int handle_showfeatures ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 2386 of file res_features.c.

References ast_cli(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_pickup_ext(), ast_rwlock_rdlock(), ast_rwlock_unlock(), ast_call_feature::default_exten, ast_call_feature::exten, exten, FEATURES_COUNT, ast_call_feature::fname, format, RESULT_SUCCESS, and ast_call_feature::sname.

02387 {
02388    int i;
02389    struct ast_call_feature *feature;
02390    char format[] = "%-25s %-7s %-7s\n";
02391 
02392    ast_cli(fd, format, "Builtin Feature", "Default", "Current");
02393    ast_cli(fd, format, "---------------", "-------", "-------");
02394 
02395    ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext());      /* default hardcoded above, so we'll hardcode it here */
02396 
02397    ast_rwlock_rdlock(&features_lock);
02398    for (i = 0; i < FEATURES_COUNT; i++)
02399       ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
02400    ast_rwlock_unlock(&features_lock);
02401 
02402    ast_cli(fd, "\n");
02403    ast_cli(fd, format, "Dynamic Feature", "Default", "Current");
02404    ast_cli(fd, format, "---------------", "-------", "-------");
02405    if (AST_LIST_EMPTY(&feature_list))
02406       ast_cli(fd, "(none)\n");
02407    else {
02408       AST_LIST_LOCK(&feature_list);
02409       AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
02410          ast_cli(fd, format, feature->sname, "no def", feature->exten); 
02411       AST_LIST_UNLOCK(&feature_list);
02412    }
02413    ast_cli(fd, "\nCall parking\n");
02414    ast_cli(fd, "------------\n");
02415    ast_cli(fd,"%-20s:   %s\n", "Parking extension", parking_ext);
02416    ast_cli(fd,"%-20s:   %s\n", "Parking context", parking_con);
02417    ast_cli(fd,"%-20s:   %d-%d\n", "Parked call extensions", parking_start, parking_stop);
02418    ast_cli(fd,"\n");
02419    
02420    return RESULT_SUCCESS;
02421 }

static int load_config ( void   )  [static]

Todo:
XXX var_name or app_args ?

Definition at line 3159 of file res_features.c.

References ast_call_feature::app, app, ast_call_feature::app_args, ast_add_extension2(), ast_calloc, ast_config_destroy(), ast_config_load(), ast_context_create(), ast_context_find(), ast_context_remove_extension2(), AST_FEATURE_FLAG_BYBOTH, AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_FLAG_NEEDSDTMF, AST_FEATURE_FLAG_ONPEER, AST_FEATURE_FLAG_ONSELF, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), AST_MAX_EXTENSION, AST_MODULE_LOAD_DECLINE, ast_parking_ext(), ast_register_feature(), ast_set_flag, ast_strdupa, ast_strlen_zero(), ast_true(), ast_unregister_features(), ast_variable_browse(), ast_verbose(), DEFAULT_FEATURE_DIGIT_TIMEOUT, DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER, DEFAULT_PARK_TIME, DEFAULT_TRANSFER_DIGIT_TIMEOUT, ast_call_feature::exten, exten, FEATURE_APP_ARGS_LEN, FEATURE_APP_LEN, feature_exec_app(), FEATURE_EXTEN_LEN, FEATURE_MOH_LEN, FEATURE_SNAME_LEN, find_dynamic_feature(), ast_variable::lineno, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_call_feature::moh_class, ast_variable::name, ast_variable::next, notify_metermaids(), ast_call_feature::operation, option_debug, option_verbose, park_add_hints(), remap_feature(), ast_call_feature::sname, strsep(), unmap_features(), ast_variable::value, var, and VERBOSE_PREFIX_2.

03160 {
03161    int start = 0, end = 0;
03162    int res;
03163    struct ast_context *con = NULL;
03164    struct ast_config *cfg = NULL;
03165    struct ast_variable *var = NULL;
03166    char old_parking_ext[AST_MAX_EXTENSION];
03167    char old_parking_con[AST_MAX_EXTENSION] = "";
03168 
03169    if (!ast_strlen_zero(parking_con)) {
03170       strcpy(old_parking_ext, parking_ext);
03171       strcpy(old_parking_con, parking_con);
03172    } 
03173 
03174    /* Reset to defaults */
03175    strcpy(parking_con, "parkedcalls");
03176    strcpy(parking_con_dial, "park-dial");
03177    strcpy(parking_ext, "700");
03178    strcpy(pickup_ext, "*8");
03179    strcpy(parkmohclass, "default");
03180    courtesytone[0] = '\0';
03181    strcpy(xfersound, "beep");
03182    strcpy(xferfailsound, "pbx-invalid");
03183    parking_start = 701;
03184    parking_stop = 750;
03185    parkfindnext = 0;
03186    adsipark = 0;
03187    parkaddhints = 0;
03188 
03189    transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
03190    featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
03191    atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
03192 
03193    cfg = ast_config_load("features.conf");
03194    if (!cfg) {
03195       ast_log(LOG_WARNING,"Could not load features.conf\n");
03196       return AST_MODULE_LOAD_DECLINE;
03197    }
03198    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
03199       if (!strcasecmp(var->name, "parkext")) {
03200          ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
03201       } else if (!strcasecmp(var->name, "context")) {
03202          ast_copy_string(parking_con, var->value, sizeof(parking_con));
03203       } else if (!strcasecmp(var->name, "parkingtime")) {
03204          if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
03205             ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
03206             parkingtime = DEFAULT_PARK_TIME;
03207          } else
03208             parkingtime = parkingtime * 1000;
03209       } else if (!strcasecmp(var->name, "parkpos")) {
03210          if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
03211             ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno);
03212          } else {
03213             parking_start = start;
03214             parking_stop = end;
03215          }
03216       } else if (!strcasecmp(var->name, "findslot")) {
03217          parkfindnext = (!strcasecmp(var->value, "next"));
03218       } else if (!strcasecmp(var->name, "parkinghints")) {
03219          parkaddhints = ast_true(var->value);
03220       } else if (!strcasecmp(var->name, "adsipark")) {
03221          adsipark = ast_true(var->value);
03222       } else if (!strcasecmp(var->name, "transferdigittimeout")) {
03223          if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
03224             ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
03225             transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
03226          } else
03227             transferdigittimeout = transferdigittimeout * 1000;
03228       } else if (!strcasecmp(var->name, "featuredigittimeout")) {
03229          if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
03230             ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
03231             featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
03232          }
03233       } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
03234          if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
03235             ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
03236             atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
03237          } else
03238             atxfernoanswertimeout = atxfernoanswertimeout * 1000;
03239       } else if (!strcasecmp(var->name, "courtesytone")) {
03240          ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
03241       }  else if (!strcasecmp(var->name, "parkedplay")) {
03242          if (!strcasecmp(var->value, "both"))
03243             parkedplay = 2;
03244          else if (!strcasecmp(var->value, "parked"))
03245             parkedplay = 1;
03246          else
03247             parkedplay = 0;
03248       } else if (!strcasecmp(var->name, "xfersound")) {
03249          ast_copy_string(xfersound, var->value, sizeof(xfersound));
03250       } else if (!strcasecmp(var->name, "xferfailsound")) {
03251          ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
03252       } else if (!strcasecmp(var->name, "pickupexten")) {
03253          ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
03254       } else if (!strcasecmp(var->name, "parkedmusicclass")) {
03255          ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
03256       }
03257    }
03258 
03259    unmap_features();
03260    for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
03261       if (remap_feature(var->name, var->value))
03262          ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
03263    }
03264 
03265    /* Map a key combination to an application*/
03266    ast_unregister_features();
03267    for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
03268       char *tmp_val = ast_strdupa(var->value);
03269       char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 
03270       struct ast_call_feature *feature;
03271 
03272       /* strsep() sets the argument to NULL if match not found, and it
03273        * is safe to use it with a NULL argument, so we don't check
03274        * between calls.
03275        */
03276       exten = strsep(&tmp_val,",");
03277       activatedby = strsep(&tmp_val,",");
03278       app = strsep(&tmp_val,",");
03279       app_args = strsep(&tmp_val,",");
03280       moh_class = strsep(&tmp_val,",");
03281 
03282       activateon = strsep(&activatedby, "/");   
03283 
03284       /*! \todo XXX var_name or app_args ? */
03285       if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
03286          ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
03287             app, exten, activateon, var->name);
03288          continue;
03289       }
03290 
03291       AST_LIST_LOCK(&feature_list);
03292       if ((feature = find_dynamic_feature(var->name))) {
03293          AST_LIST_UNLOCK(&feature_list);
03294          ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
03295          continue;
03296       }
03297       AST_LIST_UNLOCK(&feature_list);
03298             
03299       if (!(feature = ast_calloc(1, sizeof(*feature))))
03300          continue;               
03301 
03302       ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
03303       ast_copy_string(feature->app, app, FEATURE_APP_LEN);
03304       ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
03305       
03306       if (app_args) 
03307          ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
03308 
03309       if (moh_class)
03310          ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
03311          
03312       ast_copy_string(feature->exten, exten, sizeof(feature->exten));
03313       feature->operation = feature_exec_app;
03314       ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
03315 
03316       /* Allow caller and calle to be specified for backwards compatability */
03317       if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
03318          ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
03319       else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
03320          ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
03321       else {
03322          ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
03323             " must be 'self', or 'peer'\n", var->name);
03324          continue;
03325       }
03326 
03327       if (ast_strlen_zero(activatedby))
03328          ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
03329       else if (!strcasecmp(activatedby, "caller"))
03330          ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
03331       else if (!strcasecmp(activatedby, "callee"))
03332          ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
03333       else if (!strcasecmp(activatedby, "both"))
03334          ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
03335       else {
03336          ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
03337             " must be 'caller', or 'callee', or 'both'\n", var->name);
03338          continue;
03339       }
03340 
03341       ast_register_feature(feature);
03342          
03343       if (option_verbose >= 1)
03344          ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);  
03345    }   
03346    ast_config_destroy(cfg);
03347 
03348    /* Remove the old parking extension */
03349    if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
03350       if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar))
03351             notify_metermaids(old_parking_ext, old_parking_con);
03352       if (option_debug)
03353          ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
03354    }
03355    
03356    if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
03357       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
03358       return -1;
03359    }
03360    res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
03361    if (parkaddhints)
03362       park_add_hints(parking_con, parking_start, parking_stop);
03363    if (!res)
03364       notify_metermaids(ast_parking_ext(), parking_con);
03365    return res;
03366 
03367 }

static int load_module ( void   )  [static]

Definition at line 3523 of file res_features.c.

References action_bridge(), ast_cli_register_multiple(), ast_devstate_prov_add(), ast_manager_register, ast_manager_register2(), ast_pthread_create, ast_register_application(), autoanswer_exec(), autoanswer_login_exec(), bridge_exec(), do_autoanswer_thread(), do_holding_thread(), do_parking_thread(), EVENT_FLAG_CALL, EVENT_FLAG_COMMAND, load_config(), manager_park(), manager_parking_status(), metermaidstate(), park_call_exec(), park_exec(), and retrieve_call_exec().

03524 {
03525    int res;
03526    
03527    ast_register_application(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip); 
03528 
03529    memset(parking_ext, 0, sizeof(parking_ext));
03530    memset(parking_con, 0, sizeof(parking_con));
03531 
03532    if ((res = load_config()))
03533       return res;
03534    ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
03535    ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
03536    ast_pthread_create(&holding_thread, NULL, do_holding_thread, NULL);
03537    res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
03538    if (!res)
03539       res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
03540    if (!res) {
03541       ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" );
03542       ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
03543          "Park a channel", mandescr_park); 
03544       ast_manager_register2("Bridge", EVENT_FLAG_COMMAND, action_bridge, "Bridge two channels already in the PBX", mandescr_bridge);
03545    }
03546 
03547    res |= ast_register_application(holdedcall, retrieve_call_exec, synopsis, descrip);
03548    ast_pthread_create(&autoanswer_thread, NULL, do_autoanswer_thread, NULL);
03549    if (!res)
03550       res |= ast_register_application(autoanswerlogin, autoanswer_login_exec, synopsis3, descrip3);
03551    if (!res)
03552       res |= ast_register_application(autoanswer, autoanswer_exec, synopsis4, descrip4);
03553 
03554    res |= ast_devstate_prov_add("Park", metermaidstate);
03555 
03556    return res;
03557 }

static int manager_park ( struct mansession s,
const struct message m 
) [static]

Definition at line 2633 of file res_features.c.

References ast_channel_unlock, ast_get_channel_by_name_locked(), ast_masq_park_call(), ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, ast_strlen_zero(), astman_get_header(), astman_send_ack(), and astman_send_error().

Referenced by load_module().

02634 {
02635    const char *channel = astman_get_header(m, "Channel");
02636    const char *channel2 = astman_get_header(m, "Channel2");
02637    const char *timeout = astman_get_header(m, "Timeout");
02638    char buf[BUFSIZ];
02639    int to = 0;
02640    int res = 0;
02641    int parkExt = 0;
02642    struct ast_channel *ch1, *ch2;
02643 
02644    if (ast_strlen_zero(channel)) {
02645       astman_send_error(s, m, "Channel not specified");
02646       return 0;
02647    }
02648 
02649    if (ast_strlen_zero(channel2)) {
02650       astman_send_error(s, m, "Channel2 not specified");
02651       return 0;
02652    }
02653 
02654    ch1 = ast_get_channel_by_name_locked(channel);
02655    if (!ch1) {
02656       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
02657       astman_send_error(s, m, buf);
02658       return 0;
02659    }
02660 
02661    ch2 = ast_get_channel_by_name_locked(channel2);
02662    if (!ch2) {
02663       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
02664       astman_send_error(s, m, buf);
02665       ast_channel_unlock(ch1);
02666       return 0;
02667    }
02668 
02669    if (!ast_strlen_zero(timeout)) {
02670       sscanf(timeout, "%d", &to);
02671    }
02672 
02673    res = ast_masq_park_call(ch1, ch2, to, &parkExt);
02674    if (!res) {
02675       ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
02676       astman_send_ack(s, m, "Park successful");
02677    } else {
02678       astman_send_error(s, m, "Park failure");
02679    }
02680 
02681    ast_channel_unlock(ch1);
02682    ast_channel_unlock(ch2);
02683 
02684    return 0;
02685 }

static int manager_parking_status ( struct mansession s,
const struct message m 
) [static]

Dump lot status.

Definition at line 2585 of file res_features.c.

References ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), parkeduser::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, parkeduser::next, parkeduser::parkingnum, parkeduser::parkingtime, parkeduser::peername, RESULT_SUCCESS, S_OR, and parkeduser::start.

Referenced by load_module().

02586 {
02587    struct parkeduser *cur;
02588    const char *id = astman_get_header(m, "ActionID");
02589    char idText[256] = "";
02590 
02591    if (!ast_strlen_zero(id))
02592       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02593 
02594    astman_send_ack(s, m, "Parked calls will follow");
02595 
02596    ast_mutex_lock(&parking_lock);
02597 
02598    for (cur = parkinglot; cur; cur = cur->next) {
02599       astman_append(s, "Event: ParkedCall\r\n"
02600          "Exten: %d\r\n"
02601          "Channel: %s\r\n"
02602          "From: %s\r\n"
02603          "Timeout: %ld\r\n"
02604          "CallerID: %s\r\n"
02605          "CallerIDName: %s\r\n"
02606          "Uniqueid: %s\r\n\r\n"
02607          "%s"
02608          "\r\n",
02609          cur->parkingnum, cur->chan->name, cur->peername,
02610          (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
02611          S_OR(cur->chan->cid.cid_num, ""),   /* XXX in other places it is <unknown> */
02612          S_OR(cur->chan->cid.cid_name, ""), cur->chan->uniqueid,
02613          idText);
02614    }
02615 
02616    astman_append(s,
02617       "Event: ParkedCallsComplete\r\n"
02618       "%s"
02619       "\r\n",idText);
02620 
02621    ast_mutex_unlock(&parking_lock);
02622 
02623    return RESULT_SUCCESS;
02624 }

static int metermaidstate ( const char *  data  )  [static]

metermaids callback from devicestate.c

Definition at line 372 of file res_features.c.

References AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, ast_exists_extension(), ast_log(), ast_strdupa, context, exten, LOG_DEBUG, option_debug, and strsep().

Referenced by load_module().

00373 {
00374    int res = AST_DEVICE_INVALID;
00375    char *context = ast_strdupa(data);
00376    char *exten;
00377 
00378    exten = strsep(&context, "@");
00379    if (!context)
00380       return res;
00381    
00382    if (option_debug > 3)
00383       ast_log(LOG_DEBUG, "Checking state of exten %s in context %s\n", exten, context);
00384 
00385    res = ast_exists_extension(NULL, context, exten, 1, NULL);
00386 
00387    if (!res)
00388       return AST_DEVICE_NOT_INUSE;
00389    else
00390       return AST_DEVICE_INUSE;
00391 }

static void notify_metermaids ( char *  exten,
char *  context 
) [static]

Notify metermaids that we've changed an extension.

Definition at line 361 of file res_features.c.

References ast_device_state_changed(), ast_log(), LOG_DEBUG, and option_debug.

Referenced by do_parking_thread(), load_config(), park_call_full(), and park_exec().

00362 {
00363    if (option_debug > 3)
00364       ast_log(LOG_DEBUG, "Notification of state change to metermaids %s@%s\n", exten, context);
00365 
00366    /* Send notification to devicestate subsystem */
00367    ast_device_state_changed("park:%s@%s", exten, context);
00368    return;
00369 }

static void park_add_hints ( char *  context,
int  start,
int  stop 
) [static]

Add parking hints for all defined parking lots.

Definition at line 3145 of file res_features.c.

References ast_add_extension(), AST_MAX_EXTENSION, exten, and PRIORITY_HINT.

Referenced by load_config().

03146 {
03147    int numext;
03148    char device[AST_MAX_EXTENSION];
03149    char exten[10];
03150 
03151    for (numext = start; numext <= stop; numext++) {
03152       snprintf(exten, sizeof(exten), "%d", numext);
03153       snprintf(device, sizeof(device), "park:%s@%s", exten, context);
03154       ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
03155    }
03156 }

static int park_call_exec ( struct ast_channel chan,
void *  data 
) [static]

Park a call.

Definition at line 1930 of file res_features.c.

References ast_channel::_state, ast_answer(), AST_MAX_EXTENSION, ast_module_user_add, ast_module_user_remove, AST_PBX_KEEPALIVE, ast_safe_sleep(), AST_STATE_UP, ast_strdupa, ast_channel::exten, orig_exten(), park_call_full(), and ast_channel::priority.

Referenced by load_module().

01931 {
01932    /* Cache the original channel name in case we get masqueraded in the middle
01933     * of a park--it is still theoretically possible for a transfer to happen before
01934     * we get here, but it is _really_ unlikely */
01935    char *orig_chan_name = ast_strdupa(chan->name);
01936    char orig_exten[AST_MAX_EXTENSION];
01937    int orig_priority = chan->priority;
01938 
01939    /* Data is unused at the moment but could contain a parking
01940       lot context eventually */
01941    int res = 0;
01942    struct ast_module_user *u;
01943 
01944    u = ast_module_user_add(chan);
01945 
01946    ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten));
01947 
01948    /* Setup the exten/priority to be s/1 since we don't know
01949       where this call should return */
01950    strcpy(chan->exten, "s");
01951    chan->priority = 1;
01952    /* Answer if call is not up */
01953    if (chan->_state != AST_STATE_UP)
01954       res = ast_answer(chan);
01955    /* Sleep to allow VoIP streams to settle down */
01956    if (!res)
01957       res = ast_safe_sleep(chan, 1000);
01958    /* Park the call */
01959    if (!res) {
01960       res = park_call_full(chan, chan, 0, NULL, orig_chan_name);
01961       /* Continue on in the dialplan */
01962       if (res == 1) {
01963          ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
01964          chan->priority = orig_priority;
01965          res = 0;
01966       } else if (!res)
01967          res = AST_PBX_KEEPALIVE;
01968    }
01969 
01970    ast_module_user_remove(u);
01971 
01972    return res;
01973 }

static int park_call_full ( struct ast_channel chan,
struct ast_channel peer,
int  timeout,
int *  extout,
char *  orig_chan_name 
) [static]

Definition at line 393 of file res_features.c.

References adsi_announce_park(), ast_channel::appl, ast_add_extension2(), ast_adsi_available(), ast_adsi_unload_session(), ast_calloc, ast_clear_flag, ast_context_create(), ast_context_find(), AST_CONTROL_HOLD, ast_exists_extension(), AST_FLAG_MASQ_NOSTREAM, ast_free, ast_indicate_data(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_say_digits(), ast_set_flag, ast_strlen_zero(), ast_verbose(), parkeduser::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, ast_channel::context, parkeduser::context, ast_channel::data, EVENT_FLAG_CALL, ast_channel::exten, parkeduser::exten, free, LOG_ERROR, LOG_WARNING, ast_channel::macrocontext, ast_channel::macroexten, ast_channel::macropriority, manager_event(), parkeduser::next, notify_metermaids(), parkeduser::notquiteyet, option_verbose, parkeduser::parkingexten, parkeduser::parkingnum, parkeduser::parkingtime, pbx_builtin_getvar_helper(), parkeduser::peername, ast_channel::priority, parkeduser::priority, S_OR, parkeduser::start, strdup, and VERBOSE_PREFIX_2.

Referenced by ast_masq_park_call(), ast_park_call(), and park_call_exec().

00394 {
00395    struct parkeduser *pu, *cur;
00396    int i, x = -1, parking_range;
00397    struct ast_context *con;
00398    const char *parkingexten;
00399    
00400    /* Allocate memory for parking data */
00401    if (!(pu = ast_calloc(1, sizeof(*pu)))) 
00402       return -1;
00403 
00404    /* Lock parking lot */
00405    ast_mutex_lock(&parking_lock);
00406    /* Check for channel variable PARKINGEXTEN */
00407    parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN");
00408    if (!ast_strlen_zero(parkingexten)) {
00409       if (ast_exists_extension(NULL, parking_con, parkingexten, 1, NULL)) {
00410          ast_mutex_unlock(&parking_lock);
00411          free(pu);
00412          ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con);
00413          return 1;   /* Continue execution if possible */
00414       }
00415       ast_copy_string(pu->parkingexten, parkingexten, sizeof(pu->parkingexten));
00416       x = atoi(parkingexten);
00417    } else {
00418       /* Select parking space within range */
00419       parking_range = parking_stop - parking_start+1;
00420       for (i = 0; i < parking_range; i++) {
00421          x = (i + parking_offset) % parking_range + parking_start;
00422          cur = parkinglot;
00423          while(cur) {
00424             if (cur->parkingnum == x) 
00425                break;
00426             cur = cur->next;
00427          }
00428          if (!cur)
00429             break;
00430       }
00431 
00432       if (!(i < parking_range)) {
00433          ast_log(LOG_WARNING, "No more parking spaces\n");
00434          free(pu);
00435          ast_mutex_unlock(&parking_lock);
00436          return -1;
00437       }
00438       /* Set pointer for next parking */
00439       if (parkfindnext) 
00440          parking_offset = x - parking_start + 1;
00441    }
00442    
00443    chan->appl = "Parked Call";
00444    chan->data = NULL; 
00445 
00446    pu->chan = chan;
00447    
00448    /* Put the parked channel on hold if we have two different channels */
00449    if (chan != peer) {
00450       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
00451          S_OR(parkmohclass, NULL),
00452          !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
00453    }
00454    
00455    pu->start = ast_tvnow();
00456    pu->parkingnum = x;
00457    pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
00458    if (extout)
00459       *extout = x;
00460 
00461    if (peer) 
00462       ast_copy_string(pu->peername, peer->name, sizeof(pu->peername));
00463 
00464    /* Remember what had been dialed, so that if the parking
00465       expires, we try to come back to the same place */
00466    ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context));
00467    ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten));
00468    pu->priority = chan->macropriority ? chan->macropriority : chan->priority;
00469    pu->next = parkinglot;
00470    parkinglot = pu;
00471 
00472    /* If parking a channel directly, don't quiet yet get parking running on it */
00473    if (peer == chan) 
00474       pu->notquiteyet = 1;
00475    ast_mutex_unlock(&parking_lock);
00476    /* Wake up the (presumably select()ing) thread */
00477    pthread_kill(parking_thread, SIGURG);
00478    if (option_verbose > 1) 
00479       ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d@%s. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, parking_con, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
00480 
00481    if (pu->parkingnum != -1)
00482       snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x);
00483    manager_event(EVENT_FLAG_CALL, "ParkedCall",
00484       "Exten: %s\r\n"
00485       "Channel: %s\r\n"
00486       "From: %s\r\n"
00487       "Timeout: %ld\r\n"
00488       "CallerID: %s\r\n"
00489       "CallerIDName: %s\r\n"
00490       "Uniqueid: %s\r\n",
00491       pu->parkingexten, pu->chan->name, peer ? peer->name : "",
00492       (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
00493       S_OR(pu->chan->cid.cid_num, "<unknown>"),
00494       S_OR(pu->chan->cid.cid_name, "<unknown>"),
00495       pu->chan->uniqueid
00496       );
00497 
00498    if (peer && adsipark && ast_adsi_available(peer)) {
00499       adsi_announce_park(peer, pu->parkingexten);  /* Only supports parking numbers */
00500       ast_adsi_unload_session(peer);
00501    }
00502 
00503    con = ast_context_find(parking_con);
00504    if (!con) 
00505       con = ast_context_create(NULL, parking_con, registrar);
00506    if (!con)   /* Still no context? Bad */
00507       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
00508    /* Tell the peer channel the number of the parking space */
00509    if (peer && ((pu->parkingnum != -1 && ast_strlen_zero(orig_chan_name)) || !strcasecmp(peer->name, orig_chan_name))) { /* Only say number if it's a number and the channel hasn't been masqueraded away */
00510       /* Make sure we don't start saying digits to the channel being parked */
00511       ast_set_flag(peer, AST_FLAG_MASQ_NOSTREAM);
00512       ast_say_digits(peer, pu->parkingnum, "", peer->language);
00513       ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM);
00514    }
00515    if (con) {
00516       if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), ast_free, registrar))
00517          notify_metermaids(pu->parkingexten, parking_con);
00518    }
00519    if (pu->notquiteyet) {
00520       /* Wake up parking thread if we're really done */
00521       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
00522          S_OR(parkmohclass, NULL),
00523          !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
00524       pu->notquiteyet = 0;
00525       pthread_kill(parking_thread, SIGURG);
00526    }
00527    return 0;
00528 }

static int park_exec ( struct ast_channel chan,
void *  data 
) [static]

Pickup parked call.

Todo:
XXX we would like to wait on both!

Todo:
XXX Play a message XXX

Definition at line 1976 of file res_features.c.

References ast_channel::_state, ast_answer(), ast_bridge_call(), ast_cdr_setdestchan(), ast_channel_make_compatible(), ast_context_find(), ast_context_remove_extension2(), AST_CONTROL_UNHOLD, AST_FEATURE_REDIRECT, ast_hangup(), ast_indicate(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), AST_PBX_NO_HANGUP_PEER, ast_set_flag, AST_STATE_UP, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_verbose(), ast_waitstream(), ast_channel::cdr, parkeduser::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, error(), EVENT_FLAG_CALL, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, LOG_WARNING, manager_event(), parkeduser::next, notify_metermaids(), option_verbose, parkeduser::parkingexten, parkeduser::parkingnum, pbx_builtin_setvar_helper(), S_OR, and VERBOSE_PREFIX_3.

Referenced by load_module().

01977 {
01978    int res = 0;
01979    struct ast_module_user *u;
01980    struct ast_channel *peer=NULL;
01981    struct parkeduser *pu, *pl=NULL;
01982    struct ast_context *con;
01983 
01984    int park;
01985    struct ast_bridge_config config;
01986 
01987    if (!data) {
01988       ast_log(LOG_WARNING, "Parkedcall requires an argument (extension number)\n");
01989       return -1;
01990    }
01991    
01992    u = ast_module_user_add(chan);
01993 
01994    park = atoi((char *)data);
01995    ast_mutex_lock(&parking_lock);
01996    pu = parkinglot;
01997    while(pu) {
01998       if (pu->parkingnum == park) {
01999          if (pl)
02000             pl->next = pu->next;
02001          else
02002             parkinglot = pu->next;
02003          break;
02004       }
02005       pl = pu;
02006       pu = pu->next;
02007    }
02008    ast_mutex_unlock(&parking_lock);
02009    if (pu) {
02010       peer = pu->chan;
02011       con = ast_context_find(parking_con);
02012       if (con) {
02013          if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
02014             ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
02015          else
02016             notify_metermaids(pu->parkingexten, parking_con);
02017       } else
02018          ast_log(LOG_WARNING, "Whoa, no parking context?\n");
02019 
02020       manager_event(EVENT_FLAG_CALL, "UnParkedCall",
02021          "Exten: %s\r\n"
02022          "Channel: %s\r\n"
02023          "From: %s\r\n"
02024          "CallerID: %s\r\n"
02025          "CallerIDName: %s\r\n"
02026          "Uniqueid: %s\r\n",
02027          pu->parkingexten, pu->chan->name, chan->name,
02028          S_OR(pu->chan->cid.cid_num, "<unknown>"),
02029          S_OR(pu->chan->cid.cid_name, "<unknown>"),
02030          pu->chan->uniqueid
02031          );
02032 
02033       free(pu);
02034    }
02035    /* JK02: it helps to answer the channel if not already up */
02036    if (chan->_state != AST_STATE_UP)
02037       ast_answer(chan);
02038 
02039    if (peer) {
02040       /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
02041       
02042       if (!ast_strlen_zero(courtesytone)) {
02043          int error = 0;
02044          ast_indicate(peer, AST_CONTROL_UNHOLD);
02045          if (parkedplay == 0) {
02046             error = ast_stream_and_wait(chan, courtesytone, chan->language, "");
02047          } else if (parkedplay == 1) {
02048             error = ast_stream_and_wait(peer, courtesytone, chan->language, "");
02049          } else if (parkedplay == 2) {
02050             if (!ast_streamfile(chan, courtesytone, chan->language) &&
02051                   !ast_streamfile(peer, courtesytone, chan->language)) {
02052                /*! \todo XXX we would like to wait on both! */
02053                res = ast_waitstream(chan, "");
02054                if (res >= 0)
02055                   res = ast_waitstream(peer, "");
02056                if (res < 0)
02057                   error = 1;
02058             }
02059                         }
02060          if (error) {
02061             ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
02062             ast_hangup(peer);
02063             ast_module_user_remove(u);
02064             return -1;
02065          }
02066       } else
02067          ast_indicate(peer, AST_CONTROL_UNHOLD); 
02068 
02069       res = ast_channel_make_compatible(chan, peer);
02070       if (res < 0) {
02071          ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
02072          ast_hangup(peer);
02073          ast_module_user_remove(u);
02074          return -1;
02075       }
02076       /* This runs sorta backwards, since we give the incoming channel control, as if it
02077          were the person called. */
02078       if (option_verbose > 2) 
02079          ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
02080 
02081       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
02082       ast_cdr_setdestchan(chan->cdr, peer->name);
02083       memset(&config, 0, sizeof(struct ast_bridge_config));
02084       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
02085       ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
02086       res = ast_bridge_call(chan, peer, &config);
02087 
02088       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
02089       ast_cdr_setdestchan(chan->cdr, peer->name);
02090 
02091       /* Simulate the PBX hanging up */
02092       if (res != AST_PBX_NO_HANGUP_PEER)
02093          ast_hangup(peer);
02094       ast_module_user_remove(u);
02095       return res;
02096    } else {
02097       /*! \todo XXX Play a message XXX */
02098       if (ast_stream_and_wait(chan, "pbx-invalidpark", chan->language, ""))
02099          ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
02100       if (option_verbose > 2) 
02101          ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
02102       res = -1;
02103    }
02104 
02105    ast_module_user_remove(u);
02106 
02107    return res;
02108 }

static void post_manager_event ( const char *  s,
char *  parkingexten,
struct ast_channel chan 
) [static]

Definition at line 1745 of file res_features.c.

References ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, EVENT_FLAG_CALL, manager_event(), and S_OR.

Referenced by do_parking_thread().

01746 {
01747    manager_event(EVENT_FLAG_CALL, s,
01748       "Exten: %s\r\n"
01749       "Channel: %s\r\n"
01750       "CallerID: %s\r\n"
01751       "CallerIDName: %s\r\n"
01752       "Uniqueid: %s\r\n\r\n",
01753       parkingexten, 
01754       chan->name,
01755       S_OR(chan->cid.cid_num, "<unknown>"),
01756       S_OR(chan->cid.cid_name, "<unknown>"),
01757       chan->uniqueid
01758       );
01759 }

static const char* real_ctx ( struct ast_channel transferer,
struct ast_channel transferee 
) [static]

Find the context for the transfer.

Definition at line 731 of file res_features.c.

References ast_strlen_zero(), ast_channel::context, ast_channel::macrocontext, pbx_builtin_getvar_helper(), and s.

Referenced by builtin_atxfer(), and builtin_blindtransfer().

00732 {
00733         const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
00734         if (ast_strlen_zero(s))
00735                 s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
00736         if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */
00737                 s = transferer->macrocontext;
00738         if (ast_strlen_zero(s))
00739                 s = transferer->context;
00740         return s;  
00741 }

static int reload ( void   )  [static]

Definition at line 3517 of file res_features.c.

References autoanswer_reregister_extensions(), and load_config().

03518 {
03519    autoanswer_reregister_extensions();
03520    return load_config();
03521 }

static int remap_feature ( const char *  name,
const char *  value 
) [static]

Definition at line 1129 of file res_features.c.

References ast_rwlock_unlock(), ast_rwlock_wrlock(), exten, and FEATURES_COUNT.

Referenced by load_config().

01130 {
01131    int x, res = -1;
01132 
01133    ast_rwlock_wrlock(&features_lock);
01134    for (x = 0; x < FEATURES_COUNT; x++) {
01135       if (strcasecmp(builtin_features[x].sname, name))
01136          continue;
01137 
01138       ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
01139       res = 0;
01140       break;
01141    }
01142    ast_rwlock_unlock(&features_lock);
01143 
01144    return res;
01145 }

static int retrieve_call_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 2376 of file res_features.c.

References ast_module_user_add, ast_module_user_remove, and ast_retrieve_call().

Referenced by load_module().

02376                                                                     {
02377    int res=0;
02378    struct ast_module_user *u;
02379    char *uniqueid = (char *)data;
02380    u = ast_module_user_add(chan);
02381        res = ast_retrieve_call(chan, uniqueid);
02382    ast_module_user_remove(u);
02383    return res;
02384 }

static void set_c_e_p ( struct ast_channel chan,
const char *  context,
const char *  ext,
int  pri 
) [static]

store context, priority and extension

Definition at line 239 of file res_features.c.

References ast_channel::context, ast_channel::exten, and ast_channel::priority.

Referenced by ast_masq_park_call(), builtin_blindtransfer(), and do_parking_thread().

00240 {
00241    ast_copy_string(chan->context, context, sizeof(chan->context));
00242    ast_copy_string(chan->exten, ext, sizeof(chan->exten));
00243    chan->priority = pri;
00244 }

static void set_config_flags ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config 
) [static]

Definition at line 1213 of file res_features.c.

References AST_BRIDGE_DTMF_CHANNEL_0, AST_BRIDGE_DTMF_CHANNEL_1, ast_clear_flag, AST_FEATURE_FLAG_BYCALLEE, AST_FEATURE_FLAG_BYCALLER, AST_FEATURE_FLAG_NEEDSDTMF, AST_FLAGS_ALL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_rwlock_rdlock(), ast_rwlock_unlock(), ast_set_flag, ast_strdupa, ast_test_flag, ast_call_feature::feature_mask, ast_bridge_config::features_callee, ast_bridge_config::features_caller, FEATURES_COUNT, find_dynamic_feature(), pbx_builtin_getvar_helper(), and strsep().

Referenced by ast_bridge_call().

01214 {
01215    int x;
01216    
01217    ast_clear_flag(config, AST_FLAGS_ALL);
01218 
01219    ast_rwlock_rdlock(&features_lock);
01220    for (x = 0; x < FEATURES_COUNT; x++) {
01221       if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
01222          continue;
01223 
01224       if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
01225          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
01226 
01227       if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
01228          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
01229    }
01230    ast_rwlock_unlock(&features_lock);
01231    
01232    if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
01233       const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
01234 
01235       if (dynamic_features) {
01236          char *tmp = ast_strdupa(dynamic_features);
01237          char *tok;
01238          struct ast_call_feature *feature;
01239 
01240          /* while we have a feature */
01241          while ((tok = strsep(&tmp, "#"))) {
01242             AST_LIST_LOCK(&feature_list);
01243             if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
01244                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
01245                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
01246                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
01247                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
01248             }
01249             AST_LIST_UNLOCK(&feature_list);
01250          }
01251       }
01252    }
01253 }

static void set_peers ( struct ast_channel **  caller,
struct ast_channel **  callee,
struct ast_channel peer,
struct ast_channel chan,
int  sense 
) [static]

set caller and callee according to the direction

Definition at line 586 of file res_features.c.

References FEATURE_SENSE_PEER.

Referenced by builtin_atxfer(), builtin_automonitor(), builtin_blindtransfer(), and builtin_parkcall().

00588 {
00589    if (sense == FEATURE_SENSE_PEER) {
00590       *caller = peer;
00591       *callee = chan;
00592    } else {
00593       *callee = peer;
00594       *caller = chan;
00595    }
00596 }

static int unload_module ( void   )  [static]

static void unmap_features ( void   )  [static]

Definition at line 1119 of file res_features.c.

References ast_rwlock_unlock(), ast_rwlock_wrlock(), exten, and FEATURES_COUNT.

Referenced by load_config().

01120 {
01121    int x;
01122 
01123    ast_rwlock_wrlock(&features_lock);
01124    for (x = 0; x < FEATURES_COUNT; x++)
01125       strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
01126    ast_rwlock_unlock(&features_lock);
01127 }


Variable Documentation

struct aauser* aalot [static]

Definition at line 202 of file res_features.c.

int adsipark [static]

Definition at line 108 of file res_features.c.

char* app_bridge = "Bridge" [static]

Definition at line 3369 of file res_features.c.

int atxfernoanswertimeout [static]

Definition at line 113 of file res_features.c.

char* autoanswer = "Autoanswer" [static]

Definition at line 148 of file res_features.c.

pthread_t autoanswer_thread [static]

Definition at line 204 of file res_features.c.

char* autoanswerlogin = "AutoanswerLogin" [static]

Definition at line 141 of file res_features.c.

char* bridge_descrip [static]

Definition at line 3371 of file res_features.c.

char* bridge_synopsis = "Bridge two channels" [static]

Definition at line 3370 of file res_features.c.

Definition at line 993 of file res_features.c.

struct ast_cli_entry cli_features[] [static]

Definition at line 2708 of file res_features.c.

Initial value:

 {
   { "show", "features", NULL },
   handle_showfeatures, NULL,
   NULL }

Definition at line 2574 of file res_features.c.

char courtesytone[256] [static]

Courtesy tone

Definition at line 100 of file res_features.c.

char* descrip [static]

Initial value:

 "ParkedCall(exten):"
"Used to connect to a parked call.  This application is always\n"
"registered internally and does not need to be explicitly added\n"
"into the dialplan, although you should include the 'parkedcalls'\n"
"context.\n"

Definition at line 120 of file res_features.c.

char* descrip2 [static]

Definition at line 130 of file res_features.c.

char* descrip3 [static]

Initial value:

 "AutoanswerLogin([context]|exten):"
"Used to login to the autoanswer application for an extension.\n"

Definition at line 145 of file res_features.c.

char* descrip4 [static]

Initial value:

 "Autoanswer([context]|exten):"
"Used to autoanswer a call for an extension.\n"

Definition at line 152 of file res_features.c.

int featuredigittimeout [static]

Definition at line 111 of file res_features.c.

char* holdedcall = "HoldedCall" [static]

Definition at line 88 of file res_features.c.

pthread_t holding_thread [static]

Definition at line 216 of file res_features.c.

struct holdeduser* holdlist [static]

Definition at line 208 of file res_features.c.

char mandescr_bridge[] [static]

Definition at line 2423 of file res_features.c.

char mandescr_park[] [static]

Definition at line 2626 of file res_features.c.

struct ast_app* monitor_app = NULL [static]

Definition at line 155 of file res_features.c.

int monitor_ok = 1 [static]

Definition at line 156 of file res_features.c.

int parkaddhints = 0 [static]

Add parking hints automatically

Definition at line 90 of file res_features.c.

char* parkcall = "Park" [static]

Definition at line 126 of file res_features.c.

char* parkedcall = "ParkedCall" [static]

Definition at line 87 of file res_features.c.

int parkedplay = 0 [static]

Who to play the courtesy tone to

Definition at line 101 of file res_features.c.

int parkfindnext [static]

Definition at line 106 of file res_features.c.

char parking_con[AST_MAX_EXTENSION] [static]

Context for which parking is made accessible

Definition at line 92 of file res_features.c.

char parking_con_dial[AST_MAX_EXTENSION] [static]

Context for dialback for parking (KLUDGE)

Definition at line 93 of file res_features.c.

char parking_ext[AST_MAX_EXTENSION] [static]

Extension you type to park the call

Definition at line 94 of file res_features.c.

int parking_offset [static]

Definition at line 105 of file res_features.c.

int parking_start [static]

First available extension for parking

Definition at line 97 of file res_features.c.

int parking_stop [static]

Last available extension for parking

Definition at line 98 of file res_features.c.

pthread_t parking_thread [static]

Definition at line 214 of file res_features.c.

struct parkeduser* parkinglot [static]

Definition at line 206 of file res_features.c.

int parkingtime = DEFAULT_PARK_TIME [static]

No more than 45 seconds parked before you do something with them

Definition at line 91 of file res_features.c.

char parkmohclass[MAX_MUSICCLASS] [static]

Music class used for parking

Definition at line 96 of file res_features.c.

char pickup_ext[AST_MAX_EXTENSION] [static]

Call pickup extension

Definition at line 95 of file res_features.c.

char* registrar = "res_features" [static]

Registrar for operations

Definition at line 115 of file res_features.c.

char showautoanswer_help[] [static]

Initial value:

"Usage: show autoanswer\n"
"       Lists currently logged in autoanswer users.\n"

Definition at line 2579 of file res_features.c.

char showfeatures_help[] [static]

Initial value:

"Usage: feature list\n"
"       Lists currently configured features.\n"

Definition at line 2542 of file res_features.c.

char showparked_help[] [static]

Initial value:

"Usage: show parkedcalls\n"
"       Lists currently parked calls.\n"

Definition at line 2570 of file res_features.c.

char* synopsis = "Answer a parked call" [static]

Definition at line 118 of file res_features.c.

char* synopsis2 = "Park yourself" [static]

Definition at line 128 of file res_features.c.

char* synopsis3 = "Log in for autoanswer" [static]

Definition at line 143 of file res_features.c.

char* synopsis4 = "Autoanswer a call" [static]

Definition at line 150 of file res_features.c.

int transferdigittimeout [static]

Definition at line 110 of file res_features.c.

char xferfailsound[256] [static]

Call transfer failure sound

Definition at line 103 of file res_features.c.

char xfersound[256] [static]

Call transfer sound

Definition at line 102 of file res_features.c.


Generated on Sun Dec 18 20:55:53 2011 for Asterisk - the Open Source PBX by  doxygen 1.5.6