Mon Nov 24 15:34:48 2008

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/global_datastores.h"

Go to the source code of this file.

Data Structures

struct  ast_bridge_thread_obj
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)
}

Functions

static int adsi_announce_park (struct ast_channel *chan, char *parkingexten)
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)
static AST_LIST_HEAD_STATIC (feature_list, ast_call_feature)
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 (parking_lock)
int ast_park_call (struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
 Park a call and read back parked location.
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_set
 AST_RWLOCK_DEFINE_STATIC (features_lock)
void ast_unregister_feature (struct ast_call_feature *feature)
 unregister feature from feature_set
static void ast_unregister_features (void)
 Remove all features in the list.
static int builtin_atxfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data)
 Attended transfer ().
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 cmd_atxfer (struct ast_channel *a, struct ast_channel *b, struct ast_bridge_config *conf, struct ast_channel *who, char *xferto)
static int do_atxfer (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, int sense, const char *toExt, const char *toCont)
 Attended transfer implementation.
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_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 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 int adsipark
static int atxfernoanswertimeout
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 int featuredigittimeout
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 showfeatures_help []
static char showparked_help []
static char * synopsis = "Answer a parked call"
static char * synopsis2 = "Park yourself"
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 72 of file res_features.c.

#define DEFAULT_FEATURE_DIGIT_TIMEOUT   500

Definition at line 69 of file res_features.c.

Referenced by load_config().

#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER   15000

Definition at line 70 of file res_features.c.

Referenced by load_config().

#define DEFAULT_PARK_TIME   45000

Definition at line 67 of file res_features.c.

#define DEFAULT_TRANSFER_DIGIT_TIMEOUT   3000

Definition at line 68 of file res_features.c.

Referenced by load_config().

#define FEATURE_RETURN_HANGUP   -1

Definition at line 520 of file res_features.c.

Referenced by builtin_disconnect().

#define FEATURE_RETURN_KEEPTRYING   24

Definition at line 527 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 523 of file res_features.c.

Referenced by feature_exec_app().

#define FEATURE_RETURN_PASSDIGITS   21

Definition at line 524 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 522 of file res_features.c.

Referenced by feature_exec_app().

#define FEATURE_RETURN_STOREDIGITS   22

Definition at line 525 of file res_features.c.

Referenced by ast_feature_interpret().

#define FEATURE_RETURN_SUCCESS   23

Definition at line 526 of file res_features.c.

Referenced by ast_bridge_call(), builtin_automonitor(), builtin_blindtransfer(), do_atxfer(), and feature_exec_app().

#define FEATURE_RETURN_SUCCESSBREAK   0

Definition at line 521 of file res_features.c.

Referenced by feature_exec_app().

#define FEATURE_SENSE_CHAN   (1 << 0)

Definition at line 529 of file res_features.c.

Referenced by ast_bridge_call(), ast_feature_interpret(), builtin_parkcall(), cmd_atxfer(), and feature_exec_app().

#define FEATURE_SENSE_PEER   (1 << 1)

Definition at line 530 of file res_features.c.

Referenced by ast_bridge_call(), cmd_atxfer(), feature_exec_app(), and set_peers().

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

Definition at line 1006 of file res_features.c.

Referenced by ast_feature_interpret(), ast_feature_request_and_dial(), handle_showfeatures(), remap_feature(), set_config_flags(), and unmap_features().


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 74 of file res_features.c.

00074      {
00075    AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0),
00076    AST_FEATURE_FLAG_ONPEER =    (1 << 1),
00077    AST_FEATURE_FLAG_ONSELF =    (1 << 2),
00078    AST_FEATURE_FLAG_BYCALLEE =  (1 << 3),
00079    AST_FEATURE_FLAG_BYCALLER =  (1 << 4),
00080    AST_FEATURE_FLAG_BYBOTH  =   (3 << 3),
00081 };


Function Documentation

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

Definition at line 257 of file res_features.c.

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

Referenced by park_call_full().

00258 {
00259    int res;
00260    int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
00261    char tmp[256];
00262    char *message[5] = {NULL, NULL, NULL, NULL, NULL};
00263 
00264    snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
00265    message[0] = tmp;
00266    res = ast_adsi_load_session(chan, NULL, 0, 1);
00267    if (res == -1)
00268       return res;
00269    return ast_adsi_print(chan, message, justify, 1);
00270 }

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 1446 of file res_features.c.

References ast_cdr::accountcode, ast_cdr::amaflags, ast_cdr::answer, ast_answer(), ast_cdr_alloc(), ast_cdr_answer(), AST_CDR_ANSWERED, ast_cdr_detach(), ast_cdr_dup(), ast_cdr_end(), AST_CDR_FLAG_BRIDGED, AST_CDR_FLAG_MAIN, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), AST_CDR_NULL, ast_cdr_setcid(), ast_cdr_specialized_reset(), ast_cdr_start(), ast_cdr_update(), ast_channel_bridge(), ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, AST_CONTROL_ATXFERCMD, 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_default_amaflags, 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_MAX_EXTENSION, AST_OPTION_FLAG_REQUEST, ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_channel::cdr, ast_cdr::channel, cmd_atxfer(), config, ast_option_header::data, ast_cdr::dcontext, ast_cdr::disposition, ast_cdr::dst, ast_cdr::dstchannel, ast_cdr::end, f, FEATURE_MAX_LEN, FEATURE_RETURN_PASSDIGITS, FEATURE_RETURN_SUCCESS, FEATURE_SENSE_CHAN, FEATURE_SENSE_PEER, ast_option_header::flag, ast_cdr::lastapp, ast_cdr::lastdata, LOG_DEBUG, LOG_WARNING, monitor_app, ast_cdr::next, ast_option_header::option, option_debug, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), set_config_flags(), ast_cdr::start, ast_cdr::uniqueid, and ast_cdr::userfield.

Referenced by app_exec(), ast_bridge_call_thread(), do_atxfer(), park_exec(), and try_calling().

01447 {
01448    /* Copy voice back and forth between the two channels.  Give the peer
01449       the ability to transfer calls with '#<extension' syntax. */
01450    struct ast_frame *f;
01451    struct ast_channel *who;
01452    char chan_featurecode[FEATURE_MAX_LEN + 1]="";
01453    char peer_featurecode[FEATURE_MAX_LEN + 1]="";
01454    char orig_channame[AST_MAX_EXTENSION];
01455    char orig_peername[AST_MAX_EXTENSION];
01456 
01457    int res;
01458    int diff;
01459    int hasfeatures=0;
01460    int hadfeatures=0;
01461    struct ast_option_header *aoh;
01462    struct ast_bridge_config backup_config;
01463    struct ast_cdr *bridge_cdr = NULL;
01464    struct ast_cdr *orig_peer_cdr = NULL;
01465 
01466    memset(&backup_config, 0, sizeof(backup_config));
01467 
01468    config->start_time = ast_tvnow();
01469 
01470    if (chan && peer) {
01471       pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
01472       pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
01473    } else if (chan)
01474       pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
01475 
01476    if (monitor_ok) {
01477       const char *monitor_exec;
01478       struct ast_channel *src = NULL;
01479       if (!monitor_app) { 
01480          if (!(monitor_app = pbx_findapp("Monitor")))
01481             monitor_ok=0;
01482       }
01483       if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 
01484          src = chan;
01485       else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
01486          src = peer;
01487       if (monitor_app && src) {
01488          char *tmp = ast_strdupa(monitor_exec);
01489          pbx_exec(src, monitor_app, tmp);
01490       }
01491    }
01492    
01493    set_config_flags(chan, peer, config);
01494    config->firstpass = 1;
01495 
01496    /* Answer if need be */
01497    if (ast_answer(chan))
01498       return -1;
01499 
01500    ast_copy_string(orig_channame,chan->name,sizeof(orig_channame));
01501    ast_copy_string(orig_peername,peer->name,sizeof(orig_peername));
01502    orig_peer_cdr = peer->cdr;
01503    
01504    if (!chan->cdr || (chan->cdr && !ast_test_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED))) {
01505          
01506       if (chan->cdr) {
01507          ast_set_flag(chan->cdr, AST_CDR_FLAG_MAIN);
01508          ast_cdr_update(chan);
01509          bridge_cdr = ast_cdr_dup(chan->cdr);
01510          ast_copy_string(bridge_cdr->lastapp, chan->appl, sizeof(bridge_cdr->lastapp));
01511          ast_copy_string(bridge_cdr->lastdata, chan->data, sizeof(bridge_cdr->lastdata));
01512       } else {
01513          /* better yet, in a xfer situation, find out why the chan cdr got zapped (pun unintentional) */
01514          bridge_cdr = ast_cdr_alloc(); /* this should be really, really rare/impossible? */
01515          ast_copy_string(bridge_cdr->channel, chan->name, sizeof(bridge_cdr->channel));
01516          ast_copy_string(bridge_cdr->dstchannel, peer->name, sizeof(bridge_cdr->dstchannel));
01517          ast_copy_string(bridge_cdr->uniqueid, chan->uniqueid, sizeof(bridge_cdr->uniqueid));
01518          ast_copy_string(bridge_cdr->lastapp, chan->appl, sizeof(bridge_cdr->lastapp));
01519          ast_copy_string(bridge_cdr->lastdata, chan->data, sizeof(bridge_cdr->lastdata));
01520          ast_cdr_setcid(bridge_cdr, chan);
01521          bridge_cdr->disposition = (chan->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NULL;
01522          bridge_cdr->amaflags = chan->amaflags ? chan->amaflags :  ast_default_amaflags;
01523          ast_copy_string(bridge_cdr->accountcode, chan->accountcode, sizeof(bridge_cdr->accountcode));
01524          /* Destination information */
01525          ast_copy_string(bridge_cdr->dst, chan->exten, sizeof(bridge_cdr->dst));
01526          ast_copy_string(bridge_cdr->dcontext, chan->context, sizeof(bridge_cdr->dcontext));
01527          if (peer->cdr) {
01528             bridge_cdr->start = peer->cdr->start;
01529             ast_copy_string(bridge_cdr->userfield, peer->cdr->userfield, sizeof(bridge_cdr->userfield));
01530          } else {
01531             ast_cdr_start(bridge_cdr);
01532          }
01533       }
01534       /* peer->cdr->answer will be set when a macro runs on the peer;
01535          in that case, the bridge answer will be delayed while the
01536          macro plays on the peer channel. The peer answered the call
01537          before the macro started playing. To the phone system,
01538          this is billable time for the call, even tho the caller
01539          hears nothing but ringing while the macro does its thing. */
01540       if (peer->cdr && !ast_tvzero(peer->cdr->answer)) {
01541          bridge_cdr->answer = peer->cdr->answer;
01542          chan->cdr->answer = peer->cdr->answer;
01543       } else {
01544          ast_cdr_answer(bridge_cdr);
01545          ast_cdr_answer(chan->cdr); /* for the sake of cli status checks */
01546       }
01547       ast_set_flag(chan->cdr, AST_CDR_FLAG_BRIDGED);
01548       if (peer->cdr) {
01549          ast_set_flag(peer->cdr, AST_CDR_FLAG_BRIDGED);
01550       }
01551    }
01552    
01553    for (;;) {
01554       struct ast_channel *other; /* used later */
01555       
01556       res = ast_channel_bridge(chan, peer, config, &f, &who);
01557       
01558       if (config->feature_timer) {
01559          /* Update time limit for next pass */
01560          diff = ast_tvdiff_ms(ast_tvnow(), config->start_time);
01561          config->feature_timer -= diff;
01562          if (hasfeatures) {
01563             /* Running on backup config, meaning a feature might be being
01564                activated, but that's no excuse to keep things going 
01565                indefinitely! */
01566             if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) {
01567                if (option_debug)
01568                   ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
01569                config->feature_timer = 0;
01570                who = chan;
01571                if (f)
01572                   ast_frfree(f);
01573                f = NULL;
01574                res = 0;
01575             } else if (config->feature_timer <= 0) {
01576                /* Not *really* out of time, just out of time for
01577                   digits to come in for features. */
01578                if (option_debug)
01579                   ast_log(LOG_DEBUG, "Timed out for feature!\n");
01580                if (!ast_strlen_zero(peer_featurecode)) {
01581                   ast_dtmf_stream(chan, peer, peer_featurecode, 0);
01582                   memset(peer_featurecode, 0, sizeof(peer_featurecode));
01583                }
01584                if (!ast_strlen_zero(chan_featurecode)) {
01585                   ast_dtmf_stream(peer, chan, chan_featurecode, 0);
01586                   memset(chan_featurecode, 0, sizeof(chan_featurecode));
01587                }
01588                if (f)
01589                   ast_frfree(f);
01590                hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
01591                if (!hasfeatures) {
01592                   /* Restore original (possibly time modified) bridge config */
01593                   memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
01594                   memset(&backup_config, 0, sizeof(backup_config));
01595                }
01596                hadfeatures = hasfeatures;
01597                /* Continue as we were */
01598                continue;
01599             } else if (!f) {
01600                /* The bridge returned without a frame and there is a feature in progress.
01601                 * However, we don't think the feature has quite yet timed out, so just
01602                 * go back into the bridge. */
01603                continue;
01604             }
01605          } else {
01606             if (config->feature_timer <=0) {
01607                /* We ran out of time */
01608                config->feature_timer = 0;
01609                who = chan;
01610                if (f)
01611                   ast_frfree(f);
01612                f = NULL;
01613                res = 0;
01614             }
01615          }
01616       }
01617       if (res < 0) {
01618          if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer))
01619             ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
01620          goto before_you_go;
01621       }
01622       
01623       if (!f || (f->frametype == AST_FRAME_CONTROL &&
01624             (f->subclass == AST_CONTROL_HANGUP || f->subclass == AST_CONTROL_BUSY || 
01625                f->subclass == AST_CONTROL_CONGESTION ) ) ) {
01626          res = -1;
01627          break;
01628       }
01629       /* many things should be sent to the 'other' channel */
01630       other = (who == chan) ? peer : chan;
01631       if (f->frametype == AST_FRAME_CONTROL) {
01632          switch (f->subclass) {
01633          case AST_CONTROL_RINGING:
01634          case AST_CONTROL_FLASH:
01635          case -1:
01636             ast_indicate(other, f->subclass);
01637             break;
01638          case AST_CONTROL_HOLD:
01639          case AST_CONTROL_UNHOLD:
01640             ast_indicate_data(other, f->subclass, f->data, f->datalen);
01641             break;
01642          case AST_CONTROL_OPTION:
01643             aoh = f->data;
01644             /* Forward option Requests */
01645             if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
01646                ast_channel_setoption(other, ntohs(aoh->option), aoh->data, 
01647                   f->datalen - sizeof(struct ast_option_header), 0);
01648             }
01649             break;
01650          case AST_CONTROL_ATXFERCMD:
01651             cmd_atxfer(chan, peer, config, who, f->data);
01652             break;
01653          }
01654       } else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
01655          /* eat it */
01656       } else if (f->frametype == AST_FRAME_DTMF) {
01657          char *featurecode;
01658          int sense;
01659 
01660          hadfeatures = hasfeatures;
01661          /* This cannot overrun because the longest feature is one shorter than our buffer */
01662          if (who == chan) {
01663             sense = FEATURE_SENSE_CHAN;
01664             featurecode = chan_featurecode;
01665          } else  {
01666             sense = FEATURE_SENSE_PEER;
01667             featurecode = peer_featurecode;
01668          }
01669          /*! append the event to featurecode. we rely on the string being zero-filled, and
01670           * not overflowing it. 
01671           * \todo XXX how do we guarantee the latter ?
01672           */
01673          featurecode[strlen(featurecode)] = f->subclass;
01674          /* Get rid of the frame before we start doing "stuff" with the channels */
01675          ast_frfree(f);
01676          f = NULL;
01677          config->feature_timer = backup_config.feature_timer;
01678          res = ast_feature_interpret(chan, peer, config, featurecode, sense);
01679          switch(res) {
01680          case FEATURE_RETURN_PASSDIGITS:
01681             ast_dtmf_stream(other, who, featurecode, 0);
01682             /* Fall through */
01683          case FEATURE_RETURN_SUCCESS:
01684             memset(featurecode, 0, sizeof(chan_featurecode));
01685             break;
01686          }
01687          if (res >= FEATURE_RETURN_PASSDIGITS) {
01688             res = 0;
01689          } else 
01690             break;
01691          hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
01692          if (hadfeatures && !hasfeatures) {
01693             /* Restore backup */
01694             memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
01695             memset(&backup_config, 0, sizeof(struct ast_bridge_config));
01696          } else if (hasfeatures) {
01697             if (!hadfeatures) {
01698                /* Backup configuration */
01699                memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
01700                /* Setup temporary config options */
01701                config->play_warning = 0;
01702                ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
01703                ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
01704                config->warning_freq = 0;
01705                config->warning_sound = NULL;
01706                config->end_sound = NULL;
01707                config->start_sound = NULL;
01708                config->firstpass = 0;
01709             }
01710             config->start_time = ast_tvnow();
01711             config->feature_timer = featuredigittimeout;
01712             if (option_debug)
01713                ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->feature_timer);
01714          }
01715       }
01716       if (f)
01717          ast_frfree(f);
01718 
01719    }
01720    before_you_go:
01721    /* obey the NoCDR() wishes. */
01722    if (!chan->cdr || (chan->cdr && !ast_test_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED))) {
01723       
01724       ast_cdr_end(bridge_cdr);
01725       
01726       ast_cdr_detach(bridge_cdr);
01727       
01728       /* just in case, these channels get bridged again before hangup */
01729       if (chan->cdr) {
01730          ast_cdr_specialized_reset(chan->cdr,0);
01731       }
01732       if (peer->cdr) {
01733          struct ast_cdr *cur;
01734 
01735          ast_channel_lock(peer);
01736          for (cur = peer->cdr; cur; cur = cur->next) {
01737             if (cur == orig_peer_cdr) {
01738                break;
01739             }
01740          }
01741 
01742          if (!cur) {
01743             /* orig_peer_cdr is gone, probably because of a masquerade
01744              * during the bridge. */
01745             ast_channel_unlock(peer);
01746             return res;
01747          }
01748 
01749          /* before resetting the peer cdr, throw a copy of it to the
01750             backend, just in case the cdr.conf file is calling for
01751             unanswered CDR's. */
01752          
01753          /* When peer->cdr isn't the same addr as orig_peer_cdr,
01754             this can only happen if there was a transfer, methinks;
01755             at any rate, only pay attention to the original*/
01756          if (ast_cdr_isset_unanswered()) {
01757             struct ast_cdr *dupd = ast_cdr_dup(orig_peer_cdr);
01758             if (dupd) {
01759                if (ast_tvzero(dupd->end) && ast_cdr_isset_unanswered())
01760                   ast_cdr_end(dupd);
01761                ast_cdr_detach(dupd);
01762             }
01763          }
01764          ast_cdr_specialized_reset(orig_peer_cdr,0);
01765          ast_channel_unlock(peer);
01766       }
01767    }
01768    return res;
01769 }

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

Definition at line 226 of file res_features.c.

References ast_channel::appl, ast_bridge_call(), ast_hangup(), ast_bridge_thread_obj::bconfig, ast_bridge_thread_obj::chan, ast_channel::data, free, and ast_bridge_thread_obj::peer.

Referenced by ast_bridge_call_thread_launch().

00227 {
00228    struct ast_bridge_thread_obj *tobj = data;
00229 
00230    tobj->chan->appl = "Transferred Call";
00231    tobj->chan->data = tobj->peer->name;
00232    tobj->peer->appl = "Transferred Call";
00233    tobj->peer->data = tobj->chan->name;
00234 
00235    ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
00236    ast_hangup(tobj->chan);
00237    ast_hangup(tobj->peer);
00238    bzero(tobj, sizeof(*tobj)); /*! \todo XXX for safety */
00239    free(tobj);
00240    return NULL;
00241 }

static void ast_bridge_call_thread_launch ( void *  data  )  [static]

Definition at line 243 of file res_features.c.

References ast_bridge_call_thread(), ast_pthread_create, and thread.

Referenced by do_atxfer().

00244 {
00245    pthread_t thread;
00246    pthread_attr_t attr;
00247    struct sched_param sched;
00248 
00249    pthread_attr_init(&attr);
00250    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00251    ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
00252    pthread_attr_destroy(&attr);
00253    memset(&sched, 0, sizeof(sched));
00254    pthread_setschedparam(thread, SCHED_RR, &sched);
00255 }

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 1169 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(), builtin_features, config, ast_call_feature::exten, exten, ast_call_feature::feature_mask, FEATURE_RETURN_KEEPTRYING, FEATURE_RETURN_PASSDIGITS, FEATURE_RETURN_STOREDIGITS, FEATURE_SENSE_CHAN, 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().

01170 {
01171    int x;
01172    struct ast_flags features;
01173    struct ast_call_feature *feature;
01174    const char *dynamic_features;
01175    char *tmp, *tok;
01176    int res = FEATURE_RETURN_PASSDIGITS;
01177    int feature_detected = 0;
01178 
01179    if (sense == FEATURE_SENSE_CHAN) {
01180       ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);   
01181       dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
01182    } else {
01183       ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);   
01184       dynamic_features = pbx_builtin_getvar_helper(peer, "DYNAMIC_FEATURES");
01185    }
01186    if (option_debug > 2)
01187       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);
01188 
01189    ast_rwlock_rdlock(&features_lock);
01190    for (x = 0; x < FEATURES_COUNT; x++) {
01191       if ((ast_test_flag(&features, builtin_features[x].feature_mask)) &&
01192           !ast_strlen_zero(builtin_features[x].exten)) {
01193          /* Feature is up for consideration */
01194          if (!strcmp(builtin_features[x].exten, code)) {
01195             res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
01196             feature_detected = 1;
01197             break;
01198          } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
01199             if (res == FEATURE_RETURN_PASSDIGITS)
01200                res = FEATURE_RETURN_STOREDIGITS;
01201          }
01202       }
01203    }
01204    ast_rwlock_unlock(&features_lock);
01205 
01206    if (ast_strlen_zero(dynamic_features) || feature_detected)
01207       return res;
01208 
01209    tmp = ast_strdupa(dynamic_features);
01210 
01211    while ((tok = strsep(&tmp, "#"))) {
01212       AST_LIST_LOCK(&feature_list); 
01213       if (!(feature = find_dynamic_feature(tok))) {
01214          AST_LIST_UNLOCK(&feature_list);
01215          continue;
01216       }
01217          
01218       /* Feature is up for consideration */
01219       if (!strcmp(feature->exten, code)) {
01220          if (option_verbose > 2)
01221             ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok);
01222          res = feature->operation(chan, peer, config, code, sense, feature);
01223          if (res != FEATURE_RETURN_KEEPTRYING) {
01224             AST_LIST_UNLOCK(&feature_list);
01225             break;
01226          }
01227          res = FEATURE_RETURN_PASSDIGITS;
01228       } else if (!strncmp(feature->exten, code, strlen(code)))
01229          res = FEATURE_RETURN_STOREDIGITS;
01230 
01231       AST_LIST_UNLOCK(&feature_list);
01232    }
01233    
01234    return res;
01235 }

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]

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

Definition at line 1280 of file res_features.c.

References ast_channel::_softhangup, ast_channel::_state, ast_call(), AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, 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(), builtin_features, ast_call_feature::exten, f, FEATURES_COUNT, len, LOG_NOTICE, option_verbose, pbx_builtin_setvar_helper(), and VERBOSE_PREFIX_3.

Referenced by do_atxfer().

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

static AST_LIST_HEAD_STATIC ( feature_list  ,
ast_call_feature   
) [static]

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 481 of file res_features.c.

References ast_channel::amaflags, ast_channel_alloc(), ast_channel_masquerade(), ast_frfree, ast_hangup(), 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(), and ss_thread().

00482 {
00483    struct ast_channel *chan;
00484    struct ast_frame *f;
00485    char *orig_chan_name = NULL;
00486    int park_status;
00487 
00488    /* Make a new, fake channel that we'll use to masquerade in the real one */
00489    if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) {
00490       ast_log(LOG_WARNING, "Unable to create parked channel\n");
00491       return -1;
00492    }
00493 
00494    /* Make formats okay */
00495    chan->readformat = rchan->readformat;
00496    chan->writeformat = rchan->writeformat;
00497    ast_channel_masquerade(chan, rchan);
00498 
00499    /* Setup the extensions and such */
00500    set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
00501 
00502    /* Make the masq execute */
00503    f = ast_read(chan);
00504    if (f)
00505       ast_frfree(f);
00506 
00507    orig_chan_name = ast_strdupa(chan->name);
00508 
00509    park_status = park_call_full(chan, peer, timeout, extout, orig_chan_name);
00510    if (park_status == 1) {
00511       /* would be nice to play: "invalid parking extension" */
00512       ast_hangup(chan);
00513       return -1;
00514    }
00515 
00516    return 0;
00517 }

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 ( parking_lock   ) 

protects all static variables above

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

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 476 of file res_features.c.

References park_call_full().

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

00477 {
00478    return park_call_full(chan, peer, timeout, extout, NULL);
00479 }

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 160 of file res_features.c.

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

00161 {
00162    return parking_ext;
00163 }

int ast_pickup_call ( struct ast_channel chan  ) 

Pickup a call.

Definition at line 2333 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().

02334 {
02335    struct ast_channel *cur = NULL;
02336    int res = -1;
02337 
02338    while ( (cur = ast_channel_walk_locked(cur)) != NULL) {
02339       if (!cur->pbx && 
02340          (cur != chan) &&
02341          (chan->pickupgroup & cur->callgroup) &&
02342          ((cur->_state == AST_STATE_RINGING) ||
02343           (cur->_state == AST_STATE_RING))) {
02344             break;
02345       }
02346       ast_channel_unlock(cur);
02347    }
02348    if (cur) {
02349       if (option_debug)
02350          ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
02351       res = ast_answer(chan);
02352       if (res)
02353          ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
02354       res = ast_queue_control(chan, AST_CONTROL_ANSWER);
02355       if (res)
02356          ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
02357       res = ast_channel_masquerade(cur, chan);
02358       if (res)
02359          ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);     /* Done */
02360       ast_channel_unlock(cur);
02361    } else   {
02362       if (option_debug)
02363          ast_log(LOG_DEBUG, "No call pickup possible...\n");
02364    }
02365    return res;
02366 }

char* ast_pickup_ext ( void   ) 

Determine system call pickup extension.

Definition at line 165 of file res_features.c.

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

00166 {
00167    return pickup_ext;
00168 }

void ast_register_feature ( struct ast_call_feature feature  ) 

register new feature into feature_set

Parameters:
feature an ast_call_feature object which contains a keysequence and a callback function which is called when this keysequence is pressed during a call.

Definition at line 1023 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.

01024 {
01025    if (!feature) {
01026       ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
01027          return;
01028    }
01029   
01030    AST_LIST_LOCK(&feature_list);
01031    AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry);
01032    AST_LIST_UNLOCK(&feature_list);
01033 
01034    if (option_verbose >= 2) 
01035       ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname);
01036 }

AST_RWLOCK_DEFINE_STATIC ( features_lock   ) 

void ast_unregister_feature ( struct ast_call_feature feature  ) 

unregister feature from feature_set

Parameters:
feature the ast_call_feature object which was registered before

Definition at line 1039 of file res_features.c.

References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, and free.

01040 {
01041    if (!feature)
01042       return;
01043 
01044    AST_LIST_LOCK(&feature_list);
01045    AST_LIST_REMOVE(&feature_list,feature,feature_entry);
01046    AST_LIST_UNLOCK(&feature_list);
01047    free(feature);
01048 }

static void ast_unregister_features ( void   )  [static]

Remove all features in the list.

Definition at line 1051 of file res_features.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

01052 {
01053    struct ast_call_feature *feature;
01054 
01055    AST_LIST_LOCK(&feature_list);
01056    while ((feature = AST_LIST_REMOVE_HEAD(&feature_list,feature_entry)))
01057       free(feature);
01058    AST_LIST_UNLOCK(&feature_list);
01059 }

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

Attended transfer ().

Parameters:
chan 
peer 
config 
code 
sense 
data Get extension to transfer to, if you cannot generate channel (or find extension) return to host channel. After called channel answered wait for hangup of transferer, bridge call between transfer peer (taking them off hold) to attended transfer channel.
Returns:
-1 means what failure/success both?

Definition at line 1000 of file res_features.c.

References config, and do_atxfer().

01001 {
01002    return do_atxfer(chan, peer, config, sense, NULL, NULL);
01003 }

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 581 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, monitor_app, option_verbose, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), S_OR, set_peers(), and VERBOSE_PREFIX_3.

00582 {
00583    char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
00584    int x = 0;
00585    size_t len;
00586    struct ast_channel *caller_chan, *callee_chan;
00587 
00588    if (!monitor_ok) {
00589       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
00590       return -1;
00591    }
00592 
00593    if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
00594       monitor_ok = 0;
00595       ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
00596       return -1;
00597    }
00598 
00599    set_peers(&caller_chan, &callee_chan, peer, chan, sense);
00600 
00601    if (!ast_strlen_zero(courtesytone)) {
00602       if (ast_autoservice_start(callee_chan))
00603          return -1;
00604       if (ast_stream_and_wait(caller_chan, courtesytone, caller_chan->language, "")) {
00605          ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
00606          ast_autoservice_stop(callee_chan);
00607          return -1;
00608       }
00609       if (ast_autoservice_stop(callee_chan))
00610          return -1;
00611    }
00612    
00613    if (callee_chan->monitor) {
00614       if (option_verbose > 3)
00615          ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code);
00616       ast_monitor_stop(callee_chan, 1);
00617       return FEATURE_RETURN_SUCCESS;
00618    }
00619 
00620    if (caller_chan && callee_chan) {
00621       const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
00622       const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
00623 
00624       if (!touch_format)
00625          touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
00626 
00627       if (!touch_monitor)
00628          touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
00629    
00630       if (touch_monitor) {
00631          len = strlen(touch_monitor) + 50;
00632          args = alloca(len);
00633          touch_filename = alloca(len);
00634          snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
00635          snprintf(args, len, "%s|%s|m", (touch_format) ? touch_format : "wav", touch_filename);
00636       } else {
00637          caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name));
00638          callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name));
00639          len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
00640          args = alloca(len);
00641          touch_filename = alloca(len);
00642          snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
00643          snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename);
00644       }
00645 
00646       for( x = 0; x < strlen(args); x++) {
00647          if (args[x] == '/')
00648             args[x] = '-';
00649       }
00650       
00651       if (option_verbose > 3)
00652          ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args);
00653 
00654       pbx_exec(callee_chan, monitor_app, args);
00655       pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
00656       pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
00657    
00658       return FEATURE_RETURN_SUCCESS;
00659    }
00660    
00661    ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");  
00662    return -1;
00663 }

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

Definition at line 692 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(), 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.

00693 {
00694    struct ast_channel *transferer;
00695    struct ast_channel *transferee;
00696    const char *transferer_real_context;
00697    char xferto[256];
00698    int res;
00699 
00700    set_peers(&transferer, &transferee, peer, chan, sense);
00701    transferer_real_context = real_ctx(transferer, transferee);
00702    /* Start autoservice on chan while we talk to the originator */
00703    ast_autoservice_start(transferee);
00704    ast_indicate(transferee, AST_CONTROL_HOLD);
00705 
00706    memset(xferto, 0, sizeof(xferto));
00707    
00708    /* Transfer */
00709    res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
00710    if (res < 0) {
00711       finishup(transferee);
00712       return -1; /* error ? */
00713    }
00714    if (res > 0)   /* If they've typed a digit already, handle it */
00715       xferto[0] = (char) res;
00716 
00717    ast_stopstream(transferer);
00718    res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
00719    if (res < 0) {  /* hangup, would be 0 for invalid and 1 for valid */
00720       finishup(transferee);
00721       return res;
00722    }
00723    if (!strcmp(xferto, ast_parking_ext())) {
00724       res = finishup(transferee);
00725       if (res)
00726          res = -1;
00727       else if (!ast_park_call(transferee, transferer, 0, NULL)) { /* success */
00728          /* We return non-zero, but tell the PBX not to hang the channel when
00729             the thread dies -- We have to be careful now though.  We are responsible for 
00730             hanging up the channel, else it will never be hung up! */
00731 
00732          return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER;
00733       } else {
00734          ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
00735       }
00736       /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
00737    } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
00738       pbx_builtin_setvar_helper(transferer, "BLINDTRANSFER", transferee->name);
00739       pbx_builtin_setvar_helper(transferee, "BLINDTRANSFER", transferer->name);
00740       res=finishup(transferee);
00741       if (!transferer->cdr) {
00742          transferer->cdr=ast_cdr_alloc();
00743          if (transferer) {
00744             ast_cdr_init(transferer->cdr, transferer); /* initilize our channel's cdr */
00745             ast_cdr_start(transferer->cdr);
00746          }
00747       }
00748       if (transferer->cdr) {
00749          ast_cdr_setdestchan(transferer->cdr, transferee->name);
00750          ast_cdr_setapp(transferer->cdr, "BLINDTRANSFER","");
00751       }
00752       if (!transferee->pbx) {
00753          /* Doh!  Use our handy async_goto functions */
00754          if (option_verbose > 2) 
00755             ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
00756                         ,transferee->name, xferto, transferer_real_context);
00757          if (ast_async_goto(transferee, transferer_real_context, xferto, 1))
00758             ast_log(LOG_WARNING, "Async goto failed :-(\n");
00759          res = -1;
00760       } else {
00761          /* Set the channel's new extension, since it exists, using transferer context */
00762          set_c_e_p(transferee, transferer_real_context, xferto, 0);
00763       }
00764       check_goto_on_transfer(transferer);
00765       return res;
00766    } else {
00767       if (option_verbose > 2) 
00768          ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context);
00769    }
00770    if (ast_stream_and_wait(transferer, xferfailsound, transferer->language, AST_DIGIT_ANY) < 0 ) {
00771       finishup(transferee);
00772       return -1;
00773    }
00774    ast_stopstream(transferer);
00775    res = finishup(transferee);
00776    if (res) {
00777       if (option_verbose > 1)
00778          ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
00779       return res;
00780    }
00781    return FEATURE_RETURN_SUCCESS;
00782 }

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 665 of file res_features.c.

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

00666 {
00667    if (option_verbose > 3)
00668       ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
00669    return FEATURE_RETURN_HANGUP;
00670 }

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 548 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_module_user::chan, ast_channel::exten, FEATURE_SENSE_CHAN, ast_channel::priority, and set_peers().

00549 {
00550    struct ast_channel *parker;
00551         struct ast_channel *parkee;
00552    int res = 0;
00553    struct ast_module_user *u;
00554 
00555    u = ast_module_user_add(chan);
00556 
00557    set_peers(&parker, &parkee, peer, chan, sense);
00558    /* Setup the exten/priority to be s/1 since we don't know
00559       where this call should return */
00560    strcpy(chan->exten, "s");
00561    chan->priority = 1;
00562    if (chan->_state != AST_STATE_UP)
00563       res = ast_answer(chan);
00564    if (!res)
00565       res = ast_safe_sleep(chan, 1000);
00566    if (!res)
00567       res = ast_park_call(parkee, parker, 0, NULL);
00568 
00569    ast_module_user_remove(u);
00570 
00571    if (!res) {
00572       if (sense == FEATURE_SENSE_CHAN)
00573          res = AST_PBX_NO_HANGUP_PEER;
00574       else
00575          res = AST_PBX_KEEPALIVE;
00576    }
00577    return res;
00578 
00579 }

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

Definition at line 784 of file res_features.c.

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

Referenced by do_atxfer().

00785 {
00786    if (ast_channel_make_compatible(c, newchan) < 0) {
00787       ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
00788          c->name, newchan->name);
00789       ast_hangup(newchan);
00790       return -1;
00791    }
00792    return 0;
00793 }

static void check_goto_on_transfer ( struct ast_channel chan  )  [static]

Definition at line 187 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().

00188 {
00189    struct ast_channel *xferchan;
00190    const char *val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
00191    char *x, *goto_on_transfer;
00192    struct ast_frame *f;
00193 
00194    if (ast_strlen_zero(val))
00195       return;
00196 
00197    goto_on_transfer = ast_strdupa(val);
00198 
00199    if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, chan->name)))
00200       return;
00201 
00202    for (x = goto_on_transfer; x && *x; x++) {
00203       if (*x == '^')
00204          *x = '|';
00205    }
00206    /* Make formats okay */
00207    xferchan->readformat = chan->readformat;
00208    xferchan->writeformat = chan->writeformat;
00209    ast_channel_masquerade(xferchan, chan);
00210    ast_parseable_goto(xferchan, goto_on_transfer);
00211    xferchan->_state = AST_STATE_UP;
00212    ast_clear_flag(xferchan, AST_FLAGS_ALL);  
00213    xferchan->_softhangup = 0;
00214    if ((f = ast_read(xferchan))) {
00215       ast_frfree(f);
00216       f = NULL;
00217       ast_pbx_start(xferchan);
00218    } else {
00219       ast_hangup(xferchan);
00220    }
00221 }

static void cmd_atxfer ( struct ast_channel a,
struct ast_channel b,
struct ast_bridge_config conf,
struct ast_channel who,
char *  xferto 
) [static]

Definition at line 1437 of file res_features.c.

References context, do_atxfer(), FEATURE_SENSE_CHAN, and FEATURE_SENSE_PEER.

Referenced by ast_bridge_call().

01438 {
01439    int sense = (a == who) ? FEATURE_SENSE_CHAN : FEATURE_SENSE_PEER;
01440    char *context = strchr(xferto, '@');;
01441    if (context)
01442       *context++ = '\0';
01443    do_atxfer(a, b, conf, sense, xferto, context);
01444 }

static int do_atxfer ( struct ast_channel chan,
struct ast_channel peer,
struct ast_bridge_config config,
int  sense,
const char *  toExt,
const char *  toCont 
) [static]

Attended transfer implementation.

Parameters:
chan transfered user
peer person transfering call
config 
sense feature options
toExt 
toCont This is the actual implementation of attended transfer, it can be activated as a regular feature or through the AMI. "toExt" is the extension to transfer to (default: ask for it on the transferer channel) "toCont" is the context to transfer to (default: the one in which the transferer is)
Returns:
-1 on failure

Definition at line 810 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_datastore_find(), ast_channel_lock, ast_channel_masquerade(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, AST_CONTROL_BUSY, AST_CONTROL_HOLD, AST_CONTROL_UNHOLD, ast_copy_flags, 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_strlen_zero(), 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, config, ast_channel::context, ast_datastore::data, dial_features_info, ast_channel::exten, f, FEATURE_RETURN_SUCCESS, ast_bridge_config::features_callee, ast_dial_features::features_callee, ast_bridge_config::features_caller, finishup(), ast_dial_features::is_caller, LOG_DEBUG, LOG_WARNING, ast_channel::nativeformats, option_debug, ast_bridge_thread_obj::peer, ast_channel::priority, ast_channel::readformat, real_ctx(), S_OR, set_peers(), ast_channel::visible_indication, and ast_channel::writeformat.

Referenced by builtin_atxfer(), and cmd_atxfer().

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

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 1786 of file res_features.c.

References ast_add_extension2(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, 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_datastore::data, dial_features_info, ast_channel::exten, parkeduser::exten, f, ast_channel::fds, free, LOG_DEBUG, LOG_ERROR, LOG_WARNING, parkeduser::moh_trys, parkeduser::next, notify_metermaids(), parkeduser::notquiteyet, option_debug, option_verbose, ast_dial_features::options, parkeduser::parkingexten, parkinglot, parkeduser::parkingnum, parkeduser::parkingtime, parkeduser::peername, post_manager_event(), ast_channel::priority, parkeduser::priority, S_OR, set_c_e_p(), parkeduser::start, strdup, and VERBOSE_PREFIX_2.

Referenced by load_module().

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

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 1075 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_ONPEER, 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, FEATURE_SENSE_PEER, LOG_NOTICE, LOG_WARNING, ast_call_feature::moh_class, pbx_exec(), and pbx_findapp().

01076 {
01077    struct ast_app *app;
01078    struct ast_call_feature *feature = data;
01079    struct ast_channel *work, *idle;
01080    int res;
01081 
01082    if (!feature) { /* shouldn't ever happen! */
01083       ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
01084       return -1; 
01085    }
01086 
01087    if (sense == FEATURE_SENSE_CHAN) {
01088       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
01089          return FEATURE_RETURN_KEEPTRYING;
01090       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
01091          work = chan;
01092          idle = peer;
01093       } else {
01094          work = peer;
01095          idle = chan;
01096       }
01097    } else {
01098       if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
01099          return FEATURE_RETURN_KEEPTRYING;
01100       if (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF)) {
01101          work = peer;
01102          idle = chan;
01103       } else {
01104          work = chan;
01105          idle = peer;
01106       }
01107    }
01108 
01109    if (!(app = pbx_findapp(feature->app))) {
01110       ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
01111       return -2;
01112    }
01113 
01114    ast_autoservice_start(idle);
01115    
01116    if (!ast_strlen_zero(feature->moh_class))
01117       ast_moh_start(idle, feature->moh_class, NULL);
01118 
01119    res = pbx_exec(work, app, feature->app_args);
01120 
01121    if (!ast_strlen_zero(feature->moh_class))
01122       ast_moh_stop(idle);
01123 
01124    ast_autoservice_stop(idle);
01125 
01126    if (res == AST_PBX_KEEPALIVE) {
01127       /* do not hangup peer if feature is to be activated on it */
01128       if ((ast_test_flag(feature, AST_FEATURE_FLAG_ONPEER) && sense == FEATURE_SENSE_CHAN) || (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF) && sense == FEATURE_SENSE_PEER))
01129          return FEATURE_RETURN_NO_HANGUP_PEER;
01130       else
01131          return FEATURE_RETURN_PBX_KEEPALIVE;
01132    }
01133    else if (res == AST_PBX_NO_HANGUP_PEER)
01134       return FEATURE_RETURN_NO_HANGUP_PEER;
01135    else if (res)
01136       return FEATURE_RETURN_SUCCESSBREAK;
01137    
01138    return FEATURE_RETURN_SUCCESS;   /*! \todo XXX should probably return res */
01139 }

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

find a feature by name

Definition at line 1062 of file res_features.c.

References AST_LIST_TRAVERSE, and ast_call_feature::sname.

Referenced by ast_feature_interpret(), and set_config_flags().

01063 {
01064    struct ast_call_feature *tmp;
01065 
01066    AST_LIST_TRAVERSE(&feature_list, tmp, feature_entry) {
01067       if (!strcasecmp(tmp->sname, name))
01068          break;
01069    }
01070 
01071    return tmp;
01072 }

static int finishup ( struct ast_channel chan  )  [static]

Definition at line 672 of file res_features.c.

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

Referenced by builtin_blindtransfer(), and do_atxfer().

00673 {
00674         ast_indicate(chan, AST_CONTROL_UNHOLD);
00675   
00676         return ast_autoservice_stop(chan);
00677 }

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

Definition at line 2187 of file res_features.c.

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

02188 {
02189    struct parkeduser *cur;
02190    int numparked = 0;
02191 
02192    ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
02193       , "Context", "Extension", "Pri", "Timeout");
02194 
02195    ast_mutex_lock(&parking_lock);
02196 
02197    for (cur = parkinglot; cur; cur = cur->next) {
02198       ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
02199          ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
02200          ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
02201 
02202       numparked++;
02203    }
02204    ast_mutex_unlock(&parking_lock);
02205    ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : "");
02206 
02207 
02208    return RESULT_SUCCESS;
02209 }

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

Definition at line 2146 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(), builtin_features, ast_call_feature::default_exten, ast_call_feature::exten, exten, FEATURES_COUNT, ast_call_feature::fname, format, parking_con, parking_ext, parking_start, parking_stop, RESULT_SUCCESS, and ast_call_feature::sname.

02147 {
02148    int i;
02149    struct ast_call_feature *feature;
02150    char format[] = "%-25s %-7s %-7s\n";
02151 
02152    ast_cli(fd, format, "Builtin Feature", "Default", "Current");
02153    ast_cli(fd, format, "---------------", "-------", "-------");
02154 
02155    ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext());      /* default hardcoded above, so we'll hardcode it here */
02156 
02157    ast_rwlock_rdlock(&features_lock);
02158    for (i = 0; i < FEATURES_COUNT; i++)
02159       ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
02160    ast_rwlock_unlock(&features_lock);
02161 
02162    ast_cli(fd, "\n");
02163    ast_cli(fd, format, "Dynamic Feature", "Default", "Current");
02164    ast_cli(fd, format, "---------------", "-------", "-------");
02165    if (AST_LIST_EMPTY(&feature_list))
02166       ast_cli(fd, "(none)\n");
02167    else {
02168       AST_LIST_LOCK(&feature_list);
02169       AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
02170          ast_cli(fd, format, feature->sname, "no def", feature->exten); 
02171       AST_LIST_UNLOCK(&feature_list);
02172    }
02173    ast_cli(fd, "\nCall parking\n");
02174    ast_cli(fd, "------------\n");
02175    ast_cli(fd,"%-20s:   %s\n", "Parking extension", parking_ext);
02176    ast_cli(fd,"%-20s:   %s\n", "Parking context", parking_con);
02177    ast_cli(fd,"%-20s:   %d-%d\n", "Parked call extensions", parking_start, parking_stop);
02178    ast_cli(fd,"\n");
02179    
02180    return RESULT_SUCCESS;
02181 }

static int load_config ( void   )  [static]

Definition at line 2383 of file res_features.c.

References adsipark, ast_config_load(), ast_log(), AST_MAX_EXTENSION, AST_MODULE_LOAD_DECLINE, ast_strlen_zero(), ast_variable_browse(), atxfernoanswertimeout, courtesytone, DEFAULT_FEATURE_DIGIT_TIMEOUT, DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER, DEFAULT_TRANSFER_DIGIT_TIMEOUT, featuredigittimeout, LOG_WARNING, parkaddhints, parkfindnext, parking_con, parking_con_dial, parking_ext, parking_start, parking_stop, parkmohclass, pickup_ext, transferdigittimeout, var, xferfailsound, and xfersound.

02384 {
02385    int start = 0, end = 0;
02386    int res;
02387    struct ast_context *con = NULL;
02388    struct ast_config *cfg = NULL;
02389    struct ast_variable *var = NULL;
02390    char old_parking_ext[AST_MAX_EXTENSION];
02391    char old_parking_con[AST_MAX_EXTENSION] = "";
02392 
02393    if (!ast_strlen_zero(parking_con)) {
02394       strcpy(old_parking_ext, parking_ext);
02395       strcpy(old_parking_con, parking_con);
02396    } 
02397 
02398    /* Reset to defaults */
02399    strcpy(parking_con, "parkedcalls");
02400    strcpy(parking_con_dial, "park-dial");
02401    strcpy(parking_ext, "700");
02402    strcpy(pickup_ext, "*8");
02403    strcpy(parkmohclass, "default");
02404    courtesytone[0] = '\0';
02405    strcpy(xfersound, "beep");
02406    strcpy(xferfailsound, "pbx-invalid");
02407    parking_start = 701;
02408    parking_stop = 750;
02409    parkfindnext = 0;
02410    adsipark = 0;
02411    parkaddhints = 0;
02412 
02413    transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
02414    featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
02415    atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
02416 
02417    cfg = ast_config_load("features.conf");
02418    if (!cfg) {
02419       ast_log(LOG_WARNING,"Could not load features.conf\n");
02420       return AST_MODULE_LOAD_DECLINE;
02421    }
02422    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02423       if (!strcasecmp(var->name, "parkext")) {
02424          ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
02425       } else if (!strcasecmp(var->name, "context")) {
02426          ast_copy_string(parking_con, var->value, sizeof(parking_con));
02427       } else if (!strcasecmp(var->name, "parkingtime")) {
02428          if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
02429             ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
02430             parkingtime = DEFAULT_PARK_TIME;
02431          } else
02432             parkingtime = parkingtime * 1000;
02433       } else if (!strcasecmp(var->name, "parkpos")) {
02434          if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
02435             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);
02436          } else {
02437             parking_start = start;
02438             parking_stop = end;
02439          }
02440       } else if (!strcasecmp(var->name, "findslot")) {
02441          parkfindnext = (!strcasecmp(var->value, "next"));
02442       } else if (!strcasecmp(var->name, "parkinghints")) {
02443          parkaddhints = ast_true(var->value);
02444       } else if (!strcasecmp(var->name, "adsipark")) {
02445          adsipark = ast_true(var->value);
02446       } else if (!strcasecmp(var->name, "transferdigittimeout")) {
02447          if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
02448             ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
02449             transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
02450          } else
02451             transferdigittimeout = transferdigittimeout * 1000;
02452       } else if (!strcasecmp(var->name, "featuredigittimeout")) {
02453          if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
02454             ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
02455             featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
02456          }
02457       } else if (!strcasecmp(var->name, "atxfernoanswertimeout")) {
02458          if ((sscanf(var->value, "%d", &atxfernoanswertimeout) != 1) || (atxfernoanswertimeout < 1)) {
02459             ast_log(LOG_WARNING, "%s is not a valid atxfernoanswertimeout\n", var->value);
02460             atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER;
02461          } else
02462             atxfernoanswertimeout = atxfernoanswertimeout * 1000;
02463       } else if (!strcasecmp(var->name, "courtesytone")) {
02464          ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
02465       }  else if (!strcasecmp(var->name, "parkedplay")) {
02466          if (!strcasecmp(var->value, "both"))
02467             parkedplay = 2;
02468          else if (!strcasecmp(var->value, "parked"))
02469             parkedplay = 1;
02470          else
02471             parkedplay = 0;
02472       } else if (!strcasecmp(var->name, "xfersound")) {
02473          ast_copy_string(xfersound, var->value, sizeof(xfersound));
02474       } else if (!strcasecmp(var->name, "xferfailsound")) {
02475          ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
02476       } else if (!strcasecmp(var->name, "pickupexten")) {
02477          ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
02478       } else if (!strcasecmp(var->name, "parkedmusicclass")) {
02479          ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
02480       }
02481    }
02482 
02483    unmap_features();
02484    for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
02485       if (remap_feature(var->name, var->value))
02486          ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
02487    }
02488 
02489    /* Map a key combination to an application*/
02490    ast_unregister_features();
02491    for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
02492       char *tmp_val = ast_strdupa(var->value);
02493       char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 
02494       struct ast_call_feature *feature;
02495 
02496       /* strsep() sets the argument to NULL if match not found, and it
02497        * is safe to use it with a NULL argument, so we don't check
02498        * between calls.
02499        */
02500       exten = strsep(&tmp_val,",");
02501       activatedby = strsep(&tmp_val,",");
02502       app = strsep(&tmp_val,",");
02503       app_args = strsep(&tmp_val,",");
02504       moh_class = strsep(&tmp_val,",");
02505 
02506       activateon = strsep(&activatedby, "/");   
02507 
02508       /*! \todo XXX var_name or app_args ? */
02509       if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
02510          ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
02511             app, exten, activateon, var->name);
02512          continue;
02513       }
02514 
02515       AST_LIST_LOCK(&feature_list);
02516       if ((feature = find_dynamic_feature(var->name))) {
02517          AST_LIST_UNLOCK(&feature_list);
02518          ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
02519          continue;
02520       }
02521       AST_LIST_UNLOCK(&feature_list);
02522             
02523       if (!(feature = ast_calloc(1, sizeof(*feature))))
02524          continue;               
02525 
02526       ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
02527       ast_copy_string(feature->app, app, FEATURE_APP_LEN);
02528       ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
02529       
02530       if (app_args) 
02531          ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
02532 
02533       if (moh_class)
02534          ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
02535          
02536       ast_copy_string(feature->exten, exten, sizeof(feature->exten));
02537       feature->operation = feature_exec_app;
02538       ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
02539 
02540       /* Allow caller and calle to be specified for backwards compatability */
02541       if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
02542          ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
02543       else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
02544          ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
02545       else {
02546          ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
02547             " must be 'self', or 'peer'\n", var->name);
02548          continue;
02549       }
02550 
02551       if (ast_strlen_zero(activatedby))
02552          ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
02553       else if (!strcasecmp(activatedby, "caller"))
02554          ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
02555       else if (!strcasecmp(activatedby, "callee"))
02556          ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
02557       else if (!strcasecmp(activatedby, "both"))
02558          ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
02559       else {
02560          ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
02561             " must be 'caller', or 'callee', or 'both'\n", var->name);
02562          continue;
02563       }
02564 
02565       ast_register_feature(feature);
02566          
02567       if (option_verbose >= 1)
02568          ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);  
02569    }   
02570    ast_config_destroy(cfg);
02571 
02572    /* Remove the old parking extension */
02573    if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
02574       if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar))
02575             notify_metermaids(old_parking_ext, old_parking_con);
02576       if (option_debug)
02577          ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
02578    }
02579    
02580    if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
02581       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
02582       return -1;
02583    }
02584    res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
02585    if (parkaddhints)
02586       park_add_hints(parking_con, parking_start, parking_stop);
02587    if (!res)
02588       notify_metermaids(ast_parking_ext(), parking_con);
02589    return res;
02590 
02591 }

static int load_module ( void   )  [static]

Definition at line 2598 of file res_features.c.

References ast_cli_register_multiple(), ast_devstate_prov_add(), ast_manager_register, ast_manager_register2(), ast_pthread_create, ast_register_application(), cli_features, descrip, descrip2, do_parking_thread(), EVENT_FLAG_CALL, load_config(), manager_park(), manager_parking_status(), mandescr_park, metermaidstate(), park_call_exec(), park_exec(), parkcall, parkedcall, parking_con, parking_ext, parking_thread, synopsis, and synopsis2.

02599 {
02600    int res;
02601    
02602    memset(parking_ext, 0, sizeof(parking_ext));
02603    memset(parking_con, 0, sizeof(parking_con));
02604 
02605    if ((res = load_config()))
02606       return res;
02607    ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
02608    ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
02609    res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
02610    if (!res)
02611       res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
02612    if (!res) {
02613       ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" );
02614       ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
02615          "Park a channel", mandescr_park); 
02616    }
02617 
02618    res |= ast_devstate_prov_add("Park", metermaidstate);
02619 
02620    return res;
02621 }

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

Definition at line 2278 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(), astman_send_error(), and s.

Referenced by load_module().

02279 {
02280    const char *channel = astman_get_header(m, "Channel");
02281    const char *channel2 = astman_get_header(m, "Channel2");
02282    const char *timeout = astman_get_header(m, "Timeout");
02283    char buf[BUFSIZ];
02284    int to = 0;
02285    int res = 0;
02286    int parkExt = 0;
02287    struct ast_channel *ch1, *ch2;
02288 
02289    if (ast_strlen_zero(channel)) {
02290       astman_send_error(s, m, "Channel not specified");
02291       return 0;
02292    }
02293 
02294    if (ast_strlen_zero(channel2)) {
02295       astman_send_error(s, m, "Channel2 not specified");
02296       return 0;
02297    }
02298 
02299    ch1 = ast_get_channel_by_name_locked(channel);
02300    if (!ch1) {
02301       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
02302       astman_send_error(s, m, buf);
02303       return 0;
02304    }
02305 
02306    ch2 = ast_get_channel_by_name_locked(channel2);
02307    if (!ch2) {
02308       snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
02309       astman_send_error(s, m, buf);
02310       ast_channel_unlock(ch1);
02311       return 0;
02312    }
02313 
02314    if (!ast_strlen_zero(timeout)) {
02315       sscanf(timeout, "%d", &to);
02316    }
02317 
02318    res = ast_masq_park_call(ch1, ch2, to, &parkExt);
02319    if (!res) {
02320       ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
02321       astman_send_ack(s, m, "Park successful");
02322    } else {
02323       astman_send_error(s, m, "Park failure");
02324    }
02325 
02326    ast_channel_unlock(ch1);
02327    ast_channel_unlock(ch2);
02328 
02329    return 0;
02330 }

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

Dump lot status.

Definition at line 2231 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, parkinglot, parkeduser::parkingnum, parkeduser::parkingtime, parkeduser::peername, RESULT_SUCCESS, s, S_OR, and parkeduser::start.

Referenced by load_module().

02232 {
02233    struct parkeduser *cur;
02234    const char *id = astman_get_header(m, "ActionID");
02235    char idText[256] = "";
02236 
02237    if (!ast_strlen_zero(id))
02238       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02239 
02240    astman_send_ack(s, m, "Parked calls will follow");
02241 
02242    ast_mutex_lock(&parking_lock);
02243 
02244    for (cur = parkinglot; cur; cur = cur->next) {
02245       astman_append(s, "Event: ParkedCall\r\n"
02246          "Exten: %d\r\n"
02247          "Channel: %s\r\n"
02248          "From: %s\r\n"
02249          "Timeout: %ld\r\n"
02250          "CallerID: %s\r\n"
02251          "CallerIDName: %s\r\n"
02252          "%s"
02253          "\r\n",
02254          cur->parkingnum, cur->chan->name, cur->peername,
02255          (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
02256          S_OR(cur->chan->cid.cid_num, ""),   /* XXX in other places it is <unknown> */
02257          S_OR(cur->chan->cid.cid_name, ""),
02258          idText);
02259    }
02260 
02261    astman_append(s,
02262       "Event: ParkedCallsComplete\r\n"
02263       "%s"
02264       "\r\n",idText);
02265 
02266    ast_mutex_unlock(&parking_lock);
02267 
02268    return RESULT_SUCCESS;
02269 }

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

metermaids callback from devicestate.c

Definition at line 284 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().

00285 {
00286    int res = AST_DEVICE_INVALID;
00287    char *context = ast_strdupa(data);
00288    char *exten;
00289 
00290    exten = strsep(&context, "@");
00291    if (!context)
00292       return res;
00293    
00294    if (option_debug > 3)
00295       ast_log(LOG_DEBUG, "Checking state of exten %s in context %s\n", exten, context);
00296 
00297    res = ast_exists_extension(NULL, context, exten, 1, NULL);
00298 
00299    if (!res)
00300       return AST_DEVICE_NOT_INUSE;
00301    else
00302       return AST_DEVICE_INUSE;
00303 }

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

Notify metermaids that we've changed an extension.

Definition at line 273 of file res_features.c.

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

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

00274 {
00275    if (option_debug > 3)
00276       ast_log(LOG_DEBUG, "Notification of state change to metermaids %s@%s\n", exten, context);
00277 
00278    /* Send notification to devicestate subsystem */
00279    ast_device_state_changed("park:%s@%s", exten, context);
00280    return;
00281 }

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

Add parking hints for all defined parking lots.

Definition at line 2369 of file res_features.c.

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

02370 {
02371    int numext;
02372    char device[AST_MAX_EXTENSION];
02373    char exten[10];
02374 
02375    for (numext = start; numext <= stop; numext++) {
02376       snprintf(exten, sizeof(exten), "%d", numext);
02377       snprintf(device, sizeof(device), "park:%s@%s", exten, context);
02378       ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
02379    }
02380 }

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

Park a call.

Definition at line 1968 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_module_user::chan, ast_channel::exten, orig_exten(), park_call_full(), and ast_channel::priority.

Referenced by load_module().

01969 {
01970    /* Cache the original channel name in case we get masqueraded in the middle
01971     * of a park--it is still theoretically possible for a transfer to happen before
01972     * we get here, but it is _really_ unlikely */
01973    char *orig_chan_name = ast_strdupa(chan->name);
01974    char orig_exten[AST_MAX_EXTENSION];
01975    int orig_priority = chan->priority;
01976 
01977    /* Data is unused at the moment but could contain a parking
01978       lot context eventually */
01979    int res = 0;
01980    struct ast_module_user *u;
01981 
01982    u = ast_module_user_add(chan);
01983 
01984    ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten));
01985 
01986    /* Setup the exten/priority to be s/1 since we don't know
01987       where this call should return */
01988    strcpy(chan->exten, "s");
01989    chan->priority = 1;
01990    /* Answer if call is not up */
01991    if (chan->_state != AST_STATE_UP)
01992       res = ast_answer(chan);
01993    /* Sleep to allow VoIP streams to settle down */
01994    if (!res)
01995       res = ast_safe_sleep(chan, 1000);
01996    /* Park the call */
01997    if (!res) {
01998       res = park_call_full(chan, chan, 0, NULL, orig_chan_name);
01999       /* Continue on in the dialplan */
02000       if (res == 1) {
02001          ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
02002          chan->priority = orig_priority;
02003          res = 0;
02004       } else if (!res)
02005          res = AST_PBX_KEEPALIVE;
02006    }
02007 
02008    ast_module_user_remove(u);
02009 
02010    return res;
02011 }

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 305 of file res_features.c.

References adsi_announce_park(), ast_channel::appl, ast_add_extension2(), ast_adsi_available(), ast_adsi_unload_session(), ast_bridged_channel(), ast_calloc, AST_CHANNEL_NAME, ast_channel_unlock, ast_clear_flag, ast_context_create(), ast_context_find(), AST_CONTROL_HOLD, ast_exists_extension(), AST_FLAG_MASQ_NOSTREAM, ast_free, ast_get_channel_by_name_locked(), ast_indicate_data(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_say_digits(), ast_set_flag, ast_strlen_zero(), ast_verbose(), ast_channel::context, ast_channel::data, EVENT_FLAG_CALL, ast_channel::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, parkinglot, parkeduser::parkingnum, pbx_builtin_getvar_helper(), ast_channel::priority, S_OR, strdup, ast_channel::tech, ast_channel_tech::type, and VERBOSE_PREFIX_2.

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

00306 {
00307    struct parkeduser *pu, *cur;
00308    int i, x = -1, parking_range;
00309    struct ast_context *con;
00310    const char *parkingexten;
00311    
00312    /* Allocate memory for parking data */
00313    if (!(pu = ast_calloc(1, sizeof(*pu)))) 
00314       return -1;
00315 
00316    /* Lock parking lot */
00317    ast_mutex_lock(&parking_lock);
00318    /* Check for channel variable PARKINGEXTEN */
00319    parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN");
00320    if (!ast_strlen_zero(parkingexten)) {
00321       /*!\note The API forces us to specify a numeric parking slot, even
00322        * though the architecture would tend to support non-numeric extensions
00323        * (as are possible with SIP, for example).  Hence, we enforce that
00324        * limitation here.  If extout was not numeric, we could permit
00325        * arbitrary non-numeric extensions.
00326        */
00327       if (sscanf(parkingexten, "%d", &x) != 1 || x < 0) {
00328          ast_log(LOG_WARNING, "PARKINGEXTEN does not indicate a valid parking slot: '%s'.\n", parkingexten);
00329          ast_mutex_unlock(&parking_lock);
00330          free(pu);
00331          return 1;   /* Continue execution if possible */
00332       }
00333       snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x);
00334 
00335       if (ast_exists_extension(NULL, parking_con, pu->parkingexten, 1, NULL)) {
00336          ast_mutex_unlock(&parking_lock);
00337          free(pu);
00338          ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con);
00339          return 1;   /* Continue execution if possible */
00340       }
00341    } else {
00342       /* Select parking space within range */
00343       parking_range = parking_stop - parking_start+1;
00344       for (i = 0; i < parking_range; i++) {
00345          x = (i + parking_offset) % parking_range + parking_start;
00346          cur = parkinglot;
00347          while(cur) {
00348             if (cur->parkingnum == x) 
00349                break;
00350             cur = cur->next;
00351          }
00352          if (!cur)
00353             break;
00354       }
00355 
00356       if (!(i < parking_range)) {
00357          ast_log(LOG_WARNING, "No more parking spaces\n");
00358          free(pu);
00359          ast_mutex_unlock(&parking_lock);
00360          return -1;
00361       }
00362       /* Set pointer for next parking */
00363       if (parkfindnext) 
00364          parking_offset = x - parking_start + 1;
00365       snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x);
00366    }
00367    
00368    chan->appl = "Parked Call";
00369    chan->data = NULL; 
00370 
00371    pu->chan = chan;
00372    
00373    /* Put the parked channel on hold if we have two different channels */
00374    if (chan != peer) {
00375       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
00376          S_OR(parkmohclass, NULL),
00377          !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
00378    }
00379    
00380    pu->start = ast_tvnow();
00381    pu->parkingnum = x;
00382    pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
00383    if (extout)
00384       *extout = x;
00385 
00386    if (peer) { 
00387       /* This is so ugly that it hurts, but implementing get_base_channel() on local channels
00388          could have ugly side effects.  We could have transferer<->local,1<->local,2<->parking
00389          and we need the callback name to be that of transferer.  Since local,1/2 have the same
00390          name we can be tricky and just grab the bridged channel from the other side of the local
00391       */
00392       if (!strcasecmp(peer->tech->type, "Local")) {
00393          struct ast_channel *tmpchan, *base_peer;
00394          char other_side[AST_CHANNEL_NAME];
00395          char *c;
00396          ast_copy_string(other_side, peer->name, sizeof(other_side));
00397          if ((c = strrchr(other_side, ','))) {
00398             *++c = '1';
00399          }
00400          if ((tmpchan = ast_get_channel_by_name_locked(other_side))) {
00401             if ((base_peer = ast_bridged_channel(tmpchan))) {
00402                ast_copy_string(pu->peername, base_peer->name, sizeof(pu->peername));
00403             }
00404             ast_channel_unlock(tmpchan);
00405          }
00406       } else {
00407          ast_copy_string(pu->peername, peer->name, sizeof(pu->peername));
00408       }
00409    }
00410 
00411    /* Remember what had been dialed, so that if the parking
00412       expires, we try to come back to the same place */
00413    ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context));
00414    ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten));
00415    pu->priority = chan->macropriority ? chan->macropriority : chan->priority;
00416    pu->next = parkinglot;
00417    parkinglot = pu;
00418 
00419    /* If parking a channel directly, don't quiet yet get parking running on it */
00420    if (peer == chan) 
00421       pu->notquiteyet = 1;
00422    ast_mutex_unlock(&parking_lock);
00423    /* Wake up the (presumably select()ing) thread */
00424    pthread_kill(parking_thread, SIGURG);
00425    if (option_verbose > 1) 
00426       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));
00427 
00428    manager_event(EVENT_FLAG_CALL, "ParkedCall",
00429       "Exten: %s\r\n"
00430       "Channel: %s\r\n"
00431       "From: %s\r\n"
00432       "Timeout: %ld\r\n"
00433       "CallerID: %s\r\n"
00434       "CallerIDName: %s\r\n",
00435       pu->parkingexten, pu->chan->name, peer ? peer->name : "",
00436       (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
00437       S_OR(pu->chan->cid.cid_num, "<unknown>"),
00438       S_OR(pu->chan->cid.cid_name, "<unknown>")
00439       );
00440 
00441    if (peer && adsipark && ast_adsi_available(peer)) {
00442       adsi_announce_park(peer, pu->parkingexten);  /* Only supports parking numbers */
00443       ast_adsi_unload_session(peer);
00444    }
00445 
00446    con = ast_context_find(parking_con);
00447    if (!con) 
00448       con = ast_context_create(NULL, parking_con, registrar);
00449    if (!con)   /* Still no context? Bad */
00450       ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
00451    /* Tell the peer channel the number of the parking space */
00452    if (peer && (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 */
00453       /* Make sure we don't start saying digits to the channel being parked */
00454       ast_set_flag(peer, AST_FLAG_MASQ_NOSTREAM);
00455       ast_say_digits(peer, pu->parkingnum, "", peer->language);
00456       ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM);
00457    }
00458    if (con) {
00459       if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), ast_free, registrar))
00460          notify_metermaids(pu->parkingexten, parking_con);
00461    }
00462    if (pu->notquiteyet) {
00463       /* Wake up parking thread if we're really done */
00464       ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
00465          S_OR(parkmohclass, NULL),
00466          !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
00467       pu->notquiteyet = 0;
00468       pthread_kill(parking_thread, SIGURG);
00469    }
00470    return 0;
00471 }

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 2014 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(), parkeduser::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, config, courtesytone, EVENT_FLAG_CALL, free, LOG_WARNING, manager_event(), parkeduser::next, notify_metermaids(), option_verbose, parkedplay, parking_con, parkeduser::parkingexten, parkinglot, parkeduser::parkingnum, pbx_builtin_setvar_helper(), S_OR, and VERBOSE_PREFIX_3.

Referenced by load_module().

02015 {
02016    int res = 0;
02017    struct ast_module_user *u;
02018    struct ast_channel *peer=NULL;
02019    struct parkeduser *pu, *pl=NULL;
02020    struct ast_context *con;
02021 
02022    int park;
02023    struct ast_bridge_config config;
02024 
02025    if (!data) {
02026       ast_log(LOG_WARNING, "Parkedcall requires an argument (extension number)\n");
02027       return -1;
02028    }
02029    
02030    u = ast_module_user_add(chan);
02031 
02032    park = atoi((char *)data);
02033    ast_mutex_lock(&parking_lock);
02034    pu = parkinglot;
02035    while(pu) {
02036       if (pu->parkingnum == park) {
02037          if (pl)
02038             pl->next = pu->next;
02039          else
02040             parkinglot = pu->next;
02041          break;
02042       }
02043       pl = pu;
02044       pu = pu->next;
02045    }
02046    ast_mutex_unlock(&parking_lock);
02047    if (pu) {
02048       peer = pu->chan;
02049       con = ast_context_find(parking_con);
02050       if (con) {
02051          if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
02052             ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
02053          else
02054             notify_metermaids(pu->parkingexten, parking_con);
02055       } else
02056          ast_log(LOG_WARNING, "Whoa, no parking context?\n");
02057 
02058       manager_event(EVENT_FLAG_CALL, "UnParkedCall",
02059          "Exten: %s\r\n"
02060          "Channel: %s\r\n"
02061          "From: %s\r\n"
02062          "CallerID: %s\r\n"
02063          "CallerIDName: %s\r\n",
02064          pu->parkingexten, pu->chan->name, chan->name,
02065          S_OR(pu->chan->cid.cid_num, "<unknown>"),
02066          S_OR(pu->chan->cid.cid_name, "<unknown>")
02067          );
02068 
02069       free(pu);
02070    }
02071    /* JK02: it helps to answer the channel if not already up */
02072    if (chan->_state != AST_STATE_UP)
02073       ast_answer(chan);
02074 
02075    if (peer) {
02076       /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
02077       
02078       if (!ast_strlen_zero(courtesytone)) {
02079          int error = 0;
02080          ast_indicate(peer, AST_CONTROL_UNHOLD);
02081          if (parkedplay == 0) {
02082             error = ast_stream_and_wait(chan, courtesytone, chan->language, "");
02083          } else if (parkedplay == 1) {
02084             error = ast_stream_and_wait(peer, courtesytone, chan->language, "");
02085          } else if (parkedplay == 2) {
02086             if (!ast_streamfile(chan, courtesytone, chan->language) &&
02087                   !ast_streamfile(peer, courtesytone, chan->language)) {
02088                /*! \todo XXX we would like to wait on both! */
02089                res = ast_waitstream(chan, "");
02090                if (res >= 0)
02091                   res = ast_waitstream(peer, "");
02092                if (res < 0)
02093                   error = 1;
02094             }
02095                         }
02096          if (error) {
02097             ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
02098             ast_hangup(peer);
02099             ast_module_user_remove(u);
02100             return -1;
02101          }
02102       } else
02103          ast_indicate(peer, AST_CONTROL_UNHOLD); 
02104 
02105       res = ast_channel_make_compatible(chan, peer);
02106       if (res < 0) {
02107          ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
02108          ast_hangup(peer);
02109          ast_module_user_remove(u);
02110          return -1;
02111       }
02112       /* This runs sorta backwards, since we give the incoming channel control, as if it
02113          were the person called. */
02114       if (option_verbose > 2) 
02115          ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
02116 
02117       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
02118       ast_cdr_setdestchan(chan->cdr, peer->name);
02119       memset(&config, 0, sizeof(struct ast_bridge_config));
02120       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
02121       ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
02122       res = ast_bridge_call(chan, peer, &config);
02123 
02124       pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
02125       ast_cdr_setdestchan(chan->cdr, peer->name);
02126 
02127       /* Simulate the PBX hanging up */
02128       if (res != AST_PBX_NO_HANGUP_PEER)
02129          ast_hangup(peer);
02130       ast_module_user_remove(u);
02131       return res;
02132    } else {
02133       /*! \todo XXX Play a message XXX */
02134       if (ast_stream_and_wait(chan, "pbx-invalidpark", chan->language, ""))
02135          ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
02136       if (option_verbose > 2) 
02137          ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
02138       res = -1;
02139    }
02140 
02141    ast_module_user_remove(u);
02142 
02143    return res;
02144 }

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

Definition at line 1771 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().

01772 {
01773    manager_event(EVENT_FLAG_CALL, s,
01774       "Exten: %s\r\n"
01775       "Channel: %s\r\n"
01776       "CallerID: %s\r\n"
01777       "CallerIDName: %s\r\n\r\n",
01778       parkingexten, 
01779       chan->name,
01780       S_OR(chan->cid.cid_num, "<unknown>"),
01781       S_OR(chan->cid.cid_name, "<unknown>")
01782       );
01783 }

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

Find the context for the transfer.

Definition at line 680 of file res_features.c.

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

Referenced by builtin_blindtransfer(), and do_atxfer().

00681 {
00682         const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
00683         if (ast_strlen_zero(s))
00684                 s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
00685         if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */
00686                 s = transferer->macrocontext;
00687         if (ast_strlen_zero(s))
00688                 s = transferer->context;
00689         return s;  
00690 }

static int reload ( void   )  [static]

Definition at line 2593 of file res_features.c.

References load_config().

02594 {
02595    return load_config();
02596 }

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

Definition at line 1151 of file res_features.c.

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

01152 {
01153    int x, res = -1;
01154 
01155    ast_rwlock_wrlock(&features_lock);
01156    for (x = 0; x < FEATURES_COUNT; x++) {
01157       if (strcasecmp(builtin_features[x].sname, name))
01158          continue;
01159 
01160       ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
01161       res = 0;
01162       break;
01163    }
01164    ast_rwlock_unlock(&features_lock);
01165 
01166    return res;
01167 }

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 180 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().

00181 {
00182    ast_copy_string(chan->context, context, sizeof(chan->context));
00183    ast_copy_string(chan->exten, ext, sizeof(chan->exten));
00184    chan->priority = pri;
00185 }

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

Definition at line 1237 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, builtin_features, config, ast_call_feature::feature_mask, FEATURES_COUNT, find_dynamic_feature(), pbx_builtin_getvar_helper(), and strsep().

Referenced by ast_bridge_call().

01238 {
01239    int x;
01240    
01241    ast_clear_flag(config, AST_FLAGS_ALL);
01242 
01243    ast_rwlock_rdlock(&features_lock);
01244    for (x = 0; x < FEATURES_COUNT; x++) {
01245       if (!ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
01246          continue;
01247 
01248       if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
01249          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
01250 
01251       if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
01252          ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
01253    }
01254    ast_rwlock_unlock(&features_lock);
01255    
01256    if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
01257       const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
01258 
01259       if (dynamic_features) {
01260          char *tmp = ast_strdupa(dynamic_features);
01261          char *tok;
01262          struct ast_call_feature *feature;
01263 
01264          /* while we have a feature */
01265          while ((tok = strsep(&tmp, "#"))) {
01266             AST_LIST_LOCK(&feature_list);
01267             if ((feature = find_dynamic_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
01268                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
01269                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
01270                if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
01271                   ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
01272             }
01273             AST_LIST_UNLOCK(&feature_list);
01274          }
01275       }
01276    }
01277 }

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 535 of file res_features.c.

References FEATURE_SENSE_PEER.

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

00537 {
00538    if (sense == FEATURE_SENSE_PEER) {
00539       *caller = peer;
00540       *callee = chan;
00541    } else {
00542       *callee = peer;
00543       *caller = chan;
00544    }
00545 }

static int unload_module ( void   )  [static]

Definition at line 2624 of file res_features.c.

References ast_cli_unregister_multiple(), ast_devstate_prov_del(), ast_manager_unregister(), ast_module_user_hangup_all, ast_unregister_application(), cli_features, parkcall, and parkedcall.

02625 {
02626    ast_module_user_hangup_all();
02627 
02628    ast_manager_unregister("ParkedCalls");
02629    ast_manager_unregister("Park");
02630    ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
02631    ast_unregister_application(parkcall);
02632    ast_devstate_prov_del("Park");
02633    return ast_unregister_application(parkedcall);
02634 }

static void unmap_features ( void   )  [static]

Definition at line 1141 of file res_features.c.

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

01142 {
01143    int x;
01144 
01145    ast_rwlock_wrlock(&features_lock);
01146    for (x = 0; x < FEATURES_COUNT; x++)
01147       strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
01148    ast_rwlock_unlock(&features_lock);
01149 }


Variable Documentation

int adsipark [static]

Definition at line 103 of file res_features.c.

Referenced by load_config().

int atxfernoanswertimeout [static]

Definition at line 108 of file res_features.c.

Referenced by load_config().

struct ast_call_feature builtin_features[] [static]

Definition at line 1010 of file res_features.c.

Referenced by ast_feature_interpret(), ast_feature_request_and_dial(), handle_showfeatures(), remap_feature(), set_config_flags(), and unmap_features().

struct ast_cli_entry cli_features[] [static]

Definition at line 2220 of file res_features.c.

struct ast_cli_entry cli_show_features_deprecated [static]

Initial value:

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

Definition at line 2215 of file res_features.c.

char courtesytone[256] [static]

Courtesy tone

Definition at line 95 of file res_features.c.

Referenced by load_config(), and park_exec().

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 115 of file res_features.c.

char* descrip2 [static]

Definition at line 125 of file res_features.c.

int featuredigittimeout [static]

Definition at line 106 of file res_features.c.

char mandescr_park[] [static]

Definition at line 2271 of file res_features.c.

Referenced by load_module().

struct ast_app* monitor_app = NULL [static]

Definition at line 136 of file res_features.c.

Referenced by ast_bridge_call(), and builtin_automonitor().

int monitor_ok = 1 [static]

Definition at line 137 of file res_features.c.

int parkaddhints = 0 [static]

Add parking hints automatically

Definition at line 85 of file res_features.c.

Referenced by load_config().

char* parkcall = "Park" [static]

Definition at line 121 of file res_features.c.

Referenced by load_module(), and unload_module().

char* parkedcall = "ParkedCall" [static]

Definition at line 83 of file res_features.c.

Referenced by load_module(), and unload_module().

int parkedplay = 0 [static]

Who to play the courtesy tone to

Definition at line 96 of file res_features.c.

Referenced by park_exec().

int parkfindnext [static]

Definition at line 101 of file res_features.c.

Referenced by load_config().

char parking_con[AST_MAX_EXTENSION] [static]

Context for which parking is made accessible

Definition at line 87 of file res_features.c.

Referenced by handle_showfeatures(), load_config(), load_module(), and park_exec().

char parking_con_dial[AST_MAX_EXTENSION] [static]

Context for dialback for parking (KLUDGE)

Definition at line 88 of file res_features.c.

Referenced by load_config().

char parking_ext[AST_MAX_EXTENSION] [static]

Extension you type to park the call

Definition at line 89 of file res_features.c.

Referenced by handle_showfeatures(), load_config(), and load_module().

int parking_offset [static]

Definition at line 100 of file res_features.c.

int parking_start [static]

First available extension for parking

Definition at line 92 of file res_features.c.

Referenced by handle_showfeatures(), and load_config().

int parking_stop [static]

Last available extension for parking

Definition at line 93 of file res_features.c.

Referenced by handle_showfeatures(), and load_config().

pthread_t parking_thread [static]

Definition at line 158 of file res_features.c.

Referenced by load_module().

struct parkeduser* parkinglot [static]

Definition at line 154 of file res_features.c.

Referenced by do_parking_thread(), handle_parkedcalls(), manager_parking_status(), park_call_full(), and park_exec().

int parkingtime = DEFAULT_PARK_TIME [static]

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

Definition at line 86 of file res_features.c.

char parkmohclass[MAX_MUSICCLASS] [static]

Music class used for parking

Definition at line 91 of file res_features.c.

Referenced by load_config().

char pickup_ext[AST_MAX_EXTENSION] [static]

Call pickup extension

Definition at line 90 of file res_features.c.

Referenced by load_config().

char* registrar = "res_features" [static]

Registrar for operations

Definition at line 110 of file res_features.c.

char showfeatures_help[] [static]

Initial value:

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

Definition at line 2183 of file res_features.c.

char showparked_help[] [static]

Initial value:

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

Definition at line 2211 of file res_features.c.

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

Definition at line 113 of file res_features.c.

char* synopsis2 = "Park yourself" [static]

Definition at line 123 of file res_features.c.

int transferdigittimeout [static]

Definition at line 105 of file res_features.c.

Referenced by load_config().

char xferfailsound[256] [static]

Call transfer failure sound

Definition at line 98 of file res_features.c.

Referenced by load_config().

char xfersound[256] [static]

Call transfer sound

Definition at line 97 of file res_features.c.

Referenced by load_config().


Generated on Mon Nov 24 15:34:48 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7